#!/usr/bin/env bash # Run the mars_nwe AFP Linux smoke helpers as one collectable report. # # The helper intentionally keeps the password out of the printed command lines, # captures AFP server diagnostics from the mars_nwe log while the probes run, and # appends Linux xattr checks for the tested Unix file. set -u SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) SERVER="MARS" USER_NAME="SUPERVISOR" PASSWORD="" NETWARE_PATH="SYS:PUBLIC/pmdflts.ini" UNIX_PATH="/var/mars_nwe/SYS/public/pmdflts.ini" LOG_FILE="/var/log/mars_nwe/nw.log" OUT_FILE="" FINDER_TYPE="TEXT" FINDER_CREATOR="MARS" TIMESTAMP_EPOCH="1700000000" READONLY_USER="" READONLY_PASSWORD="" READONLY_NO_PASSWORD=0 PREPARE_READONLY_RIGHTS=0 READONLY_RIGHTS="[RF]" KEEP_GOING=1 CAPTURE_LOG=1 usage() { cat <&2 usage >&2 exit 2 ;; esac done if [ -z "$PASSWORD" ]; then echo "Missing password. Use -P PASSWORD." >&2 usage >&2 exit 2 fi case "$FINDER_TYPE" in ???? ) ;; *) echo "--type must be exactly four characters" >&2; exit 2 ;; esac case "$FINDER_CREATOR" in ???? ) ;; *) echo "--creator must be exactly four characters" >&2; exit 2 ;; esac DIR_PATH=$NETWARE_PATH case "$NETWARE_PATH" in */*) DIR_PATH=${NETWARE_PATH%/*} ;; esac REPORT_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-afp-smoke.XXXXXX") LOG_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-afp-log.XXXXXX") LOG_PID="" FAILURES=0 RIGHTS_PREPARED=0 cleanup() { if [ "$RIGHTS_PREPARED" -eq 1 ] && [ -n "$READONLY_USER" ]; then nwrevoke -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ -o "$READONLY_USER" -t 1 "$NETWARE_PATH" >/dev/null 2>&1 || true RIGHTS_PREPARED=0 fi if [ -n "$LOG_PID" ] && kill -0 "$LOG_PID" 2>/dev/null; then kill "$LOG_PID" 2>/dev/null || true wait "$LOG_PID" 2>/dev/null || true fi rm -f "$LOG_TMP" } trap cleanup EXIT INT TERM emit() { printf '%s\n' "$*" | tee -a "$REPORT_TMP" } section() { emit "" emit "## $*" } run_cmd() { local label=$1 local printable=$2 shift 2 section "$label" emit "\$ $printable" "$@" 2>&1 | tee -a "$REPORT_TMP" local status=${PIPESTATUS[0]} emit "[exit=$status]" if [ "$status" -ne 0 ]; then FAILURES=$((FAILURES + 1)) if [ "$KEEP_GOING" -eq 0 ]; then finish_report exit "$status" fi fi } run_optional_cmd() { local label=$1 local printable=$2 shift 2 section "$label" emit "\$ $printable" "$@" 2>&1 | tee -a "$REPORT_TMP" local status=${PIPESTATUS[0]} emit "[exit=$status]" return "$status" } readonly_print_auth() { if [ "$READONLY_NO_PASSWORD" -eq 1 ]; then printf '%s' "-n" else printf '%s' "-P ******" fi } finish_report() { if [ "$CAPTURE_LOG" -eq 1 ]; then sleep 1 if [ -n "$LOG_PID" ] && kill -0 "$LOG_PID" 2>/dev/null; then kill "$LOG_PID" 2>/dev/null || true wait "$LOG_PID" 2>/dev/null || true LOG_PID="" fi section "AFP server log excerpt" if [ -s "$LOG_TMP" ]; then cat "$LOG_TMP" | tee -a "$REPORT_TMP" else emit "No AFP lines captured from $LOG_FILE." fi fi section "Summary" emit "failures=$FAILURES" if [ -n "$OUT_FILE" ]; then cp "$REPORT_TMP" "$OUT_FILE" emit "report=$OUT_FILE" fi } section "mars_nwe AFP smoke suite" emit "date=$(date -Is)" emit "server=$SERVER" emit "user=$USER_NAME" emit "path=$NETWARE_PATH" emit "dir_path=$DIR_PATH" emit "unix_path=$UNIX_PATH" emit "log=$LOG_FILE" emit "finder_type=$FINDER_TYPE" emit "finder_creator=$FINDER_CREATOR" emit "mtime_epoch=$TIMESTAMP_EPOCH" if [ -n "$READONLY_USER" ]; then emit "readonly_user=$READONLY_USER" if [ "$READONLY_NO_PASSWORD" -eq 1 ]; then emit "readonly_auth=no-password" else emit "readonly_auth=password" fi emit "prepare_readonly_rights=$PREPARE_READONLY_RIGHTS" emit "readonly_rights=$READONLY_RIGHTS" fi for helper in \ afp_entry_id_smoke \ afp_file_info_smoke \ afp_dos_name_smoke \ afp_scan_info_smoke \ afp_temp_dir_handle_smoke \ afp_open_file_fork_smoke \ afp_set_file_info_smoke do if [ ! -x "$SCRIPT_DIR/$helper" ]; then emit "Missing executable: $SCRIPT_DIR/$helper" exit 2 fi done if [ "$CAPTURE_LOG" -eq 1 ]; then if [ -r "$LOG_FILE" ]; then # Capture only lines appended while this script is running. tail -n 0 -F "$LOG_FILE" 2>/dev/null | grep --line-buffered AFP >"$LOG_TMP" & LOG_PID=$! sleep 1 else emit "Log file is not readable, disabling log capture: $LOG_FILE" CAPTURE_LOG=0 fi fi COMMON_PRINT="-S $SERVER -U $USER_NAME -P ******" if [ -n "$READONLY_USER" ] && [ "$PREPARE_READONLY_RIGHTS" -eq 1 ]; then # Establish a conservative explicit trustee assignment for the negative # metadata-write probes. The final nwrevoke below removes only this # explicit assignment and returns the object to its inherited rights. run_optional_cmd \ "Prepare readonly trustee cleanup" \ "nwrevoke -S $SERVER -U $USER_NAME -P ****** -o $READONLY_USER -t 1 '$NETWARE_PATH'" \ nwrevoke -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ -o "$READONLY_USER" -t 1 "$NETWARE_PATH" || true run_cmd \ "Prepare readonly trustee rights" \ "nwgrant -S $SERVER -U $USER_NAME -P ****** -o $READONLY_USER -t 1 -r '$READONLY_RIGHTS' '$NETWARE_PATH'" \ nwgrant -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ -o "$READONLY_USER" -t 1 -r "$READONLY_RIGHTS" "$NETWARE_PATH" RIGHTS_PREPARED=1 fi run_cmd \ "AFP Entry ID From Path Name" \ "./afp_entry_id_smoke $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_entry_id_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP Entry ID From NetWare Handle" \ "./afp_entry_id_smoke --from-handle $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_entry_id_smoke" --from-handle -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP 2.0 Get File Information" \ "./afp_file_info_smoke $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP Get DOS Name From Entry ID" \ "./afp_dos_name_smoke $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_dos_name_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP 2.0 Scan File Information" \ "./afp_scan_info_smoke $COMMON_PRINT '$DIR_PATH'" \ "$SCRIPT_DIR/afp_scan_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$DIR_PATH" run_cmd \ "AFP Alloc Temporary Directory Handle" \ "./afp_temp_dir_handle_smoke $COMMON_PRINT '$DIR_PATH'" \ "$SCRIPT_DIR/afp_temp_dir_handle_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$DIR_PATH" run_cmd \ "AFP Open File Fork" \ "./afp_open_file_fork_smoke $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_open_file_fork_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP Open File Fork write access rejected" \ "./afp_open_file_fork_smoke --expect-completion 0x84 --access 0x02 $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_open_file_fork_smoke" --expect-completion 0x84 --access 0x02 \ -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP Open File Fork resource fork rejected" \ "./afp_open_file_fork_smoke --expect-completion 0x9c --fork 1 $COMMON_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_open_file_fork_smoke" --expect-completion 0x9c --fork 1 \ -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" run_cmd \ "AFP Set File Information FinderInfo" \ "./afp_set_file_info_smoke $COMMON_PRINT --finder-info-only --type '$FINDER_TYPE' --creator '$FINDER_CREATOR' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --finder-info-only --type "$FINDER_TYPE" --creator "$FINDER_CREATOR" "$NETWARE_PATH" run_cmd \ "AFP Set File Information FinderInfo legacy" \ "./afp_set_file_info_smoke $COMMON_PRINT --afp09 --finder-info-only --type '$FINDER_TYPE' --creator '$FINDER_CREATOR' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --afp09 --finder-info-only --type "$FINDER_TYPE" --creator "$FINDER_CREATOR" "$NETWARE_PATH" run_cmd \ "AFP Set File Information Hidden" \ "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --hidden '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --attributes-only --hidden "$NETWARE_PATH" run_cmd \ "AFP Set File Information Clear Hidden" \ "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --clear-hidden '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --attributes-only --clear-hidden "$NETWARE_PATH" run_cmd \ "AFP Set File Information Hidden legacy" \ "./afp_set_file_info_smoke $COMMON_PRINT --afp09 --attributes-only --hidden '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --afp09 --attributes-only --hidden "$NETWARE_PATH" run_cmd \ "AFP Set File Information Clear Hidden legacy" \ "./afp_set_file_info_smoke $COMMON_PRINT --afp09 --attributes-only --clear-hidden '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --afp09 --attributes-only --clear-hidden "$NETWARE_PATH" run_cmd \ "AFP Set File Information Access Timestamp" \ "./afp_set_file_info_smoke $COMMON_PRINT --access-time-only --access-time-epoch '$TIMESTAMP_EPOCH' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --access-time-only --access-time-epoch "$TIMESTAMP_EPOCH" "$NETWARE_PATH" run_cmd \ "AFP Set File Information Create Timestamp" \ "./afp_set_file_info_smoke $COMMON_PRINT --create-time-only --create-time-epoch '$TIMESTAMP_EPOCH' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --create-time-only --create-time-epoch "$TIMESTAMP_EPOCH" "$NETWARE_PATH" run_cmd \ "AFP Set File Information Modify Timestamp" \ "./afp_set_file_info_smoke $COMMON_PRINT --timestamp-only --mtime-epoch '$TIMESTAMP_EPOCH' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --timestamp-only --mtime-epoch "$TIMESTAMP_EPOCH" "$NETWARE_PATH" run_cmd \ "AFP Set File Information Backup Timestamp" \ "./afp_set_file_info_smoke $COMMON_PRINT --backup-time-only --backup-time-epoch '$TIMESTAMP_EPOCH' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --backup-time-only --backup-time-epoch "$TIMESTAMP_EPOCH" "$NETWARE_PATH" run_cmd \ "AFP Set File Information System" \ "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --system '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --attributes-only --system "$NETWARE_PATH" run_cmd \ "AFP Set File Information Clear System" \ "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --clear-system '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --attributes-only --clear-system "$NETWARE_PATH" run_cmd \ "AFP Set File Information Archive" \ "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --archive '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --attributes-only --archive "$NETWARE_PATH" run_cmd \ "AFP Set File Information Clear Archive" \ "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --clear-archive '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ --attributes-only --clear-archive "$NETWARE_PATH" if [ -n "$READONLY_USER" ]; then READONLY_PRINT="-S $SERVER -U $READONLY_USER $(readonly_print_auth)" if [ "$READONLY_NO_PASSWORD" -eq 1 ]; then READONLY_AUTH_ARGS=(-n) else READONLY_AUTH_ARGS=(-P "$READONLY_PASSWORD") fi run_cmd \ "AFP Get File Information readonly rights" \ "./afp_file_info_smoke --expect-rights-set 0x0100 --expect-rights-clear 0x9200 $READONLY_PRINT '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_file_info_smoke" --expect-rights-set 0x0100 --expect-rights-clear 0x9200 \ -S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" "$NETWARE_PATH" run_cmd \ "AFP metadata Modify rights rejected: FinderInfo" \ "./afp_set_file_info_smoke --expect-completion 0x8c $READONLY_PRINT --finder-info-only --type '$FINDER_TYPE' --creator '$FINDER_CREATOR' '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" --expect-completion 0x8c \ -S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" \ --finder-info-only --type "$FINDER_TYPE" --creator "$FINDER_CREATOR" "$NETWARE_PATH" run_cmd \ "AFP attribute Modify rights rejected: Hidden" \ "./afp_set_file_info_smoke --expect-completion 0x8c $READONLY_PRINT --attributes-only --hidden '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" --expect-completion 0x8c \ -S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" \ --attributes-only --hidden "$NETWARE_PATH" run_cmd \ "AFP metadata Modify rights rejected: System" \ "./afp_set_file_info_smoke --expect-completion 0x8c $READONLY_PRINT --attributes-only --system '$NETWARE_PATH'" \ "$SCRIPT_DIR/afp_set_file_info_smoke" --expect-completion 0x8c \ -S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" \ --attributes-only --system "$NETWARE_PATH" fi if [ -n "$READONLY_USER" ] && [ "$PREPARE_READONLY_RIGHTS" -eq 1 ]; then run_cmd \ "Restore readonly trustee assignment" \ "nwrevoke -S $SERVER -U $USER_NAME -P ****** -o $READONLY_USER -t 1 '$NETWARE_PATH'" \ nwrevoke -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ -o "$READONLY_USER" -t 1 "$NETWARE_PATH" RIGHTS_PREPARED=0 fi if command -v stat >/dev/null 2>&1; then run_cmd \ "Linux stat: AFP Modify Timestamp" \ "stat -c 'mtime_epoch=%Y mtime=%y' '$UNIX_PATH'" \ stat -c 'mtime_epoch=%Y mtime=%y' "$UNIX_PATH" fi if command -v getfattr >/dev/null 2>&1; then run_cmd \ "Linux xattr: AFP FinderInfo" \ "getfattr -n user.org.mars-nwe.afp.finder-info -e hex '$UNIX_PATH'" \ getfattr -n user.org.mars-nwe.afp.finder-info -e hex "$UNIX_PATH" run_optional_cmd \ "Linux xattr: AFP Attributes (optional AFP-only bits)" \ "getfattr -n user.org.mars-nwe.afp.attributes -e hex '$UNIX_PATH'" \ getfattr -n user.org.mars-nwe.afp.attributes -e hex "$UNIX_PATH" || \ emit "AFP-only attributes xattr is absent; this is expected when the tested Hidden/System/Archive bits are stored through the NetWare attribute path." run_cmd \ "Linux xattr: NetWare FileInfo metadata" \ "getfattr -n user.org.mars-nwe.netware.fileinfo -e hex '$UNIX_PATH'" \ getfattr -n user.org.mars-nwe.netware.fileinfo -e hex "$UNIX_PATH" run_cmd \ "Linux xattr: NetWare Archive metadata" \ "getfattr -n user.org.mars-nwe.netware.archive -e hex '$UNIX_PATH'" \ getfattr -n user.org.mars-nwe.netware.archive -e hex "$UNIX_PATH" run_cmd \ "Linux xattr: AFP Entry ID" \ "getfattr -n user.org.mars-nwe.afp.entry-id -e hex '$UNIX_PATH'" \ getfattr -n user.org.mars-nwe.afp.entry-id -e hex "$UNIX_PATH" else section "Linux xattr checks" emit "getfattr not found; install the attr package to collect xattr output." FAILURES=$((FAILURES + 1)) fi finish_report if [ "$FAILURES" -ne 0 ]; then exit 1 fi exit 0