diff --git a/TODO.md b/TODO.md index 512c65b..820f3ba 100644 --- a/TODO.md +++ b/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. diff --git a/include/nwatalk.h b/include/nwatalk.h index 12c5380..df18ebc 100644 --- a/include/nwatalk.h +++ b/include/nwatalk.h @@ -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); diff --git a/src/nwatalk.c b/src/nwatalk.c index b258ac3..85aeaa6 100644 --- a/src/nwatalk.c +++ b/src/nwatalk.c @@ -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 diff --git a/src/nwconn.c b/src/nwconn.c index 0cdd63c..b837835 100644 --- a/src/nwconn.c +++ b/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); } diff --git a/tests/linux/README.md b/tests/linux/README.md index f34822a..242f5a9 100644 --- a/tests/linux/README.md +++ b/tests/linux/README.md @@ -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 diff --git a/tests/linux/afp_set_file_info_smoke.c b/tests/linux/afp_set_file_info_smoke.c index 4abea7c..51b7529 100644 --- a/tests/linux/afp_set_file_info_smoke.c +++ b/tests/linux/afp_set_file_info_smoke.c @@ -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 @@ -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;