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