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