xref: /reactos/sdk/lib/fslib/vfatlib/check/io.c (revision 5100859e)
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     assert(interactive || rw);
356 
357 #if 1 //SAE
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 
394 #else //SAE
395 
396     if (write_immed) {
397 	did_change = 1;
398 	if (lseek(fd, pos, 0) != pos)
399 	    pdie("Seek to %lld", pos);
400 	if ((did = write(fd, data, size)) == size)
401 	    return;
402 	if (did < 0)
403 	    pdie("Write %d bytes at %lld", size, pos);
404 	die("Wrote %d bytes instead of %d at %lld", did, size, pos);
405     }
406 
407 #endif //SAE
408 
409     new = alloc(sizeof(CHANGE));
410     new->pos = pos;
411     memcpy(new->data = alloc(new->size = size), data, size);
412     new->next = NULL;
413     if (last)
414 	last->next = new;
415     else
416 	changes = new;
417     last = new;
418 }
419 
420 
421 static void fs_flush(void)
422 {
423 #if 1
424 
425     CHANGE *this;
426     int old_write_immed = (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE);
427 
428     /* Disable writes to the list now */
429     FsCheckFlags |= FSCHECK_IMMEDIATE_WRITE;
430 
431     while (changes) {
432 	this = changes;
433 	changes = changes->next;
434 
435     fs_write(this->pos, this->size, this->data);
436 
437 	free(this->data);
438 	free(this);
439     }
440 
441     /* Restore values */
442     if (!old_write_immed) FsCheckFlags ^= FSCHECK_IMMEDIATE_WRITE;
443 
444 #else
445 
446     CHANGE *this;
447     int size;
448 
449     while (changes) {
450 	this = changes;
451 	changes = changes->next;
452 	if (lseek(fd, this->pos, 0) != this->pos)
453     {
454 	    // printf("Seek to %lld failed: %s\n  Did not write %d bytes.\n",
455 		    // (long long)this->pos, strerror(errno), this->size);
456 	    printf("Seek to %lld failed\n  Did not write %d bytes.\n",
457 		    (long long)this->pos, this->size);
458     }
459 	else if ((size = write(fd, this->data, this->size)) < 0)
460     {
461 	    // printf("Writing %d bytes at %lld failed: %s\n", this->size,
462 		    // (long long)this->pos, strerror(errno));
463 	    printf("Writing %d bytes at %lld failed\n",
464 		    this->size, (long long)this->pos);
465     }
466 	else if (size != this->size)
467 	    printf("Wrote %d bytes instead of %d bytes at %lld.\n",
468 		    size, this->size, (long long)this->pos);
469 	free(this->data);
470 	free(this);
471     }
472 
473 #endif
474 }
475 
476 int fs_close(int write)
477 {
478     CHANGE *next;
479     int changed;
480 
481     changed = !!changes;
482     if (write)
483 	fs_flush();
484     else
485 	while (changes) {
486 	    next = changes->next;
487 	    free(changes->data);
488 	    free(changes);
489 	    changes = next;
490 	}
491     if (close(fd) < 0)
492 	pdie("closing filesystem");
493     return changed || did_change;
494 }
495 
496 int fs_changed(void)
497 {
498     return !!changes || did_change;
499 }
500