Add the CREATOR utility and wire it into the DOS tools build and NET command dispatch. Extend the client-side NCP helpers with the calls needed by the new and updated tools, including DOS namespace information and effective-rights queries. Also update MAP handling for automatic drive mappings used by the login script. This commit contains the tool/source support only. The DOS baseline test suite is kept separate and will be added in a follow-up commit.
525 lines
14 KiB
C
525 lines
14 KiB
C
/* creator.c - friendly creator/modifier/archiver metadata test tool
|
|
*
|
|
* Uses the same NCP 22/25 Set Directory/File Information path as Novell
|
|
* FILER for the special fileinfo/archive metadata fields. This is intended
|
|
* for testing mars_nwe xattr-backed metadata without using the FILER GUI.
|
|
*/
|
|
|
|
#include "net.h"
|
|
#include "c32ncp.h"
|
|
|
|
#define DM_CREATE_DATE 0x00000004UL
|
|
#define DM_CREATE_TIME 0x00000008UL
|
|
#define DM_CREATOR_ID 0x00000010UL
|
|
#define DM_ARCHIVE_DATE 0x00000020UL
|
|
#define DM_ARCHIVE_TIME 0x00000040UL
|
|
#define DM_ARCHIVER_ID 0x00000080UL
|
|
#define DM_MODIFY_DATE 0x00000100UL
|
|
#define DM_MODIFY_TIME 0x00000200UL
|
|
#define DM_MODIFIER_ID 0x00000400UL
|
|
|
|
#define BINDERY_USER 0x0001
|
|
#define BINDERY_GROUP 0x0002
|
|
|
|
static void creator_usage(void)
|
|
{
|
|
fprintf(stdout, "Usage:\n");
|
|
fprintf(stdout, " CREATOR file /SHOW\n");
|
|
fprintf(stdout, " CREATOR file /CREATOR user-or-hexid\n");
|
|
fprintf(stdout, " CREATOR file /MODIFIER user-or-hexid\n");
|
|
fprintf(stdout, " CREATOR file /ARCHIVER user-or-hexid\n");
|
|
fprintf(stdout, " CREATOR file /CREATE user-or-hexid [yyyy-mm-dd [hh:mm:ss]]\n");
|
|
fprintf(stdout, " CREATOR file /MODIFY user-or-hexid [yyyy-mm-dd [hh:mm:ss]]\n");
|
|
fprintf(stdout, " CREATOR file /ARCHIVE user-or-hexid [yyyy-mm-dd [hh:mm:ss]]\n");
|
|
fprintf(stdout, " CREATOR file /ALL user-or-hexid [yyyy-mm-dd [hh:mm:ss]]\n");
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "Examples:\n");
|
|
fprintf(stdout, " CREATOR F:\\CIXTEST\\SUP\\S_SUP.TXT /SHOW\n");
|
|
fprintf(stdout, " CREATOR F:\\CIXTEST\\SUP\\S_SUP.TXT /CREATOR MARIO\n");
|
|
fprintf(stdout, " CREATOR F:\\CIXTEST\\SUP\\S_SUP.TXT /ARCHIVE MARIO\n");
|
|
}
|
|
|
|
static int creator_get_current_drive(void)
|
|
{
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x19;
|
|
int86(0x21, ®s, ®s);
|
|
return((int)regs.h.al);
|
|
}
|
|
|
|
static int creator_current_dhandle(uint8 *dhandle)
|
|
{
|
|
uint8 connid = 0;
|
|
uint8 flags = 0;
|
|
int drive;
|
|
|
|
drive = creator_get_current_drive();
|
|
if (get_drive_info((uint8)drive, &connid, dhandle, &flags))
|
|
return(-1);
|
|
if (!connid || (flags & 0x80))
|
|
return(-1);
|
|
return(0);
|
|
}
|
|
|
|
static int hex_value(int c)
|
|
{
|
|
if (c >= '0' && c <= '9') return(c - '0');
|
|
if (c >= 'a' && c <= 'f') return(c - 'a' + 10);
|
|
if (c >= 'A' && c <= 'F') return(c - 'A' + 10);
|
|
return(-1);
|
|
}
|
|
|
|
static int parse_hex32(char *s, uint32 *out)
|
|
{
|
|
uint32 value = 0;
|
|
int i;
|
|
int v;
|
|
|
|
if (!s || !*s || !out)
|
|
return(-1);
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
s += 2;
|
|
if (!*s) return(-1);
|
|
}
|
|
for (i = 0; s[i]; i++) {
|
|
v = hex_value(s[i]);
|
|
if (v < 0) return(-1);
|
|
value = (value << 4) | (uint32)v;
|
|
}
|
|
*out = value;
|
|
return(0);
|
|
}
|
|
|
|
static uint32 lookup_object_id(char *name)
|
|
{
|
|
uint32 id;
|
|
uint8 namebuf[48];
|
|
|
|
if (!name || !*name)
|
|
return(0);
|
|
|
|
if (!parse_hex32(name, &id))
|
|
return(id);
|
|
|
|
strmaxcpy(namebuf, name, sizeof(namebuf) - 1);
|
|
upstr(namebuf);
|
|
id = ncp_17_35(namebuf, BINDERY_USER);
|
|
if (id)
|
|
return(id);
|
|
id = ncp_17_35(namebuf, BINDERY_GROUP);
|
|
return(id);
|
|
}
|
|
|
|
static int parse_uint(char *s, int *out)
|
|
{
|
|
int v = 0;
|
|
int i;
|
|
|
|
if (!s || !*s || !out)
|
|
return(-1);
|
|
for (i = 0; s[i]; i++) {
|
|
if (s[i] < '0' || s[i] > '9') return(-1);
|
|
v = v * 10 + (s[i] - '0');
|
|
}
|
|
*out = v;
|
|
return(0);
|
|
}
|
|
|
|
static uint16 make_dos_date(int year, int month, int day)
|
|
{
|
|
if (year < 80)
|
|
year += 2000;
|
|
if (year < 1980)
|
|
year = 1980;
|
|
return((uint16)(((year - 1980) << 9) | (month << 5) | day));
|
|
}
|
|
|
|
static uint16 make_dos_time(int hour, int min, int sec)
|
|
{
|
|
return((uint16)((hour << 11) | (min << 5) | (sec / 2)));
|
|
}
|
|
|
|
static void current_dos_datetime(uint16 *date_out, uint16 *time_out)
|
|
{
|
|
REGS regs;
|
|
int year;
|
|
int month;
|
|
int day;
|
|
int hour;
|
|
int min;
|
|
int sec;
|
|
|
|
regs.h.ah = 0x2a;
|
|
int86(0x21, ®s, ®s);
|
|
year = regs.x.cx;
|
|
month = regs.h.dh;
|
|
day = regs.h.dl;
|
|
|
|
regs.h.ah = 0x2c;
|
|
int86(0x21, ®s, ®s);
|
|
hour = regs.h.ch;
|
|
min = regs.h.cl;
|
|
sec = regs.h.dh;
|
|
|
|
if (date_out) *date_out = make_dos_date(year, month, day);
|
|
if (time_out) *time_out = make_dos_time(hour, min, sec);
|
|
}
|
|
|
|
static int parse_date_arg(char *s, uint16 *date_out)
|
|
{
|
|
char buf[32];
|
|
char *p1;
|
|
char *p2;
|
|
int y;
|
|
int m;
|
|
int d;
|
|
uint32 hex;
|
|
|
|
if (!s || !date_out)
|
|
return(-1);
|
|
if (!parse_hex32(s, &hex) && (s[0] == '0' || strlen(s) <= 4)) {
|
|
*date_out = (uint16)hex;
|
|
return(0);
|
|
}
|
|
|
|
strmaxcpy(buf, s, sizeof(buf)-1);
|
|
p1 = strchr(buf, '-');
|
|
if (!p1) p1 = strchr(buf, '.');
|
|
if (!p1) return(-1);
|
|
*p1++ = '\0';
|
|
p2 = strchr(p1, '-');
|
|
if (!p2) p2 = strchr(p1, '.');
|
|
if (!p2) return(-1);
|
|
*p2++ = '\0';
|
|
|
|
if (parse_uint(buf, &y) || parse_uint(p1, &m) || parse_uint(p2, &d))
|
|
return(-1);
|
|
|
|
/* Support both yyyy-mm-dd and dd.mm.yy. */
|
|
if (y < 100 && d > 100) {
|
|
int tmp = y;
|
|
y = d;
|
|
d = tmp;
|
|
}
|
|
if (y < 100) y += 2000;
|
|
if (m < 1 || m > 12 || d < 1 || d > 31)
|
|
return(-1);
|
|
|
|
*date_out = make_dos_date(y, m, d);
|
|
return(0);
|
|
}
|
|
|
|
static int parse_time_arg(char *s, uint16 *time_out)
|
|
{
|
|
char buf[32];
|
|
char *p1;
|
|
char *p2;
|
|
int h;
|
|
int m;
|
|
int sec = 0;
|
|
uint32 hex;
|
|
|
|
if (!s || !time_out)
|
|
return(-1);
|
|
if (!parse_hex32(s, &hex) && (s[0] == '0' || strlen(s) <= 4)) {
|
|
*time_out = (uint16)hex;
|
|
return(0);
|
|
}
|
|
|
|
strmaxcpy(buf, s, sizeof(buf)-1);
|
|
p1 = strchr(buf, ':');
|
|
if (!p1) return(-1);
|
|
*p1++ = '\0';
|
|
p2 = strchr(p1, ':');
|
|
if (p2) *p2++ = '\0';
|
|
|
|
if (parse_uint(buf, &h) || parse_uint(p1, &m))
|
|
return(-1);
|
|
if (p2 && parse_uint(p2, &sec))
|
|
return(-1);
|
|
if (h < 0 || h > 23 || m < 0 || m > 59 || sec < 0 || sec > 59)
|
|
return(-1);
|
|
|
|
*time_out = make_dos_time(h, m, sec);
|
|
return(0);
|
|
}
|
|
|
|
static void print_dos_date(uint16 date)
|
|
{
|
|
int year = ((date >> 9) & 0x7f) + 1980;
|
|
int month = (date >> 5) & 0x0f;
|
|
int day = date & 0x1f;
|
|
fprintf(stdout, "%04d-%02d-%02d", year, month, day);
|
|
}
|
|
|
|
static void print_dos_time(uint16 time)
|
|
{
|
|
int hour = (time >> 11) & 0x1f;
|
|
int min = (time >> 5) & 0x3f;
|
|
int sec = (time & 0x1f) * 2;
|
|
fprintf(stdout, "%02d:%02d:%02d", hour, min, sec);
|
|
}
|
|
|
|
static int copy_ncp22_name(uint8 *dst, char *src, uint8 *len_out)
|
|
{
|
|
char tmp[260];
|
|
int len;
|
|
|
|
if (!dst || !len_out) return(-1);
|
|
if (!src) src = "";
|
|
tool_upcopy(tmp, src, sizeof(tmp));
|
|
if (strchr(tmp, '\\') || strchr(tmp, '/') || strchr(tmp, ':'))
|
|
return(-1);
|
|
len = strlen(tmp);
|
|
if (len < 1 || len > 12)
|
|
return(-1);
|
|
memcpy(dst, tmp, len);
|
|
*len_out = (uint8)len;
|
|
return(0);
|
|
}
|
|
|
|
static int split_path(char *path, char *parent, int parent_size, char *name, int name_size)
|
|
{
|
|
char tmp[260];
|
|
char *p;
|
|
|
|
if (!path || !*path || !parent || !name)
|
|
return(-1);
|
|
|
|
strmaxcpy(tmp, path, sizeof(tmp)-1);
|
|
korrpath(tmp);
|
|
p = strrchr(tmp, '\\');
|
|
if (!p) p = strrchr(tmp, '/');
|
|
|
|
if (p) {
|
|
*p++ = '\0';
|
|
strmaxcpy(parent, tmp, parent_size-1);
|
|
strmaxcpy(name, p, name_size-1);
|
|
} else {
|
|
parent[0] = '\0';
|
|
strmaxcpy(name, tmp, name_size-1);
|
|
}
|
|
|
|
if (!name[0]) return(-1);
|
|
return(0);
|
|
}
|
|
|
|
static int set_info(uint8 dhandle, char *name, uint32 bits,
|
|
uint16 cdate, uint16 ctime, uint32 cid,
|
|
uint16 adate, uint16 atime, uint32 aid,
|
|
uint16 mdate, uint16 mtime, uint32 mid)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 created_time[2];
|
|
uint8 created_date[2];
|
|
uint8 created_id[4];
|
|
uint8 archived_time[2];
|
|
uint8 archived_date[2];
|
|
uint8 archived_id[4];
|
|
uint8 updated_time[2];
|
|
uint8 updated_date[2];
|
|
uint8 updated_id[4];
|
|
uint8 rest[80];
|
|
} req;
|
|
struct { uint16 len; } repl;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
|
|
req.func = 0x25;
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x06;
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(bits, req.change_bits);
|
|
|
|
U16_TO_16(ctime, req.created_time);
|
|
U16_TO_16(cdate, req.created_date);
|
|
U32_TO_BE32(cid, req.created_id);
|
|
U16_TO_16(atime, req.archived_time);
|
|
U16_TO_16(adate, req.archived_date);
|
|
U32_TO_BE32(aid, req.archived_id);
|
|
U16_TO_16(mtime, req.updated_time);
|
|
U16_TO_16(mdate, req.updated_date);
|
|
U32_TO_BE32(mid, req.updated_id);
|
|
|
|
if (copy_ncp22_name(req.name, name, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-neterrno);
|
|
return(0);
|
|
}
|
|
|
|
static int show_info(char *name, uint8 dhandle)
|
|
{
|
|
C32_NDIR_INFO info;
|
|
int rc;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
rc = c32_ncp87_obtain_ndir_info(tool_is_current_path(name) ? "" : name,
|
|
(uint16)dhandle,
|
|
&info,
|
|
NULL, NULL, NULL);
|
|
if (rc) {
|
|
fprintf(stdout, "CREATOR: obtain info failed for %s, rc=%d\n", name, rc);
|
|
return(rc);
|
|
}
|
|
|
|
fprintf(stdout, "File: %s\n", name);
|
|
fprintf(stdout, "Attributes: 0x%08lX\n", (unsigned long)info.attributes);
|
|
fprintf(stdout, "Create: "); print_dos_date(info.creation_date);
|
|
fprintf(stdout, " "); print_dos_time(info.creation_time);
|
|
fprintf(stdout, " creator=0x%08lX\n", (unsigned long)info.creator_id);
|
|
fprintf(stdout, "Modify: "); print_dos_date(info.modify_date);
|
|
fprintf(stdout, " "); print_dos_time(info.modify_time);
|
|
fprintf(stdout, " modifier=0x%08lX\n", (unsigned long)info.modifier_id);
|
|
fprintf(stdout, "Archive: "); print_dos_date(info.archive_date);
|
|
fprintf(stdout, " "); print_dos_time(info.archive_time);
|
|
fprintf(stdout, " archiver=0x%08lX\n", (unsigned long)info.archiver_id);
|
|
fprintf(stdout, "Access date: "); print_dos_date(info.last_access_date);
|
|
fprintf(stdout, "\nRights mask: 0x%04X\n", (unsigned)info.inherited_rights);
|
|
return(0);
|
|
}
|
|
|
|
int func_creator(int argc, char *argv[], int mode)
|
|
{
|
|
char oldcwd[260];
|
|
char parent[260];
|
|
char name[64];
|
|
char *path;
|
|
char *cmd;
|
|
char *id_arg;
|
|
uint8 dhandle = 0;
|
|
uint32 id = 0;
|
|
uint16 date;
|
|
uint16 time;
|
|
uint32 bits = 0;
|
|
int rc = 0;
|
|
int need_id = 0;
|
|
|
|
(void)mode;
|
|
|
|
if (argc < 3) {
|
|
creator_usage();
|
|
return(1);
|
|
}
|
|
|
|
path = argv[1];
|
|
cmd = argv[2];
|
|
upstr(cmd);
|
|
|
|
if (split_path(path, parent, sizeof(parent), name, sizeof(name))) {
|
|
fprintf(stdout, "CREATOR: bad path %s\n", path);
|
|
return(1);
|
|
}
|
|
|
|
if (!getcwd(oldcwd, sizeof(oldcwd)))
|
|
oldcwd[0] = '\0';
|
|
|
|
if (parent[0] && chdir(parent)) {
|
|
fprintf(stdout, "CREATOR: cannot change to parent %s\n", parent);
|
|
return(1);
|
|
}
|
|
|
|
if (creator_current_dhandle(&dhandle)) {
|
|
fprintf(stdout, "CREATOR: current drive is not a network drive\n");
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(1);
|
|
}
|
|
|
|
if (!strcmp(cmd, "/SHOW") || !strcmp(cmd, "SHOW")) {
|
|
rc = show_info(name, dhandle);
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(rc);
|
|
}
|
|
|
|
need_id = 1;
|
|
if (argc < 4) {
|
|
creator_usage();
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(1);
|
|
}
|
|
id_arg = argv[3];
|
|
id = lookup_object_id(id_arg);
|
|
if (!id) {
|
|
fprintf(stdout, "CREATOR: object not found: %s\n", id_arg);
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(1);
|
|
}
|
|
|
|
current_dos_datetime(&date, &time);
|
|
if (argc >= 5 && parse_date_arg(argv[4], &date)) {
|
|
fprintf(stdout, "CREATOR: bad date '%s'\n", argv[4]);
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(1);
|
|
}
|
|
if (argc >= 6 && parse_time_arg(argv[5], &time)) {
|
|
fprintf(stdout, "CREATOR: bad time '%s'\n", argv[5]);
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(1);
|
|
}
|
|
|
|
if (!strcmp(cmd, "/CREATOR") || !strcmp(cmd, "CREATOR")) {
|
|
bits = DM_CREATOR_ID;
|
|
rc = set_info(dhandle, name, bits, 0, 0, id, 0, 0, 0, 0, 0, 0);
|
|
} else if (!strcmp(cmd, "/MODIFIER") || !strcmp(cmd, "MODIFIER")) {
|
|
bits = DM_MODIFIER_ID;
|
|
rc = set_info(dhandle, name, bits, 0, 0, 0, 0, 0, 0, 0, 0, id);
|
|
} else if (!strcmp(cmd, "/ARCHIVER") || !strcmp(cmd, "ARCHIVER")) {
|
|
bits = DM_ARCHIVER_ID;
|
|
rc = set_info(dhandle, name, bits, 0, 0, 0, 0, 0, id, 0, 0, 0);
|
|
} else if (!strcmp(cmd, "/CREATE") || !strcmp(cmd, "CREATE")) {
|
|
bits = DM_CREATE_DATE | DM_CREATE_TIME | DM_CREATOR_ID;
|
|
rc = set_info(dhandle, name, bits, date, time, id, 0, 0, 0, 0, 0, 0);
|
|
} else if (!strcmp(cmd, "/MODIFY") || !strcmp(cmd, "MODIFY")) {
|
|
bits = DM_MODIFY_DATE | DM_MODIFY_TIME | DM_MODIFIER_ID;
|
|
rc = set_info(dhandle, name, bits, 0, 0, 0, 0, 0, 0, date, time, id);
|
|
} else if (!strcmp(cmd, "/ARCHIVE") || !strcmp(cmd, "ARCHIVE")) {
|
|
bits = DM_ARCHIVE_DATE | DM_ARCHIVE_TIME | DM_ARCHIVER_ID;
|
|
rc = set_info(dhandle, name, bits, 0, 0, 0, date, time, id, 0, 0, 0);
|
|
} else if (!strcmp(cmd, "/ALL") || !strcmp(cmd, "ALL")) {
|
|
bits = DM_CREATE_DATE | DM_CREATE_TIME | DM_CREATOR_ID |
|
|
DM_ARCHIVE_DATE | DM_ARCHIVE_TIME | DM_ARCHIVER_ID |
|
|
DM_MODIFY_DATE | DM_MODIFY_TIME | DM_MODIFIER_ID;
|
|
rc = set_info(dhandle, name, bits, date, time, id, date, time, id, date, time, id);
|
|
} else {
|
|
creator_usage();
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(1);
|
|
}
|
|
|
|
if (rc) {
|
|
fprintf(stdout, "CREATOR: NCP22/25 set info failed for %s, rc=%d neterrno=0x%02X\n",
|
|
path, rc, (unsigned)(rc < 0 ? -rc : 0));
|
|
} else {
|
|
fprintf(stdout, "CREATOR: set %s on %s to 0x%08lX OK\n",
|
|
cmd, path, (unsigned long)id);
|
|
if (bits & (DM_CREATE_DATE | DM_MODIFY_DATE | DM_ARCHIVE_DATE)) {
|
|
fprintf(stdout, "CREATOR: date/time "); print_dos_date(date);
|
|
fprintf(stdout, " "); print_dos_time(time); fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
|
|
if (!rc)
|
|
show_info(name, dhandle);
|
|
|
|
if (oldcwd[0]) chdir(oldcwd);
|
|
return(rc ? rc : 0);
|
|
}
|