nwconn: route AFP create timestamps through nwarchive fileinfo
All checks were successful
Source release / source-package (push) Successful in 50s
All checks were successful
Source release / source-package (push) Successful in 50s
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -524,3 +524,5 @@ run confirmed the backing mars_nwe metadata xattr:
|
||||
`user.org.mars-nwe.netware.archive=0x01036e57aab900000000`. That confirms the
|
||||
intended split: Archive as a file attribute stays on the NetWare attribute path,
|
||||
while Backup Date/Time is stored by `nwarchive.c`.
|
||||
|
||||
- AFP Create Date/Time now uses `mars_nwe_set_file_info()` / `mars_nwe_get_file_info()` and the existing `user.org.mars-nwe.netware.fileinfo` xattr; Access remains to be wired through the existing atime path.
|
||||
|
||||
52
src/nwconn.c
52
src/nwconn.c
@@ -1114,6 +1114,7 @@ static void afp_leaf_name_from_path(uint8 *dst, int dst_len,
|
||||
}
|
||||
|
||||
#define AFP_FILE_BITMAP_ATTRIBUTES 0x0100
|
||||
#define AFP_FILE_BITMAP_CREATE_DATE 0x0800
|
||||
#define AFP_FILE_BITMAP_MODIFY_DATE 0x1000
|
||||
#define AFP_FILE_BITMAP_BACKUP_DATE 0x2000
|
||||
#define AFP_FILE_BITMAP_FINDER_INFO 0x4000
|
||||
@@ -1203,7 +1204,22 @@ static int afp_fill_file_info_response(const char *unixname,
|
||||
U32_TO_BE32(resource_size, response + 14);
|
||||
U16_TO_BE16(afp_count_offspring(unixname, &stbuff), response + 18);
|
||||
|
||||
un_date_2_nw(stbuff.st_ctime, response + 20, 1);
|
||||
{
|
||||
uint16 create_date = 0;
|
||||
uint16 create_time = 0;
|
||||
uint8 fileinfo_flags = 0;
|
||||
|
||||
mars_nwe_get_file_info((char *)unixname, &create_date, &create_time,
|
||||
NULL, &fileinfo_flags);
|
||||
if (fileinfo_flags & MARS_NWE_FILEINFO_HAS_CREATE_DATE)
|
||||
U16_TO_BE16(create_date, response + 20);
|
||||
else
|
||||
un_date_2_nw(stbuff.st_ctime, response + 20, 1);
|
||||
/* The AFP information record used by this NCP extension has no visible
|
||||
* create-time slot; still persist SetInfo Create Time through nwarchive
|
||||
* so classic NetWare file-info calls can return it. */
|
||||
(void)create_time;
|
||||
}
|
||||
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);
|
||||
@@ -1350,9 +1366,10 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
* Netatalk's FPSetFileParams tests use the FinderInfo bitmap as a small,
|
||||
* metadata-only write probes. Mirror the safest slices for the NCP AFP
|
||||
* extension: accept the same path-backed VOL:-style smoke requests as Get File
|
||||
* Information, persist only the file attribute word, the file modification timestamp,
|
||||
* and/or 32-byte FinderInfo block. Attribute and FinderInfo data live in
|
||||
* mars_nwe's private xattr namespace; the modification timestamp is routed
|
||||
* Information, persist only the file attribute word, the file creation timestamp,
|
||||
* the file modification timestamp, and/or 32-byte FinderInfo block. Attributes are routed through the NetWare attribute path and FinderInfo data lives in
|
||||
* mars_nwe's private AFP xattr namespace; Create Date/Time reuses the existing
|
||||
* nwarchive fileinfo helper, while the modification timestamp is routed
|
||||
* through the existing NetWare timestamp helper so trustee Modify rights and
|
||||
* utime handling stay shared with the classic NCP paths. Reject all other
|
||||
* bitmap bits until DOS attribute, resource-fork, and entry-id lookup
|
||||
@@ -1399,8 +1416,9 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
return(-0x9c); /* Invalid Path until persistent entry-id lookup exists */
|
||||
}
|
||||
|
||||
if (request_mask & ~(AFP_FILE_BITMAP_ATTRIBUTES | AFP_FILE_BITMAP_MODIFY_DATE |
|
||||
AFP_FILE_BITMAP_BACKUP_DATE | AFP_FILE_BITMAP_FINDER_INFO)) {
|
||||
if (request_mask & ~(AFP_FILE_BITMAP_ATTRIBUTES | AFP_FILE_BITMAP_CREATE_DATE |
|
||||
AFP_FILE_BITMAP_MODIFY_DATE | 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)));
|
||||
@@ -1432,6 +1450,15 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
* the NetWare Modify-rights check. FinderInfo remains AFP metadata. */
|
||||
data_off += 2;
|
||||
}
|
||||
if ((request_mask & AFP_FILE_BITMAP_CREATE_DATE) && afp_len < data_off + 4) {
|
||||
XDPRINTF((2,0, "%s rejected: short create timestamp data len=%d data_off=%d",
|
||||
call_name, afp_len, data_off));
|
||||
return(-0x7e);
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_CREATE_DATE) {
|
||||
needs_afp_metadata_modify = 1;
|
||||
data_off += 4;
|
||||
}
|
||||
if ((request_mask & AFP_FILE_BITMAP_MODIFY_DATE) && afp_len < data_off + 4) {
|
||||
XDPRINTF((2,0, "%s rejected: short modify timestamp data len=%d data_off=%d",
|
||||
call_name, afp_len, data_off));
|
||||
@@ -1502,6 +1529,18 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
}
|
||||
data_off += 2;
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_CREATE_DATE) {
|
||||
uint16 create_date = GET_BE16(afp_req + data_off);
|
||||
uint16 create_time = GET_BE16(afp_req + data_off + 2);
|
||||
|
||||
result = mars_nwe_set_file_info(unixname,
|
||||
1, create_date,
|
||||
1, create_time,
|
||||
0, 0);
|
||||
if (result < 0)
|
||||
return(result);
|
||||
data_off += 4;
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_MODIFY_DATE) {
|
||||
time_t new_mtime = nw_2_un_time(afp_req + data_off, afp_req + data_off + 2);
|
||||
if (new_mtime == (time_t)-1) {
|
||||
@@ -1540,6 +1579,7 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
call_name, path_volume, (int)volume_number, request_entry_id,
|
||||
request_mask, visable_data(afp_req + 9, path_len),
|
||||
(request_mask & AFP_FILE_BITMAP_ATTRIBUTES) ? " attributes" : "",
|
||||
(request_mask & AFP_FILE_BITMAP_CREATE_DATE) ? " create_time" : "",
|
||||
(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" : "",
|
||||
|
||||
@@ -838,3 +838,23 @@ Runtime status: the Backup Date/Time smoke run is verified with `failures=0`.
|
||||
The report showed `bitmap=0x2000`, `backup=0x576eb9aa`, and the backing xattr
|
||||
`user.org.mars-nwe.netware.archive=0x01036e57aab900000000` after setting epoch
|
||||
`1700000000`.
|
||||
|
||||
|
||||
### AFP Create Date/Time smoke
|
||||
|
||||
`afp_set_file_info_smoke` supports the WebSDK Create Date/Time request bitmap
|
||||
`0x0800` via:
|
||||
|
||||
```sh
|
||||
./afp_set_file_info_smoke \
|
||||
-S MARS -U SUPERVISOR -P secret \
|
||||
--create-time-only --create-time-epoch 1700000000 \
|
||||
SYS:PUBLIC/pmdflts.ini
|
||||
```
|
||||
|
||||
The server stores Create Date/Time through `nwarchive.c` file-info metadata
|
||||
(`user.org.mars-nwe.netware.fileinfo`) using `mars_nwe_set_file_info()`. The
|
||||
AFP file-information record exposes the Create Date at offset 20; the matching
|
||||
Create Time is still persisted in the shared NetWare file-info metadata so
|
||||
classic NetWare file-info paths can return it. No AFP-specific xattr is added
|
||||
for this NetWare-semantic timestamp.
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#define AFP_SET_FILE_INFORMATION 0x09
|
||||
#define AFP20_SET_FILE_INFORMATION 0x10
|
||||
#define AFP_ATTRIBUTES_MASK 0x0100
|
||||
#define AFP_CREATE_DATE_MASK 0x0800
|
||||
#define AFP_MODIFY_DATE_MASK 0x1000
|
||||
#define AFP_BACKUP_DATE_MASK 0x2000
|
||||
#define AFP_FINDER_INFO_MASK 0x4000
|
||||
@@ -42,7 +43,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] [--backup-time-epoch SECONDS] [--finder-info-only|--attributes-only|--timestamp-only|--backup-time-only] "
|
||||
"[--create-time-epoch SECONDS] [--mtime-epoch SECONDS] [--backup-time-epoch SECONDS] [--finder-info-only|--attributes-only|--create-time-only|--timestamp-only|--backup-time-only] "
|
||||
"[ncpfs options] PATH\n"
|
||||
"\n"
|
||||
"ncpfs options are parsed by ncp_initialize(), for example:\n"
|
||||
@@ -52,11 +53,12 @@ static void usage(const char *prog)
|
||||
" %s -S MARS -U SUPERVISOR -P secret --type TEXT --creator MARS SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %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 --create-time-epoch 1700000000 --create-time-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, prog, prog);
|
||||
}
|
||||
|
||||
static int parse_u32(const char *text, uint32_t *value)
|
||||
@@ -172,12 +174,14 @@ int main(int argc, char **argv)
|
||||
uint16_t attr_request = 0;
|
||||
uint16_t attr_verify_mask = 0;
|
||||
uint16_t attr_expected = 0;
|
||||
uint8_t create_time[4];
|
||||
int have_create_time = 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 + 4 + 1 + 32];
|
||||
uint8_t request[1 + 4 + 2 + 1 + 255 + 1 + 2 + 4 + 4 + 4 + 1 + 32];
|
||||
size_t path_len;
|
||||
size_t afp_data_off;
|
||||
size_t data_off;
|
||||
@@ -185,6 +189,7 @@ int main(int argc, char **argv)
|
||||
NWCCODE err;
|
||||
|
||||
memset(finder_info, 0, sizeof(finder_info));
|
||||
memset(create_time, 0, sizeof(create_time));
|
||||
memset(modify_time, 0, sizeof(modify_time));
|
||||
memset(backup_time, 0, sizeof(backup_time));
|
||||
memcpy(finder_info + 0, "TEXT", 4);
|
||||
@@ -261,6 +266,15 @@ int main(int argc, char **argv)
|
||||
attr_request = AFP_ATTR_ARCHIVE;
|
||||
attr_verify_mask = AFP_ATTR_ARCHIVE;
|
||||
attr_expected = 0;
|
||||
} else if (!strcmp(argv[i], "--create-time-epoch")) {
|
||||
uint32_t epoch;
|
||||
if (++i >= argc || parse_u32(argv[i], &epoch) || epoch_to_nw_time(epoch, create_time)) {
|
||||
fprintf(stderr, "invalid --create-time-epoch value\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
request_mask |= AFP_CREATE_DATE_MASK;
|
||||
have_create_time = 1;
|
||||
} else if (!strcmp(argv[i], "--mtime-epoch")) {
|
||||
uint32_t epoch;
|
||||
if (++i >= argc || parse_u32(argv[i], &epoch) || epoch_to_nw_time(epoch, modify_time)) {
|
||||
@@ -280,14 +294,17 @@ int main(int argc, char **argv)
|
||||
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 | AFP_BACKUP_DATE_MASK);
|
||||
request_mask &= ~(AFP_CREATE_DATE_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 | AFP_BACKUP_DATE_MASK);
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_CREATE_DATE_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 | AFP_BACKUP_DATE_MASK);
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_CREATE_DATE_MASK | AFP_FINDER_INFO_MASK | AFP_BACKUP_DATE_MASK);
|
||||
request_mask |= AFP_MODIFY_DATE_MASK;
|
||||
} else if (!strcmp(argv[i], "--create-time-only")) {
|
||||
request_mask &= ~(AFP_ATTRIBUTES_MASK | AFP_FINDER_INFO_MASK | AFP_MODIFY_DATE_MASK | AFP_BACKUP_DATE_MASK);
|
||||
request_mask |= AFP_CREATE_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_ATTRIBUTES_MASK | AFP_CREATE_DATE_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)) {
|
||||
@@ -351,6 +368,15 @@ int main(int argc, char **argv)
|
||||
cpu_to_be16(attr_request, request + data_off);
|
||||
data_off += 2;
|
||||
}
|
||||
if (request_mask & AFP_CREATE_DATE_MASK) {
|
||||
if (!have_create_time) {
|
||||
fprintf(stderr, "--create-time-only requires --create-time-epoch\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
memcpy(request + data_off, create_time, sizeof(create_time));
|
||||
data_off += sizeof(create_time);
|
||||
}
|
||||
if (request_mask & AFP_MODIFY_DATE_MASK) {
|
||||
if (!have_modify_time) {
|
||||
fprintf(stderr, "--timestamp-only requires --mtime-epoch\n");
|
||||
@@ -370,7 +396,7 @@ int main(int argc, char **argv)
|
||||
data_off += sizeof(backup_time);
|
||||
}
|
||||
if ((request_mask & AFP_FINDER_INFO_MASK) &&
|
||||
(request_mask & (AFP_ATTRIBUTES_MASK | AFP_MODIFY_DATE_MASK | AFP_BACKUP_DATE_MASK)) &&
|
||||
(request_mask & (AFP_ATTRIBUTES_MASK | AFP_CREATE_DATE_MASK | AFP_MODIFY_DATE_MASK | AFP_BACKUP_DATE_MASK)) &&
|
||||
((data_off + 1) & 1))
|
||||
data_off++;
|
||||
if (request_mask & AFP_FINDER_INFO_MASK) {
|
||||
@@ -433,6 +459,16 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((request_mask & AFP_CREATE_DATE_MASK) &&
|
||||
memcmp(verify_buf + 20, create_time, 2)) {
|
||||
fprintf(stderr,
|
||||
"AFP Set File Information create timestamp verify mismatch: path=%s got=0x%04x expected_date=0x%04x expected_time=0x%04x\n",
|
||||
path, be16_to_cpu(verify_buf + 20),
|
||||
be16_to_cpu(create_time + 0), be16_to_cpu(create_time + 2));
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((request_mask & AFP_MODIFY_DATE_MASK) &&
|
||||
memcmp(verify_buf + 24, modify_time, sizeof(modify_time))) {
|
||||
fprintf(stderr,
|
||||
@@ -462,8 +498,9 @@ 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 backup=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 create=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 + 20),
|
||||
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));
|
||||
|
||||
@@ -362,6 +362,12 @@ run_cmd \
|
||||
"$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 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'" \
|
||||
@@ -454,6 +460,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 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'" \
|
||||
|
||||
Reference in New Issue
Block a user