dosemu2/test/func_ds3_share_open_access.py
geos_one 91736529d5
Some checks failed
Master / Scheduled (FULL) (push) Has been cancelled
Master / Triggered (push) Has been cancelled
Master / Triggered (ASAN) (push) Has been cancelled
Master / Triggered (FULL) (push) Has been cancelled
New upstream version 2.0pre9.2
2025-08-10 12:35:43 +02:00

457 lines
12 KiB
Python

import re
def _run_all(self, numprocs, fstype, tests, testtype):
testdir = self.mkworkdir('d')
share = "rem Internal share" if self.version == "FDPP kernel" else "c:\\share"
tfile = "set LFN=n\r\n" + "d:\r\n" + share + "\r\n"
for t in tests:
args = t + (testtype,)
if numprocs == "ONE":
tfile += ("c:\\shardlrn single %s %s %s %s\r\n" % args)
else:
tfile += ("c:\\shardlrn primary %s %s %s %s\r\n" % args)
tfile += "rem tests complete\r\n"
tfile += "rem end\r\n"
self.mkfile("testit.bat", tfile)
# compile sources
self.mkexe_with_djgpp("shardlrn", r"""
#include <dos.h>
#include <dir.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#include <share.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* must be 8.3 exactly for FCB tests below */
#define FN1 "FOO34567"
#define FE1 "OLD"
#define FN2 "BAR12345"
#define FE2 "NEW"
unsigned short sharemode(const char *s) {
if (strcmp(s, "SH_COMPAT") == 0)
return SH_COMPAT;
if (strcmp(s, "SH_DENYRW") == 0)
return SH_DENYRW;
if (strcmp(s, "SH_DENYWR") == 0)
return SH_DENYWR;
if (strcmp(s, "SH_DENYRD") == 0)
return SH_DENYRD;
if (strcmp(s, "SH_DENYNO") == 0)
return SH_DENYNO;
return 0xff;
}
unsigned short openmode(const char *s) {
if (strcmp(s, "RW") == 0)
return O_RDWR;
if (s[0] == 'R')
return O_RDONLY;
if (s[0] == 'W')
return O_WRONLY;
return 0xff;
}
int main(int argc, char *argv[]) {
int handle;
int ret;
enum {
PRIMARY,
SECONDARY,
SINGLE,
} ptype;
unsigned short smode, omode;
enum {
DELFCB,
RENFCB,
DELPTH,
RENPTH,
SETATT,
} testmode;
if (argc < 6) {
printf("FAIL: Missing arguments (primary|secondary|single) sharemode openmode expected (DELFCB|RENFCB|DELPTH|RENPTH|SETATT)\n");
return -2;
}
if (strcmp(argv[1], "primary") == 0)
ptype = PRIMARY;
else if (strcmp(argv[1], "secondary") == 0)
ptype = SECONDARY;
else if (strcmp(argv[1], "single") == 0)
ptype = SINGLE;
else {
printf("FAIL: Invalid argument (primary|secondary|single)\n");
return -2;
}
smode = sharemode(argv[2]);
if (smode == 0xff) {
printf("FAIL: Invalid argument sharemode '%s'\n", argv[2]);
return -2;
}
omode = openmode(argv[3]);
if (omode == 0xff) {
printf("FAIL: Invalid argument openmode '%s'\n", argv[3]);
return -2;
}
// expected result is argv[4]
if (strcmp(argv[5], "DELFCB") == 0)
testmode = DELFCB;
else if (strcmp(argv[5], "RENFCB") == 0)
testmode = RENFCB;
else if (strcmp(argv[5], "DELPTH") == 0)
testmode = DELPTH;
else if (strcmp(argv[5], "RENPTH") == 0)
testmode = RENPTH;
else if (strcmp(argv[5], "SETATT") == 0)
testmode = SETATT;
else {
printf("FAIL: Invalid argument testmode '%s'\n", argv[5]);
return -2;
}
// Print results in consistent format
// FAIL:("SH_DENYNO", "RW", "ALLOW")[secondary denied]
if (ptype == PRIMARY || ptype == SINGLE) {
unsigned short mode = smode | omode;
// remove the source file so we can create it anew
unlink(FN1 "." FE1);
// create the file
ret = _dos_creatnew(FN1 "." FE1, _A_NORMAL, &handle);
if (ret != 0) {
printf("FAIL:('%s', '%s', '%s')[primary create]\n", argv[2], argv[3], argv[4]);
return -1;
}
_dos_close(handle);
// remove the target file for rename
unlink(FN2 "." FE2);
// open with correct flags
ret = _dos_open(FN1 "." FE1, mode, &handle);
if (ret != 0) {
printf("FAIL:('%s', '%s', '%s')[primary denied]\n", argv[2], argv[3], argv[4]);
return -1;
}
// printf("INFO: primary: File was opened with mode 0x%04x\n", mode);
}
if (ptype == PRIMARY) {
// Now start second copy
spawnlp(P_WAIT, argv[0], argv[0], "secondary", argv[2], argv[3], argv[4], argv[5], NULL);
_dos_close(handle);
return 0;
}
if (ptype == SECONDARY || ptype == SINGLE) {
switch(testmode) {
case DELFCB: {
/*
DOS 1+ - DELETE FILE USING FCB
AH = 13h
DS:DX -> unopened FCB (see #01345), filename filled with template for
deletion ('?' wildcards allowed)
Return:
AL = status
00h one or more files successfully deleted
FFh no matching files or all were read-only or locked
*/
struct {
uint8_t drive;
char name[8];
char ext[3];
char padding[25];
} fcb;
uint16_t ax;
fcb.drive = 0;
memcpy(&fcb.name, FN1, 8);
memcpy(&fcb.ext, FE1, 3);
asm volatile("int $0x21\n"
: "=a"(ax)
: "a"(0x1305), "d"(&fcb)
: /* nothing clobbered */ );
ret = ax & 0xff;
break;
}
case RENFCB: {
/*
DOS 1+ - RENAME FILE USING FCB
AH = 17h
DS:DX -> modified FCB (see also #01345)
the old filename ('?' wildcards OK) is in the standard location
while the new filename ('?' wildcards OK, no drive) is stored
in the 11 bytes beginning at offset 11h
Return:
AL = status
00h successfully renamed
FFh no matching files,file is read-only, or new name already exists
*/
struct {
uint8_t drive;
char o_name[8];
char o_ext[3];
char pad1[5];
char n_name[8];
char n_ext[3];
char pad2[16];
} xfcb;
uint16_t ax;
xfcb.drive = 0;
memcpy(&xfcb.o_name, FN1, 8);
memcpy(&xfcb.o_ext, FE1, 3);
memcpy(&xfcb.n_name, FN2, 8);
memcpy(&xfcb.n_ext, FE2, 3);
asm volatile("int $0x21\n"
: "=a"(ax)
: "a"(0x1705), "d"(&xfcb)
: /* nothing clobbered */ );
ret = ax & 0xff;
break;
}
case DELPTH: {
/*
DOS 2+ - UNLINK - DELETE FILE
AH = 41h
DS:DX -> ASCIZ filename (no wildcards, but see notes)
CL = attribute mask for deletion (server call only, see notes)
Return:
CF clear if successful
AX destroyed (DOS 3.3) AL seems to be drive of deleted file
CF set on error
AX = error code (02h,03h,05h) (see #01680 at AH=59h/BX=0000h)
*/
char *fname = FN1 "." FE1;
uint8_t carry;
asm volatile("int $0x21\n"
"setc %0\n"
: "=r"(carry)
: "a"(0x4100), "d"(fname), "c"(0x0)
: "cc", "memory");
ret = carry ? -1 : 0;
break;
}
case RENPTH: {
/*
DOS 2+ - RENAME - RENAME FILE
AH = 56h
DS:DX -> ASCIZ filename of existing file (no wildcards, but see below)
ES:DI -> ASCIZ new filename (no wildcards)
CL = attribute mask (server call only, see below)
Return:
CF clear if successful
CF set on error
AX = error code (02h,03h,05h,11h) (see #01680)
*/
char *fn1 = FN1 "." FE1;
char *fn2 = FN2 "." FE2;
uint8_t carry;
asm volatile("int $0x21\n"
"setc %0\n"
: "=r"(carry)
: "a"(0x5600), "d"(fn1), "D"(fn2), "c"(0x0)
: "cc", "memory");
ret = carry ? -1 : 0;
break;
}
case SETATT: {
/*
DOS 2+ - CHMOD - SET FILE ATTRIBUTES
*/
const char *fname = FN1 "." FE1;
unsigned int oattr, nattr;
int rc;
rc = _dos_getfileattr(fname, &oattr);
if (rc != 0) {
printf("FAIL:('%s', '%s', '%s')[secondary getattr1 failed(%d)]\n",
argv[2], argv[3], argv[4], rc);
return -1;
}
if (oattr & _A_HIDDEN) {
printf("FAIL:('%s', '%s', '%s')[secondary getattr1 invalid(0x%02x)]\n",
argv[2], argv[3], argv[4], oattr);
return -1;
}
rc = _dos_setfileattr(fname, oattr | _A_HIDDEN);
if (rc != 0) {
printf("INFO:('%s', '%s', '%s')[secondary setattr failed(%d)]\n",
argv[2], argv[3], argv[4], rc);
} else {
int rc2;
printf("INFO:('%s', '%s', '%s')[secondary setattr success]\n",
argv[2], argv[3], argv[4]);
rc2 = _dos_getfileattr(fname, &nattr);
if (rc2 != 0) {
printf("INFO:('%s', '%s', '%s')[secondary getattr2 failed(%d)]\n",
argv[2], argv[3], argv[4], rc2);
} else if (nattr != (oattr | _A_HIDDEN)) {
printf("INFO:('%s', '%s', '%s')[secondary getattr2 invalid(0x%02x)]\n",
argv[2], argv[3], argv[4], nattr);
}
}
ret = (rc != 0) ? -1 : 0;
break;
}
} // switch
if (ret != 0) {
if (strcmp(argv[4], "DENY") == 0) {
printf("PASS:('%s', '%s', '%s')[secondary denied]\n",
argv[2], argv[3], argv[4]);
} else {
printf("FAIL:('%s', '%s', '%s')[secondary denied]\n",
argv[2], argv[3], argv[4]);
}
return -1;
}
if (strcmp(argv[4], "ALLOW") == 0) {
printf("PASS:('%s', '%s', '%s')[secondary allowed]\n",
argv[2], argv[3], argv[4]);
} else {
printf("FAIL:('%s', '%s', '%s')[secondary allowed]\n",
argv[2], argv[3], argv[4]);
}
}
if (ptype == SINGLE) {
_dos_close(handle);
}
return 0;
}
""")
config = """$_floppy_a = ""\n"""
if fstype == "MFS":
config += """$_hdimage = "dXXXXs/c:hdtype1 dXXXXs/d:hdtype1 +1"\n"""
else: # FAT
name = self.mkimage("12", cwd=testdir)
config += """$_hdimage = "dXXXXs/c:hdtype1 %s +1"\n""" % name
return self.runDosemu("testit.bat", config=config, timeout=60)
TESTS_ONE_PROCESS = (
("SH_COMPAT", "R" , "ALLOW"),
("SH_COMPAT", "W" , "ALLOW"),
("SH_COMPAT", "RW", "ALLOW"),
("SH_DENYRW", "R" , "DENY"),
("SH_DENYRW", "W" , "DENY"),
("SH_DENYRW", "RW", "DENY"),
("SH_DENYWR", "R" , "DENY"),
("SH_DENYWR", "W" , "DENY"),
("SH_DENYWR", "RW", "DENY"),
("SH_DENYRD", "R" , "DENY"),
("SH_DENYRD", "W" , "DENY"),
("SH_DENYRD", "RW", "DENY"),
("SH_DENYNO", "R" , "DENY"),
("SH_DENYNO", "W" , "DENY"),
("SH_DENYNO", "RW", "DENY"),
)
TESTS_TWO_PROCESS_FAT = (
("SH_COMPAT", "R" , "DENY"),
("SH_COMPAT", "W" , "DENY"),
("SH_COMPAT", "RW", "DENY"),
("SH_DENYRW", "R" , "DENY"),
("SH_DENYRW", "W" , "DENY"),
("SH_DENYRW", "RW", "DENY"),
("SH_DENYWR", "R" , "DENY"),
("SH_DENYWR", "W" , "DENY"),
("SH_DENYWR", "RW", "DENY"),
("SH_DENYRD", "R" , "DENY"),
("SH_DENYRD", "W" , "DENY"),
("SH_DENYRD", "RW", "DENY"),
("SH_DENYNO", "R" , "DENY"),
("SH_DENYNO", "W" , "DENY"),
("SH_DENYNO", "RW", "DENY"),
)
# SH_COMPAT makes no sense on network drive, see
# https://github.com/dosemu2/dosemu2/pull/1623#issuecomment-1108776612
TESTS_TWO_PROCESS_MFS = tuple(TESTS_TWO_PROCESS_FAT)[3:]
def _check_single_result(self, results, t):
m = re.search("FAIL:\('%s', '%s', '%s'\)\[.*\]" % t, results)
if m:
self.fail(msg=m.group(0))
def ds3_share_open_access(self, numprocs, fstype, testtype):
if numprocs == "ONE":
tests = TESTS_ONE_PROCESS
else: # TWO
if fstype == "MFS":
tests = TESTS_TWO_PROCESS_MFS
else: # FAT
tests = TESTS_TWO_PROCESS_FAT
results = _run_all(self, numprocs, fstype, tests, testtype)
for t in tests:
with self.subTest(t=t):
_check_single_result(self, results, t)
self.assertIn("rem tests complete", results)