1 /* #includes */ /*{{{C}}}*//*{{{*/
17 #define PHYSICAL_SECTOR_1 1 /* First physical sector */
19 /* Use the INT13 interface rather than INT25/INT26. This appears to
20 * improve performance, but is less well tested. */
23 /* Windows 95 disk I/O functions - based on Stan Mitchell's DISKDUMP.C */
24 #define VWIN32_DIOC_DOS_IOCTL 1 /* DOS ioctl calls 4400h-4411h */
25 #define VWIN32_DIOC_DOS_INT25 2 /* absolute disk read, DOS int 25h */
26 #define VWIN32_DIOC_DOS_INT26 3 /* absolute disk write, DOS int 26h */
27 #define VWIN32_DIOC_DOS_INT13 4 /* BIOS INT13 functions */
29 typedef struct _DIOC_REGISTERS {
38 DIOC_REGISTERS, *PDIOC_REGISTERS;
44 #define LEVEL1_LOCK_MAX_PERMISSION 0x0001
46 #define DRIVE_IS_REMOTE 0x1000
47 #define DRIVE_IS_SUBST 0x8000
49 /*********************************************************
50 **** Note: all MS-DOS data structures must be packed ****
51 **** on a one-byte boundary. ****
52 *********************************************************/
55 typedef struct _DISKIO {
56 DWORD diStartSector; /* sector number to start at */
57 WORD diSectors; /* number of sectors */
58 DWORD diBuffer; /* address of buffer */
63 WORD midInfoLevel; /* information level, must be 0 */
64 DWORD midSerialNum; /* serial number for the medium */
65 char midVolLabel[11]; /* volume label for the medium */
66 char midFileSysType[8]; /* type of file system as 8-byte ASCII */
70 typedef struct driveparams { /* Disk geometry */
78 BYTE sectorspercluster;
88 DWORD bigtotalsectors;
93 } DRIVEPARAMS, *PDRIVEPARAMS;
96 static char *strwin32error(void) /*{{{*/
98 static char buffer[1024];
100 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
103 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
109 static BOOL LockVolume( HANDLE hDisk ) /*{{{*/
111 DWORD ReturnedByteCount;
113 return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL,
114 0, &ReturnedByteCount, NULL );
117 static BOOL UnlockVolume( HANDLE hDisk ) /*{{{*/
119 DWORD ReturnedByteCount;
121 return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL,
122 0, &ReturnedByteCount, NULL );
125 static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/
127 DWORD ReturnedByteCount;
129 return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL,
130 0, &ReturnedByteCount, NULL );
133 static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
139 reg.reg_EAX = 0x440d; /* IOCTL for block device */
140 reg.reg_EBX = volume; /* one-based drive number */
141 reg.reg_ECX = 0x0860; /* Get Device params */
142 reg.reg_EDX = (DWORD)pParam;
143 reg.reg_Flags = 1; /* preset the carry flag */
145 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
146 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
148 if ( !bResult || (reg.reg_Flags & 1) )
149 return (reg.reg_EAX & 0xffff);
154 static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
160 reg.reg_EAX = 0x440d; /* IOCTL for block device */
161 reg.reg_EBX = volume; /* one-based drive number */
162 reg.reg_ECX = 0x0840; /* Set Device params */
163 reg.reg_EDX = (DWORD)pParam;
164 reg.reg_Flags = 1; /* preset the carry flag */
166 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
167 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
169 if ( !bResult || (reg.reg_Flags & 1) )
170 return (reg.reg_EAX & 0xffff);
175 static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/
181 reg.reg_EAX = 0x440d; /* IOCTL for block device */
182 reg.reg_EBX = volume; /* one-based drive number */
183 reg.reg_ECX = 0x0866; /* Get Media ID */
184 reg.reg_EDX = (DWORD)pMid;
185 reg.reg_Flags = 1; /* preset the carry flag */
187 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
188 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
190 if ( !bResult || (reg.reg_Flags & 1) )
191 return (reg.reg_EAX & 0xffff);
196 static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/
202 reg.reg_EAX = 0x4409; /* Is Drive Remote */
203 reg.reg_EBX = volume; /* one-based drive number */
204 reg.reg_Flags = 1; /* preset the carry flag */
206 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
207 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
209 if ( !bResult || (reg.reg_Flags & 1) )
210 return (reg.reg_EAX & 0xffff);
212 *flags = (WORD)(reg.reg_EDX & 0xffff);
216 static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/
222 reg.reg_EAX = 0x440d; /* generic IOCTL */
223 reg.reg_ECX = 0x084a; /* lock logical volume */
224 reg.reg_EBX = volume | (lock_level << 8);
225 reg.reg_EDX = permissions;
226 reg.reg_Flags = 1; /* preset the carry flag */
228 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
229 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
231 if ( !bResult || (reg.reg_Flags & 1) )
232 return (reg.reg_EAX & 0xffff);
237 static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/
243 reg.reg_EAX = 0x440d;
244 reg.reg_ECX = 0x086a; /* lock logical volume */
245 reg.reg_EBX = volume;
246 reg.reg_Flags = 1; /* preset the carry flag */
248 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
249 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
251 if ( !bResult || (reg.reg_Flags & 1) ) return -1;
255 static int w32mode(int mode) /*{{{*/
259 case O_RDONLY: return GENERIC_READ;
260 case O_WRONLY: return GENERIC_WRITE;
262 return GENERIC_READ | GENERIC_WRITE;
266 /* Device_open -- Open an image file */ /*{{{*/
267 const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts)
269 /* Windows 95/NT: floppy drives using handles */
270 if (strlen(filename) == 2 && filename[1] == ':') /* Drive name */
276 dwVers = GetVersion();
278 if (dwVers & 0x80000000L) /* Win32s (3.1) or Win32c (Win95) */
280 int lock, driveno, res, permissions;
281 unsigned short drive_flags;
284 vname[0] = toupper(filename[0]);
285 driveno = vname[0] - 'A' + 1; /* 1=A: 2=B: */
286 sb->drvtype = CPMDRV_WIN95;
287 sb->hdisk = CreateFile( "\\\\.\\vwin32",
292 FILE_FLAG_DELETE_ON_CLOSE,
296 return "Failed to open VWIN32 driver.";
298 if (VolumeCheck(sb->hdisk, driveno, &drive_flags))
300 CloseHandle(sb->hdisk);
301 return "Invalid drive";
303 res = GetMediaID( sb->hdisk, driveno, &media );
306 const char *lboo = NULL;
308 if ( res == ERROR_INVALID_FUNCTION &&
309 (drive_flags & DRIVE_IS_REMOTE ))
310 lboo = "Network drive";
311 else if (res == ERROR_ACCESS_DENIED) lboo = "Access denied";
312 /* nb: It's perfectly legitimate for GetMediaID() to fail; most CP/M */
313 /* CP/M disks won't have a media ID. */
317 CloseHandle(sb->hdisk);
322 (!memcmp( media.midFileSysType, "CDROM", 5 ) ||
323 !memcmp( media.midFileSysType, "CD001", 5 ) ||
324 !memcmp( media.midFileSysType, "CDAUDIO", 5 )))
326 CloseHandle(sb->hdisk);
327 return "CD-ROM drive";
329 if (w32mode(mode) & GENERIC_WRITE)
331 lock = LEVEL0_LOCK; /* Exclusive access */
336 lock = LEVEL1_LOCK; /* Allow other processes access */
337 permissions = LEVEL1_LOCK_MAX_PERMISSION;
339 if (LockLogicalVolume( sb->hdisk, driveno, lock, permissions))
341 CloseHandle(sb->hdisk);
342 return "Could not acquire a lock on the drive.";
345 sb->fd = driveno; /* 1=A: 2=B: etc - we will need this later */
350 sprintf(vname, "\\\\.\\%s", filename);
351 sb->drvtype = CPMDRV_WINNT;
352 sb->hdisk = CreateFile(vname, /* Name */
353 w32mode(mode), /* Access mode */
354 FILE_SHARE_READ|FILE_SHARE_WRITE, /*Sharing*/
355 NULL, /* Security attributes */
356 OPEN_EXISTING, /* See MSDN */
357 0, /* Flags & attributes */
358 NULL); /* Template file */
360 if (sb->hdisk != INVALID_HANDLE_VALUE)
362 sb->fd = 1; /* Arbitrary value >0 */
363 if (LockVolume(sb->hdisk) == FALSE) /* Lock drive */
365 char *lboo = strwin32error();
366 CloseHandle(sb->hdisk);
371 else return strwin32error();
377 /* Not a floppy. Treat it as a normal file */
380 sb->fd = open(filename, mode);
381 if (sb->fd == -1) return strerror(errno);
382 sb->drvtype = CPMDRV_FILE;
387 /* Device_setGeometry -- Set disk geometry */ /*{{{*/
388 void Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks)
392 this->secLength=secLength;
395 if (this->drvtype == CPMDRV_WIN95)
398 memset(&drvp, 0, sizeof(drvp));
399 if (GetDriveParams( this->hdisk, this->fd, &drvp )) return;
401 drvp.bytespersector = secLength;
402 drvp.sectorspertrack = sectrk;
403 drvp.totalsectors = sectrk * tracks;
405 /* Guess the cylinder/head configuration from the track count. This will
406 * get single-sided 80-track discs wrong, but it's that or double-sided
407 * 40-track (or add cylinder/head counts to diskdefs)
411 drvp.cylinders = tracks;
416 drvp.cylinders = tracks / 2;
420 /* Set up "reasonable" values for the other members */
422 drvp.sectorspercluster = 1024 / secLength;
423 drvp.reservedsectors = 1;
424 drvp.numberofFATs = 2;
425 drvp.sectorcount = sectrk;
426 drvp.rootdirsize = 64;
428 drvp.hiddensectors = 0;
429 drvp.sectorsperfat = 3;
430 for (n = 0; n < sectrk; n++)
432 drvp.sectortable[n*2] = n + PHYSICAL_SECTOR_1; /* Physical sector numbers */
433 drvp.sectortable[n*2+1] = secLength;
442 which should have been read correctly by GetDriveParams().
444 SetDriveParams( this->hdisk, this->fd, &drvp );
448 /* Device_close -- Close an image file */ /*{{{*/
449 const char *Device_close(struct Device *sb)
455 UnlockLogicalVolume(sb->hdisk, sb->fd );
456 if (!CloseHandle( sb->hdisk )) return strwin32error();
460 DismountVolume(sb->hdisk);
461 UnlockVolume(sb->hdisk);
462 if (!CloseHandle(sb->hdisk)) return strwin32error();
465 if (close(sb->fd)) return strerror(errno);
469 /* Device_readSector -- read a physical sector */ /*{{{*/
470 const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf)
476 assert(sector<drive->sectrk);
478 assert(track<drive->tracks);
480 offset = ((sector+track*drive->sectrk)*drive->secLength);
482 if (drive->drvtype == CPMDRV_WINNT)
487 if (SetFilePointer(drive->hdisk, offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
489 return strwin32error();
491 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
494 return strwin32error();
496 res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL);
499 char *lboo = strwin32error();
500 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
504 memcpy(buf, iobuffer, drive->secLength);
505 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
507 if (bytesread < (unsigned)drive->secLength)
509 memset(buf + bytesread, 0, drive->secLength - bytesread);
514 if (drive->drvtype == CPMDRV_WIN95)
523 if (drive->tracks < 44) { cyl = track; head = 0; }
524 else { cyl = track/2; head = track & 1; }
526 reg.reg_EAX = 0x0201; /* Read 1 sector */
527 reg.reg_EBX = (DWORD)buf;
528 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
529 reg.reg_EDX = (head << 8) | (drive->fd - 1);
530 reg.reg_Flags = 1; /* preset the carry flag */
531 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
532 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
536 reg.reg_EAX = drive->fd - 1; /* zero-based volume number */
537 reg.reg_EBX = (DWORD)&di;
538 reg.reg_ECX = 0xffff; /* use DISKIO structure */
539 reg.reg_Flags = 1; /* preset the carry flag */
540 di.diStartSector = sector+track*drive->sectrk;
542 di.diBuffer = (DWORD)buf;
543 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25,
544 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
547 if ( !bResult || (reg.reg_Flags & 1) )
549 if (GetLastError()) return strwin32error();
550 return "Unknown read error.";
555 if (lseek(drive->fd,offset,SEEK_SET)==-1)
557 return strerror(errno);
559 if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength)
563 return strerror(errno);
565 else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */
570 /* Device_writeSector -- write physical sector */ /*{{{*/
571 const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf)
577 assert(sector<drive->sectrk);
579 assert(track<drive->tracks);
581 offset = ((sector+track*drive->sectrk)*drive->secLength);
583 if (drive->drvtype == CPMDRV_WINNT)
588 if (SetFilePointer(drive->hdisk, offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
590 return strwin32error();
592 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
595 return strwin32error();
597 memcpy(iobuffer, buf, drive->secLength);
598 res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL);
599 if (!res || (byteswritten < (unsigned)drive->secLength))
601 char *lboo = strwin32error();
602 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
606 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
610 if (drive->drvtype == CPMDRV_WIN95)
619 if (drive->tracks < 44) { cyl = track; head = 0; }
620 else { cyl = track/2; head = track & 1; }
622 reg.reg_EAX = 0x0301; /* Write 1 sector */
623 reg.reg_EBX = (DWORD)buf;
624 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
625 reg.reg_EDX = (head << 8) | (drive->fd - 1);
626 reg.reg_Flags = 1; /* preset the carry flag */
627 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
628 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
632 reg.reg_EAX = drive->fd - 1; /* zero-based volume number */
633 reg.reg_EBX = (DWORD)&di;
634 reg.reg_ECX = 0xffff; /* use DISKIO structure */
635 reg.reg_Flags = 1; /* preset the carry flag */
636 di.diStartSector = sector+track*drive->sectrk;
638 di.diBuffer = (DWORD)buf;
639 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26,
640 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
643 if ( !bResult || (reg.reg_Flags & 1) )
645 if (GetLastError()) return strwin32error();
646 return "Unknown write error.";
651 if (lseek(drive->fd,offset, SEEK_SET)==-1)
653 return strerror(errno);
655 if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL;
656 return strerror(errno);