1 /* #includes */ /*{{{C}}}*//*{{{*/
3 #define _POSIX_SOURCE 1
5 #define _POSIX_C_SOURCE 2
15 #define PHYSICAL_SECTOR_1 1 /* First physical sector */
17 /* Use the INT13 interface rather than INT25/INT26. This appears to
18 * improve performance, but is less well tested. */
21 /* Windows 95 disk I/O functions - based on Stan Mitchell's DISKDUMP.C */
22 #define VWIN32_DIOC_DOS_IOCTL 1 // DOS ioctl calls 4400h-4411h
23 #define VWIN32_DIOC_DOS_INT25 2 // absolute disk read, DOS int 25h
24 #define VWIN32_DIOC_DOS_INT26 3 // absolute disk write, DOS int 26h
25 #define VWIN32_DIOC_DOS_INT13 4 // BIOS INT13 functions
27 typedef struct _DIOC_REGISTERS {
36 DIOC_REGISTERS, *PDIOC_REGISTERS;
42 #define LEVEL1_LOCK_MAX_PERMISSION 0x0001
44 #define DRIVE_IS_REMOTE 0x1000
45 #define DRIVE_IS_SUBST 0x8000
47 /*********************************************************
48 **** Note: all MS-DOS data structures must be packed ****
49 **** on a one-byte boundary. ****
50 *********************************************************/
53 typedef struct _DISKIO {
54 DWORD diStartSector; // sector number to start at
55 WORD diSectors; // number of sectors
56 DWORD diBuffer; // address of buffer
61 WORD midInfoLevel; // information level, must be 0
62 DWORD midSerialNum; // serial number for the medium
63 char midVolLabel[11]; // volume label for the medium
64 char midFileSysType[8]; // type of file system as 8-byte ASCII
68 typedef struct driveparams { /* Disk geometry */
76 BYTE sectorspercluster;
86 DWORD bigtotalsectors;
91 } DRIVEPARAMS, *PDRIVEPARAMS;
94 static char *strwin32error(void) /*{{{*/
96 static char buffer[1024];
98 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
101 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
107 static BOOL LockVolume( HANDLE hDisk ) /*{{{*/
109 DWORD ReturnedByteCount;
111 return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL,
112 0, &ReturnedByteCount, NULL );
115 static BOOL UnlockVolume( HANDLE hDisk ) /*{{{*/
117 DWORD ReturnedByteCount;
119 return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL,
120 0, &ReturnedByteCount, NULL );
123 static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/
125 DWORD ReturnedByteCount;
127 return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL,
128 0, &ReturnedByteCount, NULL );
131 static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
137 reg.reg_EAX = 0x440d; // IOCTL for block device
138 reg.reg_EBX = volume; // one-based drive number
139 reg.reg_ECX = 0x0860; // Get Device params
140 reg.reg_EDX = (DWORD)pParam;
141 reg.reg_Flags = 1; // preset the carry flag
143 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
144 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
146 if ( !bResult || (reg.reg_Flags & 1) )
147 return (reg.reg_EAX & 0xffff);
152 static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
158 reg.reg_EAX = 0x440d; // IOCTL for block device
159 reg.reg_EBX = volume; // one-based drive number
160 reg.reg_ECX = 0x0840; // Set Device params
161 reg.reg_EDX = (DWORD)pParam;
162 reg.reg_Flags = 1; // preset the carry flag
164 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
165 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
167 if ( !bResult || (reg.reg_Flags & 1) )
168 return (reg.reg_EAX & 0xffff);
173 static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/
179 reg.reg_EAX = 0x440d; // IOCTL for block device
180 reg.reg_EBX = volume; // one-based drive number
181 reg.reg_ECX = 0x0866; // Get Media ID
182 reg.reg_EDX = (DWORD)pMid;
183 reg.reg_Flags = 1; // preset the carry flag
185 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
186 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
188 if ( !bResult || (reg.reg_Flags & 1) )
189 return (reg.reg_EAX & 0xffff);
194 static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/
200 reg.reg_EAX = 0x4409; // Is Drive Remote
201 reg.reg_EBX = volume; // one-based drive number
202 reg.reg_Flags = 1; // preset the carry flag
204 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
205 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
207 if ( !bResult || (reg.reg_Flags & 1) )
208 return (reg.reg_EAX & 0xffff);
210 *flags = (WORD)(reg.reg_EDX & 0xffff);
214 static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/
220 reg.reg_EAX = 0x440d; // generic IOCTL
221 reg.reg_ECX = 0x084a; // lock logical volume
222 reg.reg_EBX = volume | (lock_level << 8);
223 reg.reg_EDX = permissions;
224 reg.reg_Flags = 1; // preset the carry flag
226 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
227 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
229 if ( !bResult || (reg.reg_Flags & 1) )
230 return (reg.reg_EAX & 0xffff);
235 static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/
241 reg.reg_EAX = 0x440d;
242 reg.reg_ECX = 0x086a; // lock logical volume
243 reg.reg_EBX = volume;
244 reg.reg_Flags = 1; // preset the carry flag
246 bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
247 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
249 if ( !bResult || (reg.reg_Flags & 1) ) return -1;
253 static int w32mode(int mode) /*{{{*/
257 case O_RDONLY: return GENERIC_READ;
258 case O_WRONLY: return GENERIC_WRITE;
260 return GENERIC_READ | GENERIC_WRITE;
264 /* Device_open -- Open an image file */ /*{{{*/
265 const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts)
267 /* Windows 95/NT: floppy drives using handles */
268 if (strlen(filename) == 2 && filename[1] == ':') /* Drive name */
274 dwVers = GetVersion();
276 if (dwVers & 0x80000000L) /* Win32s (3.1) or Win32c (Win95) */
278 int lock, driveno, res, permissions;
279 unsigned short drive_flags;
282 vname[0] = toupper(filename[0]);
283 driveno = vname[0] - 'A' + 1; // 1=A: 2=B:
284 sb->drvtype = CPMDRV_WIN95;
285 sb->hdisk = CreateFile( "\\\\.\\vwin32",
290 FILE_FLAG_DELETE_ON_CLOSE,
294 return "Failed to open VWIN32 driver.";
296 if (VolumeCheck(sb->hdisk, driveno, &drive_flags))
298 CloseHandle(sb->hdisk);
299 return "Invalid drive";
301 res = GetMediaID( sb->hdisk, driveno, &media );
304 const char *lboo = NULL;
306 if ( res == ERROR_INVALID_FUNCTION &&
307 (drive_flags & DRIVE_IS_REMOTE ))
308 lboo = "Network drive";
309 else if (res == ERROR_ACCESS_DENIED) lboo = "Access denied";
310 /* nb: It's perfectly legitimate for GetMediaID() to fail; most CP/M */
311 /* CP/M disks won't have a media ID. */
315 CloseHandle(sb->hdisk);
320 (!memcmp( media.midFileSysType, "CDROM", 5 ) ||
321 !memcmp( media.midFileSysType, "CD001", 5 ) ||
322 !memcmp( media.midFileSysType, "CDAUDIO", 5 )))
324 CloseHandle(sb->hdisk);
325 return "CD-ROM drive";
327 if (w32mode(mode) & GENERIC_WRITE)
329 lock = LEVEL0_LOCK; /* Exclusive access */
334 lock = LEVEL1_LOCK; /* Allow other processes access */
335 permissions = LEVEL1_LOCK_MAX_PERMISSION;
337 if (LockLogicalVolume( sb->hdisk, driveno, lock, permissions))
339 CloseHandle(sb->hdisk);
340 return "Could not acquire a lock on the drive.";
343 sb->fd = driveno; /* 1=A: 2=B: etc - we will need this later */
348 sprintf(vname, "\\\\.\\%s", filename);
349 sb->drvtype = CPMDRV_WINNT;
350 sb->hdisk = CreateFile(vname, /* Name */
351 w32mode(mode), /* Access mode */
352 FILE_SHARE_READ|FILE_SHARE_WRITE, /*Sharing*/
353 NULL, /* Security attributes */
354 OPEN_EXISTING, /* See MSDN */
355 0, /* Flags & attributes */
356 NULL); /* Template file */
358 if (sb->hdisk != INVALID_HANDLE_VALUE)
360 sb->fd = 1; /* Arbitrary value >0 */
361 if (LockVolume(sb->hdisk) == FALSE) /* Lock drive */
363 char *lboo = strwin32error();
364 CloseHandle(sb->hdisk);
369 else return strwin32error();
375 /* Not a floppy. Treat it as a normal file */
378 sb->fd = open(filename, mode);
379 if (sb->fd == -1) return strerror(errno);
380 sb->drvtype = CPMDRV_FILE;
385 /* Device_setGeometry -- Set disk geometry */ /*{{{*/
386 void Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks)
390 this->secLength=secLength;
393 if (this->drvtype == CPMDRV_WIN95)
396 memset(&drvp, 0, sizeof(drvp));
397 if (GetDriveParams( this->hdisk, this->fd, &drvp )) return;
399 drvp.bytespersector = secLength;
400 drvp.sectorspertrack = sectrk;
401 drvp.totalsectors = sectrk * tracks;
403 /* Guess the cylinder/head configuration from the track count. This will
404 * get single-sided 80-track discs wrong, but it's that or double-sided
405 * 40-track (or add cylinder/head counts to diskdefs)
409 drvp.cylinders = tracks;
414 drvp.cylinders = tracks / 2;
418 /* Set up "reasonable" values for the other members */
420 drvp.sectorspercluster = 1024 / secLength;
421 drvp.reservedsectors = 1;
422 drvp.numberofFATs = 2;
423 drvp.sectorcount = sectrk;
424 drvp.rootdirsize = 64;
426 drvp.hiddensectors = 0;
427 drvp.sectorsperfat = 3;
428 for (n = 0; n < sectrk; n++)
430 drvp.sectortable[n*2] = n + PHYSICAL_SECTOR_1; /* Physical sector numbers */
431 drvp.sectortable[n*2+1] = secLength;
440 which should have been read correctly by GetDriveParams().
442 SetDriveParams( this->hdisk, this->fd, &drvp );
446 /* Device_close -- Close an image file */ /*{{{*/
447 const char *Device_close(struct Device *sb)
453 UnlockLogicalVolume(sb->hdisk, sb->fd );
454 if (!CloseHandle( sb->hdisk )) return strwin32error();
458 DismountVolume(sb->hdisk);
459 UnlockVolume(sb->hdisk);
460 if (!CloseHandle(sb->hdisk)) return strwin32error();
463 if (close(sb->fd)) return strerror(errno);
467 /* Device_readSector -- read a physical sector */ /*{{{*/
468 const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf)
474 assert(sector<drive->sectrk);
476 assert(track<drive->tracks);
478 offset = ((sector+track*drive->sectrk)*drive->secLength);
480 if (drive->drvtype == CPMDRV_WINNT)
485 if (SetFilePointer(drive->hdisk, offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
487 return strwin32error();
489 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
492 return strwin32error();
494 res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL);
497 char *lboo = strwin32error();
498 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
502 memcpy(buf, iobuffer, drive->secLength);
503 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
505 if (bytesread < (unsigned)drive->secLength)
507 memset(buf + bytesread, 0, drive->secLength - bytesread);
512 if (drive->drvtype == CPMDRV_WIN95)
521 if (drive->tracks < 44) { cyl = track; head = 0; }
522 else { cyl = track/2; head = track & 1; }
524 reg.reg_EAX = 0x0201; // Read 1 sector
525 reg.reg_EBX = (DWORD)buf;
526 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
527 reg.reg_EDX = (head << 8) | (drive->fd - 1);
528 reg.reg_Flags = 1; // preset the carry flag
529 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
530 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
534 reg.reg_EAX = drive->fd - 1; // zero-based volume number
535 reg.reg_EBX = (DWORD)&di;
536 reg.reg_ECX = 0xffff; // use DISKIO structure
537 reg.reg_Flags = 1; // preset the carry flag
538 di.diStartSector = sector+track*drive->sectrk;
540 di.diBuffer = (DWORD)buf;
541 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25,
542 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
545 if ( !bResult || (reg.reg_Flags & 1) )
547 if (GetLastError()) return strwin32error();
548 return "Unknown read error.";
553 if (lseek(drive->fd,offset,SEEK_SET)==-1)
555 return strerror(errno);
557 if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength)
561 return strerror(errno);
563 else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */
568 /* Device_writeSector -- write physical sector */ /*{{{*/
569 const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf)
575 assert(sector<drive->sectrk);
577 assert(track<drive->tracks);
579 offset = ((sector+track*drive->sectrk)*drive->secLength);
581 if (drive->drvtype == CPMDRV_WINNT)
586 if (SetFilePointer(drive->hdisk, offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
588 return strwin32error();
590 iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
593 return strwin32error();
595 memcpy(iobuffer, buf, drive->secLength);
596 res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL);
597 if (!res || (byteswritten < (unsigned)drive->secLength))
599 char *lboo = strwin32error();
600 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
604 VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
608 if (drive->drvtype == CPMDRV_WIN95)
617 if (drive->tracks < 44) { cyl = track; head = 0; }
618 else { cyl = track/2; head = track & 1; }
620 reg.reg_EAX = 0x0301; // Write 1 sector
621 reg.reg_EBX = (DWORD)buf;
622 reg.reg_ECX = (cyl << 8) | (sector + PHYSICAL_SECTOR_1);
623 reg.reg_EDX = (head << 8) | (drive->fd - 1);
624 reg.reg_Flags = 1; // preset the carry flag
625 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
626 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
630 reg.reg_EAX = drive->fd - 1; // zero-based volume number
631 reg.reg_EBX = (DWORD)&di;
632 reg.reg_ECX = 0xffff; // use DISKIO structure
633 reg.reg_Flags = 1; // preset the carry flag
634 di.diStartSector = sector+track*drive->sectrk;
636 di.diBuffer = (DWORD)buf;
637 bResult = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26,
638 ®, sizeof( reg ), ®, sizeof( reg ), &cb, 0 );
641 if ( !bResult || (reg.reg_Flags & 1) )
643 if (GetLastError()) return strwin32error();
644 return "Unknown write error.";
649 if (lseek(drive->fd,offset, SEEK_SET)==-1)
651 return strerror(errno);
653 if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL;
654 return strerror(errno);