2008-08-03 18:50:46 +00:00
|
|
|
/* io.c - Virtual disk input/output */
|
|
|
|
|
|
|
|
/* Written 1993 by Werner Almesberger */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
|
|
|
|
* Fixed nasty bug that caused every file with a name like
|
|
|
|
* xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
|
|
|
|
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
|
|
|
|
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
#include "vfatlib.h"
|
2008-08-03 18:50:46 +00:00
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
2008-08-03 18:50:46 +00:00
|
|
|
|
2008-08-08 09:13:21 +00:00
|
|
|
#define FSCTL_IS_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
|
|
|
2008-08-03 18:50:46 +00:00
|
|
|
typedef struct _change {
|
|
|
|
void *data;
|
|
|
|
loff_t pos;
|
|
|
|
int size;
|
|
|
|
struct _change *next;
|
|
|
|
} CHANGE;
|
|
|
|
|
|
|
|
|
|
|
|
static CHANGE *changes,*last;
|
2008-08-04 10:00:32 +00:00
|
|
|
static int did_change = 0;
|
|
|
|
static HANDLE fd;
|
|
|
|
static LARGE_INTEGER CurrentOffset;
|
2008-08-03 18:50:46 +00:00
|
|
|
|
|
|
|
unsigned device_no;
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
static int WIN32close(HANDLE fd);
|
2008-08-03 18:50:46 +00:00
|
|
|
#define close WIN32close
|
2008-08-04 10:00:32 +00:00
|
|
|
static int WIN32read(HANDLE fd, void *buf, unsigned int len);
|
2008-08-03 18:50:46 +00:00
|
|
|
#define read WIN32read
|
2008-08-04 10:00:32 +00:00
|
|
|
static int WIN32write(HANDLE fd, void *buf, unsigned int len);
|
2008-08-03 18:50:46 +00:00
|
|
|
#define write WIN32write
|
2008-08-04 10:00:32 +00:00
|
|
|
static loff_t WIN32llseek(HANDLE fd, loff_t offset, int whence);
|
2008-08-03 18:50:46 +00:00
|
|
|
#ifdef llseek
|
|
|
|
#undef llseek
|
|
|
|
#endif
|
|
|
|
#define llseek WIN32llseek
|
|
|
|
|
|
|
|
//static int is_device = 0;
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
void fs_open(PUNICODE_STRING DriveRoot,int rw)
|
2008-08-03 18:50:46 +00:00
|
|
|
{
|
2008-08-04 10:00:32 +00:00
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
|
|
DriveRoot,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
Status = NtOpenFile(&fd,
|
|
|
|
FILE_GENERIC_READ | (rw ? FILE_GENERIC_WRITE : 0),
|
|
|
|
&ObjectAttributes,
|
|
|
|
&Iosb,
|
|
|
|
rw ? 0 : FILE_SHARE_READ,
|
|
|
|
FILE_SYNCHRONOUS_IO_ALERT);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2008-08-04 16:04:56 +00:00
|
|
|
DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status);
|
2008-08-04 10:00:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-08-03 18:50:46 +00:00
|
|
|
|
2008-08-23 11:41:04 +00:00
|
|
|
// If rw is specified, then the volume should be exclusively locked
|
|
|
|
if (rw) fs_lock(TRUE);
|
|
|
|
|
2008-08-23 10:47:52 +00:00
|
|
|
// Query geometry and partition info, to have bytes per sector, etc
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
CurrentOffset.QuadPart = 0LL;
|
2008-08-03 18:50:46 +00:00
|
|
|
|
|
|
|
changes = last = NULL;
|
|
|
|
did_change = 0;
|
|
|
|
}
|
|
|
|
|
2008-08-23 10:49:57 +00:00
|
|
|
BOOLEAN fs_isdirty()
|
2008-08-08 09:13:21 +00:00
|
|
|
{
|
|
|
|
ULONG DirtyMask = 0;
|
|
|
|
NTSTATUS Status;
|
2008-08-23 10:47:52 +00:00
|
|
|
IO_STATUS_BLOCK IoSb;
|
2008-08-08 09:13:21 +00:00
|
|
|
|
|
|
|
/* Check if volume is dirty */
|
2008-08-23 10:49:57 +00:00
|
|
|
Status = NtFsControlFile(fd,
|
2008-08-08 09:13:21 +00:00
|
|
|
NULL, NULL, NULL, &IoSb,
|
|
|
|
FSCTL_IS_VOLUME_DIRTY,
|
|
|
|
NULL, 0, &DirtyMask, sizeof(DirtyMask));
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2008-08-23 10:47:52 +00:00
|
|
|
/* Convert Dirty mask to a boolean value */
|
2008-08-08 09:13:21 +00:00
|
|
|
return (DirtyMask & 1);
|
|
|
|
}
|
|
|
|
|
2008-08-23 11:41:04 +00:00
|
|
|
NTSTATUS fs_lock(BOOLEAN LockVolume)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
IO_STATUS_BLOCK IoSb;
|
|
|
|
|
|
|
|
/* Check if volume is dirty */
|
|
|
|
Status = NtFsControlFile(fd,
|
|
|
|
NULL, NULL, NULL, &IoSb,
|
|
|
|
LockVolume ? FSCTL_LOCK_VOLUME :
|
|
|
|
FSCTL_UNLOCK_VOLUME,
|
|
|
|
NULL, 0, NULL, 0);
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fs_dismount()
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
IO_STATUS_BLOCK IoSb;
|
|
|
|
|
|
|
|
/* Check if volume is dirty */
|
|
|
|
Status = NtFsControlFile(fd,
|
|
|
|
NULL, NULL, NULL, &IoSb,
|
|
|
|
FSCTL_DISMOUNT_VOLUME,
|
|
|
|
NULL, 0, NULL, 0);
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-03 18:50:46 +00:00
|
|
|
void fs_read(loff_t pos,int size,void *data)
|
|
|
|
{
|
|
|
|
CHANGE *walk;
|
|
|
|
int got;
|
|
|
|
#if 1 // TMN
|
|
|
|
const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; // TMN:
|
|
|
|
const loff_t seekpos_aligned = pos - (pos % 512); // TMN:
|
|
|
|
const size_t seek_delta = (size_t)(pos - seekpos_aligned); // TMN:
|
2013-10-14 16:28:14 +00:00
|
|
|
#if DBG
|
2008-08-03 18:50:46 +00:00
|
|
|
const size_t readsize = (size_t)(pos - seekpos_aligned) + readsize_aligned; // TMN:
|
2013-10-14 16:28:14 +00:00
|
|
|
#endif
|
2008-08-06 12:03:34 +00:00
|
|
|
char* tmpBuf = vfalloc(readsize_aligned); // TMN:
|
2008-08-03 18:50:46 +00:00
|
|
|
if (llseek(fd,seekpos_aligned,0) != seekpos_aligned) pdie("Seek to %I64d",pos);
|
|
|
|
if ((got = read(fd,tmpBuf,readsize_aligned)) < 0) pdie("Read %d bytes at %I64d",size,pos);
|
|
|
|
assert(got >= size);
|
|
|
|
got = size;
|
|
|
|
assert(seek_delta + size <= readsize);
|
|
|
|
memcpy(data, tmpBuf+seek_delta, size);
|
2008-08-06 12:03:34 +00:00
|
|
|
vffree(tmpBuf);
|
2008-08-03 18:50:46 +00:00
|
|
|
#else // TMN:
|
|
|
|
if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
|
|
|
|
if ((got = read(fd,data,size)) < 0) pdie("Read %d bytes at %lld",size,pos);
|
|
|
|
#endif // TMN:
|
|
|
|
if (got != size) die("Got %d bytes instead of %d at %I64d",got,size,pos);
|
|
|
|
for (walk = changes; walk; walk = walk->next) {
|
|
|
|
if (walk->pos < pos+size && walk->pos+walk->size > pos) {
|
|
|
|
if (walk->pos < pos)
|
|
|
|
memcpy(data,(char *) walk->data+pos-walk->pos,min((size_t)size,
|
|
|
|
(size_t)(walk->size-pos+walk->pos)));
|
|
|
|
else memcpy((char *) data+walk->pos-pos,walk->data,min((size_t)walk->size,
|
|
|
|
(size_t)(size+pos-walk->pos)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int fs_test(loff_t pos,int size)
|
|
|
|
{
|
|
|
|
void *scratch;
|
|
|
|
int okay;
|
|
|
|
|
|
|
|
#if 1 // TMN
|
|
|
|
const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; // TMN:
|
|
|
|
const loff_t seekpos_aligned = pos - (pos % 512); // TMN:
|
2008-08-06 12:03:34 +00:00
|
|
|
scratch = vfalloc(readsize_aligned);
|
2008-08-03 18:50:46 +00:00
|
|
|
if (llseek(fd,seekpos_aligned,0) != seekpos_aligned) pdie("Seek to %lld",pos);
|
|
|
|
okay = read(fd,scratch,readsize_aligned) == (int)readsize_aligned;
|
2008-08-06 12:03:34 +00:00
|
|
|
vffree(scratch);
|
2008-08-03 18:50:46 +00:00
|
|
|
#else // TMN:
|
|
|
|
if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
|
2008-08-06 12:03:34 +00:00
|
|
|
scratch = vfalloc(size);
|
2008-08-03 18:50:46 +00:00
|
|
|
okay = read(fd,scratch,size) == size;
|
2008-08-06 12:03:34 +00:00
|
|
|
vffree(scratch);
|
2008-08-03 18:50:46 +00:00
|
|
|
#endif // TMN:
|
|
|
|
return okay;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void fs_write(loff_t pos,int size,void *data)
|
|
|
|
{
|
|
|
|
CHANGE *new;
|
|
|
|
int did;
|
|
|
|
|
|
|
|
#if 1 //SAE
|
2008-08-06 13:07:28 +00:00
|
|
|
if (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE) {
|
2008-08-03 18:50:46 +00:00
|
|
|
void *scratch;
|
|
|
|
const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size;
|
|
|
|
const loff_t seekpos_aligned = pos - (pos % 512);
|
|
|
|
const size_t seek_delta = (size_t)(pos - seekpos_aligned);
|
|
|
|
boolean use_read = (seek_delta != 0) || ((readsize_aligned-size) != 0);
|
|
|
|
|
|
|
|
/* Aloc temp buffer if write is not aligned */
|
|
|
|
if (use_read)
|
2008-08-06 12:03:34 +00:00
|
|
|
scratch = vfalloc(readsize_aligned);
|
2008-08-03 18:50:46 +00:00
|
|
|
else
|
|
|
|
scratch = data;
|
|
|
|
|
|
|
|
did_change = 1;
|
|
|
|
if (llseek(fd,seekpos_aligned,0) != seekpos_aligned) pdie("Seek to %I64d",seekpos_aligned);
|
|
|
|
|
|
|
|
if (use_read)
|
|
|
|
{
|
|
|
|
/* Read aligned data */
|
|
|
|
if (read(fd,scratch,readsize_aligned) < 0) pdie("Read %d bytes at %I64d",size,pos);
|
|
|
|
|
|
|
|
/* Patch data in memory */
|
|
|
|
memcpy((char *)scratch+seek_delta, data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write it back */
|
|
|
|
if ((did = write(fd,scratch,readsize_aligned)) == (int)readsize_aligned)
|
|
|
|
{
|
2008-08-06 12:03:34 +00:00
|
|
|
if (use_read) vffree(scratch);
|
2008-08-03 18:50:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (did < 0) pdie("Write %d bytes at %I64d",size,pos);
|
|
|
|
die("Wrote %d bytes instead of %d at %I64d",did,size,pos);
|
|
|
|
}
|
|
|
|
|
2008-08-06 12:03:34 +00:00
|
|
|
new = vfalloc(sizeof(CHANGE));
|
2008-08-03 18:50:46 +00:00
|
|
|
new->pos = pos;
|
2008-08-06 12:03:34 +00:00
|
|
|
memcpy(new->data = vfalloc(new->size = size),data,size);
|
2008-08-03 18:50:46 +00:00
|
|
|
new->next = NULL;
|
|
|
|
if (last) last->next = new;
|
|
|
|
else changes = new;
|
|
|
|
last = new;
|
|
|
|
|
|
|
|
#else //SAE
|
|
|
|
if (write_immed) {
|
|
|
|
did_change = 1;
|
|
|
|
if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
|
|
|
|
if ((did = write(fd,data,size)) == size) return;
|
|
|
|
if (did < 0) pdie("Write %d bytes at %lld",size,pos);
|
|
|
|
die("Wrote %d bytes instead of %d at %lld",did,size,pos);
|
|
|
|
}
|
2008-08-06 12:03:34 +00:00
|
|
|
new = vfalloc(sizeof(CHANGE));
|
2008-08-03 18:50:46 +00:00
|
|
|
new->pos = pos;
|
2008-08-06 12:03:34 +00:00
|
|
|
memcpy(new->data = vfalloc(new->size = size),data,size);
|
2008-08-03 18:50:46 +00:00
|
|
|
new->next = NULL;
|
|
|
|
if (last) last->next = new;
|
|
|
|
else changes = new;
|
|
|
|
last = new;
|
|
|
|
#endif //SAE
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void fs_flush(void)
|
|
|
|
{
|
|
|
|
CHANGE *this;
|
2008-08-06 13:07:28 +00:00
|
|
|
int old_write_immed = (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE);
|
2008-08-03 18:50:46 +00:00
|
|
|
|
|
|
|
/* Disable writes to the list now */
|
2008-08-06 13:07:28 +00:00
|
|
|
FsCheckFlags |= FSCHECK_IMMEDIATE_WRITE;
|
2008-08-03 18:50:46 +00:00
|
|
|
|
|
|
|
while (changes) {
|
|
|
|
this = changes;
|
|
|
|
changes = changes->next;
|
2008-08-04 10:00:32 +00:00
|
|
|
|
2008-08-03 18:50:46 +00:00
|
|
|
fs_write(this->pos, this->size, this->data);
|
|
|
|
|
2008-08-06 12:03:34 +00:00
|
|
|
vffree(this->data);
|
|
|
|
vffree(this);
|
2008-08-03 18:50:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Restore values */
|
2008-08-06 13:07:28 +00:00
|
|
|
if (!old_write_immed) FsCheckFlags ^= FSCHECK_IMMEDIATE_WRITE;
|
2008-08-03 18:50:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int fs_close(int write)
|
|
|
|
{
|
|
|
|
CHANGE *next;
|
|
|
|
int changed;
|
|
|
|
|
|
|
|
changed = !!changes;
|
|
|
|
if (write) fs_flush();
|
|
|
|
else while (changes) {
|
|
|
|
next = changes->next;
|
2008-08-06 12:03:34 +00:00
|
|
|
vffree(changes->data);
|
|
|
|
vffree(changes);
|
2008-08-03 18:50:46 +00:00
|
|
|
changes = next;
|
|
|
|
}
|
|
|
|
if (close(fd) < 0) pdie("closing file system");
|
|
|
|
return changed || did_change;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int fs_changed(void)
|
|
|
|
{
|
|
|
|
return !!changes || did_change;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local Variables: */
|
|
|
|
/* tab-width: 8 */
|
|
|
|
/* End: */
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
static int WIN32close(HANDLE FileHandle)
|
2008-08-03 18:50:46 +00:00
|
|
|
{
|
2008-08-04 10:00:32 +00:00
|
|
|
if (!NT_SUCCESS(NtClose(FileHandle))) return -1;
|
2008-08-03 18:50:46 +00:00
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
return 0;
|
2008-08-03 18:50:46 +00:00
|
|
|
}
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
static int WIN32read(HANDLE FileHandle, void *buf, unsigned int len)
|
2008-08-03 18:50:46 +00:00
|
|
|
{
|
2008-08-04 10:00:32 +00:00
|
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
Status = NtReadFile(FileHandle,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
&IoStatusBlock,
|
|
|
|
buf,
|
|
|
|
len,
|
|
|
|
&CurrentOffset,
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2008-08-04 16:04:56 +00:00
|
|
|
DPRINT1("NtReadFile() failed (Status %lx)\n", Status);
|
2008-08-04 10:00:32 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrentOffset.QuadPart += len;
|
|
|
|
return (int)len;
|
2008-08-03 18:50:46 +00:00
|
|
|
}
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
static int WIN32write(HANDLE FileHandle, void *buf, unsigned int len)
|
2008-08-03 18:50:46 +00:00
|
|
|
{
|
2008-08-04 10:00:32 +00:00
|
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
Status = NtWriteFile(FileHandle,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
&IoStatusBlock,
|
|
|
|
buf,
|
|
|
|
len,
|
|
|
|
&CurrentOffset,
|
|
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2008-08-04 16:04:56 +00:00
|
|
|
DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
|
2008-08-04 10:00:32 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2008-08-03 18:50:46 +00:00
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
CurrentOffset.QuadPart += len;
|
|
|
|
return (int)len;
|
2008-08-03 18:50:46 +00:00
|
|
|
}
|
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
static loff_t WIN32llseek(HANDLE fd, loff_t offset, int whence)
|
2008-08-03 18:50:46 +00:00
|
|
|
{
|
2008-08-04 10:00:32 +00:00
|
|
|
CurrentOffset.QuadPart = (ULONGLONG)offset;
|
2008-08-03 18:50:46 +00:00
|
|
|
|
2008-08-04 10:00:32 +00:00
|
|
|
return offset;
|
2008-08-03 18:50:46 +00:00
|
|
|
}
|