/* * All modifications in this file to the original code are * (C) Copyright 1992, ..., 2005 the "DOSEMU-Development-Team". * * for details see file COPYING in the DOSEMU distribution */ /*********************************************** * File: LREDIR.C * Program for Linux DOSEMU disk redirector functions * Written: 10/29/93 by Tim Bird * Fixes: 1/7/95 by Tim Josling TEJ: check correct # args * : display read/write status correctly * Changes: 3/19/95 by Kang-Jin Lee * Removed unused variables * Changes to eliminate warnings * Display read/write status when redirecting drive * Usage information is more verbose * * Changes: 11/27/97 Hans Lermen * new detection stuff, * rewrote to use plain C and intr(), instead of asm and fiddling with * (maybe used) machine registers. * * Changes: 990703 Hans Lermen * Checking for DosC and exiting with error, if not redirector capable. * Printing DOS errors in clear text. * * Changes: 010211 Bart Oldeman * Make it possible to "reredirect": LREDIR drive filepath * can overwrite the direction for the drive. * * NOTES: * LREDIR supports the following commands: * LREDIR drive filepath * redirects the indicated drive to the specified linux filepath * drive = the drive letter followed by a colon (ex. 'E:') * filepath has linux, fs and a file path (ex. 'LINUX\FS\USR\SRC') * LREDIR DEL drive * cancels redirection of the indicated drive * LREDIR * shows drives that are currently redirected (mapped) * LREDIR HELP or LREDIR ? * show usage information for LREDIR ***********************************************/ #include /* toupper */ #include /* geninterrupt and MK_FP */ #include /* printf */ #include /* exit */ #include #include "detect.h" typedef unsigned char uint8; typedef unsigned int uint16; #define CARRY_FLAG 1 /* carry bit in flags register */ #define CC_SUCCESS 0 #define DOS_GET_LIST_OF_LISTS 0x5200 #define DOS_GET_SDA_POINTER 0x5D06 #define DOS_GET_REDIRECTION 0x5F02 #define DOS_REDIRECT_DEVICE 0x5F03 #define DOS_CANCEL_REDIRECTION 0x5F04 #define MAX_RESOURCE_STRING_LENGTH 36 /* 16 + 16 + 3 for slashes + 1 for NULL */ #define MAX_RESOURCE_PATH_LENGTH 128 /* added to support Linux paths */ #define MAX_DEVICE_STRING_LENGTH 5 /* enough for printer strings */ #define REDIR_PRINTER_TYPE 3 #define REDIR_DISK_TYPE 4 #define READ_ONLY_DRIVE_ATTRIBUTE 1 /* same as NetWare Lite */ #define KEYWORD_DEL "DELETE" #define KEYWORD_DEL_COMPARE_LENGTH 3 #define DEFAULT_REDIR_PARAM 0 #define DOS_HELPER_INT 0xE6 #define MAX_DOSERROR 0x5A static char *DOSerrcodes[MAX_DOSERROR+1] = { /* the below error list is shamelessly stolen from Ralph Brown's */ /* 0x00 */ "no error", #if 0 /* 0x01 */ "function number invalid", #else /* 0x01 */ "invalid argument", #endif /* 0x02 */ "file not found", /* 0x03 */ "path not found", /* 0x04 */ "too many open files", /* 0x05 */ "access denied", /* 0x06 */ "invalid handle", /* 0x07 */ "memory control block destroyed", /* 0x08 */ "insufficient memory", /* 0x09 */ "memory block address invalid", /* 0x0A */ "environment invalid", /* 0x0B */ "format invalid", /* 0x0C */ "access code invalid", /* 0x0D */ "data invalid", /* 0x0E */ 0, /* 0x0F */ "invalid drive", /* 0x10 */ "attempted to remove current directory", /* 0x11 */ "not same device", /* 0x12 */ "no more files", /* 0x13 */ "disk write-protected", /* 0x14 */ "unknown unit", /* 0x15 */ "drive not ready", /* 0x16 */ "unknown command", /* 0x17 */ "data error (CRC)", /* 0x18 */ "bad request structure length", /* 0x19 */ "seek error", /* 0x1A */ "unknown media type", /* 0x1B */ "sector not found", /* 0x1C */ "printer out of paper", /* 0x1D */ "write fault", /* 0x1E */ "read fault", /* 0x1F */ "general failure", /* 0x20 */ "sharing violation", /* 0x21 */ "lock violation", /* 0x22 */ "disk change invalid", /* 0x23 */ "FCB unavailable", /* 0x24 */ "sharing buffer overflow", /* 0x25 */ "code page mismatch", /* 0x26 */ "cannot complete file operation (out of input)", /* 0x27 */ "insufficient disk space", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x32 */ "network request not supported", /* 0x33 */ "remote computer not listening", /* 0x34 */ "duplicate name on network", /* 0x35 */ "network name not found", /* 0x36 */ "network busy", /* 0x37 */ "network device no longer exists", /* 0x38 */ "network BIOS command limit exceeded", /* 0x39 */ "network adapter hardware error", /* 0x3A */ "incorrect response from network", /* 0x3B */ "unexpected network error", /* 0x3C */ "incompatible remote adapter", /* 0x3D */ "print queue full", /* 0x3E */ "queue not full", /* 0x3F */ "not enough space to print file", /* 0x40 */ "network name was deleted", /* 0x41 */ "network: Access denied", /* 0x42 */ "network device type incorrect", /* 0x43 */ "network name not found", /* 0x44 */ "network name limit exceeded", /* 0x45 */ "network BIOS session limit exceeded", /* 0x46 */ "temporarily paused", /* 0x47 */ "network request not accepted", /* 0x48 */ "network print/disk redirection paused", /* 0x49 */ "network software not installed", /* 0x4A */ "unexpected adapter close", /* 0x4B */ "password expired", /* 0x4C */ "login attempt invalid at this time", /* 0x4D */ "disk limit exceeded on network node", /* 0x4E */ "not logged in to network node", /* 0x4F */ 0, /* 0x50 */ "file exists", /* 0x51 */ 0, /* 0x52 */ "cannot make directory", /* 0x53 */ "fail on INT 24h", /* 0x54 */ "too many redirections", /* 0x55 */ "duplicate redirection", /* 0x56 */ "invalid password", /* 0x57 */ "invalid parameter", /* 0x58 */ "network write fault", /* 0x59 */ "function not supported on network", /* 0x5A */ "required system component not installed" #if 0 /* the below won't happen here */ 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x64 */ "(MSCDEX) unknown error", /* 0x65 */ "(MSCDEX) not ready", /* 0x66 */ "(MSCDEX) EMS memory no longer valid", /* 0x67 */ "(MSCDEX) not High Sierra or ISO-9660 format", /* 0x68 */ "(MSCDEX) door open", #endif }; static char * decode_DOS_error(uint16 errcode) { static char *unknown = "unknown error"; if (errcode > MAX_DOSERROR) return unknown; if (!DOSerrcodes[errcode]) return unknown; return DOSerrcodes[errcode]; } char far * GetListOfLists(void) { char far *LOL; struct REGPACK preg; preg.r_ax = DOS_GET_LIST_OF_LISTS; intr(0x21, &preg); LOL = MK_FP(preg.r_es, preg.r_bx); return (LOL); } char far * GetSDAPointer(void) { char far *SDA; struct REGPACK preg; preg.r_ax = DOS_GET_SDA_POINTER; intr(0x21, &preg); SDA = MK_FP(preg.r_ds, preg.r_si); return (SDA); } /******************************************** * InitMFS - call Emulator to initialize MFS ********************************************/ /* tej - changed return type to void as nothing returned */ void InitMFS(void) { char far *LOL; char far *SDA; struct REGPACK preg; LOL = GetListOfLists(); SDA = GetSDAPointer(); /* get DOS version into CX */ preg.r_cx = _osmajor | (_osminor <<8); preg.r_dx = FP_OFF(LOL); preg.r_es = FP_SEG(LOL); preg.r_si = FP_OFF(SDA); preg.r_ds = FP_SEG(SDA); preg.r_bx = 0x500; preg.r_ax = 0x20; intr(DOS_HELPER_INT, &preg); } /******************************************** * RedirectDevice - redirect a device to a remote resource * ON ENTRY: * deviceStr has a string with the device name: * either disk or printer (ex. 'D:' or 'LPT1') * resourceStr has a string with the server and name of resource * (ex. 'TIM\TOOLS') * deviceType indicates the type of device being redirected * 3 = printer, 4 = disk * deviceParameter is a value to be saved with this redirection * which will be returned on GetRedirectionList * ON EXIT: * returns CC_SUCCESS if the operation was successful, * otherwise returns the DOS error code * NOTES: * deviceParameter is used in DOSEMU to return the drive attribute * It is not actually saved and returned as specified by the redirector * specification. This type of usage is common among commercial redirectors. ********************************************/ uint16 RedirectDevice(char *deviceStr, char *resourceStr, uint8 deviceType, uint16 deviceParameter) { char slashedResourceStr[MAX_RESOURCE_PATH_LENGTH]; struct REGPACK preg; /* prepend the resource string with slashes */ strcpy(slashedResourceStr, "\\\\"); strcat(slashedResourceStr, resourceStr); /* should verify strings before sending them down ??? */ preg.r_ds = FP_SEG(deviceStr); preg.r_si = FP_OFF(deviceStr); preg.r_es = FP_SEG(slashedResourceStr); preg.r_di = FP_OFF(slashedResourceStr); preg.r_cx = deviceParameter; preg.r_bx = deviceType; preg.r_ax = DOS_REDIRECT_DEVICE; intr(0x21, &preg); if (preg.r_flags & CARRY_FLAG) { return (preg.r_ax); } else { return (CC_SUCCESS); } } /******************************************** * GetRedirection - get next entry from list of redirected devices * ON ENTRY: * redirIndex has the index of the next device to return * this should start at 0, and be incremented between calls * to retrieve all elements of the redirection list * ON EXIT: * returns CC_SUCCESS if the operation was successful, and * deviceStr has a string with the device name: * either disk or printer (ex. 'D:' or 'LPT1') * resourceStr has a string with the server and name of resource * (ex. 'TIM\TOOLS') * deviceType indicates the type of device which was redirected * 3 = printer, 4 = disk * deviceParameter has my rights to this resource * NOTES: * ********************************************/ uint16 GetRedirection(uint16 redirIndex, char *deviceStr, char *resourceStr, uint8 * deviceType, uint16 * deviceParameter) { uint16 ccode; uint8 deviceTypeTemp; char slashedResourceStr[MAX_RESOURCE_PATH_LENGTH]; struct REGPACK preg; preg.r_ds = FP_SEG(deviceStr); preg.r_si = FP_OFF(deviceStr); preg.r_es = FP_SEG(slashedResourceStr); preg.r_di = FP_OFF(slashedResourceStr); preg.r_bx = redirIndex; preg.r_ax = DOS_GET_REDIRECTION; intr(0x21, &preg); ccode = preg.r_ax; deviceTypeTemp = preg.r_bx & 0xff; /* save device type before C ruins it */ *deviceType = deviceTypeTemp; *deviceParameter = preg.r_cx; /* copy back unslashed portion of resource string */ if (preg.r_flags & CARRY_FLAG) { return (ccode); } else { /* eat the leading slashes */ strcpy(resourceStr, slashedResourceStr + 2); return (CC_SUCCESS); } } /******************************************** * CancelRedirection - delete a device mapped to a remote resource * ON ENTRY: * deviceStr has a string with the device name: * either disk or printer (ex. 'D:' or 'LPT1') * ON EXIT: * returns CC_SUCCESS if the operation was successful, * otherwise returns the DOS error code * NOTES: * ********************************************/ uint16 CancelRedirection(char *deviceStr) { struct REGPACK preg; preg.r_ds = FP_SEG(deviceStr); preg.r_si = FP_OFF(deviceStr); preg.r_ax = DOS_CANCEL_REDIRECTION; intr(0x21, &preg); if (preg.r_flags & CARRY_FLAG) { return (preg.r_ax); } else { return (CC_SUCCESS); } } /************************************* * ShowMyRedirections * show my current drive redirections * NOTES: * I show the read-only attribute for each drive * which is returned in deviceParam. *************************************/ void ShowMyRedirections(void) { int driveCount; uint16 redirIndex, deviceParam, ccode; uint8 deviceType; char deviceStr[MAX_DEVICE_STRING_LENGTH]; char resourceStr[MAX_RESOURCE_PATH_LENGTH]; redirIndex = 0; driveCount = 0; ccode = GetRedirection(redirIndex, deviceStr, resourceStr, &deviceType, &deviceParam); while (ccode == CC_SUCCESS) { /* only print disk redirections here */ if (deviceType == REDIR_DISK_TYPE) { if (driveCount == 0) { printf("Current Drive Redirections:\n"); } driveCount++; printf("%-2s = %-20s ", deviceStr, resourceStr); /* read attribute is returned in the device parameter */ if (deviceParam & 0x80) { switch (deviceParam & 0x7f) { /* tej 1/7/95 */ case READ_ONLY_DRIVE_ATTRIBUTE: printf("attrib = READ ONLY\n"); break; default: printf("attrib = READ/WRITE\n"); break; } } } redirIndex++; ccode = GetRedirection(redirIndex, deviceStr, resourceStr, &deviceType, &deviceParam); } if (driveCount == 0) { printf("No drives are currently redirected to Linux.\n"); } } void DeleteDriveRedirection(char *deviceStr) { uint16 ccode; /* convert device string to upper case */ strupr(deviceStr); ccode = CancelRedirection(deviceStr); if (ccode) { printf("Error %x (%s)\ncanceling redirection on drive %s\n", ccode, decode_DOS_error(ccode), deviceStr); } else { printf("Redirection for drive %s was deleted.\n", deviceStr); } } /******************************************** * Check wether we are running DosC (FreeDos) * and check wether this version can cope with redirection * ON ENTRY: * nothing * ON EXIT: * returns 0 if not running DosC * otherwise returns the DosC 'build' number * ********************************************/ uint16 CheckForDosc(void) { struct REGPACK preg; preg.r_ax = 0xdddc; intr(0xe6, &preg); if (preg.r_ax == 0xdddc) { return 0; } else { return (preg.r_bx); } } main(int argc, char **argv) { uint16 ccode; uint16 deviceParam; unsigned long dversion; char deviceStr[MAX_DEVICE_STRING_LENGTH]; char resourceStr[MAX_RESOURCE_PATH_LENGTH]; if ((dversion = is_dosemu()) == 0) { printf("This program requires DOSEMU to run, aborting\n"); exit(1); } #define D98min ((98UL << 16) | (7 << 8) | 1) #define D99min ((99UL << 16) | (12 << 8) | 1) if (((dversion >= D98min) && (dversion < (99UL << 16))) || (dversion >= D99min)) { if ((ccode = CheckForDosc()) != 0) { if (ccode <= 1937) { printf("You are running the FreeDos kernel.\n" "This has no support for lredir, aborting\n"); exit(1); } } } /* initialize the MFS, just in case the user didn't run EMUFS.SYS */ InitMFS(); /* need to parse the command line */ /* if no parameters, then just show current mappings */ if (argc == 1) { ShowMyRedirections(); exit(0); } /* tej one parm is either error or HELP/-help etc */ if (argc == 2) { printf("Usage: LREDIR [drive: LINUX\\FS\\path [R] | HELP]\n"); printf("Redirect a drive to the Linux file system.\n\n"); printf("LREDIR X: LINUX\\FS\\tmp\n"); printf(" Redirect drive X: to /tmp of Linux file system for read/write\n"); printf(" If R is specified, the drive will be read-only\n"); printf(" ${home} represents user's home directory\n\n"); printf("LREDIR DEL drive:\n"); printf(" delete a drive redirection\n\n"); printf("LREDIR\n"); printf(" show current drive redirections\n\n"); printf("LREDIR HELP\n"); printf(" show this help screen\n"); exit(0); } if (strncmpi(argv[1], KEYWORD_DEL, KEYWORD_DEL_COMPARE_LENGTH) == 0) { DeleteDriveRedirection(argv[2]); exit(0); } /* assume the command is to redirect a drive */ /* read the drive letter and resource string */ strcpy(deviceStr, argv[1]); strcpy(resourceStr, argv[2]); deviceParam = DEFAULT_REDIR_PARAM; if (argc > 3) { if (toupper(argv[3][0]) == 'R') { deviceParam = 1; } } /* upper-case both strings */ strupr(deviceStr); strupr(resourceStr); /* now actually redirect the drive */ ccode = RedirectDevice(deviceStr, resourceStr, REDIR_DISK_TYPE, deviceParam); /* duplicate redirection: try to reredirect */ if (ccode == 0x55) { DeleteDriveRedirection(deviceStr); ccode = RedirectDevice(deviceStr, resourceStr, REDIR_DISK_TYPE, deviceParam); } if (ccode) { printf("Error %x (%s)\nwhile redirecting drive %s to %s\n", ccode, decode_DOS_error(ccode), deviceStr, resourceStr); goto MainExit; } printf("%s = %s attrib = ", deviceStr, resourceStr); switch (deviceParam) { case READ_ONLY_DRIVE_ATTRIBUTE: printf("READ ONLY\n"); break; default: printf("READ/WRITE\n"); break; } MainExit: return (ccode); }