nwconn: persist AFP invisible file attributes
All checks were successful
Source release / source-package (push) Successful in 47s
All checks were successful
Source release / source-package (push) Successful in 47s
Extend the conservative NCP 0x2222/35/16 AFP 2.0 Set File Information smoke path beyond FinderInfo-only writes by accepting the file Attributes bitmap for one deliberately narrow bit: Finder Invisible. WebSDK and Netatalk FPSetFileParams semantics carry file attributes as bitmap bit 0, with ATTRBIT_SETCLR selecting set-vs-clear behavior. Mirror that model only for ATTRBIT_INVISIBLE and reject all other AFP attribute bits so DOS/NetWare mode bits, timestamp writes, resource forks, and broader file protection semantics are not implied accidentally. Persist the mars_nwe-owned AFP attribute word in org.mars-nwe.afp.attributes via the local xattr abstraction. On Linux this maps to user.org.mars-nwe.afp.attributes, matching the org.mars-nwe.* source-level namespace while remaining portable on Linux xattr backends. Get File Information and Scan File Information now merge that stored Invisible bit into the existing 120-byte AFP file-info record. Update the Linux Set File Information smoke helper with --invisible, --clear-invisible, --attributes-only, and --finder-info-only so FinderInfo and the narrow AFP attribute path can be tested independently or together. Tests: git diff --check Tests: gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_set_file_info_smoke.c TODO: keep all other AFP Set File Information bits rejected until their write-safe mapping to NetWare/DOS attributes, timestamps, CNID, and resource-fork metadata is designed.
This commit is contained in:
21
TODO.md
21
TODO.md
@@ -246,12 +246,15 @@ Current status:
|
||||
privileged directories can show `rights=0x1ff` while the client prints
|
||||
`rights=0xff`. Entry-ID-only allocation remains TODO until persistent
|
||||
CNID/base-ID lookup exists.
|
||||
- `AFP 2.0 Set File Information` is implemented only for the FinderInfo bitmap
|
||||
(`0x0020`) on path-backed file requests. Linux smoke coverage exists in
|
||||
`tests/linux/afp_set_file_info_smoke`; runtime coverage is green for
|
||||
`SYS:PUBLIC/pmdflts.ini` with Finder type `TEXT` and creator `MARS`. The
|
||||
helper writes 32 bytes of FinderInfo to `org.mars-nwe.afp.finder-info` and
|
||||
verifies the result through AFP 2.0 Get File Information. The first
|
||||
- `AFP 2.0 Set File Information` is implemented only for path-backed file
|
||||
metadata smoke writes: the FinderInfo bitmap (`0x0020`) and the AFP
|
||||
Attributes bitmap (`0x0001`) restricted to the Finder Invisible bit. Linux
|
||||
smoke coverage exists in `tests/linux/afp_set_file_info_smoke`; runtime
|
||||
FinderInfo coverage is green for `SYS:PUBLIC/pmdflts.ini` with Finder type
|
||||
`TEXT` and creator `MARS`. The helper writes 32 bytes of FinderInfo to
|
||||
`org.mars-nwe.afp.finder-info`, stores the narrow attribute word in
|
||||
`org.mars-nwe.afp.attributes`, and verifies the result through AFP 2.0 Get
|
||||
File Information. The first
|
||||
stat-derived AFP entry id for a path is now cached in the versioned
|
||||
`org.mars-nwe.afp.entry-id` xattr; a `fallback` marker on that first
|
||||
verification Get File Information diagnostic describes the entry-id origin,
|
||||
@@ -296,9 +299,9 @@ Follow-up:
|
||||
`0x7b9c42e1` Entry ID. AFP directory-scan continuation remains directory
|
||||
iteration based: `last_seen` skips past the previously returned object, but
|
||||
the next returned Entry ID is not required to be numerically greater than the
|
||||
continuation token. FinderInfo now has a deliberately narrow write path
|
||||
through AFP 2.0 Set File Information; CNID allocation and broader AFP metadata
|
||||
writes still need a deliberate write-safe design.
|
||||
continuation token. FinderInfo and the Finder Invisible AFP attribute now have deliberately narrow
|
||||
write paths through AFP 2.0 Set File Information; CNID allocation and broader
|
||||
AFP metadata writes still need a deliberate write-safe design.
|
||||
- Put additional future mars_nwe-owned AFP metadata under `org.mars-nwe.afp.*`
|
||||
(or a compact `org.mars-nwe.afp.metadata` record) and keep Netatalk-owned
|
||||
metadata under Netatalk's own `org.netatalk.*` keys.
|
||||
|
||||
@@ -10,6 +10,8 @@ int nwatalk_get_finder_info(const char *path, uint8 *finder_info,
|
||||
int finder_info_len);
|
||||
int nwatalk_set_finder_info(const char *path, const uint8 *finder_info,
|
||||
int finder_info_len);
|
||||
int nwatalk_get_afp_attributes(const char *path, uint16 *attributes);
|
||||
int nwatalk_set_afp_attributes(const char *path, uint16 attributes);
|
||||
int nwatalk_get_resource_fork_size(const char *path, uint32 *resource_size);
|
||||
int nwatalk_get_entry_id(const char *path, uint32 *entry_id);
|
||||
int nwatalk_set_entry_id(const char *path, uint32 entry_id);
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
|
||||
#define MARS_NWE_AFP_ENTRY_ID_XATTR "org.mars-nwe.afp.entry-id"
|
||||
#define MARS_NWE_AFP_FINDER_INFO_XATTR "org.mars-nwe.afp.finder-info"
|
||||
#define MARS_NWE_AFP_ATTRIBUTES_XATTR "org.mars-nwe.afp.attributes"
|
||||
#define MARS_NWE_AFP_ENTRY_ID_VERSION 1
|
||||
#define MARS_NWE_AFP_ATTRIBUTES_VERSION 1
|
||||
|
||||
#define NWATALK_AFP_ATTR_INVISIBLE 0x0001
|
||||
#define NWATALK_AFP_ATTR_SETCLR 0x8000
|
||||
|
||||
typedef struct {
|
||||
uint8 version;
|
||||
@@ -26,6 +31,12 @@ typedef struct {
|
||||
uint8 entry_id[4];
|
||||
} MARS_NWE_AFP_ENTRY_ID_XATTR_DATA;
|
||||
|
||||
typedef struct {
|
||||
uint8 version;
|
||||
uint8 reserved;
|
||||
uint8 attributes[2];
|
||||
} MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA;
|
||||
|
||||
|
||||
#if XATTR_SUPPORT
|
||||
static int nwatalk_get_mars_entry_id_xattr(const char *path, uint32 *entry_id)
|
||||
@@ -157,6 +168,73 @@ int nwatalk_set_finder_info(const char *path, const uint8 *finder_info,
|
||||
}
|
||||
|
||||
|
||||
|
||||
int nwatalk_get_afp_attributes(const char *path, uint16 *attributes)
|
||||
{
|
||||
#if XATTR_SUPPORT
|
||||
MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA d;
|
||||
ssize_t len;
|
||||
#endif
|
||||
|
||||
if (!attributes) return(-0x9c);
|
||||
*attributes = 0;
|
||||
if (!path || !*path) return(-0x9c);
|
||||
|
||||
#if XATTR_SUPPORT
|
||||
memset(&d, 0, sizeof(d));
|
||||
len = mars_nwe_getxattr(path, MARS_NWE_AFP_ATTRIBUTES_XATTR,
|
||||
&d, sizeof(d));
|
||||
if (len == sizeof(d) && d.version == MARS_NWE_AFP_ATTRIBUTES_VERSION) {
|
||||
*attributes = GET_BE16(d.attributes) & NWATALK_AFP_ATTR_INVISIBLE;
|
||||
return(0);
|
||||
}
|
||||
#else
|
||||
(void)path;
|
||||
#endif
|
||||
|
||||
return(-0x9c);
|
||||
}
|
||||
|
||||
int nwatalk_set_afp_attributes(const char *path, uint16 attributes)
|
||||
{
|
||||
#if XATTR_SUPPORT
|
||||
MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA d;
|
||||
uint16 stored = 0;
|
||||
uint16 requested = attributes & ~NWATALK_AFP_ATTR_SETCLR;
|
||||
uint16 current = 0;
|
||||
|
||||
if (!path || !*path) return(-0x9c);
|
||||
|
||||
if (requested & ~NWATALK_AFP_ATTR_INVISIBLE) {
|
||||
XDPRINTF((3,0,"AFP attributes xattr write rejected for %s attrs=0x%04x", path, attributes));
|
||||
return(-0x9c);
|
||||
}
|
||||
|
||||
(void)nwatalk_get_afp_attributes(path, ¤t);
|
||||
if (attributes & NWATALK_AFP_ATTR_SETCLR)
|
||||
stored = current | requested;
|
||||
else
|
||||
stored = current & ~requested;
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.version = MARS_NWE_AFP_ATTRIBUTES_VERSION;
|
||||
U16_TO_BE16(stored, d.attributes);
|
||||
|
||||
if (mars_nwe_setxattr(path, MARS_NWE_AFP_ATTRIBUTES_XATTR,
|
||||
&d, sizeof(d), 0)) {
|
||||
int err = errno;
|
||||
XDPRINTF((3,0,"AFP attributes xattr write failed for %s attrs=0x%04x errno=%d", path, stored, err));
|
||||
return(-0x8c);
|
||||
}
|
||||
|
||||
return(0);
|
||||
#else
|
||||
(void)path;
|
||||
(void)attributes;
|
||||
return(-0xbf);
|
||||
#endif
|
||||
}
|
||||
|
||||
int nwatalk_set_entry_id(const char *path, uint32 entry_id)
|
||||
{
|
||||
#if XATTR_SUPPORT
|
||||
|
||||
92
src/nwconn.c
92
src/nwconn.c
@@ -932,11 +932,20 @@ static void afp_leaf_name_from_path(uint8 *dst, int dst_len,
|
||||
afp_copy_fixed_name(dst, dst_len, path + start, end - start);
|
||||
}
|
||||
|
||||
static uint16 afp_basic_attributes(const struct stat *stb)
|
||||
static uint16 afp_basic_attributes(const char *unixname, const struct stat *stb)
|
||||
{
|
||||
uint16 attributes = 0;
|
||||
|
||||
if (S_ISDIR(stb->st_mode))
|
||||
return(0x0400); /* AFP_SA_SUBDIR */
|
||||
return(0x0000); /* AFP_SA_NORMAL */
|
||||
attributes |= 0x0400; /* AFP_SA_SUBDIR */
|
||||
|
||||
if (!S_ISDIR(stb->st_mode)) {
|
||||
uint16 stored_attrs = 0;
|
||||
if (!nwatalk_get_afp_attributes(unixname, &stored_attrs))
|
||||
attributes |= stored_attrs;
|
||||
}
|
||||
|
||||
return(attributes);
|
||||
}
|
||||
|
||||
static uint16 afp_basic_access_privileges(const struct stat *stb)
|
||||
@@ -987,7 +996,7 @@ static int afp_fill_file_info_response(const char *unixname,
|
||||
memset(response, 0, 120);
|
||||
U32_TO_BE32(entry_id, response + 0);
|
||||
U32_TO_BE32(parent_id, response + 4);
|
||||
U16_TO_BE16(afp_basic_attributes(&stbuff), response + 8);
|
||||
U16_TO_BE16(afp_basic_attributes(unixname, &stbuff), response + 8);
|
||||
U32_TO_BE32(S_ISDIR(stbuff.st_mode) ? 0 : (uint32)stbuff.st_size, response + 10);
|
||||
|
||||
if (!S_ISDIR(stbuff.st_mode))
|
||||
@@ -1084,7 +1093,10 @@ static int afp_get_file_information(uint8 *afp_req, int afp_len,
|
||||
}
|
||||
|
||||
|
||||
#define AFP_FILE_BITMAP_ATTRIBUTES 0x0001
|
||||
#define AFP_FILE_BITMAP_FINDER_INFO 0x0020
|
||||
#define AFP_ATTR_INVISIBLE 0x0001
|
||||
#define AFP_ATTR_SETCLR 0x8000
|
||||
|
||||
static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
const char *call_name)
|
||||
@@ -1092,12 +1104,12 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
* Conservative AFP 2.0 Set File Information subset.
|
||||
*
|
||||
* Netatalk's FPSetFileParams tests use the FinderInfo bitmap as a small,
|
||||
* metadata-only write probe. Mirror that safest slice for the NCP AFP
|
||||
* 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 32-byte FinderInfo block in mars_nwe's private
|
||||
* xattr namespace, and reject all other bitmap bits until DOS attribute,
|
||||
* timestamp, resource-fork, and entry-id lookup semantics are deliberately
|
||||
* wired in.
|
||||
* Information, persist only the file attribute word and/or 32-byte FinderInfo
|
||||
* block in mars_nwe's private xattr namespace, and reject all other bitmap
|
||||
* bits until DOS attribute, timestamp, resource-fork, and entry-id lookup
|
||||
* semantics are deliberately wired in.
|
||||
*/
|
||||
{
|
||||
uint8 volume_number;
|
||||
@@ -1109,6 +1121,7 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
char unixname[PATH_MAX];
|
||||
struct stat stbuff;
|
||||
int result;
|
||||
uint16 log_attrs = 0;
|
||||
|
||||
if (afp_len < 9) {
|
||||
XDPRINTF((2,0, "%s rejected: short request len=%d",
|
||||
@@ -1137,16 +1150,39 @@ 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_FINDER_INFO) {
|
||||
if (request_mask & ~(AFP_FILE_BITMAP_ATTRIBUTES | 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)));
|
||||
return(-0x9c); /* keep unsupported write semantics conservative */
|
||||
}
|
||||
if (!request_mask) {
|
||||
XDPRINTF((2,0, "%s rejected: empty bitmap vol=%d entry=0x%08x path='%s'",
|
||||
call_name, (int)volume_number, request_entry_id,
|
||||
visable_data(afp_req + 9, path_len)));
|
||||
return(-0x9c);
|
||||
}
|
||||
|
||||
data_off = 9 + path_len;
|
||||
if (data_off & 1) data_off++;
|
||||
if (afp_len < data_off + NWATALK_FINDER_INFO_LEN) {
|
||||
if ((request_mask & AFP_FILE_BITMAP_ATTRIBUTES) && afp_len < data_off + 2) {
|
||||
XDPRINTF((2,0, "%s rejected: short attribute data len=%d data_off=%d",
|
||||
call_name, afp_len, data_off));
|
||||
return(-0x7e);
|
||||
}
|
||||
if (request_mask & AFP_FILE_BITMAP_ATTRIBUTES) {
|
||||
uint16 requested_attrs = GET_BE16(afp_req + data_off);
|
||||
uint16 requested_bits = requested_attrs & ~AFP_ATTR_SETCLR;
|
||||
if (requested_bits & ~AFP_ATTR_INVISIBLE) {
|
||||
XDPRINTF((2,0, "%s rejected: unsupported AFP attributes attrs=0x%04x path='%s'",
|
||||
call_name, requested_attrs, visable_data(afp_req + 9, path_len)));
|
||||
return(-0x9c);
|
||||
}
|
||||
data_off += 2;
|
||||
}
|
||||
if ((request_mask & AFP_FILE_BITMAP_FINDER_INFO) && data_off & 1) data_off++;
|
||||
if ((request_mask & AFP_FILE_BITMAP_FINDER_INFO) &&
|
||||
afp_len < data_off + NWATALK_FINDER_INFO_LEN) {
|
||||
XDPRINTF((2,0, "%s rejected: short FinderInfo data len=%d data_off=%d",
|
||||
call_name, afp_len, data_off));
|
||||
return(-0x7e);
|
||||
@@ -1174,22 +1210,30 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||||
return(-0x9c);
|
||||
}
|
||||
|
||||
result = nwatalk_set_finder_info(unixname, afp_req + data_off,
|
||||
NWATALK_FINDER_INFO_LEN);
|
||||
if (result < 0)
|
||||
return(result);
|
||||
data_off = 9 + path_len;
|
||||
if (data_off & 1) data_off++;
|
||||
if (request_mask & AFP_FILE_BITMAP_ATTRIBUTES) {
|
||||
uint16 requested_attrs = GET_BE16(afp_req + data_off);
|
||||
log_attrs = requested_attrs;
|
||||
result = nwatalk_set_afp_attributes(unixname, requested_attrs);
|
||||
if (result < 0)
|
||||
return(result);
|
||||
data_off += 2;
|
||||
}
|
||||
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,
|
||||
NWATALK_FINDER_INFO_LEN);
|
||||
if (result < 0)
|
||||
return(result);
|
||||
}
|
||||
|
||||
XDPRINTF((3,0, "%s: vol=%d request_vol=%d entry=0x%08x mask=0x%04x path='%s' finder_type='%c%c%c%c' finder_creator='%c%c%c%c'",
|
||||
XDPRINTF((3,0, "%s: vol=%d request_vol=%d entry=0x%08x mask=0x%04x path='%s'%s%s attrs=0x%04x",
|
||||
call_name, path_volume, (int)volume_number, request_entry_id,
|
||||
request_mask, visable_data(afp_req + 9, path_len),
|
||||
afp_req[data_off + 0] ? afp_req[data_off + 0] : ' ',
|
||||
afp_req[data_off + 1] ? afp_req[data_off + 1] : ' ',
|
||||
afp_req[data_off + 2] ? afp_req[data_off + 2] : ' ',
|
||||
afp_req[data_off + 3] ? afp_req[data_off + 3] : ' ',
|
||||
afp_req[data_off + 4] ? afp_req[data_off + 4] : ' ',
|
||||
afp_req[data_off + 5] ? afp_req[data_off + 5] : ' ',
|
||||
afp_req[data_off + 6] ? afp_req[data_off + 6] : ' ',
|
||||
afp_req[data_off + 7] ? afp_req[data_off + 7] : ' '));
|
||||
(request_mask & AFP_FILE_BITMAP_ATTRIBUTES) ? " attributes" : "",
|
||||
(request_mask & AFP_FILE_BITMAP_FINDER_INFO) ? " finder_info" : "",
|
||||
log_attrs));
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -383,7 +383,7 @@ If the server was built without the optional Netatalk/libatalk backend, use
|
||||
when a scan continuation is expected to reach the end of the directory.
|
||||
|
||||
|
||||
## AFP Set File Information FinderInfo smoke test
|
||||
## AFP Set File Information metadata smoke test
|
||||
|
||||
`afp_set_file_info_smoke` sends the WebSDK-documented NetWare AFP 2.0 request:
|
||||
|
||||
@@ -391,11 +391,13 @@ when a scan continuation is expected to reach the end of the directory.
|
||||
NCP 0x2222/35/16 AFP 2.0 Set File Information
|
||||
```
|
||||
|
||||
The helper exercises only the first write-safe AFP subset: the file FinderInfo
|
||||
bitmap (`0x0020`). It sends a path-backed raw `VOL:`-style request, writes the
|
||||
32-byte FinderInfo block to mars_nwe's private `org.mars-nwe.afp.finder-info`
|
||||
metadata key, and immediately verifies the update through AFP 2.0 Get File
|
||||
Information. On Linux the source-level `org.mars-nwe.afp.*` name is stored via the
|
||||
The helper exercises two deliberately narrow write-safe AFP metadata subsets:
|
||||
the file FinderInfo bitmap (`0x0020`) and the file Attributes bitmap (`0x0001`)
|
||||
restricted to the Finder Invisible bit. It sends path-backed raw `VOL:`-style
|
||||
requests, writes the 32-byte FinderInfo block to mars_nwe's private
|
||||
`org.mars-nwe.afp.finder-info` metadata key, writes the narrow AFP attribute word
|
||||
to `org.mars-nwe.afp.attributes`, and immediately verifies the updates through
|
||||
AFP 2.0 Get File Information. On Linux the source-level `org.mars-nwe.afp.*` name is stored via the
|
||||
portable `user.` xattr namespace by mars_nwe's local xattr wrapper, the same
|
||||
pattern Netatalk uses for its `org.netatalk.*` metadata names.
|
||||
|
||||
@@ -445,10 +447,29 @@ For the verified FinderInfo smoke run, the FinderInfo xattr starts with
|
||||
user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000
|
||||
```
|
||||
|
||||
All other Set File Information bitmap bits are intentionally rejected for now.
|
||||
That keeps attribute, timestamp, DOS/NetWare mode-bit mapping, resource-fork,
|
||||
and Entry-ID-only write semantics out of this first metadata-only write smoke
|
||||
path.
|
||||
Finder Invisible can be tested without mutating DOS/NetWare mode bits:
|
||||
|
||||
```sh
|
||||
./tests/linux/afp_set_file_info_smoke \
|
||||
-S MARS -U SUPERVISOR -P secret \
|
||||
--attributes-only --invisible \
|
||||
SYS:PUBLIC/pmdflts.ini
|
||||
|
||||
getfattr -n user.org.mars-nwe.afp.attributes -e hex /var/mars_nwe/SYS/public/pmdflts.ini
|
||||
```
|
||||
|
||||
The xattr payload is versioned. For an invisible file the expected Linux form
|
||||
is:
|
||||
|
||||
```text
|
||||
user.org.mars-nwe.afp.attributes=0x01000001
|
||||
```
|
||||
|
||||
Use `--clear-invisible --attributes-only` to clear that bit; the same xattr then
|
||||
stores `0x01000000`. All other Set File Information bitmap bits and all other
|
||||
AFP attribute bits are intentionally rejected for now. That keeps timestamp,
|
||||
DOS/NetWare mode-bit mapping, resource-fork, and Entry-ID-only write semantics
|
||||
out of this metadata-only smoke path.
|
||||
|
||||
If the server was built without the optional Netatalk/libatalk backend, use
|
||||
`--allow-invalid-namespace` for the expected negative test. Use
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Linux smoke test for NetWare AFP 2.0 Set File Information FinderInfo writes.
|
||||
* Linux smoke test for NetWare AFP 2.0 Set File Information metadata writes.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
@@ -21,7 +21,10 @@
|
||||
|
||||
#define AFP20_GET_FILE_INFORMATION 0x0f
|
||||
#define AFP20_SET_FILE_INFORMATION 0x10
|
||||
#define AFP_ATTRIBUTES_MASK 0x0001
|
||||
#define AFP_FINDER_INFO_MASK 0x0020
|
||||
#define AFP_ATTR_INVISIBLE 0x0001
|
||||
#define AFP_ATTR_SETCLR 0x8000
|
||||
#define AFP_REPLY_LEN 120
|
||||
#define NWE_INVALID_NAMESPACE 0xbf
|
||||
#define NWE_INVALID_PATH 0x9c
|
||||
@@ -31,6 +34,7 @@ static void usage(const char *prog)
|
||||
fprintf(stderr,
|
||||
"Usage: %s [--allow-invalid-namespace] [--allow-invalid-path] "
|
||||
"[--volume N] [--entry-id ID] [--type FOUR] [--creator FOUR] "
|
||||
"[--invisible|--clear-invisible] [--finder-info-only|--attributes-only] "
|
||||
"[ncpfs options] PATH\n"
|
||||
"\n"
|
||||
"ncpfs options are parsed by ncp_initialize(), for example:\n"
|
||||
@@ -38,8 +42,9 @@ static void usage(const char *prog)
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --type TEXT --creator MARS SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --invisible --attributes-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s --allow-invalid-namespace -S MARS SYS:PUBLIC/pmdflts.ini\n",
|
||||
prog, prog, prog);
|
||||
prog, prog, prog, prog);
|
||||
}
|
||||
|
||||
static int parse_u32(const char *text, uint32_t *value)
|
||||
@@ -55,6 +60,11 @@ static int parse_u32(const char *text, uint32_t *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t be16_to_cpu(const uint8_t p[2])
|
||||
{
|
||||
return ((uint16_t)p[0] << 8) | p[1];
|
||||
}
|
||||
|
||||
static uint32_t be32_to_cpu(const uint8_t p[4])
|
||||
{
|
||||
return ((uint32_t)p[0] << 24) |
|
||||
@@ -123,8 +133,11 @@ int main(int argc, char **argv)
|
||||
uint32_t volume_number = 0;
|
||||
uint32_t entry_id = 0;
|
||||
uint8_t finder_info[32];
|
||||
uint16_t request_mask = AFP_FINDER_INFO_MASK;
|
||||
uint16_t attr_request = 0;
|
||||
uint16_t attr_expected = 0;
|
||||
uint8_t verify_buf[AFP_REPLY_LEN];
|
||||
uint8_t request[1 + 4 + 2 + 1 + 255 + 1 + 32];
|
||||
uint8_t request[1 + 4 + 2 + 1 + 255 + 1 + 2 + 1 + 32];
|
||||
size_t path_len;
|
||||
size_t afp_data_off;
|
||||
size_t data_off;
|
||||
@@ -164,6 +177,18 @@ int main(int argc, char **argv)
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
} else if (!strcmp(argv[i], "--invisible")) {
|
||||
request_mask |= AFP_ATTRIBUTES_MASK;
|
||||
attr_request = AFP_ATTR_SETCLR | AFP_ATTR_INVISIBLE;
|
||||
attr_expected = AFP_ATTR_INVISIBLE;
|
||||
} else if (!strcmp(argv[i], "--clear-invisible")) {
|
||||
request_mask |= AFP_ATTRIBUTES_MASK;
|
||||
attr_request = AFP_ATTR_INVISIBLE;
|
||||
attr_expected = 0;
|
||||
} else if (!strcmp(argv[i], "--attributes-only")) {
|
||||
request_mask &= ~AFP_FINDER_INFO_MASK;
|
||||
} else if (!strcmp(argv[i], "--finder-info-only")) {
|
||||
request_mask &= ~AFP_ATTRIBUTES_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");
|
||||
@@ -207,19 +232,28 @@ int main(int argc, char **argv)
|
||||
memset(request, 0, sizeof(request));
|
||||
request[0] = (uint8_t)volume_number;
|
||||
cpu_to_be32(entry_id, request + 1);
|
||||
cpu_to_be16(AFP_FINDER_INFO_MASK, request + 5);
|
||||
cpu_to_be16(request_mask, request + 5);
|
||||
request[7] = (uint8_t)path_len;
|
||||
memcpy(request + 8, path, path_len);
|
||||
afp_data_off = 1 + 8 + path_len;
|
||||
if (afp_data_off & 1)
|
||||
afp_data_off++;
|
||||
data_off = afp_data_off - 1;
|
||||
memcpy(request + data_off, finder_info, sizeof(finder_info));
|
||||
if (request_mask & AFP_ATTRIBUTES_MASK) {
|
||||
cpu_to_be16(attr_request, request + data_off);
|
||||
data_off += 2;
|
||||
}
|
||||
if ((request_mask & AFP_FINDER_INFO_MASK) && (data_off & 1))
|
||||
data_off++;
|
||||
if (request_mask & AFP_FINDER_INFO_MASK) {
|
||||
memcpy(request + data_off, finder_info, sizeof(finder_info));
|
||||
data_off += sizeof(finder_info);
|
||||
}
|
||||
|
||||
err = NWRequestSimple(conn,
|
||||
NCPC_SFN(0x23, AFP20_SET_FILE_INFORMATION),
|
||||
request,
|
||||
data_off + sizeof(finder_info),
|
||||
data_off,
|
||||
NULL);
|
||||
if (err == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
|
||||
printf("AFP Set File Information returned invalid namespace as expected for path=%s\n", path);
|
||||
@@ -248,7 +282,8 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(verify_buf + 32, finder_info, sizeof(finder_info))) {
|
||||
if ((request_mask & AFP_FINDER_INFO_MASK) &&
|
||||
memcmp(verify_buf + 32, finder_info, sizeof(finder_info))) {
|
||||
fprintf(stderr,
|
||||
"AFP Set File Information verify mismatch: path=%s got_type=%.4s got_creator=%.4s\n",
|
||||
path, verify_buf + 32, verify_buf + 36);
|
||||
@@ -256,9 +291,18 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("AFP Set File Info path=%s bitmap=0x%04x finder_type=%.4s finder_creator=%.4s entry_id=0x%08x verified\n",
|
||||
path, AFP_FINDER_INFO_MASK, finder_info + 0, finder_info + 4,
|
||||
be32_to_cpu(verify_buf + 0));
|
||||
if ((request_mask & AFP_ATTRIBUTES_MASK) &&
|
||||
((be16_to_cpu(verify_buf + 8) & AFP_ATTR_INVISIBLE) != attr_expected)) {
|
||||
fprintf(stderr,
|
||||
"AFP Set File Information attr verify mismatch: path=%s attrs=0x%04x expected_invisible=0x%04x\n",
|
||||
path, be16_to_cpu(verify_buf + 8), attr_expected);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("AFP Set File Info path=%s bitmap=0x%04x attrs=0x%04x finder_type=%.4s finder_creator=%.4s entry_id=0x%08x verified\n",
|
||||
path, request_mask, be16_to_cpu(verify_buf + 8),
|
||||
finder_info + 0, finder_info + 4, be32_to_cpu(verify_buf + 0));
|
||||
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user