1 /* #includes */ /*{{{C}}}*//*{{{*/
2 #include "config.h"
3 
4 #include <assert.h>
5 #include <ctype.h>
6 #include <errno.h>
7 #include <stdio.h>
8 
9 #include "cpmdir.h"
10 #include "cpmfs.h"
11 /*}}}*/
12 /* types */ /*{{{*/
13 #define PHYSICAL_SECTOR_1       1 /* First physical sector */
14 
15 /* Use the INT13 interface rather than INT25/INT26. This appears to
16  * improve performance, but is less well tested. */
17 #define USE_INT13
18 
19 /* Windows 95 disk I/O functions - based on Stan Mitchell's DISKDUMP.C */
20 #define VWIN32_DIOC_DOS_IOCTL   1   /* DOS ioctl calls 4400h-4411h */
21 #define VWIN32_DIOC_DOS_INT25   2   /* absolute disk read, DOS int 25h */
22 #define VWIN32_DIOC_DOS_INT26   3   /* absolute disk write, DOS int 26h */
23 #define VWIN32_DIOC_DOS_INT13   4   /* BIOS INT13 functions */
24 
25 typedef struct _DIOC_REGISTERS {
26     DWORD reg_EBX;
27     DWORD reg_EDX;
28     DWORD reg_ECX;
29     DWORD reg_EAX;
30     DWORD reg_EDI;
31     DWORD reg_ESI;
32     DWORD reg_Flags;
33     }
34     DIOC_REGISTERS, *PDIOC_REGISTERS;
35 
36 #define   LEVEL0_LOCK   0
37 #define   LEVEL1_LOCK   1
38 #define   LEVEL2_LOCK   2
39 #define   LEVEL3_LOCK   3
40 #define   LEVEL1_LOCK_MAX_PERMISSION      0x0001
41 
42 #define   DRIVE_IS_REMOTE                 0x1000
43 #define   DRIVE_IS_SUBST                  0x8000
44 
45 /*********************************************************
46  **** Note: all MS-DOS data structures must be packed ****
47  ****       on a one-byte boundary.                   ****
48  *********************************************************/
49 #pragma pack(1)
50 
51 typedef struct _DISKIO {
52     DWORD diStartSector;    /* sector number to start at */
53     WORD  diSectors;        /* number of sectors */
54     DWORD diBuffer;         /* address of buffer */
55     }
56     DISKIO, *PDISKIO;
57 
58 typedef struct MID {
59     WORD  midInfoLevel;       /* information level, must be 0 */
60     DWORD midSerialNum;       /* serial number for the medium */
61     char  midVolLabel[11];    /* volume label for the medium */
62     char  midFileSysType[8];  /* type of file system as 8-byte ASCII */
63     }
64     MID, *PMID;
65 
66 typedef struct driveparams {    /* Disk geometry */
67     BYTE special;
68     BYTE devicetype;
69     WORD deviceattrs;
70     WORD cylinders;
71     BYTE mediatype;
72     /* BPB starts here */
73     WORD bytespersector;
74     BYTE sectorspercluster;
75     WORD reservedsectors;
76     BYTE numberofFATs;
77     WORD rootdirsize;
78     WORD totalsectors;
79     BYTE mediaid;
80     WORD sectorsperfat;
81     WORD sectorspertrack;
82     WORD heads;
83     DWORD hiddensectors;
84     DWORD bigtotalsectors;
85     BYTE  reserved[6];
86     /* BPB ends here */
87     WORD sectorcount;
88     WORD sectortable[80];
89     } DRIVEPARAMS, *PDRIVEPARAMS;
90 /*}}}*/
91 
strwin32error(void)92 static char *strwin32error(void) /*{{{*/
93 {
94     static char buffer[1024];
95 
96     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
97                   NULL,
98                   GetLastError(),
99                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
100                   (LPTSTR)buffer,
101                   1023, NULL);
102     return buffer;
103 }
104 /*}}}*/
LockVolume(HANDLE hDisk)105 static BOOL LockVolume( HANDLE hDisk ) /*{{{*/
106 {
107     DWORD ReturnedByteCount;
108 
109     return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL,
110                 0, &ReturnedByteCount, NULL );
111 }
112 /*}}}*/
UnlockVolume(HANDLE hDisk)113 static BOOL UnlockVolume( HANDLE hDisk )  /*{{{*/
114 {
115     DWORD ReturnedByteCount;
116 
117     return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL,
118                 0, &ReturnedByteCount, NULL );
119 }
120 /*}}}*/
DismountVolume(HANDLE hDisk)121 static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/
122 {
123     DWORD ReturnedByteCount;
124 
125     return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL,
126                 0, &ReturnedByteCount, NULL );
127 }
128 /*}}}*/
GetDriveParams(HANDLE hVWin32Device,int volume,DRIVEPARAMS * pParam)129 static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
130   {
131   DIOC_REGISTERS reg;
132   BOOL bResult;
133   DWORD cb;
134 
135   reg.reg_EAX = 0x440d; /* IOCTL for block device */
136   reg.reg_EBX = volume; /* one-based drive number */
137   reg.reg_ECX = 0x0860; /* Get Device params */
138   reg.reg_EDX = (DWORD)pParam;
139   reg.reg_Flags = 1; /* preset the carry flag */
140 
141   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
142               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
143 
144   if ( !bResult || (reg.reg_Flags & 1) )
145       return (reg.reg_EAX & 0xffff);
146 
147   return 0;
148   }
149 /*}}}*/
SetDriveParams(HANDLE hVWin32Device,int volume,DRIVEPARAMS * pParam)150 static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
151   {
152   DIOC_REGISTERS reg;
153   BOOL bResult;
154   DWORD cb;
155 
156   reg.reg_EAX = 0x440d; /* IOCTL for block device */
157   reg.reg_EBX = volume; /* one-based drive number */
158   reg.reg_ECX = 0x0840; /* Set Device params */
159   reg.reg_EDX = (DWORD)pParam;
160   reg.reg_Flags = 1; /* preset the carry flag */
161 
162   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
163               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
164 
165   if ( !bResult || (reg.reg_Flags & 1) )
166       return (reg.reg_EAX & 0xffff);
167 
168   return 0;
169   }
170 /*}}}*/
GetMediaID(HANDLE hVWin32Device,int volume,MID * pMid)171 static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/
172   {
173   DIOC_REGISTERS reg;
174   BOOL bResult;
175   DWORD cb;
176 
177   reg.reg_EAX = 0x440d; /* IOCTL for block device */
178   reg.reg_EBX = volume; /* one-based drive number */
179   reg.reg_ECX = 0x0866; /* Get Media ID */
180   reg.reg_EDX = (DWORD)pMid;
181   reg.reg_Flags = 1; /* preset the carry flag */
182 
183   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
184               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
185 
186   if ( !bResult || (reg.reg_Flags & 1) )
187       return (reg.reg_EAX & 0xffff);
188 
189   return 0;
190   }
191 /*}}}*/
VolumeCheck(HANDLE hVWin32Device,int volume,WORD * flags)192 static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/
193 {
194   DIOC_REGISTERS reg;
195   BOOL bResult;
196   DWORD cb;
197 
198   reg.reg_EAX = 0x4409; /* Is Drive Remote */
199   reg.reg_EBX = volume; /* one-based drive number */
200   reg.reg_Flags = 1; /* preset the carry flag */
201 
202   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
203               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
204 
205   if ( !bResult || (reg.reg_Flags & 1) )
206       return (reg.reg_EAX & 0xffff);
207 
208   *flags = (WORD)(reg.reg_EDX & 0xffff);
209   return 0;
210 }
211 /*}}}*/
LockLogicalVolume(HANDLE hVWin32Device,int volume,int lock_level,int permissions)212 static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/
213 {
214   DIOC_REGISTERS reg;
215   BOOL bResult;
216   DWORD cb;
217 
218   reg.reg_EAX = 0x440d; /* generic IOCTL */
219   reg.reg_ECX = 0x084a; /* lock logical volume */
220   reg.reg_EBX = volume | (lock_level << 8);
221   reg.reg_EDX = permissions;
222   reg.reg_Flags = 1; /* preset the carry flag */
223 
224   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
225               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
226 
227   if ( !bResult || (reg.reg_Flags & 1) )
228       return (reg.reg_EAX & 0xffff);
229 
230   return 0;
231 }
232 /*}}}*/
UnlockLogicalVolume(HANDLE hVWin32Device,int volume)233 static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/
234 {
235   DIOC_REGISTERS reg;
236   BOOL bResult;
237   DWORD cb;
238 
239   reg.reg_EAX = 0x440d;
240   reg.reg_ECX = 0x086a; /* lock logical volume  */
241   reg.reg_EBX = volume;
242   reg.reg_Flags = 1; /* preset the carry flag */
243 
244   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
245               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
246 
247   if ( !bResult || (reg.reg_Flags & 1) ) return -1;
248   return 0;
249 }
250 /*}}}*/
w32mode(int mode)251 static int w32mode(int mode) /*{{{*/
252 {
253     switch(mode)
254     {
255         case O_RDONLY: return GENERIC_READ;
256         case O_WRONLY: return GENERIC_WRITE;
257     }
258     return GENERIC_READ | GENERIC_WRITE;
259 }
260 /*}}}*/
261 
262 /* Device_open           -- Open an image file                      */ /*{{{*/
Device_open(struct Device * sb,const char * filename,int mode,const char * deviceOpts)263 const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts)
264 {
265     if (deviceOpts != NULL)
266     {
267         return "Win32 driver accepts no options (build compiled without libdsk)";
268     }
269     /* Windows 95/NT: floppy drives using handles */
270     if (strlen(filename) == 2 && filename[1] == ':')    /* Drive name */
271     {
272         char vname[20];
273         DWORD dwVers;
274 
275         sb->fd = -1;
276         dwVers = GetVersion();
277 
278         if (dwVers & 0x80000000L) /* Win32s (3.1) or Win32c (Win95) */
279         {
280             int lock, driveno, res, permissions;
281             unsigned short drive_flags;
282             MID media;
283 
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",
288                                        0,
289                                        0,
290                                        NULL,
291                                        0,
292                                        FILE_FLAG_DELETE_ON_CLOSE,
293                                        NULL );
294             if (!sb->hdisk)
295             {
296                 return "Failed to open VWIN32 driver.";
297             }
298             if (VolumeCheck(sb->hdisk, driveno, &drive_flags))
299             {
300                 CloseHandle(sb->hdisk);
301                 return "Invalid drive";
302             }
303             res = GetMediaID( sb->hdisk, driveno, &media );
304             if ( res )
305             {
306                 const char *lboo = NULL;
307 
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. */
314 
315                 if (lboo != NULL)
316                 {
317                    CloseHandle(sb->hdisk);
318                    return lboo;
319                 }
320             }
321             if (!res &&
322                 (!memcmp( media.midFileSysType, "CDROM", 5 ) ||
323                  !memcmp( media.midFileSysType, "CD001", 5 ) ||
324                  !memcmp( media.midFileSysType, "CDAUDIO", 5 )))
325             {
326                 CloseHandle(sb->hdisk);
327                 return "CD-ROM drive";
328             }
329             if (w32mode(mode) & GENERIC_WRITE)
330             {
331                 lock = LEVEL0_LOCK; /* Exclusive access */
332                 permissions = 0;
333             }
334             else
335             {
336                 lock = LEVEL1_LOCK; /* Allow other processes access */
337                 permissions = LEVEL1_LOCK_MAX_PERMISSION;
338             }
339             if (LockLogicalVolume( sb->hdisk, driveno, lock, permissions))
340             {
341                 CloseHandle(sb->hdisk);
342                 return "Could not acquire a lock on the drive.";
343             }
344 
345             sb->fd = driveno;   /* 1=A: 2=B: etc - we will need this later */
346 
347         }
348         else
349         {
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 */
359 
360             if (sb->hdisk != INVALID_HANDLE_VALUE)
361             {
362                 sb->fd = 1;   /* Arbitrary value >0 */
363                 if (LockVolume(sb->hdisk) == FALSE)    /* Lock drive */
364                 {
365                     char *lboo = strwin32error();
366                     CloseHandle(sb->hdisk);
367                     sb->fd = -1;
368                     return lboo;
369                 }
370             }
371             else return strwin32error();
372         }
373         sb->opened = 1;
374         return NULL;
375     }
376 
377     /* Not a floppy. Treat it as a normal file */
378 
379     mode |= O_BINARY;
380     sb->fd = open(filename, mode);
381     if (sb->fd == -1) return strerror(errno);
382     sb->drvtype = CPMDRV_FILE;
383     sb->opened  = 1;
384     return NULL;
385 }
386 /*}}}*/
387 /* Device_setGeometry    -- Set disk geometry                       */ /*{{{*/
Device_setGeometry(struct Device * this,int secLength,int sectrk,int tracks,off_t offset,const char * libdskGeometry)388 const char * Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry)
389 {
390   int n;
391 
392   this->secLength=secLength;
393   this->sectrk=sectrk;
394   this->tracks=tracks;
395   // Bill Buckels - add this->offset
396   this->offset=offset;
397 
398 
399   // Bill Buckels - not sure what to do here
400   if (this->drvtype == CPMDRV_WIN95)
401   {
402       DRIVEPARAMS drvp;
403       memset(&drvp, 0, sizeof(drvp));
404       if (GetDriveParams( this->hdisk, this->fd, &drvp )) return "GetDriveParams failed";
405 
406       drvp.bytespersector  = secLength;
407       drvp.sectorspertrack = sectrk;
408       drvp.totalsectors    = sectrk * tracks;
409 
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)
413  */
414       if (tracks < 44)
415       {
416         drvp.cylinders       = tracks;
417         drvp.heads           = 1;
418       }
419       else
420       {
421         drvp.cylinders       = tracks / 2;
422         drvp.heads           = 2;
423       }
424 
425 /* Set up "reasonable" values for the other members */
426 
427       drvp.sectorspercluster = 1024 / secLength;
428       drvp.reservedsectors   = 1;
429       drvp.numberofFATs      = 2;
430       drvp.sectorcount       = sectrk;
431       drvp.rootdirsize       = 64;
432       drvp.mediaid           = 0xF0;
433       drvp.hiddensectors     = 0;
434       drvp.sectorsperfat     = 3;
435       for (n = 0; n < sectrk; n++)
436       {
437           drvp.sectortable[n*2]   = n + PHYSICAL_SECTOR_1;    /* Physical sector numbers */
438           drvp.sectortable[n*2+1] = secLength;
439       }
440       drvp.special = 6;
441 /* We have not set:
442 
443     drvp.mediatype
444     drvp.devicetype
445     drvp.deviceattrs
446 
447     which should have been read correctly by GetDriveParams().
448   */
449       SetDriveParams( this->hdisk, this->fd, &drvp );
450   }
451   return NULL;
452 }
453 /*}}}*/
454 /* Device_close          -- Close an image file                     */ /*{{{*/
Device_close(struct Device * sb)455 const char *Device_close(struct Device *sb)
456 {
457     sb->opened = 0;
458     switch(sb->drvtype)
459     {
460         case CPMDRV_WIN95:
461             UnlockLogicalVolume(sb->hdisk, sb->fd );
462             if (!CloseHandle( sb->hdisk )) return strwin32error();
463             return NULL;
464 
465         case CPMDRV_WINNT:
466             DismountVolume(sb->hdisk);
467             UnlockVolume(sb->hdisk);
468             if (!CloseHandle(sb->hdisk)) return strwin32error();
469             return NULL;
470     }
471     if (close(sb->fd)) return strerror(errno);
472     return NULL;
473 }
474 /*}}}*/
475 /* Device_readSector     -- read a physical sector                  */ /*{{{*/
Device_readSector(const struct Device * drive,int track,int sector,char * buf)476 const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf)
477 {
478   int res;
479   off_t offset;
480 
481   assert(sector>=0);
482   assert(sector<drive->sectrk);
483   assert(track>=0);
484   assert(track<drive->tracks);
485 
486   offset = ((sector+track*drive->sectrk)*drive->secLength);
487 
488   if (drive->drvtype == CPMDRV_WINNT)
489   {
490         LPVOID iobuffer;
491         DWORD  bytesread;
492 
493         // Bill Buckels - add drive->offset
494         if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
495         {
496             return strwin32error();
497         }
498         iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
499         if (!iobuffer)
500         {
501             return strwin32error();
502         }
503         res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL);
504         if (!res)
505         {
506             char *lboo = strwin32error();
507             VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
508             return lboo;
509         }
510 
511         memcpy(buf, iobuffer, drive->secLength);
512         VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
513 
514         if (bytesread < (unsigned)drive->secLength)
515         {
516             memset(buf + bytesread, 0, drive->secLength - bytesread);
517         }
518         return NULL;
519   }
520 
521   // Bill Buckels - not sure what to do here
522   if (drive->drvtype == CPMDRV_WIN95)
523   {
524         DIOC_REGISTERS reg;
525         BOOL bResult;
526         DWORD cb;
527 
528 #ifdef USE_INT13
529         int cyl, head;
530 
531         if (drive->tracks < 44) { cyl = track;    head = 0; }
532         else                    { cyl = track/2;  head = track & 1; }
533 
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               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
541 #else
542         DISKIO di;
543 
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;
549         di.diSectors     = 1;
550         di.diBuffer      = (DWORD)buf;
551         bResult          = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25,
552              &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
553 
554 #endif
555         if ( !bResult || (reg.reg_Flags & 1) )
556         {
557             if (GetLastError()) return strwin32error();
558             return "Unknown read error.";
559         }
560         return 0;
561   }
562 
563   // Bill Buckels - add drive->offset
564   if (lseek(drive->fd,offset+drive->offset,SEEK_SET)==-1)
565   {
566     return strerror(errno);
567   }
568   if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength)
569   {
570     if (res==-1)
571     {
572       return strerror(errno);
573     }
574     else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */
575   }
576   return NULL;
577 }
578 /*}}}*/
579 /* Device_writeSector    -- write physical sector                   */ /*{{{*/
Device_writeSector(const struct Device * drive,int track,int sector,const char * buf)580 const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf)
581 {
582   off_t offset;
583   int res;
584 
585   assert(sector>=0);
586   assert(sector<drive->sectrk);
587   assert(track>=0);
588   assert(track<drive->tracks);
589 
590   offset = ((sector+track*drive->sectrk)*drive->secLength);
591 
592   if (drive->drvtype == CPMDRV_WINNT)
593   {
594         LPVOID iobuffer;
595         DWORD  byteswritten;
596 
597         // Bill Buckels - add drive->offset
598         if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
599         {
600             return strwin32error();
601         }
602         iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
603         if (!iobuffer)
604         {
605             return strwin32error();
606         }
607         memcpy(iobuffer, buf, drive->secLength);
608         res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL);
609         if (!res || (byteswritten < (unsigned)drive->secLength))
610         {
611             char *lboo = strwin32error();
612             VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
613             return lboo;
614         }
615 
616         VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
617         return NULL;
618   }
619 
620   // Bill Buckels - not sure what to do here
621   if (drive->drvtype == CPMDRV_WIN95)
622   {
623         DIOC_REGISTERS reg;
624         BOOL bResult;
625         DWORD cb;
626 
627 #ifdef USE_INT13
628         int cyl, head;
629 
630         if (drive->tracks < 44) { cyl = track;    head = 0; }
631         else                    { cyl = track/2;  head = track & 1; }
632 
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               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
640 #else
641         DISKIO di;
642 
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;
648         di.diSectors     = 1;
649         di.diBuffer      = (DWORD)buf;
650         bResult          = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26,
651               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
652 #endif
653 
654         if ( !bResult || (reg.reg_Flags & 1) )
655         {
656             if (GetLastError()) return strwin32error();
657             return "Unknown write error.";
658         }
659         return NULL;
660   }
661 
662   // Bill Buckels - add drive->offset
663   if (lseek(drive->fd,offset+drive->offset, SEEK_SET)==-1)
664   {
665     return strerror(errno);
666   }
667   if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL;
668   return strerror(errno);
669 }
670 /*}}}*/
671