]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/fhandler_floppy.cc
Throughout, update copyrights to reflect dates which correspond to main-branch
[newlib-cygwin.git] / winsup / cygwin / fhandler_floppy.cc
CommitLineData
1fd5e000
CF
1/* fhandler_floppy.cc. See fhandler.h for a description of the
2 fhandler classes.
3
bc837d22
CF
4 Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
5 2011, 2012 Red Hat, Inc.
1fd5e000
CF
6
7This file is part of Cygwin.
8
9This software is a copyrighted work licensed under the terms of the
10Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11details. */
12
4c8d72de 13#include "winsup.h"
667f1871 14#include <alloca.h>
1fd5e000 15#include <unistd.h>
75543537 16#include <sys/param.h>
6b91b8d5 17#include <winioctl.h>
f0987675 18#include <cygwin/rdevio.h>
cad2c685
CV
19#include <cygwin/hdreg.h>
20#include <cygwin/fs.h>
169c465a 21#include "cygerrno.h"
6b91b8d5 22#include "security.h"
7ac61736 23#include "path.h"
bccd5e0d 24#include "fhandler.h"
3ff1a063 25#include "ntdll.h"
1fd5e000 26
f0987675
CV
27#define IS_EOM(err) ((err) == ERROR_INVALID_PARAMETER \
28 || (err) == ERROR_SEEK \
29 || (err) == ERROR_SECTOR_NOT_FOUND)
30
db8224e8
CV
31#define bytes_per_sector devbufalign
32
1fd5e000
CF
33/**********************************************************************/
34/* fhandler_dev_floppy */
35
7ac61736 36fhandler_dev_floppy::fhandler_dev_floppy ()
f0987675 37 : fhandler_dev_raw (), status ()
1fd5e000 38{
1fd5e000
CF
39}
40
41int
b470a0e8 42fhandler_dev_floppy::get_drive_info (struct hd_geometry *geo)
1fd5e000 43{
357d4301
CV
44 char dbuf[256];
45 char pbuf[256];
46
5f660ecf 47 DISK_GEOMETRY_EX *dix = NULL;
3ff1a063 48 DISK_GEOMETRY *di = NULL;
357d4301
CV
49 PARTITION_INFORMATION_EX *pix = NULL;
50 PARTITION_INFORMATION *pi = NULL;
b470a0e8 51 DWORD bytes_read = 0;
37c23731 52
357d4301 53 /* Always try using the new EX ioctls first (>= XP). If not available,
8afc05fa
CV
54 fall back to trying the old non-EX ioctls.
55 Unfortunately the EX ioctls are not implemented in the floppy driver. */
56 if (wincap.has_disk_ex_ioctls () && get_major () != DEV_FLOPPY_MAJOR)
f0987675
CV
57 {
58 if (!DeviceIoControl (get_handle (),
59 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
60 dbuf, 256, &bytes_read, NULL))
3ff1a063
CV
61 __seterrno ();
62 else
f0987675 63 {
5f660ecf
CV
64 dix = (DISK_GEOMETRY_EX *) dbuf;
65 di = &dix->Geometry;
3ff1a063
CV
66 if (!DeviceIoControl (get_handle (),
67 IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
68 pbuf, 256, &bytes_read, NULL))
69 __seterrno ();
70 else
71 pix = (PARTITION_INFORMATION_EX *) pbuf;
f0987675 72 }
f0987675 73 }
3ff1a063 74 if (!di)
ae9b22c6 75 {
357d4301
CV
76 if (!DeviceIoControl (get_handle (),
77 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
78 dbuf, 256, &bytes_read, NULL))
3ff1a063
CV
79 __seterrno ();
80 else
357d4301 81 {
3ff1a063
CV
82 di = (DISK_GEOMETRY *) dbuf;
83 if (!DeviceIoControl (get_handle (),
84 IOCTL_DISK_GET_PARTITION_INFO, NULL, 0,
85 pbuf, 256, &bytes_read, NULL))
86 __seterrno ();
87 else
88 pi = (PARTITION_INFORMATION *) pbuf;
357d4301 89 }
357d4301 90 }
3ff1a063 91 if (!di)
37c23731 92 {
3ff1a063 93 /* Up to Win2K, even IOCTL_DISK_GET_DRIVE_GEOMETRY fails when trying
34f5d087 94 it on CD or DVD drives. In that case fall back to requesting
3ff1a063
CV
95 simple file system information. */
96 NTSTATUS status;
97 IO_STATUS_BLOCK io;
98 FILE_FS_SIZE_INFORMATION ffsi;
34f5d087 99
3ff1a063
CV
100 status = NtQueryVolumeInformationFile (get_handle (), &io, &ffsi,
101 sizeof ffsi,
102 FileFsSizeInformation);
103 if (!NT_SUCCESS (status))
34f5d087 104 {
3ff1a063
CV
105 __seterrno_from_nt_status (status);
106 return -1;
107 }
108 debug_printf ("fsys geometry: (%D units)*(%u sec)*(%u bps)",
109 ffsi.TotalAllocationUnits.QuadPart,
110 ffsi.SectorsPerAllocationUnit,
111 ffsi.BytesPerSector);
112 bytes_per_sector = ffsi.BytesPerSector;
113 drive_size = ffsi.TotalAllocationUnits.QuadPart
114 * ffsi.SectorsPerAllocationUnit
34f5d087 115 * ffsi.BytesPerSector;
3ff1a063 116 if (geo)
34f5d087 117 {
3ff1a063
CV
118 geo->heads = 1;
119 geo->sectors = ffsi.SectorsPerAllocationUnit;
120 geo->cylinders = ffsi.TotalAllocationUnits.LowPart;
121 geo->start = 0;
122 }
37c23731
CV
123 }
124 else
125 {
3ff1a063
CV
126 debug_printf ("disk geometry: (%D cyl)*(%u trk)*(%u sec)*(%u bps)",
127 di->Cylinders.QuadPart,
128 di->TracksPerCylinder,
129 di->SectorsPerTrack,
130 di->BytesPerSector);
131 bytes_per_sector = di->BytesPerSector;
357d4301 132 if (pix)
3ff1a063
CV
133 {
134 debug_printf ("partition info: offset %D length %D",
135 pix->StartingOffset.QuadPart,
136 pix->PartitionLength.QuadPart);
137 drive_size = pix->PartitionLength.QuadPart;
138 }
357d4301 139 else if (pi)
3ff1a063
CV
140 {
141 debug_printf ("partition info: offset %D length %D",
142 pi->StartingOffset.QuadPart,
143 pi->PartitionLength.QuadPart);
144 drive_size = pi->PartitionLength.QuadPart;
145 }
357d4301 146 else
3ff1a063
CV
147 {
148 /* Getting the partition size by using the drive geometry information
149 looks wrong, but this is a historical necessity. NT4 didn't
150 maintain partition information for the whole drive (aka
151 "partition 0"), but returned ERROR_INVALID_HANDLE instead. That
152 got fixed in W2K. */
153 drive_size = di->Cylinders.QuadPart * di->TracksPerCylinder
154 * di->SectorsPerTrack * di->BytesPerSector;
155 }
156 if (geo)
157 {
158 geo->heads = di->TracksPerCylinder;
159 geo->sectors = di->SectorsPerTrack;
160 geo->cylinders = di->Cylinders.LowPart;
161 if (pix)
162 geo->start = pix->StartingOffset.QuadPart >> 9ULL;
163 else if (pi)
164 geo->start = pi->StartingOffset.QuadPart >> 9ULL;
165 else
166 geo->start = 0;
167 }
b470a0e8 168 }
3ff1a063 169 debug_printf ("drive size: %D", drive_size);
34f5d087 170
b470a0e8
CV
171 return 0;
172}
173
f0987675
CV
174/* Wrapper functions for ReadFile and WriteFile to simplify error handling. */
175BOOL
176fhandler_dev_floppy::read_file (void *buf, DWORD to_read, DWORD *read, int *err)
177{
178 BOOL ret;
179
180 *err = 0;
181 if (!(ret = ReadFile (get_handle (), buf, to_read, read, 0)))
182 *err = GetLastError ();
183 syscall_printf ("%d (err %d) = ReadFile (%d, %d, to_read %d, read %d, 0)",
184 ret, *err, get_handle (), buf, to_read, *read);
185 return ret;
186}
187
667f1871
CV
188/* See comment in write_file below. */
189BOOL
190fhandler_dev_floppy::lock_partition (DWORD to_write)
191{
192 DWORD bytes_read;
193
b86f999a 194 /* The simple case. We have only a single partition open anyway.
667f1871
CV
195 Try to lock the partition so that a subsequent write succeeds.
196 If there's some file handle open on one of the affected partitions,
197 this fails, but that's how it works on Vista and later... */
198 if (get_minor () % 16 != 0)
199 {
200 if (!DeviceIoControl (get_handle (), FSCTL_LOCK_VOLUME,
201 NULL, 0, NULL, 0, &bytes_read, NULL))
202 {
203 debug_printf ("DeviceIoControl (FSCTL_LOCK_VOLUME) failed, %E");
204 return FALSE;
205 }
206 return TRUE;
207 }
208
209 /* The tricky case. We're writing to the entire disk. What this code
210 basically does is to find out if the current write operation affects
211 one or more partitions on the disk. If so, it tries to lock all these
212 partitions and stores the handles for a subsequent close(). */
213 NTSTATUS status;
214 IO_STATUS_BLOCK io;
215 FILE_POSITION_INFORMATION fpi;
216 /* Allocate space for 4 times the maximum partition count we can handle.
217 The reason is that for *every* single logical drive in an extended
218 partition on an MBR drive, 3 filler entries with partition number set
219 to 0 are added into the partition table returned by
220 IOCTL_DISK_GET_DRIVE_LAYOUT_EX. The first of them reproduces the data
221 of the next partition entry, if any, except for the partiton number.
222 Then two entries with everything set to 0 follow. Well, the
223 documentation states that for MBR drives the number of partition entries
224 in the PARTITION_INFORMATION_EX array is always a multiple of 4, but,
225 nevertheless, how crappy is that layout? */
226 const DWORD size = sizeof (DRIVE_LAYOUT_INFORMATION_EX)
227 + 4 * MAX_PARTITIONS * sizeof (PARTITION_INFORMATION_EX);
228 PDRIVE_LAYOUT_INFORMATION_EX pdlix = (PDRIVE_LAYOUT_INFORMATION_EX)
229 alloca (size);
230 BOOL found = FALSE;
b86f999a 231
667f1871
CV
232 /* Fetch current file pointer position on disk. */
233 status = NtQueryInformationFile (get_handle (), &io, &fpi, sizeof fpi,
234 FilePositionInformation);
235 if (!NT_SUCCESS (status))
236 {
237 debug_printf ("NtQueryInformationFile(FilePositionInformation): %p",
238 status);
239 return FALSE;
240 }
241 /* Fetch drive layout to get start and end positions of partitions on disk. */
242 if (!DeviceIoControl (get_handle (), IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0,
b86f999a 243 pdlix, size, &bytes_read, NULL))
667f1871
CV
244 {
245 debug_printf ("DeviceIoControl(IOCTL_DISK_GET_DRIVE_LAYOUT_EX): %E");
246 return FALSE;
247 }
248 /* Scan through partition info to find the partition(s) into which we're
249 currently trying to write. */
250 PARTITION_INFORMATION_EX *ppie = pdlix->PartitionEntry;
251 for (DWORD i = 0; i < pdlix->PartitionCount; ++i, ++ppie)
252 {
253 /* A partition number of 0 denotes an extended partition or one of the
254 aforementioned filler entries. Just skip. */
255 if (ppie->PartitionNumber == 0)
b86f999a 256 continue;
667f1871
CV
257 /* Check if our writing range affects this partition. */
258 if (fpi.CurrentByteOffset.QuadPart < ppie->StartingOffset.QuadPart
259 + ppie->PartitionLength.QuadPart
260 && ppie->StartingOffset.QuadPart < fpi.CurrentByteOffset.QuadPart
261 + to_write)
262 {
263 /* Yes. Now check if we can handle it. We can only handle
264 up to MAX_PARTITIONS partitions. The partition numbering is
265 one-based, so we decrement the partition number by 1 when using
266 as index into the partition array. */
267 DWORD &part_no = ppie->PartitionNumber;
268 if (part_no >= MAX_PARTITIONS)
269 return FALSE;
270 found = TRUE;
271 debug_printf ("%d %D->%D : %D->%D", part_no,
272 ppie->StartingOffset.QuadPart,
273 ppie->StartingOffset.QuadPart
274 + ppie->PartitionLength.QuadPart,
275 fpi.CurrentByteOffset.QuadPart,
276 fpi.CurrentByteOffset.QuadPart + to_write);
277 /* Do we already have partitions? If not, create it. */
278 if (!partitions)
279 {
280 partitions = (part_t *) ccalloc_abort (HEAP_FHANDLER, 1,
281 sizeof (part_t));
282 partitions->refcnt = 1;
283 }
284 /* Next, check if the partition is already open. If so, skip it. */
285 if (partitions->hdl[part_no - 1])
286 continue;
287 /* Now open the partition and lock it. */
288 WCHAR part[MAX_PATH], *p;
289 NTSTATUS status;
290 UNICODE_STRING upart;
291 OBJECT_ATTRIBUTES attr;
292 IO_STATUS_BLOCK io;
293
294 sys_mbstowcs (part, MAX_PATH, get_win32_name ());
295 p = wcschr (part, L'\0') - 1;
296 __small_swprintf (p, L"%d", part_no);
297 RtlInitUnicodeString (&upart, part);
298 InitializeObjectAttributes (&attr, &upart,
299 OBJ_CASE_INSENSITIVE
300 | ((get_flags () & O_CLOEXEC)
301 ? 0 : OBJ_INHERIT),
302 NULL, NULL);
303 status = NtOpenFile (&partitions->hdl[part_no - 1],
304 GENERIC_READ | GENERIC_WRITE, &attr,
305 &io, FILE_SHARE_READ | FILE_SHARE_WRITE, 0);
306 if (!NT_SUCCESS (status))
307 {
308 debug_printf ("NtCreateFile(%W): %p", part, status);
309 return FALSE;
310 }
311 if (!DeviceIoControl (partitions->hdl[part_no - 1], FSCTL_LOCK_VOLUME,
312 NULL, 0, NULL, 0, &bytes_read, NULL))
313 {
314 debug_printf ("DeviceIoControl (%W, FSCTL_LOCK_VOLUME) "
315 "failed, %E", part);
316 return FALSE;
317 }
318 }
319 }
320 /* If we didn't find a single matching partition, the "Access denied"
321 had another reason, so return FALSE in that case. */
322 return found;
323}
324
f0987675
CV
325BOOL
326fhandler_dev_floppy::write_file (const void *buf, DWORD to_write,
327 DWORD *written, int *err)
328{
329 BOOL ret;
330
331 *err = 0;
332 if (!(ret = WriteFile (get_handle (), buf, to_write, written, 0)))
333 *err = GetLastError ();
667f1871
CV
334 /* When writing to a disk or partition on Vista, an "Access denied" error
335 is potentially a result of the raw disk write restriction. See
336 http://support.microsoft.com/kb/942448 for details. What we have to
337 do here is to lock the partition and retry. The previous solution
338 locked one or all partitions immediately in open. Which is overly
339 wasteful, given that the user might only want to change, say, the boot
340 sector. */
341 if (*err == ERROR_ACCESS_DENIED
342 && wincap.has_restricted_raw_disk_access ()
343 && get_major () != DEV_FLOPPY_MAJOR
344 && get_major () != DEV_CDROM_MAJOR
345 && (get_flags () & O_ACCMODE) != O_RDONLY
346 && lock_partition (to_write))
347 {
348 *err = 0;
349 if (!(ret = WriteFile (get_handle (), buf, to_write, written, 0)))
350 *err = GetLastError ();
351 }
f0987675
CV
352 syscall_printf ("%d (err %d) = WriteFile (%d, %d, write %d, written %d, 0)",
353 ret, *err, get_handle (), buf, to_write, *written);
354 return ret;
355}
356
b470a0e8
CV
357int
358fhandler_dev_floppy::open (int flags, mode_t)
359{
29915d8e 360 int ret = fhandler_dev_raw::open (flags);
b470a0e8 361
29915d8e 362 if (ret)
b470a0e8 363 {
29915d8e
CV
364 DWORD bytes_read;
365
366 if (get_drive_info (NULL))
367 {
368 close ();
369 return 0;
370 }
db8224e8
CV
371 if (!(flags & O_DIRECT))
372 {
373 /* Create sector-aligned buffer. As default buffer size, we're using
374 some big, sector-aligned value. Since direct blockdev IO is
375 usually non-buffered and non-cached, the performance without
376 buffering is worse than access to a file system on same device.
377 Whoever uses O_DIRECT has my condolences. */
378 devbufsiz = MAX (16 * bytes_per_sector, 65536);
379 devbufalloc = new char [devbufsiz + devbufalign];
380 devbuf = (char *) roundup2 ((uintptr_t) devbufalloc, devbufalign);
381 }
382
abadc078
CV
383 /* If we're not trying to access a floppy disk, make sure we're actually
384 allowed to read *all* of the device or volume. This is actually
385 documented in the MSDN CreateFile man page. */
29915d8e 386 if (get_major () != DEV_FLOPPY_MAJOR
29915d8e
CV
387 && !DeviceIoControl (get_handle (), FSCTL_ALLOW_EXTENDED_DASD_IO,
388 NULL, 0, NULL, 0, &bytes_read, NULL))
389 debug_printf ("DeviceIoControl (FSCTL_ALLOW_EXTENDED_DASD_IO) "
390 "failed, %E");
b470a0e8
CV
391 }
392
393 return ret;
394}
395
95a5c969
CV
396int
397fhandler_dev_floppy::close ()
398{
399 int ret = fhandler_dev_raw::close ();
400
667f1871
CV
401 if (partitions && InterlockedDecrement (&partitions->refcnt) == 0)
402 {
403 for (int i = 0; i < MAX_PARTITIONS; ++i)
404 if (partitions->hdl[i])
405 NtClose (partitions->hdl[i]);
406 cfree (partitions);
407 }
95a5c969
CV
408 return ret;
409}
410
f0987675 411int
23771fa1 412fhandler_dev_floppy::dup (fhandler_base *child, int flags)
f0987675 413{
23771fa1 414 int ret = fhandler_dev_raw::dup (child, flags);
f0987675 415
c1146045
CV
416 if (!ret && partitions)
417 InterlockedIncrement (&partitions->refcnt);
f0987675
CV
418 return ret;
419}
420
421inline _off64_t
422fhandler_dev_floppy::get_current_position ()
423{
424 LARGE_INTEGER off = { QuadPart: 0LL };
425 off.LowPart = SetFilePointer (get_handle (), 0, &off.HighPart, FILE_CURRENT);
426 return off.QuadPart;
427}
428
43c23d4b 429void __stdcall
f0987675
CV
430fhandler_dev_floppy::raw_read (void *ptr, size_t& ulen)
431{
432 DWORD bytes_read = 0;
433 DWORD read2;
99abc952 434 DWORD bytes_to_read;
f0987675
CV
435 int ret;
436 size_t len = ulen;
437 char *tgt;
438 char *p = (char *) ptr;
439
440 /* Checking a previous end of media */
441 if (eom_detected () && !lastblk_to_read ())
442 {
443 set_errno (ENOSPC);
444 goto err;
445 }
446
447 if (devbuf)
448 {
449 while (len > 0)
450 {
451 if (devbufstart < devbufend)
452 {
75543537 453 bytes_to_read = MIN (len, devbufend - devbufstart);
f0987675
CV
454 debug_printf ("read %d bytes from buffer (rest %d)",
455 bytes_to_read,
456 devbufend - devbufstart - bytes_to_read);
457 memcpy (p, devbuf + devbufstart, bytes_to_read);
458 len -= bytes_to_read;
459 p += bytes_to_read;
460 bytes_read += bytes_to_read;
461 devbufstart += bytes_to_read;
462
463 if (lastblk_to_read ())
464 {
465 lastblk_to_read (false);
466 break;
467 }
468 }
469 if (len > 0)
470 {
471 if (len >= devbufsiz)
472 {
473 bytes_to_read = (len / bytes_per_sector) * bytes_per_sector;
474 tgt = p;
475 }
476 else
477 {
478 tgt = devbuf;
479 bytes_to_read = devbufsiz;
480 }
481 _off64_t current_position = get_current_position ();
482 if (current_position + bytes_to_read >= drive_size)
483 bytes_to_read = drive_size - current_position;
484 if (!bytes_to_read)
36f17b4b 485 break;
f0987675 486
29915d8e
CV
487 debug_printf ("read %d bytes from pos %U %s", bytes_to_read,
488 current_position,
f0987675
CV
489 len < devbufsiz ? "into buffer" : "directly");
490 if (!read_file (tgt, bytes_to_read, &read2, &ret))
491 {
492 if (!IS_EOM (ret))
493 {
494 __seterrno ();
495 goto err;
496 }
497
498 eom_detected (true);
499
500 if (!read2)
501 {
502 if (!bytes_read)
503 {
504 debug_printf ("return -1, set errno to ENOSPC");
505 set_errno (ENOSPC);
506 goto err;
507 }
508 break;
509 }
510 lastblk_to_read (true);
511 }
512 if (!read2)
513 break;
514 if (tgt == devbuf)
515 {
516 devbufstart = 0;
517 devbufend = read2;
518 }
519 else
520 {
521 len -= read2;
522 p += read2;
523 bytes_read += read2;
524 }
525 }
526 }
527 }
3059d182 528 else
f0987675 529 {
3059d182 530 _off64_t current_position = get_current_position ();
99abc952 531 bytes_to_read = len;
3059d182
CV
532 if (current_position + bytes_to_read >= drive_size)
533 bytes_to_read = drive_size - current_position;
29915d8e
CV
534 debug_printf ("read %d bytes from pos %U directly", bytes_to_read,
535 current_position);
99abc952 536 if (bytes_to_read && !read_file (p, bytes_to_read, &bytes_read, &ret))
f0987675 537 {
3059d182
CV
538 if (!IS_EOM (ret))
539 {
540 __seterrno ();
541 goto err;
542 }
543 if (bytes_read)
544 eom_detected (true);
545 else
546 {
547 debug_printf ("return -1, set errno to ENOSPC");
548 set_errno (ENOSPC);
549 goto err;
550 }
f0987675
CV
551 }
552 }
553
554 ulen = (size_t) bytes_read;
555 return;
556
557err:
558 ulen = (size_t) -1;
559}
560
43c23d4b 561int __stdcall
f0987675
CV
562fhandler_dev_floppy::raw_write (const void *ptr, size_t len)
563{
564 DWORD bytes_written = 0;
565 char *p = (char *) ptr;
566 int ret;
567
db8224e8 568 /* Checking a previous end of media */
f0987675
CV
569 if (eom_detected ())
570 {
571 set_errno (ENOSPC);
572 return -1;
573 }
574
db8224e8
CV
575 if (!len)
576 return 0;
f0987675 577
db8224e8 578 if (devbuf)
f0987675 579 {
db8224e8
CV
580 DWORD cplen, written;
581
582 /* First check if we have an active read buffer. If so, try to fit in
583 the start of the input buffer and write out the entire result.
584 This also covers the situation after lseek since lseek fills the read
585 buffer in case we seek to an address which is not sector aligned. */
586 if (devbufend && devbufstart < devbufend)
587 {
588 _off64_t current_pos = get_current_position ();
589 cplen = MIN (len, devbufend - devbufstart);
590 memcpy (devbuf + devbufstart, p, cplen);
591 LARGE_INTEGER off = { QuadPart:current_pos - devbufend };
592 if (!SetFilePointerEx (get_handle (), off, NULL, FILE_BEGIN))
f0987675 593 {
db8224e8 594 devbufstart = devbufend = 0;
f0987675
CV
595 __seterrno ();
596 return -1;
597 }
db8224e8 598 if (!write_file (devbuf, devbufend, &written, &ret))
f0987675 599 {
db8224e8
CV
600 devbufstart = devbufend = 0;
601 goto err;
602 }
603 /* Align pointers, lengths, etc. */
604 cplen = MIN (cplen, written);
605 devbufstart += cplen;
606 p += cplen;
607 len -= cplen;
608 bytes_written += cplen;
609 if (len)
610 devbufstart = devbufend = 0;
611 }
612 /* As long as there's still something left in the input buffer ... */
613 while (len)
614 {
615 /* Compute the length to write. The problem is that the underlying
616 driver may require sector aligned read/write. So we copy the data
617 over to devbuf, which is guaranteed to be sector aligned. */
618 cplen = MIN (len, devbufsiz);
619 if (cplen >= bytes_per_sector)
620 /* If the remaining len is >= sector size, write out the maximum
621 possible multiple of the sector size which fits into devbuf. */
622 cplen = rounddown (cplen, bytes_per_sector);
623 else
624 {
625 /* If len < sector size, read in the next sector, seek back,
626 and just copy the new data over the old one before writing. */
627 LARGE_INTEGER off = { QuadPart:get_current_position () };
628 if (!read_file (devbuf, bytes_per_sector, &written, &ret))
629 goto err;
630 if (!SetFilePointerEx (get_handle (), off, NULL, FILE_BEGIN))
631 {
632 __seterrno ();
633 return -1;
634 }
635 }
636 memcpy (devbuf, p, cplen);
637 if (!write_file (devbuf, MAX (cplen, bytes_per_sector), &written,
638 &ret))
639 {
640 bytes_written += MIN (cplen, written);
641 goto err;
f0987675 642 }
db8224e8
CV
643 cplen = MIN (cplen, written);
644 p += cplen;
645 len -= cplen;
646 bytes_written += cplen;
f0987675 647 }
db8224e8
CV
648 return bytes_written;
649 }
650
651 /* In O_DIRECT case, just write. */
652 if (write_file (p, len, &bytes_written, &ret))
653 return bytes_written;
654
655err:
656 if (IS_EOM (ret))
657 {
658 eom_detected (true);
659 if (!bytes_written)
660 set_errno (ENOSPC);
f0987675 661 }
db8224e8
CV
662 else if (!bytes_written)
663 __seterrno ();
664 return bytes_written ?: -1;
f0987675
CV
665}
666
b470a0e8
CV
667_off64_t
668fhandler_dev_floppy::lseek (_off64_t offset, int whence)
669{
b7a37e8d 670 char buf[bytes_per_sector];
ba75e8c8 671 _off64_t current_pos = (_off64_t) -1;
f0987675 672 LARGE_INTEGER sector_aligned_offset;
b7a37e8d 673 size_t bytes_left;
739db26a 674
f0987675 675 if (whence == SEEK_END)
ae9b22c6 676 {
c74e68b7 677 offset += drive_size;
0312ede4 678 whence = SEEK_SET;
739db26a 679 }
f0987675 680 else if (whence == SEEK_CUR)
739db26a 681 {
ba75e8c8 682 current_pos = get_current_position ();
c74e68b7
CV
683 _off64_t exact_pos = current_pos - (devbufend - devbufstart);
684 /* Shortcut when used to get current position. */
685 if (offset == 0)
686 return exact_pos;
687 offset += exact_pos;
739db26a
ED
688 whence = SEEK_SET;
689 }
690
c74e68b7 691 if (whence != SEEK_SET || offset < 0 || offset > drive_size)
739db26a
ED
692 {
693 set_errno (EINVAL);
694 return -1;
695 }
739db26a 696
ba75e8c8
CV
697 /* If new position is in buffered range, adjust buffer and return */
698 if (devbufstart < devbufend)
699 {
700 if (current_pos == (_off64_t) -1)
701 current_pos = get_current_position ();
c74e68b7 702 if (current_pos - devbufend <= offset && offset <= current_pos)
ba75e8c8 703 {
c74e68b7
CV
704 devbufstart = devbufend - (current_pos - offset);
705 return offset;
ba75e8c8
CV
706 }
707 }
708
c74e68b7
CV
709 sector_aligned_offset.QuadPart = rounddown (offset, bytes_per_sector);
710 bytes_left = offset - sector_aligned_offset.QuadPart;
739db26a 711
b470a0e8
CV
712 /* Invalidate buffer. */
713 devbufstart = devbufend = 0;
ae9b22c6 714
db8224e8
CV
715 if (!SetFilePointerEx (get_handle (), sector_aligned_offset, NULL,
716 FILE_BEGIN))
b470a0e8
CV
717 {
718 __seterrno ();
719 return -1;
720 }
8bce0d72 721
b470a0e8 722 eom_detected (false);
f0987675 723
b470a0e8
CV
724 if (bytes_left)
725 {
b7a37e8d
CV
726 raw_read (buf, bytes_left);
727 if (bytes_left == (size_t) -1)
728 return -1;
ae9b22c6 729 }
b7a37e8d 730
f0987675 731 return sector_aligned_offset.QuadPart + bytes_left;
1fd5e000
CF
732}
733
734int
735fhandler_dev_floppy::ioctl (unsigned int cmd, void *buf)
736{
db8224e8 737 int ret = 0;
cad2c685 738 DWORD bytes_read;
db8224e8 739
cad2c685
CV
740 switch (cmd)
741 {
742 case HDIO_GETGEO:
db8224e8
CV
743 debug_printf ("HDIO_GETGEO");
744 ret = get_drive_info ((struct hd_geometry *) buf);
745 break;
cad2c685
CV
746 case BLKGETSIZE:
747 case BLKGETSIZE64:
db8224e8
CV
748 debug_printf ("BLKGETSIZE");
749 if (cmd == BLKGETSIZE)
750 *(long *)buf = drive_size >> 9UL;
751 else
752 *(_off64_t *)buf = drive_size;
753 break;
cad2c685 754 case BLKRRPART:
db8224e8
CV
755 debug_printf ("BLKRRPART");
756 if (!DeviceIoControl (get_handle (), IOCTL_DISK_UPDATE_PROPERTIES,
757 NULL, 0, NULL, 0, &bytes_read, NULL))
758 {
759 __seterrno ();
760 ret = -1;
761 }
762 else
b470a0e8 763 get_drive_info (NULL);
db8224e8 764 break;
cad2c685 765 case BLKSSZGET:
db8224e8
CV
766 debug_printf ("BLKSSZGET");
767 *(int *)buf = bytes_per_sector;
768 break;
769 case BLKIOMIN:
770 debug_printf ("BLKIOMIN");
771 *(int *)buf = bytes_per_sector;
772 break;
773 case BLKIOOPT:
774 debug_printf ("BLKIOOPT");
775 *(int *)buf = bytes_per_sector;
776 break;
777 case BLKPBSZGET:
778 debug_printf ("BLKPBSZGET");
779 *(int *)buf = bytes_per_sector;
780 break;
781 case BLKALIGNOFF:
782 debug_printf ("BLKALIGNOFF");
783 *(int *)buf = 0;
784 break;
cad2c685 785 default:
db8224e8
CV
786 ret = fhandler_dev_raw::ioctl (cmd, buf);
787 break;
cad2c685 788 }
db8224e8 789 return ret;
1fd5e000
CF
790}
791
This page took 0.442938 seconds and 5 git commands to generate.