xref: /reactos/sdk/lib/fslib/vfatlib/check/io.c (revision fb5d5ecd)
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 }
486