dosutils: use NCP87 DOS info fields in NDIR

Add a Client32 NCP87 helper for obtaining DOS namespace file and
subdirectory information and use it in NDIR.

The new helper reads the classic NCP87 subfunction 6 RIM_ALL DOS info
block, including timestamps, inherited rights, directory identifiers and
size metadata.

Use the NCP87 inherited rights mask for the inherited-rights column while
keeping the existing Client32 effective-rights path for the effective
rights column.  Also use the NCP87 modify, archive, last-access and
creation date fields for the /DATES display, falling back to DOS
findfirst timestamps when the NCP87 info request is not available.

Tighten the /DATES layout so the full Created/Copied timestamp remains
within an 80-column DOS screen.
This commit is contained in:
Mario Fetka
2026-05-25 09:17:56 +02:00
parent 7117bfff68
commit 6d2d3f367f
5 changed files with 244 additions and 18 deletions

View File

@@ -534,10 +534,10 @@ NDIR PUBLIC /DO
The first version intentionally focuses on the common listing path. More The first version intentionally focuses on the common listing path. More
advanced Novell NDIR features such as sorting and restrictions are accepted 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 only as future compatibility points or will be implemented in later revisions. The `/RIGHTS` display uses NCP87 information for inherited rights and
rights columns until inherited-right masks are exposed. The `/DATES` display Client32 effective-rights calls for effective rights. The `/DATES` display
uses the DOS update timestamp and placeholder values for archive/access fields uses NCP87 DOS info fields when available and falls back to DOS findfirst
until full NetWare namespace date fields are available. timestamps otherwise.
### `DEBUG` ### `DEBUG`

115
c32ncp.c
View File

@@ -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, int c32_ncp87_modify_dos_attributes(char *name,
uint16 dir_handle, uint16 dir_handle,
uint32 attrs, uint32 attrs,

View File

@@ -12,6 +12,43 @@ int c32_ncp87_obtain_rim_attributes(const char *name,
uint16 *handle_hi_out); 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, int c32_ncp87_modify_dos_attributes(char *name,
uint16 dir_handle, uint16 dir_handle,
uint32 attrs, uint32 attrs,

102
ndir.c
View File

@@ -48,6 +48,7 @@
#define NDIR_NCP_RIGHT_SUPER 0x0100 #define NDIR_NCP_RIGHT_SUPER 0x0100
static void ndir_dos_date(unsigned date, char *out); 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_S 0x01
#define NDIR_OLD_RIGHT_R 0x02 #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); 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) static void ndir_dos_datetime(unsigned date, unsigned time, char *out)
{ {
int year; int year;
@@ -296,6 +318,35 @@ static void ndir_old_rights_string(uint8 old_rights, char *out)
out[8] = '\0'; 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) static void ndir_rights_string(uint16 ncp_rights, char *out)
{ {
out[0] = (ncp_rights & NDIR_NCP_RIGHT_SUPER) ? 'S' : '-'; 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 fl[20]; /* [RW-A------------] plus NUL */
char path[260]; char path[260];
char eff[10]; 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_dos_date(ff->wr_date, d);
ndir_flags(ff->attrib, fl); ndir_flags(ff->attrib, fl);
if (options & NDIR_OPT_RIGHTS) { if (options & NDIR_OPT_RIGHTS) {
tool_join_path(path, dir, ff->name, sizeof(path));
ndir_effective_rights(path, eff); ndir_effective_rights(path, eff);
/* ndir_inherited_rights(path, inh);
* We do not have the inherited-rights mask yet. Use the effective
* rights in both columns for this first /RIGHTS implementation.
*/
fprintf(stdout, "%-16.16s %-18.18s [%8.8s] [%8.8s]\n", 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) { } else if (options & NDIR_OPT_DATES) {
/* /*
* DOS findfirst gives us the update timestamp only. Keep the Novell * DOS findfirst gives us the update timestamp only. Keep the Novell
* /DATES layout and use placeholders for archive/access data until * /DATES layout and use placeholders for archive/access data until
* full NetWare namespace date fields are available. * full NetWare namespace date fields are available.
*/ */
fprintf(stdout, "%-16.16s %-17.17s 0-00-00 0:00 A %-8.8s %-17.17s\n", if (have_info) {
ff->name, dt, d, dt); 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 { } else {
fprintf(stdout, "%-16.16s %12lu %-17.17s %-18.18s\n", fprintf(stdout, "%-16.16s %12lu %-17.17s %-18.18s\n",
ff->name, (unsigned long)ff->size, dt, fl); 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 dt[24];
char path[260]; char path[260];
char eff[10]; 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)) { if (options & (NDIR_OPT_RIGHTS | NDIR_OPT_DIRS_ONLY)) {
tool_join_path(path, dir, ff->name, sizeof(path));
ndir_effective_rights(path, eff); ndir_effective_rights(path, eff);
ndir_inherited_rights(path, inh);
fprintf(stdout, "%-16.16s [%8.8s] [%8.8s] %-17.17s\n", fprintf(stdout, "%-16.16s [%8.8s] [%8.8s] %-17.17s\n",
ff->name, eff, eff, dt); ff->name, inh, eff, dt);
} else { } else {
fprintf(stdout, "%-16.16s <DIR> %-17.17s\n", ff->name, dt); fprintf(stdout, "%-16.16s <DIR> %-17.17s\n", ff->name, dt);
} }
@@ -499,8 +573,8 @@ static int ndir_list_one(char *spec, int options, int *continuous)
fprintf(stdout, "Files: Flags Rights Rights Owner\n"); fprintf(stdout, "Files: Flags Rights Rights Owner\n");
fprintf(stdout, "---------------- ------------------ ----------- ----------- --------\n"); fprintf(stdout, "---------------- ------------------ ----------- ----------- --------\n");
} else if (options & NDIR_OPT_DATES) { } else if (options & NDIR_OPT_DATES) {
fprintf(stdout, "Files: Last Updated Last Archived * Accessed Created/Copied\n"); fprintf(stdout, "Files: Last Updated Last Archived Accessed Created/Copied\n");
fprintf(stdout, "---------------- ----------------- ------------- -- -------- -----------------\n"); fprintf(stdout, "---------------- ----------------- ----------------- -------- -----------------\n");
} else { } else {
fprintf(stdout, "Files: Size Last Updated Flags Owner\n"); fprintf(stdout, "Files: Size Last Updated Flags Owner\n");
fprintf(stdout, "---------------- ---------------- ----------------- ------------------ --------\n"); fprintf(stdout, "---------------- ---------------- ----------------- ------------------ --------\n");

BIN
net.exe

Binary file not shown.