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 void 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
187 if (read_write
) fs_lock(TRUE
);
189 // Query geometry and partition info, to have bytes per sector, etc
191 CurrentOffset
.QuadPart
= 0LL;
193 changes
= last
= NULL
;
197 BOOLEAN
fs_isdirty(void)
201 IO_STATUS_BLOCK IoSb
;
203 /* Check if volume is dirty */
204 Status
= NtFsControlFile(fd
,
205 NULL
, NULL
, NULL
, &IoSb
,
206 FSCTL_IS_VOLUME_DIRTY
,
207 NULL
, 0, &DirtyMask
, sizeof(DirtyMask
));
209 if (!NT_SUCCESS(Status
))
211 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
215 /* Convert Dirty mask to a boolean value */
216 return (DirtyMask
& 1);
219 NTSTATUS
fs_lock(BOOLEAN LockVolume
)
222 IO_STATUS_BLOCK IoSb
;
224 /* Check if volume is dirty */
225 Status
= NtFsControlFile(fd
,
226 NULL
, NULL
, NULL
, &IoSb
,
227 LockVolume
? FSCTL_LOCK_VOLUME
228 : FSCTL_UNLOCK_VOLUME
,
231 if (!NT_SUCCESS(Status
))
233 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
239 void fs_dismount(void)
242 IO_STATUS_BLOCK IoSb
;
244 /* Check if volume is dirty */
245 Status
= NtFsControlFile(fd
,
246 NULL
, NULL
, NULL
, &IoSb
,
247 FSCTL_DISMOUNT_VOLUME
,
250 if (!NT_SUCCESS(Status
))
252 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status
);
257 * Read data from the partition, accounting for any pending updates that are
258 * queued for writing.
260 * @param[in] pos Byte offset, relative to the beginning of the partition,
262 * @param[in] size Number of bytes to read
263 * @param[out] data Where to put the data read
265 void fs_read(off_t pos
, int size
, void *data
)
272 const size_t readsize_aligned
= (size
% 512) ? (size
+ (512 - (size
% 512))) : size
;
273 const off_t seekpos_aligned
= pos
- (pos
% 512);
274 const size_t seek_delta
= (size_t)(pos
- seekpos_aligned
);
276 const size_t readsize
= (size_t)(pos
- seekpos_aligned
) + readsize_aligned
;
278 char* tmpBuf
= alloc(readsize_aligned
);
279 if (lseek(fd
, seekpos_aligned
, 0) != seekpos_aligned
) pdie("Seek to %lld",pos
);
280 if ((got
= read(fd
, tmpBuf
, readsize_aligned
)) < 0) pdie("Read %d bytes at %lld",size
,pos
);
283 assert(seek_delta
+ size
<= readsize
);
284 memcpy(data
, tmpBuf
+seek_delta
, size
);
289 if (lseek(fd
, pos
, 0) != pos
)
290 pdie("Seek to %lld", pos
);
291 if ((got
= read(fd
, data
, size
)) < 0)
292 pdie("Read %d bytes at %lld", size
, pos
);
297 die("Got %d bytes instead of %d at %lld", got
, size
, pos
);
298 for (walk
= changes
; walk
; walk
= walk
->next
) {
299 if (walk
->pos
< pos
+ size
&& walk
->pos
+ walk
->size
> pos
) {
301 memcpy(data
, (char *)walk
->data
+ pos
- walk
->pos
,
302 min(size
, walk
->size
- pos
+ walk
->pos
));
304 memcpy((char *)data
+ walk
->pos
- pos
, walk
->data
,
305 min(walk
->size
, size
+ pos
- walk
->pos
));
311 int fs_test(off_t pos
, int size
)
318 const size_t readsize_aligned
= (size
% 512) ? (size
+ (512 - (size
% 512))) : size
; // TMN:
319 const off_t seekpos_aligned
= pos
- (pos
% 512); // TMN:
320 scratch
= alloc(readsize_aligned
);
321 if (lseek(fd
, seekpos_aligned
, 0) != seekpos_aligned
) pdie("Seek to %lld",pos
);
322 okay
= read(fd
, scratch
, readsize_aligned
) == (int)readsize_aligned
;
327 if (lseek(fd
, pos
, 0) != pos
)
328 pdie("Seek to %lld", pos
);
329 scratch
= alloc(size
);
330 okay
= read(fd
, scratch
, size
) == size
;
338 void fs_write(off_t pos
, int size
, void *data
)
345 if (FsCheckFlags
& FSCHECK_IMMEDIATE_WRITE
) {
347 const size_t readsize_aligned
= (size
% 512) ? (size
+ (512 - (size
% 512))) : size
;
348 const off_t seekpos_aligned
= pos
- (pos
% 512);
349 const size_t seek_delta
= (size_t)(pos
- seekpos_aligned
);
350 BOOLEAN use_read
= (seek_delta
!= 0) || ((readsize_aligned
-size
) != 0);
352 /* Aloc temp buffer if write is not aligned */
354 scratch
= alloc(readsize_aligned
);
359 if (lseek(fd
, seekpos_aligned
, 0) != seekpos_aligned
) pdie("Seek to %lld",seekpos_aligned
);
363 /* Read aligned data */
364 if (read(fd
, scratch
, readsize_aligned
) < 0) pdie("Read %d bytes at %lld",size
,pos
);
366 /* Patch data in memory */
367 memcpy((char *)scratch
+ seek_delta
, data
, size
);
371 if ((did
= write(fd
, scratch
, readsize_aligned
)) == (int)readsize_aligned
)
373 if (use_read
) free(scratch
);
376 if (did
< 0) pdie("Write %d bytes at %lld", size
, pos
);
377 die("Wrote %d bytes instead of %d at %lld", did
, size
, pos
);
384 if (lseek(fd
, pos
, 0) != pos
)
385 pdie("Seek to %lld", pos
);
386 if ((did
= write(fd
, data
, size
)) == size
)
389 pdie("Write %d bytes at %lld", size
, pos
);
390 die("Wrote %d bytes instead of %d at %lld", did
, size
, pos
);
395 new = alloc(sizeof(CHANGE
));
397 memcpy(new->data
= alloc(new->size
= size
), data
, size
);
407 static void fs_flush(void)
412 int old_write_immed
= (FsCheckFlags
& FSCHECK_IMMEDIATE_WRITE
);
414 /* Disable writes to the list now */
415 FsCheckFlags
|= FSCHECK_IMMEDIATE_WRITE
;
419 changes
= changes
->next
;
421 fs_write(this->pos
, this->size
, this->data
);
428 if (!old_write_immed
) FsCheckFlags
^= FSCHECK_IMMEDIATE_WRITE
;
437 changes
= changes
->next
;
438 if (lseek(fd
, this->pos
, 0) != this->pos
)
440 // printf("Seek to %lld failed: %s\n Did not write %d bytes.\n",
441 // (long long)this->pos, strerror(errno), this->size);
442 printf("Seek to %lld failed\n Did not write %d bytes.\n",
443 (long long)this->pos
, this->size
);
445 else if ((size
= write(fd
, this->data
, this->size
)) < 0)
447 // printf("Writing %d bytes at %lld failed: %s\n", this->size,
448 // (long long)this->pos, strerror(errno));
449 printf("Writing %d bytes at %lld failed\n",
450 this->size
, (long long)this->pos
);
452 else if (size
!= this->size
)
453 printf("Wrote %d bytes instead of %d bytes at %lld.\n",
454 size
, this->size
, (long long)this->pos
);
462 int fs_close(int write
)
472 next
= changes
->next
;
478 pdie("closing filesystem");
479 return changed
|| did_change
;
484 return !!changes
|| did_change
;