nwconn: route AFP backup timestamps through nwarchive
All checks were successful
Source release / source-package (push) Successful in 48s
All checks were successful
Source release / source-package (push) Successful in 48s
Implement the WebSDK AFP Set File Information Backup Date/Time request bitmap as a narrow metadata write that reuses mars_nwe's existing archive metadata helper instead of adding a new AFP-specific storage path. The AFP Attributes word already maps Archive through the NetWare FILE_ATTR_A path; Backup Date/Time is a separate SetInfo field and belongs in the existing nwarchive.c archive date/time metadata. Include nwarchive.h in nwconn, fill the AFP information record's Backup Date/Time fields from mars_nwe_get_archive_info(), and accept the SetInfo 0x2000 bitmap by calling mars_nwe_set_archive_info() after the normal AFP path resolution and Modify-rights gate. Extend the Linux Set File Information smoke helper with --backup-time-epoch/--backup-time-only, verify the returned 120-byte file information record at offsets 28/30, and have afp_smoke_suite.sh dump user.org.mars-nwe.netware.archive so reports prove that the WebSDK field is stored through the NetWare archive metadata path. Tests: git diff --check; bash -n tests/linux/afp_smoke_suite.sh; gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_set_file_info_smoke.c
This commit is contained in:
17
TODO.md
17
TODO.md
@@ -499,3 +499,20 @@ set/clear as `0x0400`/`0x0000`, and `Archive` set/clear as
|
||||
FinderInfo. Because all tested attribute bits are now stored through the
|
||||
NetWare attribute path, `user.org.mars-nwe.afp.attributes` may be absent in the
|
||||
final xattr dump; this is expected and no longer counts as a smoke failure.
|
||||
|
||||
### AFP Backup Date/Time convergence
|
||||
|
||||
AFP Set File Information now treats the WebSDK `0x2000` request bitmap as the
|
||||
Backup Date/Time field, distinct from the `0x2000` Archive attribute bit inside
|
||||
the Attributes word. The timestamp payload is routed through the existing
|
||||
`mars_nwe_set_archive_info()` helper in `nwarchive.c`, so the data is stored in
|
||||
`org.mars-nwe.netware.archive` together with the NetWare archive date/time
|
||||
metadata rather than in an AFP-specific xattr. AFP Get/Scan File Information
|
||||
merge the stored archive date/time back into the 120-byte AFP information record.
|
||||
|
||||
This keeps the split explicit: Archive as an attribute is `FILE_ATTR_A` in the
|
||||
NetWare attribute store; Backup Date/Time is archive metadata in
|
||||
`nwarchive.c`; FinderInfo remains AFP metadata in `org.mars-nwe.afp.finder-info`.
|
||||
The Linux smoke suite covers `--backup-time-only --backup-time-epoch` and dumps
|
||||
`user.org.mars-nwe.netware.archive` so runtime reports show the underlying
|
||||
mars_nwe storage path.
|
||||
|
||||
40
src/nwconn.c
40
src/nwconn.c
@@ -50,6 +50,7 @@
|
||||
#include "trustee.h"
|
||||
#include "nwatalk.h"
|
||||
#include "nwattrib.h"
|
||||
#include "nwarchive.h"
|
||||
#include "namedos.h"
|
||||
|
||||
int act_pid = 0;
|
||||
@@ -1114,6 +1115,7 @@ static void afp_leaf_name_from_path(uint8 *dst, int dst_len,
|
||||
|
||||
#define AFP_FILE_BITMAP_ATTRIBUTES 0x0100
|
||||
#define AFP_FILE_BITMAP_MODIFY_DATE 0x1000
|
||||
#define AFP_FILE_BITMAP_BACKUP_DATE 0x2000
|
||||
#define AFP_FILE_BITMAP_FINDER_INFO 0x4000
|
||||
#define AFP_ATTR_HIDDEN 0x0200
|
||||
#define AFP_ATTR_SYSTEM 0x0400
|
||||
@@ -1205,8 +1207,18 @@ static int afp_fill_file_info_response(const char *unixname,
|
||||
un_date_2_nw(stbuff.st_atime, response + 22, 1);
|
||||
un_date_2_nw(stbuff.st_mtime, response + 24, 1);
|
||||
un_time_2_nw(stbuff.st_mtime, response + 26, 1);
|
||||
U16_TO_BE16(0, response + 28); /* Backup Date */
|
||||
U16_TO_BE16(0, response + 30); /* Backup Time */
|
||||
{
|
||||
uint16 archive_date = 0;
|
||||
uint16 archive_time = 0;
|
||||
uint8 archive_flags = 0;
|
||||
|
||||
mars_nwe_get_archive_info((char *)unixname, &archive_date, &archive_time,
|
||||
NULL, &archive_flags);
|
||||
U16_TO_BE16((archive_flags & MARS_NWE_ARCHIVE_HAS_DATE) ? archive_date : 0,
|
||||
response + 28);
|
||||
U16_TO_BE16((archive_flags & MARS_NWE_ARCHIVE_HAS_TIME) ? archive_time : 0,
|
||||
response + 30);
|
||||
}
|
||||
|
||||
memset(finder_info, 0, sizeof(finder_info));
|
||||
(void)nwatalk_get_finder_info(unixname, finder_info, sizeof(finder_info));
|
||||
@@ -1388,7 +1400,7 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
}
|
||||
|
||||
if (request_mask & ~(AFP_FILE_BITMAP_ATTRIBUTES | AFP_FILE_BITMAP_MODIFY_DATE |
|
||||
AFP_FILE_BITMAP_FINDER_INFO)) {
|
||||
AFP_FILE_BITMAP_BACKUP_DATE | AFP_FILE_BITMAP_FINDER_INFO)) {
|
||||
XDPRINTF((2,0, "%s rejected: unsupported bitmap vol=%d entry=0x%08x mask=0x%04x path='%s'",
|
||||
call_name, (int)volume_number, request_entry_id, request_mask,
|
||||
visable_data(afp_req + 9, path_len)));
|
||||
@@ -1427,6 +1439,15 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_MODIFY_DATE)
|
||||
data_off += 4;
|
||||
if ((request_mask & AFP_FILE_BITMAP_BACKUP_DATE) && afp_len < data_off + 4) {
|
||||
XDPRINTF((2,0, "%s rejected: short backup timestamp data len=%d data_off=%d",
|
||||
call_name, afp_len, data_off));
|
||||
return(-0x7e);
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_BACKUP_DATE) {
|
||||
needs_afp_metadata_modify = 1;
|
||||
data_off += 4;
|
||||
}
|
||||
if ((request_mask & AFP_FILE_BITMAP_FINDER_INFO) && data_off & 1) data_off++;
|
||||
if (request_mask & AFP_FILE_BITMAP_FINDER_INFO)
|
||||
needs_afp_metadata_modify = 1;
|
||||
@@ -1495,6 +1516,18 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
if (!stat(unixname, &stbuff)) {}
|
||||
data_off += 4;
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_BACKUP_DATE) {
|
||||
uint16 archive_date = GET_BE16(afp_req + data_off);
|
||||
uint16 archive_time = GET_BE16(afp_req + data_off + 2);
|
||||
|
||||
result = mars_nwe_set_archive_info(unixname,
|
||||
1, archive_date,
|
||||
1, archive_time,
|
||||
0, 0);
|
||||
if (result < 0)
|
||||
return(result);
|
||||
data_off += 4;
|
||||
}
|
||||
if ((request_mask & AFP_FILE_BITMAP_FINDER_INFO) && data_off & 1) data_off++;
|
||||
if (request_mask & AFP_FILE_BITMAP_FINDER_INFO) {
|
||||
result = nwatalk_set_finder_info(unixname, afp_req + data_off,
|
||||
@@ -1508,6 +1541,7 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
request_mask, visable_data(afp_req + 9, path_len),
|
||||
(request_mask & AFP_FILE_BITMAP_ATTRIBUTES) ? " attributes" : "",
|
||||
(request_mask & AFP_FILE_BITMAP_MODIFY_DATE) ? " modify_time" : "",
|
||||
(request_mask & AFP_FILE_BITMAP_BACKUP_DATE) ? " backup_time" : "",
|
||||
(request_mask & AFP_FILE_BITMAP_FINDER_INFO) ? " finder_info" : "",
|
||||
log_attrs, (long)log_mtime));
|
||||
return(0);
|
||||
|
||||
@@ -809,3 +809,27 @@ Attributes, `0x1000` Modify Date/Time, and `0x4000` FinderInfo. Since these
|
||||
attributes now use the existing NetWare attribute path, the final
|
||||
`user.org.mars-nwe.afp.attributes` dump is optional and may report `ENODATA`;
|
||||
that is expected when no AFP-only attribute bits remain set.
|
||||
|
||||
### AFP Backup Date/Time smoke
|
||||
|
||||
`afp_set_file_info_smoke` supports the WebSDK Backup Date/Time request bitmap
|
||||
`0x2000` via:
|
||||
|
||||
```sh
|
||||
./afp_set_file_info_smoke \
|
||||
-S MARS -U SUPERVISOR -P secret \
|
||||
--backup-time-only --backup-time-epoch 1700000000 \
|
||||
SYS:PUBLIC/pmdflts.ini
|
||||
```
|
||||
|
||||
This is intentionally separate from the Archive attribute bit in the Attributes
|
||||
word. The server stores Backup Date/Time through `nwarchive.c` and the Linux
|
||||
suite dumps the corresponding xattr as:
|
||||
|
||||
```sh
|
||||
getfattr -n user.org.mars-nwe.netware.archive -e hex /var/mars_nwe/SYS/public/pmdflts.ini
|
||||
```
|
||||
|
||||
The expected AFP reply shows the same Backup Date/Time at offsets 28/30 of the
|
||||
120-byte file information record, while Archive/Hidden/System attributes remain
|
||||
mapped through the normal NetWare attribute store.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define AFP20_SET_FILE_INFORMATION 0x10
|
||||
#define AFP_ATTRIBUTES_MASK 0x0100
|
||||
#define AFP_MODIFY_DATE_MASK 0x1000
|
||||
#define AFP_BACKUP_DATE_MASK 0x2000
|
||||
#define AFP_FINDER_INFO_MASK 0x4000
|
||||
#define AFP_ATTR_HIDDEN 0x0200
|
||||
#define AFP_ATTR_SYSTEM 0x0400
|
||||
@@ -41,7 +42,7 @@ static void usage(const char *prog)
|
||||
"Usage: %s [--afp09|--afp20] [--expect-completion CODE] [--allow-invalid-namespace] [--allow-invalid-path] "
|
||||
"[--volume N] [--entry-id ID] [--type FOUR] [--creator FOUR] "
|
||||
"[--hidden|--clear-hidden|--invisible|--clear-invisible|--system|--clear-system|--archive|--clear-archive] "
|
||||
"[--mtime-epoch SECONDS] [--finder-info-only|--attributes-only|--timestamp-only] "
|
||||
"[--mtime-epoch SECONDS] [--backup-time-epoch SECONDS] [--finder-info-only|--attributes-only|--timestamp-only|--backup-time-only] "
|
||||
"[ncpfs options] PATH\n"
|
||||
"\n"
|
||||
"ncpfs options are parsed by ncp_initialize(), for example:\n"
|
||||
@@ -52,9 +53,10 @@ static void usage(const char *prog)
|
||||
" %s -S MARS -U SUPERVISOR -P secret --hidden --attributes-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --archive --attributes-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --mtime-epoch 1700000000 --timestamp-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --backup-time-epoch 1700000000 --backup-time-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s --allow-invalid-namespace -S MARS SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s --expect-completion 0x8c -S MARS -U NOPASSUSER -n --finder-info-only SYS:PUBLIC/pmdflts.ini\n",
|
||||
prog, prog, prog, prog, prog, prog, prog);
|
||||
prog, prog, prog, prog, prog, prog, prog, prog);
|
||||
}
|
||||
|
||||
static int parse_u32(const char *text, uint32_t *value)
|
||||
@@ -172,8 +174,10 @@ int main(int argc, char **argv)
|
||||
uint16_t attr_expected = 0;
|
||||
uint8_t modify_time[4];
|
||||
int have_modify_time = 0;
|
||||
uint8_t backup_time[4];
|
||||
int have_backup_time = 0;
|
||||
uint8_t verify_buf[AFP_REPLY_LEN];
|
||||
uint8_t request[1 + 4 + 2 + 1 + 255 + 1 + 2 + 4 + 1 + 32];
|
||||
uint8_t request[1 + 4 + 2 + 1 + 255 + 1 + 2 + 4 + 4 + 1 + 32];
|
||||
size_t path_len;
|
||||
size_t afp_data_off;
|
||||
size_t data_off;
|
||||
@@ -182,6 +186,7 @@ int main(int argc, char **argv)
|
||||
|
||||
memset(finder_info, 0, sizeof(finder_info));
|
||||
memset(modify_time, 0, sizeof(modify_time));
|
||||
memset(backup_time, 0, sizeof(backup_time));
|
||||
memcpy(finder_info + 0, "TEXT", 4);
|
||||
memcpy(finder_info + 4, "MARS", 4);
|
||||
|
||||
@@ -265,13 +270,25 @@ int main(int argc, char **argv)
|
||||
}
|
||||
request_mask |= AFP_MODIFY_DATE_MASK;
|
||||
have_modify_time = 1;
|
||||
} else if (!strcmp(argv[i], "--backup-time-epoch")) {
|
||||
uint32_t epoch;
|
||||
if (++i >= argc || parse_u32(argv[i], &epoch) || epoch_to_nw_time(epoch, backup_time)) {
|
||||
fprintf(stderr, "invalid --backup-time-epoch value\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
request_mask |= AFP_BACKUP_DATE_MASK;
|
||||
have_backup_time = 1;
|
||||
} else if (!strcmp(argv[i], "--attributes-only")) {
|
||||
request_mask &= ~(AFP_FINDER_INFO_MASK | AFP_MODIFY_DATE_MASK);
|
||||
request_mask &= ~(AFP_FINDER_INFO_MASK | AFP_MODIFY_DATE_MASK | AFP_BACKUP_DATE_MASK);
|
||||
} else if (!strcmp(argv[i], "--finder-info-only")) {
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_MODIFY_DATE_MASK);
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_MODIFY_DATE_MASK | AFP_BACKUP_DATE_MASK);
|
||||
} else if (!strcmp(argv[i], "--timestamp-only")) {
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_FINDER_INFO_MASK);
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_FINDER_INFO_MASK | AFP_BACKUP_DATE_MASK);
|
||||
request_mask |= AFP_MODIFY_DATE_MASK;
|
||||
} else if (!strcmp(argv[i], "--backup-time-only")) {
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_FINDER_INFO_MASK | AFP_MODIFY_DATE_MASK);
|
||||
request_mask |= AFP_BACKUP_DATE_MASK;
|
||||
} else if (!strcmp(argv[i], "--type")) {
|
||||
if (++i >= argc || copy_fourcc(argv[i], finder_info + 0)) {
|
||||
fprintf(stderr, "invalid --type value, expected four characters\n");
|
||||
@@ -343,8 +360,17 @@ int main(int argc, char **argv)
|
||||
memcpy(request + data_off, modify_time, sizeof(modify_time));
|
||||
data_off += sizeof(modify_time);
|
||||
}
|
||||
if (request_mask & AFP_BACKUP_DATE_MASK) {
|
||||
if (!have_backup_time) {
|
||||
fprintf(stderr, "--backup-time-only requires --backup-time-epoch\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
memcpy(request + data_off, backup_time, sizeof(backup_time));
|
||||
data_off += sizeof(backup_time);
|
||||
}
|
||||
if ((request_mask & AFP_FINDER_INFO_MASK) &&
|
||||
(request_mask & (AFP_ATTRIBUTES_MASK | AFP_MODIFY_DATE_MASK)) &&
|
||||
(request_mask & (AFP_ATTRIBUTES_MASK | AFP_MODIFY_DATE_MASK | AFP_BACKUP_DATE_MASK)) &&
|
||||
((data_off + 1) & 1))
|
||||
data_off++;
|
||||
if (request_mask & AFP_FINDER_INFO_MASK) {
|
||||
@@ -417,6 +443,16 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((request_mask & AFP_BACKUP_DATE_MASK) &&
|
||||
memcmp(verify_buf + 28, backup_time, sizeof(backup_time))) {
|
||||
fprintf(stderr,
|
||||
"AFP Set File Information backup timestamp verify mismatch: path=%s got=0x%04x%04x expected=0x%04x%04x\n",
|
||||
path, be16_to_cpu(verify_buf + 28), be16_to_cpu(verify_buf + 30),
|
||||
be16_to_cpu(backup_time + 0), be16_to_cpu(backup_time + 2));
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((request_mask & AFP_ATTRIBUTES_MASK) &&
|
||||
((be16_to_cpu(verify_buf + 8) & attr_verify_mask) != attr_expected)) {
|
||||
fprintf(stderr,
|
||||
@@ -426,9 +462,10 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("AFP Set File Info subfunction=0x%02x path=%s bitmap=0x%04x attrs=0x%04x modify=0x%04x%04x finder_type=%.4s finder_creator=%.4s entry_id=0x%08x verified\n",
|
||||
printf("AFP Set File Info subfunction=0x%02x path=%s bitmap=0x%04x attrs=0x%04x modify=0x%04x%04x backup=0x%04x%04x finder_type=%.4s finder_creator=%.4s entry_id=0x%08x verified\n",
|
||||
set_subfunction, path, request_mask, be16_to_cpu(verify_buf + 8),
|
||||
be16_to_cpu(verify_buf + 24), be16_to_cpu(verify_buf + 26),
|
||||
be16_to_cpu(verify_buf + 28), be16_to_cpu(verify_buf + 30),
|
||||
finder_info + 0, finder_info + 4, be32_to_cpu(verify_buf + 0));
|
||||
|
||||
ncp_close(conn);
|
||||
|
||||
@@ -37,7 +37,7 @@ Options:
|
||||
--out FILE Write the complete report to FILE as well as stdout
|
||||
--type FOURCC FinderInfo type written by Set File Info (default: $FINDER_TYPE)
|
||||
--creator FOURCC FinderInfo creator written by Set File Info (default: $FINDER_CREATOR)
|
||||
--mtime-epoch SECONDS AFP modify timestamp to write (default: $TIMESTAMP_EPOCH)
|
||||
--mtime-epoch SECONDS AFP modify/backup timestamp to write (default: $TIMESTAMP_EPOCH)
|
||||
--readonly-user USER Optional user expected to lack Modify rights
|
||||
--readonly-password PASS Password for --readonly-user
|
||||
--readonly-no-password Use ncpfs -n for --readonly-user
|
||||
@@ -368,6 +368,12 @@ run_cmd \
|
||||
"$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'" \
|
||||
@@ -448,6 +454,11 @@ if command -v getfattr >/dev/null 2>&1; then
|
||||
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 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'" \
|
||||
|
||||
Reference in New Issue
Block a user