diff --git a/src/nwconn.c b/src/nwconn.c index 7594801..377484a 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -1150,11 +1150,56 @@ static uint16 afp_basic_attributes(int volume, const char *unixname, const struc return(attributes); } -static uint16 afp_basic_access_privileges(const struct stat *stb) +#define AFP_PRIV_READ 0x0100 +#define AFP_PRIV_WRITE 0x0200 +#define AFP_PRIV_OPEN 0x0400 +#define AFP_PRIV_CREATE 0x0800 +#define AFP_PRIV_DELETE 0x1000 +#define AFP_PRIV_PARENTAL 0x2000 +#define AFP_PRIV_SEARCH 0x4000 +#define AFP_PRIV_MODIFY_ATTRS 0x8000 + +static uint16 afp_access_privileges(int volume, const char *unixname, + const struct stat *stb) { - if (S_ISDIR(stb->st_mode)) - return(0x4000 | 0x2000 | 0x8000); /* search, parental, modify attrs */ - return(0x0100 | 0x0200 | 0x0400 | 0x1000 | 0x8000); /* rw open delete modify */ + int rights; + uint16 privileges = 0; + + rights = tru_get_eff_rights(volume, (uint8 *)unixname, (struct stat *)stb); + if (rights < 0) + return(0); + + if (rights & TRUSTEE_S) + rights |= TRUSTEE_R | TRUSTEE_W | TRUSTEE_O | TRUSTEE_C | + TRUSTEE_E | TRUSTEE_A | TRUSTEE_F | TRUSTEE_M; + + if (S_ISDIR(stb->st_mode)) { + if (rights & (TRUSTEE_C | TRUSTEE_E | TRUSTEE_M)) + privileges |= AFP_PRIV_PARENTAL; + if (rights & TRUSTEE_F) + privileges |= AFP_PRIV_SEARCH; + if (rights & TRUSTEE_M) + privileges |= AFP_PRIV_MODIFY_ATTRS; + return(privileges); + } + + if (rights & TRUSTEE_R) + privileges |= AFP_PRIV_READ; + if (rights & TRUSTEE_W) + privileges |= AFP_PRIV_WRITE; + if (rights & TRUSTEE_O) + privileges |= AFP_PRIV_OPEN; + if (rights & TRUSTEE_C) + privileges |= AFP_PRIV_CREATE; + if (rights & TRUSTEE_E) + privileges |= AFP_PRIV_DELETE; + if (rights & TRUSTEE_M) + privileges |= AFP_PRIV_MODIFY_ATTRS; + + if (get_nw_attrib_dword(volume, (char *)unixname, (struct stat *)stb) & FILE_ATTR_R) + privileges &= ~(AFP_PRIV_WRITE | AFP_PRIV_DELETE); + + return(privileges); } static uint16 afp_count_offspring(const char *unixname, const struct stat *stb) @@ -1246,7 +1291,7 @@ static int afp_fill_file_info_response(const char *unixname, afp_leaf_name_from_path(response + 64, 32, display_path, display_path_len); U32_TO_BE32(get_file_owner(&stbuff), response + 96); afp_leaf_name_from_path(response + 100, 12, display_path, display_path_len); - U16_TO_BE16(afp_basic_access_privileges(&stbuff), response + 112); + U16_TO_BE16(afp_access_privileges(volume, unixname, &stbuff), response + 112); /* ProDOS info at offset 114 stays zero until a real Mac namespace maps it. */ if (entry_id_out) *entry_id_out = entry_id; diff --git a/tests/linux/afp_file_info_smoke.c b/tests/linux/afp_file_info_smoke.c index d276b92..affbb53 100644 --- a/tests/linux/afp_file_info_smoke.c +++ b/tests/linux/afp_file_info_smoke.c @@ -30,7 +30,7 @@ static void usage(const char *prog) { fprintf(stderr, "Usage: %s [--afp20] [--allow-invalid-namespace] [--allow-invalid-path] " - "[--volume N] [--entry-id ID] [--request-mask MASK] [ncpfs options] PATH\n" + "[--volume N] [--entry-id ID] [--request-mask MASK] [--expect-rights-set MASK] [--expect-rights-clear MASK] [ncpfs options] PATH\n" "\n" "ncpfs options are parsed by ncp_initialize(), for example:\n" " -S SERVER -U USER -P PASSWORD -n\n" @@ -106,6 +106,8 @@ int main(int argc, char **argv) uint32_t volume_number = 0; uint32_t entry_id = 0; uint32_t request_mask = AFP_GET_ALL; + uint32_t expect_rights_set = 0; + uint32_t expect_rights_clear = 0; uint32_t afp_subfunction = AFP_GET_FILE_INFORMATION; int i; size_t path_len; @@ -152,6 +154,18 @@ int main(int argc, char **argv) ncp_close(conn); return 2; } + } else if (!strcmp(argv[i], "--expect-rights-set")) { + if (++i >= argc || parse_u32(argv[i], &expect_rights_set) || expect_rights_set > 0xffff) { + fprintf(stderr, "invalid --expect-rights-set value\n"); + ncp_close(conn); + return 2; + } + } else if (!strcmp(argv[i], "--expect-rights-clear")) { + if (++i >= argc || parse_u32(argv[i], &expect_rights_clear) || expect_rights_clear > 0xffff) { + fprintf(stderr, "invalid --expect-rights-clear value\n"); + ncp_close(conn); + return 2; + } } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { usage(argv[0]); ncp_close(conn); @@ -226,6 +240,21 @@ int main(int argc, char **argv) copy_fixed_string(long_name, sizeof(long_name), reply_buf + 64, 32); copy_fixed_string(short_name, sizeof(short_name), reply_buf + 100, 12); + if (expect_rights_set && + (be16_to_cpu(reply_buf + 112) & expect_rights_set) != expect_rights_set) { + fprintf(stderr, "AFP File Info rights missing expected bits: rights=0x%04x expected_set=0x%04x path=%s\n", + be16_to_cpu(reply_buf + 112), (unsigned int)expect_rights_set, path); + ncp_close(conn); + return 1; + } + if (expect_rights_clear && + (be16_to_cpu(reply_buf + 112) & expect_rights_clear) != 0) { + fprintf(stderr, "AFP File Info rights has unexpected bits: rights=0x%04x expected_clear=0x%04x path=%s\n", + be16_to_cpu(reply_buf + 112), (unsigned int)expect_rights_clear, path); + ncp_close(conn); + return 1; + } + printf("AFP File Info subfunction=0x%02x path=%s entry_id=0x%08x parent_id=0x%08x attrs=0x%04x " "data_len=%u resource_len=%u offspring=%u long_name=%s short_name=%s rights=0x%04x\n", (unsigned int)afp_subfunction, diff --git a/tests/linux/afp_smoke_suite.sh b/tests/linux/afp_smoke_suite.sh index 42971ae..6fc1335 100755 --- a/tests/linux/afp_smoke_suite.sh +++ b/tests/linux/afp_smoke_suite.sh @@ -418,6 +418,12 @@ if [ -n "$READONLY_USER" ]; then 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'" \