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 const char * Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry)
392 this->secLength=secLength;
395 // Bill Buckels - add this->offset
399 // Bill Buckels - not sure what to do here
400 if (this->drvtype == CPMDRV_WIN95)
403 memset(&drvp, 0, sizeof(drvp));
404 if (GetDriveParams( this->hdisk, this->fd, &drvp )) return "GetDriveParams failed";
406 drvp.bytespersector = secLength;
407 drvp.sectorspertrack = sectrk;
408 drvp.totalsectors = sectrk * tracks;
410 /* Guess the cylinder/head configuration from the track count. This will
411 * get single-sided 80-track discs wrong, but it's that or double-sided
412 * 40-track (or add cylinder/head counts to diskdefs)
416 drvp.cylinders = tracks;
421 drvp.cylinders = tracks / 2;
425 /* Set up "reasonable" values for the other members */
427 drvp.sectorspercluster = 1024 / secLength;
428 drvp.reservedsectors = 1;
429 drvp.numberofFATs = 2;
430 drvp.sectorcount = sectrk;
431 drvp.rootdirsize = 64;
433 drvp.hiddensectors = 0;
434 drvp.sectorsperfat = 3;
435 for (n = 0; n < sectrk; n++)
437 drvp.sectortable[n*2] = n + PHYSICAL_SECTOR_1; /* Physical sector numbers */
438 drvp.sectortable[n*2+1] = secLength;
447 which should have been read correctly by GetDriveParams().
449 SetDriveParams( this->hdisk, this->fd, &drvp );
454 /* Device_close -- Close an image file */ /*{{{*/
455 const char *Device_close(struct Device *sb)
461 UnlockLogicalVolume(sb->hdisk, sb->fd );
462 if (!CloseHandle( sb->hdisk )) return strwin32error();
466 DismountVolume(sb->hdisk);
467 UnlockVolume(sb->hdisk);
468 if (!CloseHandle(sb->hdisk)) return strwin32error();
471 if (close(sb->fd)) return strerror(errno);
475 /* Device_readSector -- read a physical sector */ /*{{{*/
476 const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf)
482 assert(sector<drive->sectrk);
484 assert(track<drive->tracks);
486 offset = ((sector+track*drive->sectrk)*drive->secLength);
488 if (drive->drvtype == CPMDRV_WINNT)
493 // Bill Buckels - add drive->offset
494 if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
496 return strwin32error();
498 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
501 return strwin32error();
503 res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL);
506 char *lboo = strwin32error();
507 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
511 memcpy(buf, iobuffer, drive->secLength);
512 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
514 if (bytesread < (unsigned)drive->secLength)
516 memset(buf + bytesread, 0, drive->secLength - bytesread);
521 // Bill Buckels - not sure what to do here
522 if (drive->drvtype == CPMDRV_WIN95)
531 if (drive->tracks < 44) { cyl = track; head = 0; }
532 else { cyl = track/2; head = track & 1; }
534 reg.reg_EAX = 0x0201; /* Read 1 sector */
535 reg.reg_EBX = (DWORD)buf;
536 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
537 reg.reg_EDX = (head << 8) | (drive->fd - 1);
538 reg.reg_Flags = 1; /* preset the carry flag */
539 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
540 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
544 reg.reg_EAX = drive->fd - 1; /* zero-based volume number */
545 reg.reg_EBX = (DWORD)&di;
546 reg.reg_ECX = 0xffff; /* use DISKIO structure */
547 reg.reg_Flags = 1; /* preset the carry flag */
548 di.diStartSector = sector+track*drive->sectrk;
550 di.diBuffer = (DWORD)buf;
551 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25,
552 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
555 if ( !bResult || (reg.reg_Flags & 1) )
557 if (GetLastError()) return strwin32error();
558 return "Unknown read error.";
563 // Bill Buckels - add drive->offset
564 if (lseek(drive->fd,offset+drive->offset,SEEK_SET)==-1)
566 return strerror(errno);
568 if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength)
572 return strerror(errno);
574 else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */
579 /* Device_writeSector -- write physical sector */ /*{{{*/
580 const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf)
586 assert(sector<drive->sectrk);
588 assert(track<drive->tracks);
590 offset = ((sector+track*drive->sectrk)*drive->secLength);
592 if (drive->drvtype == CPMDRV_WINNT)
597 // Bill Buckels - add drive->offset
598 if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
600 return strwin32error();
602 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
605 return strwin32error();
607 memcpy(iobuffer, buf, drive->secLength);
608 res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL);
609 if (!res || (byteswritten < (unsigned)drive->secLength))
611 char *lboo = strwin32error();
612 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
616 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
620 // Bill Buckels - not sure what to do here
621 if (drive->drvtype == CPMDRV_WIN95)
630 if (drive->tracks < 44) { cyl = track; head = 0; }
631 else { cyl = track/2; head = track & 1; }
633 reg.reg_EAX = 0x0301; /* Write 1 sector */
634 reg.reg_EBX = (DWORD)buf;
635 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
636 reg.reg_EDX = (head << 8) | (drive->fd - 1);
637 reg.reg_Flags = 1; /* preset the carry flag */
638 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
639 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
643 reg.reg_EAX = drive->fd - 1; /* zero-based volume number */
644 reg.reg_EBX = (DWORD)&di;
645 reg.reg_ECX = 0xffff; /* use DISKIO structure */
646 reg.reg_Flags = 1; /* preset the carry flag */
647 di.diStartSector = sector+track*drive->sectrk;
649 di.diBuffer = (DWORD)buf;
650 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26,
651 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
654 if ( !bResult || (reg.reg_Flags & 1) )
656 if (GetLastError()) return strwin32error();
657 return "Unknown write error.";
662 // Bill Buckels - add drive->offset
663 if (lseek(drive->fd,offset+drive->offset, SEEK_SET)==-1)
665 return strerror(errno);
667 if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL;
668 return strerror(errno);