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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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 ®, sizeof( 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