Imported Upstream version 2.17
[debian/cpmtools] / device_win32.c
1 /* #includes */ /*{{{C}}}*//*{{{*/
2 #include "config.h"
3
4 #include <assert.h>
5 #include <errno.h>
6 #include <ctype.h>
7 #include <stdio.h>
8
9 #include "cpmdir.h"
10 #include "cpmfs.h"
11
12 #ifdef USE_DMALLOC
13 #include <dmalloc.h>
14 #endif
15 /*}}}*/
16 /* types */ /*{{{*/
17 #define PHYSICAL_SECTOR_1       1 /* First physical sector */
18
19 /* Use the INT13 interface rather than INT25/INT26. This appears to
20  * improve performance, but is less well tested. */
21 #define USE_INT13
22
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 */
28
29 typedef struct _DIOC_REGISTERS {
30     DWORD reg_EBX;
31     DWORD reg_EDX;
32     DWORD reg_ECX;
33     DWORD reg_EAX;
34     DWORD reg_EDI;
35     DWORD reg_ESI;
36     DWORD reg_Flags;
37     }
38     DIOC_REGISTERS, *PDIOC_REGISTERS;
39
40 #define   LEVEL0_LOCK   0
41 #define   LEVEL1_LOCK   1
42 #define   LEVEL2_LOCK   2
43 #define   LEVEL3_LOCK   3
44 #define   LEVEL1_LOCK_MAX_PERMISSION      0x0001
45
46 #define   DRIVE_IS_REMOTE                 0x1000
47 #define   DRIVE_IS_SUBST                  0x8000
48
49 /*********************************************************
50  **** Note: all MS-DOS data structures must be packed ****
51  ****       on a one-byte boundary.                   ****
52  *********************************************************/
53 #pragma pack(1)
54
55 typedef struct _DISKIO {
56     DWORD diStartSector;    /* sector number to start at */
57     WORD  diSectors;        /* number of sectors */
58     DWORD diBuffer;         /* address of buffer */
59     }
60     DISKIO, *PDISKIO;
61
62 typedef struct MID {
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 */
67     }
68     MID, *PMID;
69
70 typedef struct driveparams {    /* Disk geometry */
71     BYTE special;
72     BYTE devicetype;
73     WORD deviceattrs;
74     WORD cylinders;
75     BYTE mediatype;
76     /* BPB starts here */
77     WORD bytespersector;
78     BYTE sectorspercluster;
79     WORD reservedsectors;
80     BYTE numberofFATs;
81     WORD rootdirsize;
82     WORD totalsectors;
83     BYTE mediaid;
84     WORD sectorsperfat;
85     WORD sectorspertrack;
86     WORD heads;
87     DWORD hiddensectors;
88     DWORD bigtotalsectors;
89     BYTE  reserved[6];
90     /* BPB ends here */
91     WORD sectorcount;
92     WORD sectortable[80];
93     } DRIVEPARAMS, *PDRIVEPARAMS;
94 /*}}}*/
95
96 static char *strwin32error(void) /*{{{*/
97 {
98     static char buffer[1024];
99
100     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
101                   NULL,
102                   GetLastError(),
103                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
104                   (LPTSTR)buffer,
105                   1023, NULL);
106     return buffer;
107 }
108 /*}}}*/
109 static BOOL LockVolume( HANDLE hDisk ) /*{{{*/
110 {
111     DWORD ReturnedByteCount;
112
113     return DeviceIoControl( hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL,
114                 0, &ReturnedByteCount, NULL );
115 }
116 /*}}}*/
117 static BOOL UnlockVolume( HANDLE hDisk )  /*{{{*/
118 {
119     DWORD ReturnedByteCount;
120
121     return DeviceIoControl( hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL,
122                 0, &ReturnedByteCount, NULL );
123 }
124 /*}}}*/
125 static BOOL DismountVolume( HANDLE hDisk ) /*{{{*/
126 {
127     DWORD ReturnedByteCount;
128
129     return DeviceIoControl( hDisk, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL,
130                 0, &ReturnedByteCount, NULL );
131 }
132 /*}}}*/
133 static int GetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
134   {
135   DIOC_REGISTERS reg;
136   BOOL bResult;
137   DWORD cb;
138
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 */
144
145   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
146               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 ); 
147
148   if ( !bResult || (reg.reg_Flags & 1) ) 
149       return (reg.reg_EAX & 0xffff);
150
151   return 0;
152   }
153 /*}}}*/
154 static int SetDriveParams( HANDLE hVWin32Device, int volume, DRIVEPARAMS* pParam ) /*{{{*/
155   {
156   DIOC_REGISTERS reg;
157   BOOL bResult;
158   DWORD cb;
159
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 */
165
166   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
167               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 ); 
168
169   if ( !bResult || (reg.reg_Flags & 1) ) 
170       return (reg.reg_EAX & 0xffff);
171
172   return 0;
173   }
174 /*}}}*/
175 static int GetMediaID( HANDLE hVWin32Device, int volume, MID* pMid ) /*{{{*/
176   {
177   DIOC_REGISTERS reg;
178   BOOL bResult;
179   DWORD cb;
180
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 */
186
187   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
188               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
189
190   if ( !bResult || (reg.reg_Flags & 1) ) 
191       return (reg.reg_EAX & 0xffff);
192
193   return 0;
194   }
195 /*}}}*/
196 static int VolumeCheck(HANDLE hVWin32Device, int volume, WORD* flags ) /*{{{*/
197 {
198   DIOC_REGISTERS reg;
199   BOOL bResult;
200   DWORD cb;
201
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 */
205
206   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
207               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 ); 
208
209   if ( !bResult || (reg.reg_Flags & 1) ) 
210       return (reg.reg_EAX & 0xffff);
211
212   *flags = (WORD)(reg.reg_EDX & 0xffff);
213   return 0;
214 }
215 /*}}}*/
216 static int LockLogicalVolume(HANDLE hVWin32Device, int volume, int lock_level, int permissions) /*{{{*/
217 {
218   DIOC_REGISTERS reg;
219   BOOL bResult;
220   DWORD cb;
221
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 */
227
228   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
229               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 ); 
230
231   if ( !bResult || (reg.reg_Flags & 1) ) 
232       return (reg.reg_EAX & 0xffff);
233
234   return 0;
235 }
236 /*}}}*/
237 static int UnlockLogicalVolume( HANDLE hVWin32Device, int volume ) /*{{{*/
238 {
239   DIOC_REGISTERS reg;
240   BOOL bResult;
241   DWORD cb;
242
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 */
247
248   bResult = DeviceIoControl( hVWin32Device, VWIN32_DIOC_DOS_IOCTL,
249               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 ); 
250
251   if ( !bResult || (reg.reg_Flags & 1) ) return -1;
252   return 0;
253 }
254 /*}}}*/
255 static int w32mode(int mode) /*{{{*/
256 {
257     switch(mode)
258     {
259         case O_RDONLY: return GENERIC_READ;
260         case O_WRONLY: return GENERIC_WRITE;
261     }
262     return GENERIC_READ | GENERIC_WRITE;
263 }
264 /*}}}*/
265
266 /* Device_open           -- Open an image file                      */ /*{{{*/
267 const char *Device_open(struct Device *sb, const char *filename, int mode, const char *deviceOpts)
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                       */ /*{{{*/
388 void Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset)
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;
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 }
452 /*}}}*/
453 /* Device_close          -- Close an image file                     */ /*{{{*/
454 const char *Device_close(struct Device *sb)
455 {
456     sb->opened = 0;
457     switch(sb->drvtype)
458     {
459         case CPMDRV_WIN95:
460             UnlockLogicalVolume(sb->hdisk, sb->fd );
461             if (!CloseHandle( sb->hdisk )) return strwin32error();
462             return NULL;
463
464         case CPMDRV_WINNT:
465             DismountVolume(sb->hdisk);
466             UnlockVolume(sb->hdisk);
467             if (!CloseHandle(sb->hdisk)) return strwin32error();
468             return NULL; 
469     }
470     if (close(sb->fd)) return strerror(errno);
471     return NULL; 
472 }
473 /*}}}*/
474 /* Device_readSector     -- read a physical sector                  */ /*{{{*/
475 const char *Device_readSector(const struct Device *drive, int track, int sector, char *buf)
476 {
477   int res;
478   off_t offset;
479
480   assert(sector>=0);
481   assert(sector<drive->sectrk);
482   assert(track>=0);
483   assert(track<drive->tracks);
484
485   offset = ((sector+track*drive->sectrk)*drive->secLength);
486
487   if (drive->drvtype == CPMDRV_WINNT)
488   {
489         LPVOID iobuffer;
490         DWORD  bytesread;
491     
492         // Bill Buckels - add drive->offset
493         if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
494         {
495             return strwin32error();
496         }
497         iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
498         if (!iobuffer) 
499         {
500             return strwin32error();
501         }
502         res = ReadFile(drive->hdisk, iobuffer, drive->secLength, &bytesread, NULL);
503         if (!res)
504         {
505             char *lboo = strwin32error();
506             VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
507             return lboo;
508         } 
509
510         memcpy(buf, iobuffer, drive->secLength);
511         VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
512
513         if (bytesread < (unsigned)drive->secLength)
514         {
515             memset(buf + bytesread, 0, drive->secLength - bytesread);
516         }
517         return NULL;
518   }
519
520   // Bill Buckels - not sure what to do here
521   if (drive->drvtype == CPMDRV_WIN95)
522   {
523         DIOC_REGISTERS reg;
524         BOOL bResult;
525         DWORD cb;
526
527 #ifdef USE_INT13
528         int cyl, head;
529
530         if (drive->tracks < 44) { cyl = track;    head = 0; }
531         else                    { cyl = track/2;  head = track & 1; }
532
533         reg.reg_EAX      = 0x0201;  /* Read 1 sector */
534         reg.reg_EBX      = (DWORD)buf;
535         reg.reg_ECX      = (cyl << 8)  | (sector + PHYSICAL_SECTOR_1);
536         reg.reg_EDX      = (head << 8) | (drive->fd - 1);
537         reg.reg_Flags    = 1;       /* preset the carry flag */
538         bResult          = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
539               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
540 #else
541         DISKIO di;
542
543         reg.reg_EAX      = drive->fd - 1;  /* zero-based volume number */
544         reg.reg_EBX      = (DWORD)&di;
545         reg.reg_ECX      = 0xffff;  /* use DISKIO structure */
546         reg.reg_Flags    = 1;       /* preset the carry flag */
547         di.diStartSector = sector+track*drive->sectrk;
548         di.diSectors     = 1;
549         di.diBuffer      = (DWORD)buf;
550         bResult          = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT25,
551              &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
552
553 #endif
554         if ( !bResult || (reg.reg_Flags & 1) )
555         {
556             if (GetLastError()) return strwin32error();
557             return "Unknown read error.";
558         }
559         return 0;
560   }
561
562   // Bill Buckels - add drive->offset
563   if (lseek(drive->fd,offset+drive->offset,SEEK_SET)==-1)
564   {
565     return strerror(errno);
566   }
567   if ((res=read(drive->fd, buf, drive->secLength)) != drive->secLength)
568   {
569     if (res==-1)
570     {
571       return strerror(errno);
572     }
573     else memset(buf+res,0,drive->secLength-res); /* hit end of disk image */
574   }
575   return NULL;
576 }
577 /*}}}*/
578 /* Device_writeSector    -- write physical sector                   */ /*{{{*/
579 const char *Device_writeSector(const struct Device *drive, int track, int sector, const char *buf)
580 {
581   off_t offset;
582   int res;
583
584   assert(sector>=0);
585   assert(sector<drive->sectrk);
586   assert(track>=0);
587   assert(track<drive->tracks);
588
589   offset = ((sector+track*drive->sectrk)*drive->secLength);
590
591   if (drive->drvtype == CPMDRV_WINNT)
592   {
593         LPVOID iobuffer;
594         DWORD  byteswritten;
595
596         // Bill Buckels - add drive->offset
597         if (SetFilePointer(drive->hdisk, offset+drive->offset, NULL, FILE_BEGIN) == INVALID_FILE_SIZE)
598         {
599             return strwin32error();
600         }
601         iobuffer = VirtualAlloc(NULL, drive->secLength, MEM_COMMIT, PAGE_READWRITE);
602         if (!iobuffer)
603         {
604             return strwin32error();
605         }
606         memcpy(iobuffer, buf, drive->secLength);
607         res = WriteFile(drive->hdisk, iobuffer, drive->secLength, &byteswritten, NULL);
608         if (!res || (byteswritten < (unsigned)drive->secLength))
609         {
610             char *lboo = strwin32error();
611             VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
612             return lboo;
613         }
614
615         VirtualFree(iobuffer, drive->secLength, MEM_RELEASE);
616         return NULL;
617   }
618
619   // Bill Buckels - not sure what to do here
620   if (drive->drvtype == CPMDRV_WIN95)
621   {
622         DIOC_REGISTERS reg;
623         BOOL bResult;
624         DWORD cb;
625
626 #ifdef USE_INT13
627         int cyl, head;
628
629         if (drive->tracks < 44) { cyl = track;    head = 0; }
630         else                    { cyl = track/2;  head = track & 1; }
631
632         reg.reg_EAX      = 0x0301;  /* Write 1 sector */
633         reg.reg_EBX      = (DWORD)buf;
634         reg.reg_ECX      = (cyl << 8)  | (sector + PHYSICAL_SECTOR_1);
635         reg.reg_EDX      = (head << 8) | (drive->fd - 1);
636         reg.reg_Flags    = 1;       /* preset the carry flag */
637         bResult          = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT13,
638               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 );
639 #else
640         DISKIO di;
641
642         reg.reg_EAX      = drive->fd - 1;  /* zero-based volume number */
643         reg.reg_EBX      = (DWORD)&di;
644         reg.reg_ECX      = 0xffff;  /* use DISKIO structure */
645         reg.reg_Flags    = 1;       /* preset the carry flag */
646         di.diStartSector = sector+track*drive->sectrk;
647         di.diSectors     = 1;
648         di.diBuffer      = (DWORD)buf;
649         bResult          = DeviceIoControl( drive->hdisk, VWIN32_DIOC_DOS_INT26,
650               &reg, sizeof( reg ), &reg, sizeof( reg ), &cb, 0 ); 
651 #endif
652
653         if ( !bResult || (reg.reg_Flags & 1) )
654         {
655             if (GetLastError()) return strwin32error();
656             return "Unknown write error.";
657         }
658         return NULL;
659   }
660
661   // Bill Buckels - add drive->offset
662   if (lseek(drive->fd,offset+drive->offset, SEEK_SET)==-1)
663   {
664     return strerror(errno);
665   }
666   if (write(drive->fd, buf, drive->secLength) == drive->secLength) return NULL;
667   return strerror(errno);
668 }
669 /*}}}*/