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