[VFATLIB] Make Chkdsk handle volume opening locking failures.
[reactos.git] / sdk / lib / fslib / vfatlib / check / io.c
1 /****
2 ** Platform-dependent file
3 ****/
4
5 /* io.c - Virtual disk input/output
6
7 Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
8 Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
9 Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
10 Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25 The complete text of the GNU General Public License
26 can be found in /usr/share/common-licenses/GPL-3 file.
27 */
28
29 /*
30 * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
31 * Fixed nasty bug that caused every file with a name like
32 * xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
33 */
34
35 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
36 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
37
38 #include "vfatlib.h"
39
40 #define NDEBUG
41 #include <debug.h>
42
43
44 #define FSCTL_IS_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
45
46 typedef struct _change {
47 void *data;
48 off_t pos;
49 int size;
50 struct _change *next;
51 } CHANGE;
52
53 static CHANGE *changes, *last;
54 static int did_change = 0;
55 static HANDLE fd;
56 static LARGE_INTEGER CurrentOffset;
57
58
59 /**** Win32 / NT support ******************************************************/
60
61 static int WIN32close(HANDLE FileHandle)
62 {
63 if (!NT_SUCCESS(NtClose(FileHandle)))
64 return -1;
65 return 0;
66 }
67 #define close WIN32close
68
69 static int WIN32read(HANDLE FileHandle, void *buf, unsigned int len)
70 {
71 NTSTATUS Status;
72 IO_STATUS_BLOCK IoStatusBlock;
73
74 Status = NtReadFile(FileHandle,
75 NULL,
76 NULL,
77 NULL,
78 &IoStatusBlock,
79 buf,
80 len,
81 &CurrentOffset,
82 NULL);
83 if (!NT_SUCCESS(Status))
84 {
85 DPRINT1("NtReadFile() failed (Status %lx)\n", Status);
86 return -1;
87 }
88
89 CurrentOffset.QuadPart += len;
90 return (int)len;
91 }
92 #define read WIN32read
93
94 static int WIN32write(HANDLE FileHandle, void *buf, unsigned int len)
95 {
96 NTSTATUS Status;
97 IO_STATUS_BLOCK IoStatusBlock;
98
99 Status = NtWriteFile(FileHandle,
100 NULL,
101 NULL,
102 NULL,
103 &IoStatusBlock,
104 buf,
105 len,
106 &CurrentOffset,
107 NULL);
108 if (!NT_SUCCESS(Status))
109 {
110 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
111 return -1;
112 }
113
114 CurrentOffset.QuadPart += len;
115 return (int)len;
116 }
117 #define write WIN32write
118
119 static off_t WIN32lseek(HANDLE fd, off_t offset, int whence)
120 {
121 LARGE_INTEGER Offset;
122 Offset.QuadPart = (LONGLONG)offset;
123
124 switch (whence)
125 {
126 case SEEK_SET:
127 break;
128
129 case SEEK_CUR:
130 Offset.QuadPart += CurrentOffset.QuadPart;
131 break;
132
133 // case SEEK_END:
134 // Offset.QuadPart += FileSize.QuadPart;
135 // break;
136
137 default:
138 // errno = EINVAL;
139 return (off_t)-1;
140 }
141
142 if (Offset.QuadPart < 0LL)
143 {
144 // errno = EINVAL;
145 return (off_t)-1;
146 }
147 // if (Offset.QuadPart > FileSize.QuadPart)
148 // {
149 // // errno = EINVAL;
150 // return (off_t)-1;
151 // }
152
153 CurrentOffset = Offset;
154
155 return CurrentOffset.QuadPart;
156 }
157 #define lseek WIN32lseek
158
159 /******************************************************************************/
160
161
162 NTSTATUS fs_open(PUNICODE_STRING DriveRoot, int read_write)
163 {
164 NTSTATUS Status;
165 OBJECT_ATTRIBUTES ObjectAttributes;
166 IO_STATUS_BLOCK Iosb;
167
168 InitializeObjectAttributes(&ObjectAttributes,
169 DriveRoot,
170 0,
171 NULL,
172 NULL);
173
174 Status = NtOpenFile(&fd,
175 FILE_GENERIC_READ | (read_write ? FILE_GENERIC_WRITE : 0),
176 &ObjectAttributes,
177 &Iosb,
178 read_write ? 0 : FILE_SHARE_READ,
179 FILE_SYNCHRONOUS_IO_ALERT);
180 if (!NT_SUCCESS(Status))
181 {
182 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status);
183 return Status;
184 }
185
186 // If read_write is specified, then the volume should be exclusively locked
187 if (read_write)
188 {
189 Status = fs_lock(TRUE);
190 }
191
192 // Query geometry and partition info, to have bytes per sector, etc
193
194 CurrentOffset.QuadPart = 0LL;
195
196 changes = last = NULL;
197 did_change = 0;
198
199 return Status;
200 }
201
202 BOOLEAN fs_isdirty(void)
203 {
204 NTSTATUS Status;
205 ULONG DirtyMask = 0;
206 IO_STATUS_BLOCK IoSb;
207
208 /* Check if volume is dirty */
209 Status = NtFsControlFile(fd,
210 NULL, NULL, NULL, &IoSb,
211 FSCTL_IS_VOLUME_DIRTY,
212 NULL, 0, &DirtyMask, sizeof(DirtyMask));
213
214 if (!NT_SUCCESS(Status))
215 {
216 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
217 return FALSE;
218 }
219
220 /* Convert Dirty mask to a boolean value */
221 return (DirtyMask & 1);
222 }
223
224 NTSTATUS fs_lock(BOOLEAN LockVolume)
225 {
226 NTSTATUS Status;
227 IO_STATUS_BLOCK IoSb;
228
229 /* Check if volume is dirty */
230 Status = NtFsControlFile(fd,
231 NULL, NULL, NULL, &IoSb,
232 LockVolume ? FSCTL_LOCK_VOLUME
233 : FSCTL_UNLOCK_VOLUME,
234 NULL, 0, NULL, 0);
235
236 if (!NT_SUCCESS(Status))
237 {
238 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
239 #if 1
240 /* FIXME: ReactOS HACK for 1stage due to IopParseDevice() hack */
241 if (Status == STATUS_INVALID_DEVICE_REQUEST)
242 {
243 Status = STATUS_ACCESS_DENIED;
244 }
245 #endif
246 }
247
248 return Status;
249 }
250
251 void fs_dismount(void)
252 {
253 NTSTATUS Status;
254 IO_STATUS_BLOCK IoSb;
255
256 /* Check if volume is dirty */
257 Status = NtFsControlFile(fd,
258 NULL, NULL, NULL, &IoSb,
259 FSCTL_DISMOUNT_VOLUME,
260 NULL, 0, NULL, 0);
261
262 if (!NT_SUCCESS(Status))
263 {
264 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
265 }
266 }
267
268 /**
269 * Read data from the partition, accounting for any pending updates that are
270 * queued for writing.
271 *
272 * @param[in] pos Byte offset, relative to the beginning of the partition,
273 * at which to read
274 * @param[in] size Number of bytes to read
275 * @param[out] data Where to put the data read
276 */
277 void fs_read(off_t pos, int size, void *data)
278 {
279 CHANGE *walk;
280 int got;
281
282 #if 1 // TMN
283
284 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
285 const off_t seekpos_aligned = pos - (pos % 512);
286 const size_t seek_delta = (size_t)(pos - seekpos_aligned);
287 #if DBG
288 const size_t readsize = (size_t)(pos - seekpos_aligned) + readsize_aligned;
289 #endif
290 char* tmpBuf = alloc(readsize_aligned);
291 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos);
292 if ((got = read(fd, tmpBuf, readsize_aligned)) < 0) pdie("Read %d bytes at %lld",size,pos);
293 assert(got >= size);
294 got = size;
295 assert(seek_delta + size <= readsize);
296 memcpy(data, tmpBuf+seek_delta, size);
297 free(tmpBuf);
298
299 #else // TMN:
300
301 if (lseek(fd, pos, 0) != pos)
302 pdie("Seek to %lld", pos);
303 if ((got = read(fd, data, size)) < 0)
304 pdie("Read %d bytes at %lld", size, pos);
305
306 #endif // TMN:
307
308 if (got != size)
309 die("Got %d bytes instead of %d at %lld", got, size, pos);
310 for (walk = changes; walk; walk = walk->next) {
311 if (walk->pos < pos + size && walk->pos + walk->size > pos) {
312 if (walk->pos < pos)
313 memcpy(data, (char *)walk->data + pos - walk->pos,
314 min(size, walk->size - pos + walk->pos));
315 else
316 memcpy((char *)data + walk->pos - pos, walk->data,
317 min(walk->size, size + pos - walk->pos));
318 }
319 }
320 }
321
322
323 int fs_test(off_t pos, int size)
324 {
325 void *scratch;
326 int okay;
327
328 #if 1 // TMN
329
330 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; // TMN:
331 const off_t seekpos_aligned = pos - (pos % 512); // TMN:
332 scratch = alloc(readsize_aligned);
333 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos);
334 okay = read(fd, scratch, readsize_aligned) == (int)readsize_aligned;
335 free(scratch);
336
337 #else // TMN:
338
339 if (lseek(fd, pos, 0) != pos)
340 pdie("Seek to %lld", pos);
341 scratch = alloc(size);
342 okay = read(fd, scratch, size) == size;
343 free(scratch);
344
345 #endif // TMN:
346 return okay;
347 }
348
349
350 void fs_write(off_t pos, int size, void *data)
351 {
352 CHANGE *new;
353 int did;
354
355 #if 1 //SAE
356
357 if (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE) {
358 void *scratch;
359 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
360 const off_t seekpos_aligned = pos - (pos % 512);
361 const size_t seek_delta = (size_t)(pos - seekpos_aligned);
362 BOOLEAN use_read = (seek_delta != 0) || ((readsize_aligned-size) != 0);
363
364 /* Aloc temp buffer if write is not aligned */
365 if (use_read)
366 scratch = alloc(readsize_aligned);
367 else
368 scratch = data;
369
370 did_change = 1;
371 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",seekpos_aligned);
372
373 if (use_read)
374 {
375 /* Read aligned data */
376 if (read(fd, scratch, readsize_aligned) < 0) pdie("Read %d bytes at %lld",size,pos);
377
378 /* Patch data in memory */
379 memcpy((char *)scratch + seek_delta, data, size);
380 }
381
382 /* Write it back */
383 if ((did = write(fd, scratch, readsize_aligned)) == (int)readsize_aligned)
384 {
385 if (use_read) free(scratch);
386 return;
387 }
388 if (did < 0) pdie("Write %d bytes at %lld", size, pos);
389 die("Wrote %d bytes instead of %d at %lld", did, size, pos);
390 }
391
392 #else //SAE
393
394 if (write_immed) {
395 did_change = 1;
396 if (lseek(fd, pos, 0) != pos)
397 pdie("Seek to %lld", pos);
398 if ((did = write(fd, data, size)) == size)
399 return;
400 if (did < 0)
401 pdie("Write %d bytes at %lld", size, pos);
402 die("Wrote %d bytes instead of %d at %lld", did, size, pos);
403 }
404
405 #endif //SAE
406
407 new = alloc(sizeof(CHANGE));
408 new->pos = pos;
409 memcpy(new->data = alloc(new->size = size), data, size);
410 new->next = NULL;
411 if (last)
412 last->next = new;
413 else
414 changes = new;
415 last = new;
416 }
417
418
419 static void fs_flush(void)
420 {
421 #if 1
422
423 CHANGE *this;
424 int old_write_immed = (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE);
425
426 /* Disable writes to the list now */
427 FsCheckFlags |= FSCHECK_IMMEDIATE_WRITE;
428
429 while (changes) {
430 this = changes;
431 changes = changes->next;
432
433 fs_write(this->pos, this->size, this->data);
434
435 free(this->data);
436 free(this);
437 }
438
439 /* Restore values */
440 if (!old_write_immed) FsCheckFlags ^= FSCHECK_IMMEDIATE_WRITE;
441
442 #else
443
444 CHANGE *this;
445 int size;
446
447 while (changes) {
448 this = changes;
449 changes = changes->next;
450 if (lseek(fd, this->pos, 0) != this->pos)
451 {
452 // printf("Seek to %lld failed: %s\n Did not write %d bytes.\n",
453 // (long long)this->pos, strerror(errno), this->size);
454 printf("Seek to %lld failed\n Did not write %d bytes.\n",
455 (long long)this->pos, this->size);
456 }
457 else if ((size = write(fd, this->data, this->size)) < 0)
458 {
459 // printf("Writing %d bytes at %lld failed: %s\n", this->size,
460 // (long long)this->pos, strerror(errno));
461 printf("Writing %d bytes at %lld failed\n",
462 this->size, (long long)this->pos);
463 }
464 else if (size != this->size)
465 printf("Wrote %d bytes instead of %d bytes at %lld.\n",
466 size, this->size, (long long)this->pos);
467 free(this->data);
468 free(this);
469 }
470
471 #endif
472 }
473
474 int fs_close(int write)
475 {
476 CHANGE *next;
477 int changed;
478
479 changed = !!changes;
480 if (write)
481 fs_flush();
482 else
483 while (changes) {
484 next = changes->next;
485 free(changes->data);
486 free(changes);
487 changes = next;
488 }
489 if (close(fd) < 0)
490 pdie("closing filesystem");
491 return changed || did_change;
492 }
493
494 int fs_changed(void)
495 {
496 return !!changes || did_change;
497 }