xref: /reactos/sdk/lib/fslib/vfatlib/check/io.c (revision cdf47d52)
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 
WIN32close(HANDLE FileHandle)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 
WIN32read(HANDLE FileHandle,void * buf,unsigned int len)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 
WIN32write(HANDLE FileHandle,void * buf,unsigned int len)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 
WIN32lseek(HANDLE fd,off_t offset,int whence)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__
fs_open(char * path,int rw)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
fs_open(PUNICODE_STRING DriveRoot,int read_write)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 
fs_isdirty(void)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 
fs_lock(BOOLEAN LockVolume)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     }
251 
252     return Status;
253 }
254 
fs_dismount(void)255 void fs_dismount(void)
256 {
257     NTSTATUS Status;
258     IO_STATUS_BLOCK IoSb;
259 
260     /* Check if volume is dirty */
261     Status = NtFsControlFile(fd,
262                              NULL, NULL, NULL, &IoSb,
263                              FSCTL_DISMOUNT_VOLUME,
264                              NULL, 0, NULL, 0);
265 
266     if (!NT_SUCCESS(Status))
267     {
268         DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
269     }
270 }
271 #endif
272 
273 /**
274  * Read data from the partition, accounting for any pending updates that are
275  * queued for writing.
276  *
277  * @param[in]   pos     Byte offset, relative to the beginning of the partition,
278  *                      at which to read
279  * @param[in]   size    Number of bytes to read
280  * @param[out]  data    Where to put the data read
281  */
fs_read(off_t pos,int size,void * data)282 void fs_read(off_t pos, int size, void *data)
283 {
284     CHANGE *walk;
285     int got;
286 
287 #ifdef __REACTOS__
288 	const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
289  	const off_t seekpos_aligned = pos - (pos % 512);
290  	const size_t seek_delta = (size_t)(pos - seekpos_aligned);
291 #if DBG
292 	const size_t readsize = (size_t)(pos - seekpos_aligned) + readsize_aligned;
293 #endif
294 	char* tmpBuf = alloc(readsize_aligned);
295     if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos);
296     if ((got = read(fd, tmpBuf, readsize_aligned)) < 0) pdie("Read %d bytes at %lld",size,pos);
297 	assert(got >= size);
298 	got = size;
299 	assert(seek_delta + size <= readsize);
300 	memcpy(data, tmpBuf+seek_delta, size);
301 	free(tmpBuf);
302 #else
303     if (lseek(fd, pos, 0) != pos)
304 	pdie("Seek to %lld", (long long)pos);
305     if ((got = read(fd, data, size)) < 0)
306 	pdie("Read %d bytes at %lld", size, (long long)pos);
307 #endif
308     if (got != size)
309 	die("Got %d bytes instead of %d at %lld", got, size, (long long)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 
fs_test(off_t pos,int size)322 int fs_test(off_t pos, int size)
323 {
324     void *scratch;
325     int okay;
326 
327 #ifdef __REACTOS__
328 	const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;        // TMN:
329 	const off_t seekpos_aligned = pos - (pos % 512);                   // TMN:
330     scratch = alloc(readsize_aligned);
331     if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos);
332     okay = read(fd, scratch, readsize_aligned) == (int)readsize_aligned;
333     free(scratch);
334 #else
335     if (lseek(fd, pos, 0) != pos)
336 	pdie("Seek to %lld", (long long)pos);
337     scratch = alloc(size);
338     okay = read(fd, scratch, size) == size;
339     free(scratch);
340 #endif
341     return okay;
342 }
343 
fs_write(off_t pos,int size,void * data)344 void fs_write(off_t pos, int size, void *data)
345 {
346     CHANGE *new;
347     int did;
348 
349 #ifdef __REACTOS__
350     assert(interactive || rw);
351 
352     if (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE) {
353         void *scratch;
354         const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
355         const off_t seekpos_aligned = pos - (pos % 512);
356         const size_t seek_delta = (size_t)(pos - seekpos_aligned);
357         BOOLEAN use_read = (seek_delta != 0) || ((readsize_aligned-size) != 0);
358 
359         /* Aloc temp buffer if write is not aligned */
360         if (use_read)
361             scratch = alloc(readsize_aligned);
362         else
363             scratch = data;
364 
365         did_change = 1;
366         if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",seekpos_aligned);
367 
368         if (use_read)
369         {
370             /* Read aligned data */
371             if (read(fd, scratch, readsize_aligned) < 0) pdie("Read %d bytes at %lld",size,pos);
372 
373             /* Patch data in memory */
374             memcpy((char *)scratch + seek_delta, data, size);
375         }
376 
377         /* Write it back */
378         if ((did = write(fd, scratch, readsize_aligned)) == (int)readsize_aligned)
379         {
380             if (use_read) free(scratch);
381             return;
382         }
383         if (did < 0) pdie("Write %d bytes at %lld", size, pos);
384         die("Wrote %d bytes instead of %d at %lld", did, size, pos);
385     }
386 #else
387     if (write_immed) {
388 	did_change = 1;
389 	if (lseek(fd, pos, 0) != pos)
390 	    pdie("Seek to %lld", (long long)pos);
391 	if ((did = write(fd, data, size)) == size)
392 	    return;
393 	if (did < 0)
394 	    pdie("Write %d bytes at %lld", size, (long long)pos);
395 	die("Wrote %d bytes instead of %d at %lld", did, size, (long long)pos);
396     }
397 #endif
398     new = alloc(sizeof(CHANGE));
399     new->pos = pos;
400     memcpy(new->data = alloc(new->size = size), data, size);
401     new->next = NULL;
402     if (last)
403 	last->next = new;
404     else
405 	changes = new;
406     last = new;
407 }
408 
fs_flush(void)409 static void fs_flush(void)
410 {
411 #ifdef __REACTOS__
412 
413     CHANGE *this;
414     int old_write_immed = (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE);
415 
416     /* Disable writes to the list now */
417     FsCheckFlags |= FSCHECK_IMMEDIATE_WRITE;
418 
419     while (changes) {
420 	this = changes;
421 	changes = changes->next;
422 
423     fs_write(this->pos, this->size, this->data);
424 
425 	free(this->data);
426 	free(this);
427     }
428 
429     /* Restore values */
430     if (!old_write_immed) FsCheckFlags ^= FSCHECK_IMMEDIATE_WRITE;
431 
432 #else
433     CHANGE *this;
434     int size;
435 
436     while (changes) {
437 	this = changes;
438 	changes = changes->next;
439 	if (lseek(fd, this->pos, 0) != this->pos)
440 	    fprintf(stderr,
441 		    "Seek to %lld failed: %s\n  Did not write %d bytes.\n",
442 		    (long long)this->pos, strerror(errno), this->size);
443 	else if ((size = write(fd, this->data, this->size)) < 0)
444 	    fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size,
445 		    (long long)this->pos, strerror(errno));
446 	else if (size != this->size)
447 	    fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld."
448 		    "\n", size, this->size, (long long)this->pos);
449 	free(this->data);
450 	free(this);
451     }
452 #endif
453 }
454 
fs_close(int write)455 int fs_close(int write)
456 {
457     CHANGE *next;
458     int changed;
459 
460     changed = ! !changes;
461     if (write)
462 	fs_flush();
463     else
464 	while (changes) {
465 	    next = changes->next;
466 	    free(changes->data);
467 	    free(changes);
468 	    changes = next;
469 	}
470     if (close(fd) < 0)
471 	pdie("closing filesystem");
472     return changed || did_change;
473 }
474 
fs_changed(void)475 int fs_changed(void)
476 {
477     return ! !changes || did_change;
478 }
479