1 /* io.c - Virtual disk input/output
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>
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.
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.
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/>.
21 The complete text of the GNU General Public License
22 can be found in /usr/share/common-licenses/GPL-3 file.
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.
31 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
32 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
40 #define FSCTL_IS_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
42 typedef struct _change
{
49 static CHANGE
*changes
, *last
;
51 static int fd
, did_change
= 0;
53 static int did_change
= 0;
55 static LARGE_INTEGER CurrentOffset
;
58 /**** Win32 / NT support ******************************************************/
60 static int WIN32close(HANDLE FileHandle
)
62 if (!NT_SUCCESS(NtClose(FileHandle
)))
66 #define close WIN32close
68 static int WIN32read(HANDLE FileHandle
, void *buf
, unsigned int len
)
71 IO_STATUS_BLOCK IoStatusBlock
;
73 Status
= NtReadFile(FileHandle
,
82 if (!NT_SUCCESS(Status
))
84 DPRINT1("NtReadFile() failed (Status %lx)\n", Status
);
88 CurrentOffset
.QuadPart
+= len
;
91 #define read WIN32read
93 static int WIN32write(HANDLE FileHandle
, void *buf
, unsigned int len
)
96 IO_STATUS_BLOCK IoStatusBlock
;
98 Status
= NtWriteFile(FileHandle
,
107 if (!NT_SUCCESS(Status
))
109 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status
);
113 CurrentOffset
.QuadPart
+= len
;
116 #define write WIN32write
118 static off_t
WIN32lseek(HANDLE fd
, off_t offset
, int whence
)
120 LARGE_INTEGER Offset
;
121 Offset
.QuadPart
= (LONGLONG
)offset
;
129 Offset
.QuadPart
+= CurrentOffset
.QuadPart
;
133 // Offset.QuadPart += FileSize.QuadPart;
141 if (Offset
.QuadPart
< 0LL)
146 // if (Offset.QuadPart > FileSize.QuadPart)
148 // // errno = EINVAL;
152 CurrentOffset
= Offset
;
154 return CurrentOffset
.QuadPart
;
156 #define lseek WIN32lseek
158 /******************************************************************************/
163 void fs_open(char *path
, int rw
)
165 if ((fd
= open(path
, rw
? O_RDWR
: O_RDONLY
)) < 0) {
169 changes
= last
= NULL
;
173 NTSTATUS
fs_open(PUNICODE_STRING DriveRoot
, int read_write
)
176 OBJECT_ATTRIBUTES ObjectAttributes
;
177 IO_STATUS_BLOCK Iosb
;
179 InitializeObjectAttributes(&ObjectAttributes
,
185 Status
= NtOpenFile(&fd
,
186 FILE_GENERIC_READ
| (read_write
? FILE_GENERIC_WRITE
: 0),
189 read_write
? 0 : FILE_SHARE_READ
,
190 FILE_SYNCHRONOUS_IO_ALERT
);
191 if (!NT_SUCCESS(Status
))
193 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status
);
197 // If read_write is specified, then the volume should be exclusively locked
200 Status
= fs_lock(TRUE
);
203 // Query geometry and partition info, to have bytes per sector, etc
205 CurrentOffset
.QuadPart
= 0LL;
207 changes
= last
= NULL
;
213 BOOLEAN
fs_isdirty(void)
217 IO_STATUS_BLOCK IoSb
;
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
));
225 if (!NT_SUCCESS(Status
))
227 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
231 /* Convert Dirty mask to a boolean value */
232 return (DirtyMask
& 1);
235 NTSTATUS
fs_lock(BOOLEAN LockVolume
)
238 IO_STATUS_BLOCK IoSb
;
240 /* Check if volume is dirty */
241 Status
= NtFsControlFile(fd
,
242 NULL
, NULL
, NULL
, &IoSb
,
243 LockVolume
? FSCTL_LOCK_VOLUME
244 : FSCTL_UNLOCK_VOLUME
,
247 if (!NT_SUCCESS(Status
))
249 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
251 /* FIXME: ReactOS HACK for 1stage due to IopParseDevice() hack */
252 if (Status
== STATUS_INVALID_DEVICE_REQUEST
)
254 Status
= STATUS_ACCESS_DENIED
;
262 void fs_dismount(void)
265 IO_STATUS_BLOCK IoSb
;
267 /* Check if volume is dirty */
268 Status
= NtFsControlFile(fd
,
269 NULL
, NULL
, NULL
, &IoSb
,
270 FSCTL_DISMOUNT_VOLUME
,
273 if (!NT_SUCCESS(Status
))
275 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
281 * Read data from the partition, accounting for any pending updates that are
282 * queued for writing.
284 * @param[in] pos Byte offset, relative to the beginning of the partition,
286 * @param[in] size Number of bytes to read
287 * @param[out] data Where to put the data read
289 void fs_read(off_t pos
, int size
, void *data
)
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
);
299 const size_t readsize
= (size_t)(pos
- seekpos_aligned
) + readsize_aligned
;
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
);
306 assert(seek_delta
+ size
<= readsize
);
307 memcpy(data
, tmpBuf
+seek_delta
, size
);
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
);
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
) {
320 memcpy(data
, (char *)walk
->data
+ pos
- walk
->pos
,
321 min(size
, walk
->size
- pos
+ walk
->pos
));
323 memcpy((char *)data
+ walk
->pos
- pos
, walk
->data
,
324 min(walk
->size
, size
+ pos
- walk
->pos
));
329 int fs_test(off_t pos
, int size
)
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
;
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
;
351 void fs_write(off_t pos
, int size
, void *data
)
357 assert(interactive
|| rw
);
359 if (FsCheckFlags
& FSCHECK_IMMEDIATE_WRITE
) {
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);
366 /* Aloc temp buffer if write is not aligned */
368 scratch
= alloc(readsize_aligned
);
373 if (lseek(fd
, seekpos_aligned
, 0) != seekpos_aligned
) pdie("Seek to %lld",seekpos_aligned
);
377 /* Read aligned data */
378 if (read(fd
, scratch
, readsize_aligned
) < 0) pdie("Read %d bytes at %lld",size
,pos
);
380 /* Patch data in memory */
381 memcpy((char *)scratch
+ seek_delta
, data
, size
);
385 if ((did
= write(fd
, scratch
, readsize_aligned
)) == (int)readsize_aligned
)
387 if (use_read
) free(scratch
);
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
);
396 if (lseek(fd
, pos
, 0) != pos
)
397 pdie("Seek to %lld", (long long)pos
);
398 if ((did
= write(fd
, data
, size
)) == size
)
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
);
405 new = alloc(sizeof(CHANGE
));
407 memcpy(new->data
= alloc(new->size
= size
), data
, size
);
416 static void fs_flush(void)
421 int old_write_immed
= (FsCheckFlags
& FSCHECK_IMMEDIATE_WRITE
);
423 /* Disable writes to the list now */
424 FsCheckFlags
|= FSCHECK_IMMEDIATE_WRITE
;
428 changes
= changes
->next
;
430 fs_write(this->pos
, this->size
, this->data
);
437 if (!old_write_immed
) FsCheckFlags
^= FSCHECK_IMMEDIATE_WRITE
;
445 changes
= changes
->next
;
446 if (lseek(fd
, this->pos
, 0) != this->pos
)
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
);
462 int fs_close(int write
)
467 changed
= ! !changes
;
472 next
= changes
->next
;
478 pdie("closing filesystem");
479 return changed
|| did_change
;
484 return ! !changes
|| did_change
;