1 /* #includes */ /*{{{C}}}*//*{{{*/
16 #define PHYSICAL_SECTOR_1 1 /* First physical sector */
18 /* Use the INT13 interface rather than INT25/INT26. This appears to
19 * improve performance, but is less well tested. */
22 /* Windows 95 disk I/O functions - based on Stan Mitchell's DISKDUMP.C */
23 #define VWIN32_DIOC_DOS_IOCTL 1 // DOS ioctl calls 4400h-4411h
24 #define VWIN32_DIOC_DOS_INT25 2 // absolute disk read, DOS int 25h
25 #define VWIN32_DIOC_DOS_INT26 3 // absolute disk write, DOS int 26h
26 #define VWIN32_DIOC_DOS_INT13 4 // BIOS INT13 functions
28 typedef struct _DIOC_REGISTERS {
37 DIOC_REGISTERS, *PDIOC_REGISTERS;
43 #define LEVEL1_LOCK_MAX_PERMISSION 0x0001
45 #define DRIVE_IS_REMOTE 0x1000
46 #define DRIVE_IS_SUBST 0x8000
48 /*********************************************************
49 **** Note: all MS-DOS data structures must be packed ****
50 **** on a one-byte boundary. ****
51 *********************************************************/
54 typedef struct _DISKIO {
55 DWORD diStartSector; // sector number to start at
56 WORD diSectors; // number of sectors
57 DWORD diBuffer; // address of buffer
62 WORD midInfoLevel; // information level, must be 0
63 DWORD midSerialNum; // serial number for the medium
64 char midVolLabel[11]; // volume label for the medium
65 char midFileSysType[8]; // type of file system as 8-byte ASCII
69 typedef struct driveparams { /* Disk geometry */
77 BYTE sectorspercluster;
87 DWORD bigtotalsectors;
92 } DRIVEPARAMS, *PDRIVEPARAMS;
95 static char *strwin32error(void) /*{{{*/
97 static char buffer[1024];
99 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
102 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
108 static BOOL LockVolume( HANDLE hDisk ) /*{{{*/
110 DWORD ReturnedByteCount;
112 return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL,
113 0, &ReturnedByteCount, NULL );
116 static BOOL UnlockVolume( HANDLE hDisk ) /*{{{*/
118 DWORD ReturnedByteCount;
120 return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL,
121 0, &ReturnedByteCount, NULL );
124 static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/
126 DWORD ReturnedByteCount;
128 return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL,
129 0, &ReturnedByteCount, NULL );
132 static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
138 reg.reg_EAX = 0x440d; // IOCTL for block device
139 reg.reg_EBX = volume; // one-based drive number
140 reg.reg_ECX = 0x0860; // Get Device params
141 reg.reg_EDX = (DWORD)pParam;
142 reg.reg_Flags = 1; // preset the carry flag
144 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
145 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
147 if ( !bResult || (reg.reg_Flags & 1) )
148 return (reg.reg_EAX & 0xffff);
153 static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
159 reg.reg_EAX = 0x440d; // IOCTL for block device
160 reg.reg_EBX = volume; // one-based drive number
161 reg.reg_ECX = 0x0840; // Set Device params
162 reg.reg_EDX = (DWORD)pParam;
163 reg.reg_Flags = 1; // preset the carry flag
165 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
166 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
168 if ( !bResult || (reg.reg_Flags & 1) )
169 return (reg.reg_EAX & 0xffff);
174 static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/
180 reg.reg_EAX = 0x440d; // IOCTL for block device
181 reg.reg_EBX = volume; // one-based drive number
182 reg.reg_ECX = 0x0866; // Get Media ID
183 reg.reg_EDX = (DWORD)pMid;
184 reg.reg_Flags = 1; // preset the carry flag
186 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
187 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
189 if ( !bResult || (reg.reg_Flags & 1) )
190 return (reg.reg_EAX & 0xffff);
195 static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/
201 reg.reg_EAX = 0x4409; // Is Drive Remote
202 reg.reg_EBX = volume; // one-based drive number
203 reg.reg_Flags = 1; // preset the carry flag
205 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
206 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
208 if ( !bResult || (reg.reg_Flags & 1) )
209 return (reg.reg_EAX & 0xffff);
211 *flags = (WORD)(reg.reg_EDX & 0xffff);
215 static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/
221 reg.reg_EAX = 0x440d; // generic IOCTL
222 reg.reg_ECX = 0x084a; // lock logical volume
223 reg.reg_EBX = volume | (lock_level << 8);
224 reg.reg_EDX = permissions;
225 reg.reg_Flags = 1; // preset the carry flag
227 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
228 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
230 if ( !bResult || (reg.reg_Flags & 1) )
231 return (reg.reg_EAX & 0xffff);
236 static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/
242 reg.reg_EAX = 0x440d;
243 reg.reg_ECX = 0x086a; // lock logical volume
244 reg.reg_EBX = volume;
245 reg.reg_Flags = 1; // preset the carry flag
247 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
248 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
250 if ( !bResult || (reg.reg_Flags & 1) ) return -1;
254 static int w32mode(int mode) /*{{{*/
258 case O_RDONLY: return GENERIC_READ;
259 case O_WRONLY: return GENERIC_WRITE;
261 return GENERIC_READ | GENERIC_WRITE;
265 /* Device_open -- Open an image file */ /*{{{*/
266 const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts)
268 /* Windows 95/NT: floppy drives using handles */
269 if (strlen(filename) == 2 && filename[1] == ':') /* Drive name */
275 dwVers = GetVersion();
277 if (dwVers & 0x80000000L) /* Win32s (3.1) or Win32c (Win95) */
279 int lock, driveno, res, permissions;
280 unsigned short drive_flags;
283 vname[0] = toupper(filename[0]);
284 driveno = vname[0] - 'A' + 1; // 1=A: 2=B:
285 sb->drvtype = CPMDRV_WIN95;
286 sb->hdisk = CreateFile( "\\\\.\\vwin32",
291 FILE_FLAG_DELETE_ON_CLOSE,
295 return "Failed to open VWIN32 driver.";
297 if (VolumeCheck(sb->hdisk, driveno, &drive_flags))
299 CloseHandle(sb->hdisk);
300 return "Invalid drive";
302 res = GetMediaID( sb->hdisk, driveno, &media );
305 const char *lboo = NULL;
307 if ( res == ERROR_INVALID_FUNCTION &&
308 (drive_flags & DRIVE_IS_REMOTE ))
309 lboo = "Network drive";
310 else if (res == ERROR_ACCESS_DENIED) lboo = "Access denied";
311 /* nb: It's perfectly legitimate for GetMediaID() to fail; most CP/M */
312 /* CP/M disks won't have a media ID. */
316 CloseHandle(sb->hdisk);
321 (!memcmp( media.midFileSysType, "CDROM", 5 ) ||
322 !memcmp( media.midFileSysType, "CD001", 5 ) ||
323 !memcmp( media.midFileSysType, "CDAUDIO", 5 )))
325 CloseHandle(sb->hdisk);
326 return "CD-ROM drive";
328 if (w32mode(mode) & GENERIC_WRITE)
330 lock = LEVEL0_LOCK; /* Exclusive access */
335 lock = LEVEL1_LOCK; /* Allow other processes access */
336 permissions = LEVEL1_LOCK_MAX_PERMISSION;
338 if (LockLogicalVolume( sb->hdisk, driveno, lock, permissions))
340 CloseHandle(sb->hdisk);
341 return "Could not acquire a lock on the drive.";
344 sb->fd = driveno; /* 1=A: 2=B: etc - we will need this later */
349 sprintf(vname, "\\\\.\\%s", filename);
350 sb->drvtype = CPMDRV_WINNT;
351 sb->hdisk = CreateFile(vname, /* Name */
352 w32mode(mode), /* Access mode */
353 FILE_SHARE_READ|FILE_SHARE_WRITE, /*Sharing*/
354 NULL, /* Security attributes */
355 OPEN_EXISTING, /* See MSDN */
356 0, /* Flags & attributes */
357 NULL); /* Template file */
359 if (sb->hdisk != INVALID_HANDLE_VALUE)
361 sb->fd = 1; /* Arbitrary value >0 */
362 if (LockVolume(sb->hdisk) == FALSE) /* Lock drive */
364 char *lboo = strwin32error();
365 CloseHandle(sb->hdisk);
370 else return strwin32error();
376 /* Not a floppy. Treat it as a normal file */
379 sb->fd = open(filename, mode);
380 if (sb->fd == -1) return strerror(errno);
381 sb->drvtype = CPMDRV_FILE;
386 /* Device_setGeometry -- Set disk geometry */ /*{{{*/
387 void Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks)
391 this->secLength=secLength;
394 if (this->drvtype == CPMDRV_WIN95)
397 memset(&drvp, 0, sizeof(drvp));
398 if (GetDriveParams( this->hdisk, this->fd, &drvp )) return;
400 drvp.bytespersector = secLength;
401 drvp.sectorspertrack = sectrk;
402 drvp.totalsectors = sectrk * tracks;
404 /* Guess the cylinder/head configuration from the track count. This will
405 * get single-sided 80-track discs wrong, but it's that or double-sided
406 * 40-track (or add cylinder/head counts to diskdefs)
410 drvp.cylinders = tracks;
415 drvp.cylinders = tracks / 2;
419 /* Set up "reasonable" values for the other members */
421 drvp.sectorspercluster = 1024 / secLength;
422 drvp.reservedsectors = 1;
423 drvp.numberofFATs = 2;
424 drvp.sectorcount = sectrk;
425 drvp.rootdirsize = 64;
427 drvp.hiddensectors = 0;
428 drvp.sectorsperfat = 3;
429 for (n = 0; n < sectrk; n++)
431 drvp.sectortable[n*2] = n + PHYSICAL_SECTOR_1; /* Physical sector numbers */
432 drvp.sectortable[n*2+1] = secLength;
441 which should have been read correctly by GetDriveParams().
443 SetDriveParams( this->hdisk, this->fd, &drvp );
447 /* Device_close -- Close an image file */ /*{{{*/
448 const char *Device_close(struct Device *sb)
454 UnlockLogicalVolume(sb->hdisk, sb->fd );
455 if (!CloseHandle( sb->hdisk )) return strwin32error();
459 DismountVolume(sb->hdisk);
460 UnlockVolume(sb->hdisk);
461 if (!CloseHandle(sb->hdisk)) return strwin32error();
464 if (close(sb->fd)) return strerror(errno);
468 /* Device_readSector -- read a physical sector */ /*{{{*/
469 const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf)
475 assert(sector<drive->sectrk);
477 assert(track<drive->tracks);
479 offset = ((sector+track*drive->sectrk)*drive->secLength);
481 if (drive->drvtype == CPMDRV_WINNT)
486 if (SetFilePointer(drive->hdisk, offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
488 return strwin32error();
490 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
493 return strwin32error();
495 res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL);
498 char *lboo = strwin32error();
499 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
503 memcpy(buf, iobuffer, drive->secLength);
504 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
506 if (bytesread < (unsigned)drive->secLength)
508 memset(buf + bytesread, 0, drive->secLength - bytesread);
513 if (drive->drvtype == CPMDRV_WIN95)
522 if (drive->tracks < 44) { cyl = track; head = 0; }
523 else { cyl = track/2; head = track & 1; }
525 reg.reg_EAX = 0x0201; // Read 1 sector
526 reg.reg_EBX = (DWORD)buf;
527 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
528 reg.reg_EDX = (head << 8) | (drive->fd - 1);
529 reg.reg_Flags = 1; // preset the carry flag
530 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
531 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
535 reg.reg_EAX = drive->fd - 1; // zero-based volume number
536 reg.reg_EBX = (DWORD)&di;
537 reg.reg_ECX = 0xffff; // use DISKIO structure
538 reg.reg_Flags = 1; // preset the carry flag
539 di.diStartSector = sector+track*drive->sectrk;
541 di.diBuffer = (DWORD)buf;
542 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25,
543 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
546 if ( !bResult || (reg.reg_Flags & 1) )
548 if (GetLastError()) return strwin32error();
549 return "Unknown read error.";
554 if (lseek(drive->fd,offset,SEEK_SET)==-1)
556 return strerror(errno);
558 if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength)
562 return strerror(errno);
564 else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */
569 /* Device_writeSector -- write physical sector */ /*{{{*/
570 const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf)
576 assert(sector<drive->sectrk);
578 assert(track<drive->tracks);
580 offset = ((sector+track*drive->sectrk)*drive->secLength);
582 if (drive->drvtype == CPMDRV_WINNT)
587 if (SetFilePointer(drive->hdisk, offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
589 return strwin32error();
591 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
594 return strwin32error();
596 memcpy(iobuffer, buf, drive->secLength);
597 res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL);
598 if (!res || (byteswritten < (unsigned)drive->secLength))
600 char *lboo = strwin32error();
601 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
605 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
609 if (drive->drvtype == CPMDRV_WIN95)
618 if (drive->tracks < 44) { cyl = track; head = 0; }
619 else { cyl = track/2; head = track & 1; }
621 reg.reg_EAX = 0x0301; // Write 1 sector
622 reg.reg_EBX = (DWORD)buf;
623 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
624 reg.reg_EDX = (head << 8) | (drive->fd - 1);
625 reg.reg_Flags = 1; // preset the carry flag
626 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
627 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
631 reg.reg_EAX = drive->fd - 1; // zero-based volume number
632 reg.reg_EBX = (DWORD)&di;
633 reg.reg_ECX = 0xffff; // use DISKIO structure
634 reg.reg_Flags = 1; // preset the carry flag
635 di.diStartSector = sector+track*drive->sectrk;
637 di.diBuffer = (DWORD)buf;
638 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26,
639 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
642 if ( !bResult || (reg.reg_Flags & 1) )
644 if (GetLastError()) return strwin32error();
645 return "Unknown write error.";
650 if (lseek(drive->fd,offset, SEEK_SET)==-1)
652 return strerror(errno);
654 if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL;
655 return strerror(errno);