]>
Commit | Line | Data |
---|---|---|
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 | |
7 | This file is part of Cygwin. | |
8 | ||
9 | This software is a copyrighted work licensed under the terms of the | |
10 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
11 | details. */ | |
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 | 36 | fhandler_dev_floppy::fhandler_dev_floppy () |
f0987675 | 37 | : fhandler_dev_raw (), status () |
1fd5e000 | 38 | { |
1fd5e000 CF |
39 | } |
40 | ||
41 | int | |
b470a0e8 | 42 | fhandler_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. */ |
175 | BOOL | |
176 | fhandler_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. */ |
189 | BOOL | |
190 | fhandler_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 |
325 | BOOL |
326 | fhandler_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 |
357 | int |
358 | fhandler_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 |
396 | int |
397 | fhandler_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 | 411 | int |
23771fa1 | 412 | fhandler_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 | ||
421 | inline _off64_t | |
422 | fhandler_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 | 429 | void __stdcall |
f0987675 CV |
430 | fhandler_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 | ||
557 | err: | |
558 | ulen = (size_t) -1; | |
559 | } | |
560 | ||
43c23d4b | 561 | int __stdcall |
f0987675 CV |
562 | fhandler_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 | ||
655 | err: | |
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 |
668 | fhandler_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 | ||
734 | int | |
735 | fhandler_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 |