17db7a1c8e76181475052da87e27305e57394900
[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 void 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;
184 }
185
186 // If read_write is specified, then the volume should be exclusively locked
187 if (read_write) fs_lock(TRUE);
188
189 // Query geometry and partition info, to have bytes per sector, etc
190
191 CurrentOffset.QuadPart = 0LL;
192
193 changes = last = NULL;
194 did_change = 0;
195 }
196
197 BOOLEAN fs_isdirty(void)
198 {
199 NTSTATUS Status;
200 ULONG DirtyMask = 0;
201 IO_STATUS_BLOCK IoSb;
202
203 /* Check if volume is dirty */
204 Status = NtFsControlFile(fd,
205 NULL, NULL, NULL, &IoSb,
206 FSCTL_IS_VOLUME_DIRTY,
207 NULL, 0, &DirtyMask, sizeof(DirtyMask));
208
209 if (!NT_SUCCESS(Status))
210 {
211 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
212 return FALSE;
213 }
214
215 /* Convert Dirty mask to a boolean value */
216 return (DirtyMask & 1);
217 }
218
219 NTSTATUS fs_lock(BOOLEAN LockVolume)
220 {
221 NTSTATUS Status;
222 IO_STATUS_BLOCK IoSb;
223
224 /* Check if volume is dirty */
225 Status = NtFsControlFile(fd,
226 NULL, NULL, NULL, &IoSb,
227 LockVolume ? FSCTL_LOCK_VOLUME
228 : FSCTL_UNLOCK_VOLUME,
229 NULL, 0, NULL, 0);
230
231 if (!NT_SUCCESS(Status))
232 {
233 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
234 }
235
236 return Status;
237 }
238
239 void fs_dismount(void)
240 {
241 NTSTATUS Status;
242 IO_STATUS_BLOCK IoSb;
243
244 /* Check if volume is dirty */
245 Status = NtFsControlFile(fd,
246 NULL, NULL, NULL, &IoSb,
247 FSCTL_DISMOUNT_VOLUME,
248 NULL, 0, NULL, 0);
249
250 if (!NT_SUCCESS(Status))
251 {
252 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
253 }
254 }
255
256 /**
257 * Read data from the partition, accounting for any pending updates that are
258 * queued for writing.
259 *
260 * @param[in] pos Byte offset, relative to the beginning of the partition,
261 * at which to read
262 * @param[in] size Number of bytes to read
263 * @param[out] data Where to put the data read
264 */
265 void fs_read(off_t pos, int size, void *data)
266 {
267 CHANGE *walk;
268 int got;
269
270 #if 1 // TMN
271
272 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
273 const off_t seekpos_aligned = pos - (pos % 512);
274 const size_t seek_delta = (size_t)(pos - seekpos_aligned);
275 #if DBG
276 const size_t readsize = (size_t)(pos - seekpos_aligned) + readsize_aligned;
277 #endif
278 char* tmpBuf = alloc(readsize_aligned);
279 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos);
280 if ((got = read(fd, tmpBuf, readsize_aligned)) < 0) pdie("Read %d bytes at %lld",size,pos);
281 assert(got >= size);
282 got = size;
283 assert(seek_delta + size <= readsize);
284 memcpy(data, tmpBuf+seek_delta, size);
285 free(tmpBuf);
286
287 #else // TMN:
288
289 if (lseek(fd, pos, 0) != pos)
290 pdie("Seek to %lld", pos);
291 if ((got = read(fd, data, size)) < 0)
292 pdie("Read %d bytes at %lld", size, pos);
293
294 #endif // TMN:
295
296 if (got != size)
297 die("Got %d bytes instead of %d at %lld", got, size, pos);
298 for (walk = changes; walk; walk = walk->next) {
299 if (walk->pos < pos + size && walk->pos + walk->size > pos) {
300 if (walk->pos < pos)
301 memcpy(data, (char *)walk->data + pos - walk->pos,
302 min(size, walk->size - pos + walk->pos));
303 else
304 memcpy((char *)data + walk->pos - pos, walk->data,
305 min(walk->size, size + pos - walk->pos));
306 }
307 }
308 }
309
310
311 int fs_test(off_t pos, int size)
312 {
313 void *scratch;
314 int okay;
315
316 #if 1 // TMN
317
318 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; // TMN:
319 const off_t seekpos_aligned = pos - (pos % 512); // TMN:
320 scratch = alloc(readsize_aligned);
321 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos);
322 okay = read(fd, scratch, readsize_aligned) == (int)readsize_aligned;
323 free(scratch);
324
325 #else // TMN:
326
327 if (lseek(fd, pos, 0) != pos)
328 pdie("Seek to %lld", pos);
329 scratch = alloc(size);
330 okay = read(fd, scratch, size) == size;
331 free(scratch);
332
333 #endif // TMN:
334 return okay;
335 }
336
337
338 void fs_write(off_t pos, int size, void *data)
339 {
340 CHANGE *new;
341 int did;
342
343 #if 1 //SAE
344
345 if (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE) {
346 void *scratch;
347 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
348 const off_t seekpos_aligned = pos - (pos % 512);
349 const size_t seek_delta = (size_t)(pos - seekpos_aligned);
350 BOOLEAN use_read = (seek_delta != 0) || ((readsize_aligned-size) != 0);
351
352 /* Aloc temp buffer if write is not aligned */
353 if (use_read)
354 scratch = alloc(readsize_aligned);
355 else
356 scratch = data;
357
358 did_change = 1;
359 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",seekpos_aligned);
360
361 if (use_read)
362 {
363 /* Read aligned data */
364 if (read(fd, scratch, readsize_aligned) < 0) pdie("Read %d bytes at %lld",size,pos);
365
366 /* Patch data in memory */
367 memcpy((char *)scratch + seek_delta, data, size);
368 }
369
370 /* Write it back */
371 if ((did = write(fd, scratch, readsize_aligned)) == (int)readsize_aligned)
372 {
373 if (use_read) free(scratch);
374 return;
375 }
376 if (did < 0) pdie("Write %d bytes at %lld", size, pos);
377 die("Wrote %d bytes instead of %d at %lld", did, size, pos);
378 }
379
380 #else //SAE
381
382 if (write_immed) {
383 did_change = 1;
384 if (lseek(fd, pos, 0) != pos)
385 pdie("Seek to %lld", pos);
386 if ((did = write(fd, data, size)) == size)
387 return;
388 if (did < 0)
389 pdie("Write %d bytes at %lld", size, pos);
390 die("Wrote %d bytes instead of %d at %lld", did, size, pos);
391 }
392
393 #endif //SAE
394
395 new = alloc(sizeof(CHANGE));
396 new->pos = pos;
397 memcpy(new->data = alloc(new->size = size), data, size);
398 new->next = NULL;
399 if (last)
400 last->next = new;
401 else
402 changes = new;
403 last = new;
404 }
405
406
407 static void fs_flush(void)
408 {
409 #if 1
410
411 CHANGE *this;
412 int old_write_immed = (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE);
413
414 /* Disable writes to the list now */
415 FsCheckFlags |= FSCHECK_IMMEDIATE_WRITE;
416
417 while (changes) {
418 this = changes;
419 changes = changes->next;
420
421 fs_write(this->pos, this->size, this->data);
422
423 free(this->data);
424 free(this);
425 }
426
427 /* Restore values */
428 if (!old_write_immed) FsCheckFlags ^= FSCHECK_IMMEDIATE_WRITE;
429
430 #else
431
432 CHANGE *this;
433 int size;
434
435 while (changes) {
436 this = changes;
437 changes = changes->next;
438 if (lseek(fd, this->pos, 0) != this->pos)
439 {
440 // printf("Seek to %lld failed: %s\n Did not write %d bytes.\n",
441 // (long long)this->pos, strerror(errno), this->size);
442 printf("Seek to %lld failed\n Did not write %d bytes.\n",
443 (long long)this->pos, this->size);
444 }
445 else if ((size = write(fd, this->data, this->size)) < 0)
446 {
447 // printf("Writing %d bytes at %lld failed: %s\n", this->size,
448 // (long long)this->pos, strerror(errno));
449 printf("Writing %d bytes at %lld failed\n",
450 this->size, (long long)this->pos);
451 }
452 else if (size != this->size)
453 printf("Wrote %d bytes instead of %d bytes at %lld.\n",
454 size, this->size, (long long)this->pos);
455 free(this->data);
456 free(this);
457 }
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 }