diff --git a/README.md b/README.md index e5d54cd..a0441f8 100644 --- a/README.md +++ b/README.md @@ -534,10 +534,10 @@ NDIR PUBLIC /DO The first version intentionally focuses on the common listing path. More advanced Novell NDIR features such as sorting and restrictions are accepted -only as future compatibility points or will be implemented in later revisions. The `/RIGHTS` display currently uses effective rights for both -rights columns until inherited-right masks are exposed. The `/DATES` display -uses the DOS update timestamp and placeholder values for archive/access fields -until full NetWare namespace date fields are available. +only as future compatibility points or will be implemented in later revisions. The `/RIGHTS` display uses NCP87 information for inherited rights and +Client32 effective-rights calls for effective rights. The `/DATES` display +uses NCP87 DOS info fields when available and falls back to DOS findfirst +timestamps otherwise. ### `DEBUG` diff --git a/c32ncp.c b/c32ncp.c index 6cc7c70..6efb100 100644 --- a/c32ncp.c +++ b/c32ncp.c @@ -314,6 +314,121 @@ int c32_ncp87_obtain_rim_attributes(const char *name, } +int c32_ncp87_obtain_ndir_info(const char *path_name, + uint16 dir_handle, + C32_NDIR_INFO *info_out, + uint16 *actual_out, + uint16 *handle_lo_out, + uint16 *handle_hi_out) +{ + uint16 handle_lo, handle_hi; + uint8 hdr[16]; + uint8 path[0x140]; + uint8 rep0[0x180]; + uint8 rep1[0x40]; + uint8 rawout[32]; + uint16 raw_ret_ax, raw_ret_dx; + uint16 actual_lo; + UI path_len; + int rc; + int namelen; + + if (!info_out) + return(1); + + memset(info_out, 0, sizeof(*info_out)); + if (actual_out) *actual_out = 0; + if (handle_lo_out) *handle_lo_out = 0; + if (handle_hi_out) *handle_hi_out = 0; + + rc = c32_get_ncp_handle(&handle_lo, &handle_hi); + if (rc) + return(10 + rc); + + /* + * NCP87 subfunction 6: Obtain File or Subdirectory Information. + * + * This asks for the classic DOS info block (RIM_ALL). ncpfs' old + * nw_info_struct layout is: + * +00 space allocated + * +04 attributes + * +20 creation time/date/id + * +28 modify time/date/id + * +36 last access date + * +38 archive time/date/id + * +46 inherited rights mask + * +48 directory numbers + * +76 name length/name + */ + memset(hdr, 0, sizeof(hdr)); + hdr[0] = 6; /* NCP87 subfunction 6 */ + hdr[1] = 0; /* source namespace DOS */ + hdr[2] = 0; /* target namespace DOS */ + c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */ + c32_put_dword_lh(hdr + 5, 0x00000FFFUL); /* RIM_ALL */ + + path_len = c32_build_handle_path_from_dos_path(path, (uint8)dir_handle, + 0, 0, path_name); + + memset(rep0, 0, sizeof(rep0)); + memset(rep1, 0, sizeof(rep1)); + memset(rawout, 0, sizeof(rawout)); + + C32_NCP87_Raw5_Probe(handle_lo, handle_hi, + hdr, 9, + path, path_len, + rep0, sizeof(rep0), + rep1, sizeof(rep1), + rawout); + + raw_ret_ax = c32_get_word_lh(rawout + 14); + raw_ret_dx = c32_get_word_lh(rawout + 16); + actual_lo = c32_get_word_lh(rawout + 18); + + if (actual_out) *actual_out = actual_lo; + if (handle_lo_out) *handle_lo_out = handle_lo; + if (handle_hi_out) *handle_hi_out = handle_hi; + + if (raw_ret_ax != 0 || raw_ret_dx != 0) + return(20); + + info_out->space_allocated = c32_get_dword_lh(rep0 + 0); + info_out->attributes = c32_get_dword_lh(rep0 + 4); + info_out->flags = c32_get_word_lh(rep0 + 8); + info_out->data_size = c32_get_dword_lh(rep0 + 10); + info_out->total_size = c32_get_dword_lh(rep0 + 14); + info_out->number_of_streams = c32_get_word_lh(rep0 + 18); + info_out->creation_time = c32_get_word_lh(rep0 + 20); + info_out->creation_date = c32_get_word_lh(rep0 + 22); + info_out->creator_id = c32_get_dword_hl(rep0 + 24); + info_out->modify_time = c32_get_word_lh(rep0 + 28); + info_out->modify_date = c32_get_word_lh(rep0 + 30); + info_out->modifier_id = c32_get_dword_hl(rep0 + 32); + info_out->last_access_date = c32_get_word_lh(rep0 + 36); + info_out->archive_time = c32_get_word_lh(rep0 + 38); + info_out->archive_date = c32_get_word_lh(rep0 + 40); + info_out->archiver_id = c32_get_dword_hl(rep0 + 42); + info_out->inherited_rights = c32_get_word_lh(rep0 + 46); + info_out->dir_ent_num = c32_get_dword_lh(rep0 + 48); + info_out->dos_dir_num = c32_get_dword_lh(rep0 + 52); + info_out->vol_number = c32_get_dword_lh(rep0 + 56); + info_out->ea_data_size = c32_get_dword_lh(rep0 + 60); + info_out->ea_key_count = c32_get_dword_lh(rep0 + 64); + info_out->ea_key_size = c32_get_dword_lh(rep0 + 68); + info_out->ns_creator = c32_get_dword_lh(rep0 + 72); + + namelen = rep0[76]; + if (namelen > 255) + namelen = 255; + info_out->name_len = (uint8)namelen; + memcpy(info_out->name, rep0 + 77, namelen); + info_out->name[namelen] = '\0'; + + return(0); +} + + + int c32_ncp87_modify_dos_attributes(char *name, uint16 dir_handle, uint32 attrs, diff --git a/c32ncp.h b/c32ncp.h index 7cf823d..1279591 100644 --- a/c32ncp.h +++ b/c32ncp.h @@ -12,6 +12,43 @@ int c32_ncp87_obtain_rim_attributes(const char *name, uint16 *handle_hi_out); +typedef struct c32_ndir_info { + uint32 space_allocated; + uint32 attributes; + uint16 flags; + uint32 data_size; + uint32 total_size; + uint16 number_of_streams; + uint16 creation_time; + uint16 creation_date; + uint32 creator_id; + uint16 modify_time; + uint16 modify_date; + uint32 modifier_id; + uint16 last_access_date; + uint16 archive_time; + uint16 archive_date; + uint32 archiver_id; + uint16 inherited_rights; + uint32 dir_ent_num; + uint32 dos_dir_num; + uint32 vol_number; + uint32 ea_data_size; + uint32 ea_key_count; + uint32 ea_key_size; + uint32 ns_creator; + uint8 name_len; + char name[256]; +} C32_NDIR_INFO; + +int c32_ncp87_obtain_ndir_info(const char *path_name, + uint16 dir_handle, + C32_NDIR_INFO *info_out, + uint16 *actual_out, + uint16 *handle_lo_out, + uint16 *handle_hi_out); + + int c32_ncp87_modify_dos_attributes(char *name, uint16 dir_handle, uint32 attrs, diff --git a/ndir.c b/ndir.c index 1ee07c6..e8007dd 100644 --- a/ndir.c +++ b/ndir.c @@ -48,6 +48,7 @@ #define NDIR_NCP_RIGHT_SUPER 0x0100 static void ndir_dos_date(unsigned date, char *out); +static void ndir_dos_datetime(unsigned date, unsigned time, char *out); #define NDIR_OLD_RIGHT_S 0x01 #define NDIR_OLD_RIGHT_R 0x02 @@ -207,6 +208,27 @@ static void ndir_dos_date(unsigned date, char *out) sprintf(out, "%02d-%02d-%02d", month, day, year % 100); } +static void ndir_dos_datetime_or_blank(unsigned date, unsigned time, char *out) +{ + if (!date) { + strcpy(out, "0-00-00 0:00 A"); + return; + } + + ndir_dos_datetime(date, time, out); +} + +static void ndir_dos_date_or_blank(unsigned date, char *out) +{ + if (!date) { + strcpy(out, "0-00-00"); + return; + } + + ndir_dos_date(date, out); +} + + static void ndir_dos_datetime(unsigned date, unsigned time, char *out) { int year; @@ -296,6 +318,35 @@ static void ndir_old_rights_string(uint8 old_rights, char *out) out[8] = '\0'; } +static int ndir_get_ncp_info(char *path, C32_NDIR_INFO *info) +{ + uint8 connid = 0; + uint8 dhandle = 0; + + if (!info) + return(1); + + memset(info, 0, sizeof(*info)); + + if (tool_current_dhandle(&connid, &dhandle)) + return(1); + + return(c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path, + (uint16)dhandle, + info, + NULL, NULL, NULL)); +} + +static void ndir_inherited_rights(char *path, char *out) +{ + C32_NDIR_INFO info; + + strcpy(out, "--------"); + + if (!ndir_get_ncp_info(path, &info)) + ndir_old_rights_string((uint8)info.inherited_rights, out); +} + static void ndir_rights_string(uint16 ncp_rights, char *out) { out[0] = (ncp_rights & NDIR_NCP_RIGHT_SUPER) ? 'S' : '-'; @@ -371,28 +422,45 @@ static void ndir_print_file(char *dir, struct find_t *ff, int options, char fl[20]; /* [RW-A------------] plus NUL */ char path[260]; char eff[10]; + char inh[10]; + char arch[24]; + char acc[12]; + char crea[24]; + C32_NDIR_INFO info; + int have_info = 0; + + tool_join_path(path, dir, ff->name, sizeof(path)); + have_info = !ndir_get_ncp_info(path, &info); + + if (have_info) + ndir_dos_datetime(info.modify_date, info.modify_time, dt); + else + ndir_dos_datetime(ff->wr_date, ff->wr_time, dt); - ndir_dos_datetime(ff->wr_date, ff->wr_time, dt); ndir_dos_date(ff->wr_date, d); ndir_flags(ff->attrib, fl); if (options & NDIR_OPT_RIGHTS) { - tool_join_path(path, dir, ff->name, sizeof(path)); ndir_effective_rights(path, eff); - /* - * We do not have the inherited-rights mask yet. Use the effective - * rights in both columns for this first /RIGHTS implementation. - */ + ndir_inherited_rights(path, inh); fprintf(stdout, "%-16.16s %-18.18s [%8.8s] [%8.8s]\n", - ff->name, fl, eff, eff); + ff->name, fl, inh, eff); } else if (options & NDIR_OPT_DATES) { /* * DOS findfirst gives us the update timestamp only. Keep the Novell * /DATES layout and use placeholders for archive/access data until * full NetWare namespace date fields are available. */ - fprintf(stdout, "%-16.16s %-17.17s 0-00-00 0:00 A %-8.8s %-17.17s\n", - ff->name, dt, d, dt); + if (have_info) { + ndir_dos_datetime_or_blank(info.archive_date, info.archive_time, arch); + ndir_dos_date_or_blank(info.last_access_date, acc); + ndir_dos_datetime_or_blank(info.creation_date, info.creation_time, crea); + fprintf(stdout, "%-16.16s %-17.17s %-17.17s %-8.8s %-17.17s\n", + ff->name, dt, arch, acc, crea); + } else { + fprintf(stdout, "%-16.16s %-17.17s 0-00-00 0:00 A %-8.8s %-17.17s\n", + ff->name, dt, d, dt); + } } else { fprintf(stdout, "%-16.16s %12lu %-17.17s %-18.18s\n", ff->name, (unsigned long)ff->size, dt, fl); @@ -407,14 +475,20 @@ static void ndir_print_dir(char *dir, struct find_t *ff, int options, char dt[24]; char path[260]; char eff[10]; + char inh[10]; + C32_NDIR_INFO info; - ndir_dos_datetime(ff->wr_date, ff->wr_time, dt); + tool_join_path(path, dir, ff->name, sizeof(path)); + if (!ndir_get_ncp_info(path, &info)) + ndir_dos_datetime(info.creation_date, info.creation_time, dt); + else + ndir_dos_datetime(ff->wr_date, ff->wr_time, dt); if (options & (NDIR_OPT_RIGHTS | NDIR_OPT_DIRS_ONLY)) { - tool_join_path(path, dir, ff->name, sizeof(path)); ndir_effective_rights(path, eff); + ndir_inherited_rights(path, inh); fprintf(stdout, "%-16.16s [%8.8s] [%8.8s] %-17.17s\n", - ff->name, eff, eff, dt); + ff->name, inh, eff, dt); } else { fprintf(stdout, "%-16.16s