/* #includes */ /*{{{C}}}*//*{{{*/ #include "config.h" #include #include #include #include #include "cpmdir.h" #include "cpmfs.h" #ifdef USE_DMALLOC #include #endif /*}}}*/ /* types */ /*{{{*/ #define PHYSICAL_SECTOR_1 1 /* First physical sector */ /* Use the INT13 interface rather than INT25/INT26. This appears to * improve performance, but is less well tested. */ #define USE_INT13 /* Windows 95 disk I/O functions - based on Stan Mitchell's DISKDUMP.C */ #define VWIN32_DIOC_DOS_IOCTL 1 /* DOS ioctl calls 4400h-4411h */ #define VWIN32_DIOC_DOS_INT25 2 /* absolute disk read, DOS int 25h */ #define VWIN32_DIOC_DOS_INT26 3 /* absolute disk write, DOS int 26h */ #define VWIN32_DIOC_DOS_INT13 4 /* BIOS INT13 functions */ typedef struct _DIOC_REGISTERS { DWORD reg_EBX; DWORD reg_EDX; DWORD reg_ECX; DWORD reg_EAX; DWORD reg_EDI; DWORD reg_ESI; DWORD reg_Flags; } DIOC_REGISTERS, *PDIOC_REGISTERS; #define LEVEL0_LOCK 0 #define LEVEL1_LOCK 1 #define LEVEL2_LOCK 2 #define LEVEL3_LOCK 3 #define LEVEL1_LOCK_MAX_PERMISSION 0x0001 #define DRIVE_IS_REMOTE 0x1000 #define DRIVE_IS_SUBST 0x8000 /********************************************************* **** Note: all MS-DOS data structures must be packed **** **** on a one-byte boundary. **** *********************************************************/ #pragma pack(1) typedef struct _DISKIO { DWORD diStartSector; /* sector number to start at */ WORD diSectors; /* number of sectors */ DWORD diBuffer; /* address of buffer */ } DISKIO, *PDISKIO; typedef struct MID { WORD midInfoLevel; /* information level, must be 0 */ DWORD midSerialNum; /* serial number for the medium */ char midVolLabel[11]; /* volume label for the medium */ char midFileSysType[8]; /* type of file system as 8-byte ASCII */ } MID, *PMID; typedef struct driveparams { /* Disk geometry */ BYTE special; BYTE devicetype; WORD deviceattrs; WORD cylinders; BYTE mediatype; /* BPB starts here */ WORD bytespersector; BYTE sectorspercluster; WORD reservedsectors; BYTE numberofFATs; WORD rootdirsize; WORD totalsectors; BYTE mediaid; WORD sectorsperfat; WORD sectorspertrack; WORD heads; DWORD hiddensectors; DWORD bigtotalsectors; BYTE reserved[6]; /* BPB ends here */ WORD sectorcount; WORD sectortable[80]; } DRIVEPARAMS, *PDRIVEPARAMS; /*}}}*/ static char *strwin32error(void) /*{{{*/ { static char buffer[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR)buffer, 1023, NULL); return buffer; } /*}}}*/ static BOOL LockVolume( HANDLE hDisk ) /*{{{*/ { DWORD ReturnedByteCount; return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &ReturnedByteCount, NULL ); } /*}}}*/ static BOOL UnlockVolume( HANDLE hDisk ) /*{{{*/ { DWORD ReturnedByteCount; return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &ReturnedByteCount, NULL ); } /*}}}*/ static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/ { DWORD ReturnedByteCount; return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &ReturnedByteCount, NULL ); } /*}}}*/ static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/ { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; reg.reg_EAX = 0x440d; /* IOCTL for block device */ reg.reg_EBX = volume; /* one-based drive number */ reg.reg_ECX = 0x0860; /* Get Device params */ reg.reg_EDX = (DWORD)pParam; reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); if ( !bResult || (reg.reg_Flags & 1) ) return (reg.reg_EAX & 0xffff); return 0; } /*}}}*/ static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/ { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; reg.reg_EAX = 0x440d; /* IOCTL for block device */ reg.reg_EBX = volume; /* one-based drive number */ reg.reg_ECX = 0x0840; /* Set Device params */ reg.reg_EDX = (DWORD)pParam; reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); if ( !bResult || (reg.reg_Flags & 1) ) return (reg.reg_EAX & 0xffff); return 0; } /*}}}*/ static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/ { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; reg.reg_EAX = 0x440d; /* IOCTL for block device */ reg.reg_EBX = volume; /* one-based drive number */ reg.reg_ECX = 0x0866; /* Get Media ID */ reg.reg_EDX = (DWORD)pMid; reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); if ( !bResult || (reg.reg_Flags & 1) ) return (reg.reg_EAX & 0xffff); return 0; } /*}}}*/ static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/ { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; reg.reg_EAX = 0x4409; /* Is Drive Remote */ reg.reg_EBX = volume; /* one-based drive number */ reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); if ( !bResult || (reg.reg_Flags & 1) ) return (reg.reg_EAX & 0xffff); *flags = (WORD)(reg.reg_EDX & 0xffff); return 0; } /*}}}*/ static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/ { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; reg.reg_EAX = 0x440d; /* generic IOCTL */ reg.reg_ECX = 0x084a; /* lock logical volume */ reg.reg_EBX = volume | (lock_level << 8); reg.reg_EDX = permissions; reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); if ( !bResult || (reg.reg_Flags & 1) ) return (reg.reg_EAX & 0xffff); return 0; } /*}}}*/ static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/ { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; reg.reg_EAX = 0x440d; reg.reg_ECX = 0x086a; /* lock logical volume */ reg.reg_EBX = volume; reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); if ( !bResult || (reg.reg_Flags & 1) ) return -1; return 0; } /*}}}*/ static int w32mode(int mode) /*{{{*/ { switch(mode) { case O_RDONLY: return GENERIC_READ; case O_WRONLY: return GENERIC_WRITE; } return GENERIC_READ | GENERIC_WRITE; } /*}}}*/ /* Device_open -- Open an image file */ /*{{{*/ const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts) { /* Windows 95/NT: floppy drives using handles */ if (strlen(filename) == 2 && filename[1] == ':') /* Drive name */ { char vname[20]; DWORD dwVers; sb->fd = -1; dwVers = GetVersion(); if (dwVers & 0x80000000L) /* Win32s (3.1) or Win32c (Win95) */ { int lock, driveno, res, permissions; unsigned short drive_flags; MID media; vname[0] = toupper(filename[0]); driveno = vname[0] - 'A' + 1; /* 1=A: 2=B: */ sb->drvtype = CPMDRV_WIN95; sb->hdisk = CreateFile( "\\\\.\\vwin32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL ); if (!sb->hdisk) { return "Failed to open VWIN32 driver."; } if (VolumeCheck(sb->hdisk, driveno, &drive_flags)) { CloseHandle(sb->hdisk); return "Invalid drive"; } res = GetMediaID( sb->hdisk, driveno, &media ); if ( res ) { const char *lboo = NULL; if ( res == ERROR_INVALID_FUNCTION && (drive_flags & DRIVE_IS_REMOTE )) lboo = "Network drive"; else if (res == ERROR_ACCESS_DENIED) lboo = "Access denied"; /* nb: It's perfectly legitimate for GetMediaID() to fail; most CP/M */ /* CP/M disks won't have a media ID. */ if (lboo != NULL) { CloseHandle(sb->hdisk); return lboo; } } if (!res && (!memcmp( media.midFileSysType, "CDROM", 5 ) || !memcmp( media.midFileSysType, "CD001", 5 ) || !memcmp( media.midFileSysType, "CDAUDIO", 5 ))) { CloseHandle(sb->hdisk); return "CD-ROM drive"; } if (w32mode(mode) & GENERIC_WRITE) { lock = LEVEL0_LOCK; /* Exclusive access */ permissions = 0; } else { lock = LEVEL1_LOCK; /* Allow other processes access */ permissions = LEVEL1_LOCK_MAX_PERMISSION; } if (LockLogicalVolume( sb->hdisk, driveno, lock, permissions)) { CloseHandle(sb->hdisk); return "Could not acquire a lock on the drive."; } sb->fd = driveno; /* 1=A: 2=B: etc - we will need this later */ } else { sprintf(vname, "\\\\.\\%s", filename); sb->drvtype = CPMDRV_WINNT; sb->hdisk = CreateFile(vname, /* Name */ w32mode(mode), /* Access mode */ FILE_SHARE_READ|FILE_SHARE_WRITE, /*Sharing*/ NULL, /* Security attributes */ OPEN_EXISTING, /* See MSDN */ 0, /* Flags & attributes */ NULL); /* Template file */ if (sb->hdisk != INVALID_HANDLE_VALUE) { sb->fd = 1; /* Arbitrary value >0 */ if (LockVolume(sb->hdisk) == FALSE) /* Lock drive */ { char *lboo = strwin32error(); CloseHandle(sb->hdisk); sb->fd = -1; return lboo; } } else return strwin32error(); } sb->opened = 1; return NULL; } /* Not a floppy. Treat it as a normal file */ mode |= O_BINARY; sb->fd = open(filename, mode); if (sb->fd == -1) return strerror(errno); sb->drvtype = CPMDRV_FILE; sb->opened = 1; return NULL; } /*}}}*/ /* Device_setGeometry -- Set disk geometry */ /*{{{*/ const char * Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry) { int n; this->secLength=secLength; this->sectrk=sectrk; this->tracks=tracks; // Bill Buckels - add this->offset this->offset=offset; // Bill Buckels - not sure what to do here if (this->drvtype == CPMDRV_WIN95) { DRIVEPARAMS drvp; memset(&drvp, 0, sizeof(drvp)); if (GetDriveParams( this->hdisk, this->fd, &drvp )) return "GetDriveParams failed"; drvp.bytespersector = secLength; drvp.sectorspertrack = sectrk; drvp.totalsectors = sectrk * tracks; /* Guess the cylinder/head configuration from the track count. This will * get single-sided 80-track discs wrong, but it's that or double-sided * 40-track (or add cylinder/head counts to diskdefs) */ if (tracks < 44) { drvp.cylinders = tracks; drvp.heads = 1; } else { drvp.cylinders = tracks / 2; drvp.heads = 2; } /* Set up "reasonable" values for the other members */ drvp.sectorspercluster = 1024 / secLength; drvp.reservedsectors = 1; drvp.numberofFATs = 2; drvp.sectorcount = sectrk; drvp.rootdirsize = 64; drvp.mediaid = 0xF0; drvp.hiddensectors = 0; drvp.sectorsperfat = 3; for (n = 0; n < sectrk; n++) { drvp.sectortable[n*2] = n + PHYSICAL_SECTOR_1; /* Physical sector numbers */ drvp.sectortable[n*2+1] = secLength; } drvp.special = 6; /* We have not set: drvp.mediatype drvp.devicetype drvp.deviceattrs which should have been read correctly by GetDriveParams(). */ SetDriveParams( this->hdisk, this->fd, &drvp ); } return NULL; } /*}}}*/ /* Device_close -- Close an image file */ /*{{{*/ const char *Device_close(struct Device *sb) { sb->opened = 0; switch(sb->drvtype) { case CPMDRV_WIN95: UnlockLogicalVolume(sb->hdisk, sb->fd ); if (!CloseHandle( sb->hdisk )) return strwin32error(); return NULL; case CPMDRV_WINNT: DismountVolume(sb->hdisk); UnlockVolume(sb->hdisk); if (!CloseHandle(sb->hdisk)) return strwin32error(); return NULL; } if (close(sb->fd)) return strerror(errno); return NULL; } /*}}}*/ /* Device_readSector -- read a physical sector */ /*{{{*/ const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf) { int res; off_t offset; assert(sector>=0); assert(sectorsectrk); assert(track>=0); assert(tracktracks); offset = ((sector+track*drive->sectrk)*drive->secLength); if (drive->drvtype == CPMDRV_WINNT) { LPVOID iobuffer; DWORD bytesread; // Bill Buckels - add drive->offset if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE) { return strwin32error(); } iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE); if (!iobuffer) { return strwin32error(); } res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL); if (!res) { char *lboo = strwin32error(); VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); return lboo; } memcpy(buf, iobuffer, drive->secLength); VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); if (bytesread < (unsigned)drive->secLength) { memset(buf + bytesread, 0, drive->secLength - bytesread); } return NULL; } // Bill Buckels - not sure what to do here if (drive->drvtype == CPMDRV_WIN95) { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; #ifdef USE_INT13 int cyl, head; if (drive->tracks < 44) { cyl = track; head = 0; } else { cyl = track/2; head = track & 1; } reg.reg_EAX = 0x0201; /* Read 1 sector */ reg.reg_EBX = (DWORD)buf; reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1); reg.reg_EDX = (head << 8) | (drive->fd - 1); reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); #else DISKIO di; reg.reg_EAX = drive->fd - 1; /* zero-based volume number */ reg.reg_EBX = (DWORD)&di; reg.reg_ECX = 0xffff; /* use DISKIO structure */ reg.reg_Flags = 1; /* preset the carry flag */ di.diStartSector = sector+track*drive->sectrk; di.diSectors = 1; di.diBuffer = (DWORD)buf; bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); #endif if ( !bResult || (reg.reg_Flags & 1) ) { if (GetLastError()) return strwin32error(); return "Unknown read error."; } return 0; } // Bill Buckels - add drive->offset if (lseek(drive->fd,offset+drive->offset,SEEK_SET)==-1) { return strerror(errno); } if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength) { if (res==-1) { return strerror(errno); } else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */ } return NULL; } /*}}}*/ /* Device_writeSector -- write physical sector */ /*{{{*/ const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf) { off_t offset; int res; assert(sector>=0); assert(sectorsectrk); assert(track>=0); assert(tracktracks); offset = ((sector+track*drive->sectrk)*drive->secLength); if (drive->drvtype == CPMDRV_WINNT) { LPVOID iobuffer; DWORD byteswritten; // Bill Buckels - add drive->offset if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE) { return strwin32error(); } iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE); if (!iobuffer) { return strwin32error(); } memcpy(iobuffer, buf, drive->secLength); res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL); if (!res || (byteswritten < (unsigned)drive->secLength)) { char *lboo = strwin32error(); VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); return lboo; } VirtualFree(iobuffer, drive->secLength, MEM_RELEASE); return NULL; } // Bill Buckels - not sure what to do here if (drive->drvtype == CPMDRV_WIN95) { DIOC_REGISTERS reg; BOOL bResult; DWORD cb; #ifdef USE_INT13 int cyl, head; if (drive->tracks < 44) { cyl = track; head = 0; } else { cyl = track/2; head = track & 1; } reg.reg_EAX = 0x0301; /* Write 1 sector */ reg.reg_EBX = (DWORD)buf; reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1); reg.reg_EDX = (head << 8) | (drive->fd - 1); reg.reg_Flags = 1; /* preset the carry flag */ bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); #else DISKIO di; reg.reg_EAX = drive->fd - 1; /* zero-based volume number */ reg.reg_EBX = (DWORD)&di; reg.reg_ECX = 0xffff; /* use DISKIO structure */ reg.reg_Flags = 1; /* preset the carry flag */ di.diStartSector = sector+track*drive->sectrk; di.diSectors = 1; di.diBuffer = (DWORD)buf; bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26, ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 ); #endif if ( !bResult || (reg.reg_Flags & 1) ) { if (GetLastError()) return strwin32error(); return "Unknown write error."; } return NULL; } // Bill Buckels - add drive->offset if (lseek(drive->fd,offset+drive->offset, SEEK_SET)==-1) { return strerror(errno); } if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL; return strerror(errno); } /*}}}*/