2 ** Platform-dependent file
5 /* io.c - Virtual disk input/output
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>
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.
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.
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/>.
25 The complete text of the GNU General Public License
26 can be found in /usr/share/common-licenses/GPL-3 file.
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.
35 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
36 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
44 #define FSCTL_IS_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
46 typedef struct _change
{
53 static CHANGE
*changes
, *last
;
54 static int did_change
= 0;
56 static LARGE_INTEGER CurrentOffset
;
59 /**** Win32 / NT support ******************************************************/
61 static int WIN32close(HANDLE FileHandle
)
63 if (!NT_SUCCESS(NtClose(FileHandle
)))
67 #define close WIN32close
69 static int WIN32read(HANDLE FileHandle
, void *buf
, unsigned int len
)
72 IO_STATUS_BLOCK IoStatusBlock
;
74 Status
= NtReadFile(FileHandle
,
83 if (!NT_SUCCESS(Status
))
85 DPRINT1("NtReadFile() failed (Status %lx)\n", Status
);
89 CurrentOffset
.QuadPart
+= len
;
92 #define read WIN32read
94 static int WIN32write(HANDLE FileHandle
, void *buf
, unsigned int len
)
97 IO_STATUS_BLOCK IoStatusBlock
;
99 Status
= NtWriteFile(FileHandle
,
108 if (!NT_SUCCESS(Status
))
110 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status
);
114 CurrentOffset
.QuadPart
+= len
;
117 #define write WIN32write
119 static off_t
WIN32lseek(HANDLE fd
, off_t offset
, int whence
)
121 LARGE_INTEGER Offset
;
122 Offset
.QuadPart
= (LONGLONG
)offset
;
130 Offset
.QuadPart
+= CurrentOffset
.QuadPart
;
134 // Offset.QuadPart += FileSize.QuadPart;
142 if (Offset
.QuadPart
< 0LL)
147 // if (Offset.QuadPart > FileSize.QuadPart)
149 // // errno = EINVAL;
153 CurrentOffset
= Offset
;
155 return CurrentOffset
.QuadPart
;
157 #define lseek WIN32lseek
159 /******************************************************************************/
162 NTSTATUS
fs_open(PUNICODE_STRING DriveRoot
, int read_write
)
165 OBJECT_ATTRIBUTES ObjectAttributes
;
166 IO_STATUS_BLOCK Iosb
;
168 InitializeObjectAttributes(&ObjectAttributes
,
174 Status
= NtOpenFile(&fd
,
175 FILE_GENERIC_READ
| (read_write
? FILE_GENERIC_WRITE
: 0),
178 read_write
? 0 : FILE_SHARE_READ
,
179 FILE_SYNCHRONOUS_IO_ALERT
);
180 if (!NT_SUCCESS(Status
))
182 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status
);
186 // If read_write is specified, then the volume should be exclusively locked
189 Status
= fs_lock(TRUE
);
192 // Query geometry and partition info, to have bytes per sector, etc
194 CurrentOffset
.QuadPart
= 0LL;
196 changes
= last
= NULL
;
202 BOOLEAN
fs_isdirty(void)
206 IO_STATUS_BLOCK IoSb
;
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
));
214 if (!NT_SUCCESS(Status
))
216 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
220 /* Convert Dirty mask to a boolean value */
221 return (DirtyMask
& 1);
224 NTSTATUS
fs_lock(BOOLEAN LockVolume
)
227 IO_STATUS_BLOCK IoSb
;
229 /* Check if volume is dirty */
230 Status
= NtFsControlFile(fd
,
231 NULL
, NULL
, NULL
, &IoSb
,
232 LockVolume
? FSCTL_LOCK_VOLUME
233 : FSCTL_UNLOCK_VOLUME
,
236 if (!NT_SUCCESS(Status
))
238 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
240 /* FIXME: ReactOS HACK for 1stage due to IopParseDevice() hack */
241 if (Status
== STATUS_INVALID_DEVICE_REQUEST
)
243 Status
= STATUS_ACCESS_DENIED
;
251 void fs_dismount(void)
254 IO_STATUS_BLOCK IoSb
;
256 /* Check if volume is dirty */
257 Status
= NtFsControlFile(fd
,
258 NULL
, NULL
, NULL
, &IoSb
,
259 FSCTL_DISMOUNT_VOLUME
,
262 if (!NT_SUCCESS(Status
))
264 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
269 * Read data from the partition, accounting for any pending updates that are
270 * queued for writing.
272 * @param[in] pos Byte offset, relative to the beginning of the partition,
274 * @param[in] size Number of bytes to read
275 * @param[out] data Where to put the data read
277 void fs_read(off_t pos
, int size
, void *data
)
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
);
288 const size_t readsize
= (size_t)(pos
- seekpos_aligned
) + readsize_aligned
;
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
);
295 assert(seek_delta
+ size
<= readsize
);
296 memcpy(data
, tmpBuf
+seek_delta
, size
);
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
);
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
) {
313 memcpy(data
, (char *)walk
->data
+ pos
- walk
->pos
,
314 min(size
, walk
->size
- pos
+ walk
->pos
));
316 memcpy((char *)data
+ walk
->pos
- pos
, walk
->data
,
317 min(walk
->size
, size
+ pos
- walk
->pos
));
323 int fs_test(off_t pos
, int size
)
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
;
339 if (lseek(fd
, pos
, 0) != pos
)
340 pdie("Seek to %lld", pos
);
341 scratch
= alloc(size
);
342 okay
= read(fd
, scratch
, size
) == size
;
350 void fs_write(off_t pos
, int size
, void *data
)
357 if (FsCheckFlags
& FSCHECK_IMMEDIATE_WRITE
) {
359 const size_t readsize_aligned
= (size
% 512) ? (size
+ (512 - (size
% 512))) : size
;
360 const off_t seekpos_aligned
= pos
- (pos
% 512);
361 const size_t seek_delta
= (size_t)(pos
- seekpos_aligned
);
362 BOOLEAN use_read
= (seek_delta
!= 0) || ((readsize_aligned
-size
) != 0);
364 /* Aloc temp buffer if write is not aligned */
366 scratch
= alloc(readsize_aligned
);
371 if (lseek(fd
, seekpos_aligned
, 0) != seekpos_aligned
) pdie("Seek to %lld",seekpos_aligned
);
375 /* Read aligned data */
376 if (read(fd
, scratch
, readsize_aligned
) < 0) pdie("Read %d bytes at %lld",size
,pos
);
378 /* Patch data in memory */
379 memcpy((char *)scratch
+ seek_delta
, data
, size
);
383 if ((did
= write(fd
, scratch
, readsize_aligned
)) == (int)readsize_aligned
)
385 if (use_read
) free(scratch
);
388 if (did
< 0) pdie("Write %d bytes at %lld", size
, pos
);
389 die("Wrote %d bytes instead of %d at %lld", did
, size
, pos
);
396 if (lseek(fd
, pos
, 0) != pos
)
397 pdie("Seek to %lld", pos
);
398 if ((did
= write(fd
, data
, size
)) == size
)
401 pdie("Write %d bytes at %lld", size
, pos
);
402 die("Wrote %d bytes instead of %d at %lld", did
, size
, pos
);
407 new = alloc(sizeof(CHANGE
));
409 memcpy(new->data
= alloc(new->size
= size
), data
, size
);
419 static void fs_flush(void)
424 int old_write_immed
= (FsCheckFlags
& FSCHECK_IMMEDIATE_WRITE
);
426 /* Disable writes to the list now */
427 FsCheckFlags
|= FSCHECK_IMMEDIATE_WRITE
;
431 changes
= changes
->next
;
433 fs_write(this->pos
, this->size
, this->data
);
440 if (!old_write_immed
) FsCheckFlags
^= FSCHECK_IMMEDIATE_WRITE
;
449 changes
= changes
->next
;
450 if (lseek(fd
, this->pos
, 0) != this->pos
)
452 // printf("Seek to %lld failed: %s\n Did not write %d bytes.\n",
453 // (long long)this->pos, strerror(errno), this->size);
454 printf("Seek to %lld failed\n Did not write %d bytes.\n",
455 (long long)this->pos
, this->size
);
457 else if ((size
= write(fd
, this->data
, this->size
)) < 0)
459 // printf("Writing %d bytes at %lld failed: %s\n", this->size,
460 // (long long)this->pos, strerror(errno));
461 printf("Writing %d bytes at %lld failed\n",
462 this->size
, (long long)this->pos
);
464 else if (size
!= this->size
)
465 printf("Wrote %d bytes instead of %d bytes at %lld.\n",
466 size
, this->size
, (long long)this->pos
);
474 int fs_close(int write
)
484 next
= changes
->next
;
490 pdie("closing filesystem");
491 return changed
|| did_change
;
496 return !!changes
|| did_change
;