From 0bd7816f85d3e8cd7f3055742a48b7bd21937dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20H?= Date: Sun, 12 Mar 2017 20:31:03 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 5 + CMakeLists.txt | 20 +++ LICENSE.txt | 19 +++ README.md | 35 ++++++ bitmap.c | 100 +++++++++++++++ bitmap.h | 21 ++++ cache.c | 184 +++++++++++++++++++++++++++ cache.h | 35 ++++++ debug.c | 35 ++++++ debug.h | 30 +++++ dir.c | 141 +++++++++++++++++++++ dir.h | 15 +++ disk_image.c | 68 ++++++++++ disk_image.h | 19 +++ dokan.h | 335 +++++++++++++++++++++++++++++++++++++++++++++++++ dokan.lib | Bin 0 -> 4822 bytes file.c | 177 ++++++++++++++++++++++++++ file.h | 43 +++++++ file_info.c | 107 ++++++++++++++++ file_info.h | 24 ++++ general.c | 75 +++++++++++ general.h | 42 +++++++ inode.c | 285 +++++++++++++++++++++++++++++++++++++++++ inode.h | 28 +++++ internal.h | 148 ++++++++++++++++++++++ list.h | 120 ++++++++++++++++++ main.c | 260 ++++++++++++++++++++++++++++++++++++++ mbr.h | 43 +++++++ partitions.c | 106 ++++++++++++++++ partitions.h | 25 ++++ read_write.c | 131 +++++++++++++++++++ read_write.h | 25 ++++ stubs.c | 118 +++++++++++++++++ stubs.h | 75 +++++++++++ volume_info.c | 66 ++++++++++ volume_info.h | 31 +++++ 36 files changed, 2991 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 bitmap.c create mode 100644 bitmap.h create mode 100644 cache.c create mode 100644 cache.h create mode 100644 debug.c create mode 100644 debug.h create mode 100644 dir.c create mode 100644 dir.h create mode 100644 disk_image.c create mode 100644 disk_image.h create mode 100644 dokan.h create mode 100644 dokan.lib create mode 100644 file.c create mode 100644 file.h create mode 100644 file_info.c create mode 100644 file_info.h create mode 100644 general.c create mode 100644 general.h create mode 100644 inode.c create mode 100644 inode.h create mode 100644 internal.h create mode 100644 list.h create mode 100644 main.c create mode 100644 mbr.h create mode 100644 partitions.c create mode 100644 partitions.h create mode 100644 read_write.c create mode 100644 read_write.h create mode 100644 stubs.c create mode 100644 stubs.h create mode 100644 volume_info.c create mode 100644 volume_info.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df351b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +dist +doc +minix_fs_src +dokan_src diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1e8950c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required (VERSION 3.1) + +project (MinixFS) + +if (UNIX OR MINGW) + add_compile_options(-Wall -Wno-sign-compare) +elseif (MSVC) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS + _CRT_SECURE_NO_WARNINGS) + add_compile_options(/W3) +endif() + +file(GLOB SOURCES *.c) +file(GLOB HEADERS *.h) + +add_executable(MinixFS ${SOURCES} ${HEADERS}) + +target_link_libraries(MinixFS ${CMAKE_SOURCE_DIR}/dokan.lib) + +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MinixFS) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..39f2f1c --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013 Rafał Harabień + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7433d36 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +MINIX Filesystem Driver +======================= + +General +------- +This is read/write Minix Filesystem driver for Windows. +Driver operates in user mode and uses a Dokan library to communicate with the kernel. +Driver was made for educational purposes in few days and gave me a lot of fun. + +Requirements +------------ + +Dokan has to be installed before using this driver. +You can download it from: http://dokan-dev.net/en/download/ +Filesystem has been tested with version 0.6. It may not work with new versions. + +Usage +----- + +Listing subpartitions in disk image: + + minix -f image_path -l + +Mounting partition: + + minix -f image_path p0s0=mount_point_path + +Mount point has to be unused partition letter or path to a non-existing folder. +You can specify more than one subpartion and mount point in command line. +To unmount all partitions terminate program by pressing Ctrl+C. + +Notes +----- +Using this program can make damage to your disk image, so be sure to make a backup. +Blue Screens could happen too. I am not responsible for any loss of data. diff --git a/bitmap.c b/bitmap.c new file mode 100644 index 0000000..3eada8d --- /dev/null +++ b/bitmap.c @@ -0,0 +1,100 @@ +#include +#include +#include "general.h" +#include "debug.h" + +static unsigned MxfsCountWordBits(uint32_t v) +{ + v = v - ((v >> 1) & 0x55555555); // reuse input as temporary + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp + return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; // count +} + +unsigned MxfsCountBitsSet(MX_BITMAP *Bm) +{ + unsigned i, Result = 0; + for(i = 0; i < Bm->Size; ++i) + Result += MxfsCountWordBits(Bm->Buffer[i]); + return Result; +} + +void MxfsInitBitmap(MX_BITMAP *Bm, uint32_t *Buffer, unsigned BitsCount) +{ + ASSERT(MxfsCountWordBits(0) == 0); + ASSERT(MxfsCountWordBits(0x11111111) == 8); + ASSERT(MxfsCountWordBits(0xFFFFFFFF) == 32); + + Bm->Buffer = Buffer; + Bm->Size = BitsCount / 32; + Bm->Hint = 0; +} + +void MxfsDestroyBitmap(MX_BITMAP *Bm) +{ + MxfsFree(Bm->Buffer); + Bm->Buffer = NULL; +} + +int MxfsFindClearBit(MX_BITMAP *Bm) +{ + unsigned i, j; + + for(i = 0; i < Bm->Size; ++i) + { + if(Bm->Buffer[i] != 0xFFFFFFFF) + break; + } + + if(i == Bm->Size) + { + ERR("Bitmap is full!\n"); + return -ERROR_DISK_FULL; + } + + for(j = 0; j < 32; ++j) + { + unsigned Bit = Bm->Buffer[i] & (1 << j); + if(!Bit) break; + } + + ASSERT(j < 32); + return i * 32 + j; +} + +int MxfsSetBit(MX_BITMAP *Bm, unsigned Bit) +{ + unsigned i = Bit / 32; + Bit = Bit & 31; + + if(i >= Bm->Size) + return -ERROR_INVALID_PARAMETER; + + Bm->Buffer[i] |= (1 << Bit); + return 0; +} + +int MxfsClearBit(MX_BITMAP *Bm, unsigned Bit) +{ + unsigned i = Bit / 32; + Bit = Bit & 31; + + if(i >= Bm->Size) + return -ERROR_INVALID_PARAMETER; + + Bm->Buffer[i] &= ~(1 << Bit); + return 0; +} + +int MxfsGetBit(MX_BITMAP *Bm, unsigned Bit) +{ + unsigned i = Bit / 32; + Bit = Bit & 31; + + if(i >= Bm->Size) + return -ERROR_INVALID_PARAMETER; + + if(Bm->Buffer[i] & (1 << Bit)) + return 1; + else + return 0; +} diff --git a/bitmap.h b/bitmap.h new file mode 100644 index 0000000..e204271 --- /dev/null +++ b/bitmap.h @@ -0,0 +1,21 @@ +#ifndef BITMAP_H_INCLUDED +#define BITMAP_H_INCLUDED + +#include + +typedef struct +{ + uint32_t *Buffer; + unsigned Size; + unsigned Hint; +} MX_BITMAP; + +void MxfsInitBitmap(MX_BITMAP *Bm, uint32_t *Buffer, unsigned BitsCount); +void MxfsDestroyBitmap(MX_BITMAP *Bm); +unsigned MxfsCountBitsSet(MX_BITMAP *Bm); +int MxfsFindClearBit(MX_BITMAP *Bm); +int MxfsSetBit(MX_BITMAP *Bm, unsigned Bit); +int MxfsClearBit(MX_BITMAP *Bm, unsigned Bit); +int MxfsGetBit(MX_BITMAP *Bm, unsigned Bit); + +#endif // BITMAP_H_INCLUDED diff --git a/cache.c b/cache.c new file mode 100644 index 0000000..46ffa3a --- /dev/null +++ b/cache.c @@ -0,0 +1,184 @@ +#include +#include "debug.h" +#include "cache.h" +#include "general.h" + +void MxfsCacheInit(MINIX_FS *FileSys) +{ + InitializeListHead(&FileSys->Cache.MruList); + FileSys->Cache.Count = 0; + InitializeCriticalSection(&FileSys->Cache.Lock); +} + +static int MxfsCacheFlushItem(MINIX_FS *FileSys, MX_CACHE_ITEM *Item) +{ + unsigned Offset = FileSys->uOffset + Item->Index*MINIX_BLOCK_SIZE; + int Result = ImgWrite(FileSys->pImg, Offset, MINIX_BLOCK_SIZE, Item->Data); + Item->Dirty = FALSE; + + if(Result < 0) + ERR("MxfsCacheFlushItem: ImgWrite failed %d\n", Result); + return Result; +} + +void MxfsCacheFlush(MINIX_FS *FileSys) +{ + EnterCriticalSection(&FileSys->Cache.Lock); + + LIST_ENTRY *Entry = FileSys->Cache.MruList.Flink; + while(Entry != &FileSys->Cache.MruList) + { + MX_CACHE_ITEM *Item = CONTAINING_RECORD(Entry, MX_CACHE_ITEM, MruList); + if(Item->Dirty) + MxfsCacheFlushItem(FileSys, Item); + + Entry = Entry->Flink; + } + + LeaveCriticalSection(&FileSys->Cache.Lock); +} + +static void MxfsCacheDestroyLastItem(MINIX_FS *FileSys) +{ + ASSERT(FileSys->Cache.Count > 0); + + LIST_ENTRY *Entry = RemoveTailList(&FileSys->Cache.MruList); + MX_CACHE_ITEM *Item = CONTAINING_RECORD(Entry, MX_CACHE_ITEM, MruList); + --FileSys->Cache.Count; + + if(Item->Dirty) + MxfsCacheFlushItem(FileSys, Item); + + MxfsFree(Item); +} + +void MxfsCacheDestroy(MINIX_FS *FileSys) +{ + while(!IsListEmpty(&FileSys->Cache.MruList)) + MxfsCacheDestroyLastItem(FileSys); +} + +MX_CACHE_ITEM *MxfsCacheFind(MINIX_FS *FileSys, unsigned Block) +{ + LIST_ENTRY *Entry = FileSys->Cache.MruList.Flink; + while(Entry != &FileSys->Cache.MruList) + { + MX_CACHE_ITEM *Item = CONTAINING_RECORD(Entry, MX_CACHE_ITEM, MruList); + if(Item->Index == Block) + return Item; + + Entry = Entry->Flink; + } + return NULL; +} + +MX_CACHE_ITEM *MxfsCacheLoadBlock(MINIX_FS *FileSys, unsigned Block, BOOL Zero) +{ + MX_CACHE_ITEM *Item = MxfsAlloc(sizeof(MX_CACHE_ITEM)); + if(!Item) return NULL; + + unsigned BlockOffset = Block * MINIX_BLOCK_SIZE; + if(BlockOffset + MINIX_BLOCK_SIZE > FileSys->cbLen) + { + ERR("MxfsCacheLoadBlock: Invalid block %u\n", Block); + return NULL; + } + + Item->Index = Block; + + if(!Zero) + { + int Result = ImgRead(FileSys->pImg, FileSys->uOffset + BlockOffset, MINIX_BLOCK_SIZE, Item->Data); + if(Result < 0) + { + MxfsFree(Item); + ERR("MxfsCacheLoadBlock: ImgRead failed %d\n", Result); + return NULL; + } + Item->Dirty = FALSE; + } else { + memset(Item->Data, 0, MINIX_BLOCK_SIZE); + Item->Dirty = TRUE; + } + + if(FileSys->Cache.Count == BLOCK_CACHE_SIZE) + MxfsCacheDestroyLastItem(FileSys); + + InsertHeadList(&FileSys->Cache.MruList, &Item->MruList); + ++FileSys->Cache.Count; + + return Item; +} + +int MxfsCacheRead(MINIX_FS *FileSys, void *Buffer, unsigned Block, unsigned Offset, unsigned Length) +{ + if(!(Offset < MINIX_BLOCK_SIZE && Offset + Length <= MINIX_BLOCK_SIZE)) + { + WARN("MxfsCacheRead: wrong offset (%u) or len (%u)\n", Offset, Length); + return -ERROR_INVALID_PARAMETER; + } + + EnterCriticalSection(&FileSys->Cache.Lock); + + int Result = -ERROR_NOT_FOUND; + MX_CACHE_ITEM *Item = MxfsCacheFind(FileSys, Block); + if(Item) + { + // Push at front + RemoveEntryList(&Item->MruList); + InsertHeadList(&FileSys->Cache.MruList, &Item->MruList); + } else + Item = MxfsCacheLoadBlock(FileSys, Block, FALSE); + + if(Item) + { + memcpy(Buffer, Item->Data + Offset, Length); + Result = 0; + } + + LeaveCriticalSection(&FileSys->Cache.Lock); + return Result; +} + +int MxfsCacheWrite(MINIX_FS *FileSys, const void *Buffer, unsigned Block, unsigned Offset, unsigned Length) +{ + if(!(Offset < MINIX_BLOCK_SIZE && Offset + Length <= MINIX_BLOCK_SIZE)) + { + WARN("MxfsCacheWrite: wrong offset (%u) or len (%u)\n", Offset, Length); + return -ERROR_INVALID_PARAMETER; + } + + EnterCriticalSection(&FileSys->Cache.Lock); + + int Result = -ERROR_WRITE_FAULT; + MX_CACHE_ITEM *Item = MxfsCacheFind(FileSys, Block); + if(!Item) + Item = MxfsCacheLoadBlock(FileSys, Block, FALSE); + + if(Item) + { + memcpy(Item->Data + Offset, Buffer, Length); + Item->Dirty = TRUE; + Result = 0; + } + + LeaveCriticalSection(&FileSys->Cache.Lock); + return Result; +} + +void MxfsCacheZero(MINIX_FS *FileSys, unsigned Block) +{ + EnterCriticalSection(&FileSys->Cache.Lock); + + MX_CACHE_ITEM *Item = MxfsCacheFind(FileSys, Block); + if(Item) + { + memset(Item->Data, 0, MINIX_BLOCK_SIZE); + Item->Dirty = TRUE; + } + else + MxfsCacheLoadBlock(FileSys, Block, TRUE); + + LeaveCriticalSection(&FileSys->Cache.Lock); +} + diff --git a/cache.h b/cache.h new file mode 100644 index 0000000..df8903b --- /dev/null +++ b/cache.h @@ -0,0 +1,35 @@ +#ifndef CACHE_H_INCLUDED +#define CACHE_H_INCLUDED + +#include +#include "internal.h" +#include "list.h" + +#define BLOCK_CACHE_SIZE 128 + +struct _MINIX_FS; + +typedef struct _MX_CACHE_ITEM +{ + LIST_ENTRY MruList; + unsigned Index; + BOOL Dirty; + BYTE Data[MINIX_BLOCK_SIZE]; +} MX_CACHE_ITEM; + +typedef struct +{ + LIST_ENTRY MruList; + unsigned Count; + CRITICAL_SECTION Lock; + struct _MINIX_FS *FileSys; +} MX_BLOCK_CACHE; + +void MxfsCacheInit(struct _MINIX_FS *FileSys); +void MxfsCacheDestroy(struct _MINIX_FS *FileSys); +void MxfsCacheFlush(struct _MINIX_FS *FileSys); +int MxfsCacheRead(struct _MINIX_FS *FileSys, void *Buffer, unsigned Block, unsigned Offset, unsigned Length); +int MxfsCacheWrite(struct _MINIX_FS *FileSys, const void *Buffer, unsigned Block, unsigned Offset, unsigned Length); +void MxfsCacheZero(struct _MINIX_FS *FileSys, unsigned Block); + +#endif // CACHE_H_INCLUDED diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..4691c72 --- /dev/null +++ b/debug.c @@ -0,0 +1,35 @@ +#include "debug.h" +#include + +#ifndef NDEBUG +void MxfsDump(const void *pvBuf, unsigned uSize) +{ + const unsigned COLS = 16; + unsigned i = 0, j; + unsigned char *pcBuf = (unsigned char*)pvBuf; + + while(i < uSize) + { + for(j = 0; j < COLS; ++j) + { + if(i + j < uSize) + printf("%02X ", pcBuf[i + j]); + else + printf(" "); + } + + printf(" "); + + for(j = 0; j < COLS; ++j) + { + if(i + j < uSize) + printf("%c", isprint(pcBuf[i + j]) ? pcBuf[i + j] : '.'); + else + printf(" "); + } + + printf("\n"); + i += COLS; + } +} +#endif // NDEBUG diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..e1614f8 --- /dev/null +++ b/debug.h @@ -0,0 +1,30 @@ +#ifndef DEBUG_H_INCLUDED +#define DEBUG_H_INCLUDED + +#include +#include + +#ifdef NDEBUG + +#define ERR(...) printf(__VA_ARGS__) +#define WARN(...) +#define INFO(...) +#define TRACE(fmt, ...) +#define UNIMPLEMENTED(fmt, ...) +#define ASSERT(...) +#define MxfsDump(...) + +#else // NDEBUG + +#define ERR(...) printf(__VA_ARGS__) +#define WARN(...) printf(__VA_ARGS__) +#define INFO(...) //printf(__VA_ARGS__) +#define TRACE(fmt, ...) //printf("Trace: %s(" fmt ")\n", __func__, ##__VA_ARGS__) +#define UNIMPLEMENTED(fmt, ...) printf("Unimplemented function %s(" fmt ") called\n", __func__, ##__VA_ARGS__) +#define ASSERT(...) assert(__VA_ARGS__) + +void MxfsDump(const void *pvBuf, unsigned uSize); + +#endif // NDEBUG + +#endif // DEBUG_H_INCLUDED diff --git a/dir.c b/dir.c new file mode 100644 index 0000000..f2cea05 --- /dev/null +++ b/dir.c @@ -0,0 +1,141 @@ +#include +#include +#include "dir.h" +#include "general.h" +#include "debug.h" +#include "internal.h" +#include "file.h" +#include "inode.h" +#include "file_info.h" + +int MxfsFindFileInDir(MINIX_FS *FileSys, unsigned Inode, minix_inode *DirInfo, const char *Filename) +{ + unsigned TotalEntries, i, j = 0, Offset; + int Result; + minix_dir_entry Entries[MINIX_BLOCK_SIZE / sizeof(minix_dir_entry)]; + + TotalEntries = DirInfo->d2_size / sizeof(minix_dir_entry); + + for(i = 0; i < TotalEntries; ++i) + { + if(i % COUNTOF(Entries) == 0) + { + Offset = i * sizeof(minix_dir_entry); + Result = MxfsGetBlockFromFileOffset(FileSys, Inode, DirInfo, Offset, FALSE); + if(Result < 0) + return Result; + + Result = MxfsCacheRead(FileSys, Entries, Result, 0, MINIX_BLOCK_SIZE); + if(Result < 0) + return Result; + + j = 0; + } + + if(Entries[j].inode != 0 && strncmp(Filename, Entries[j].name, MAX_NAME_LEN) == 0) + return Entries[j].inode; + + ++j; + } + return -1; +} + +int MxfsParsePath(MINIX_FS *FileSys, const char *Path, BOOL Parent) +{ + char Filename[32]; + unsigned FilenameLen = 0; + int Result = 1; + minix_inode DirInfo; + + while(1) + { + if(*Path != '/' && *Path != '\\' && *Path != '\0') + { + if(FilenameLen + 1 >= sizeof(Filename)) + return -ERROR_NOT_FOUND; + Filename[FilenameLen++] = *Path; + } + else if(FilenameLen != 0) + { + if(Parent && *Path == '\0') + break; + + Filename[FilenameLen] = '\0'; + MxfsReadInode(FileSys, Result, &DirInfo); + + if(!(DirInfo.d2_mode & I_DIRECTORY)) + return -ERROR_NOT_FOUND; + + Result = MxfsFindFileInDir(FileSys, Result, &DirInfo, Filename); + FilenameLen = 0; + } + + if(Result < 0 || *Path == '\0') + break; + + ++Path; + } + + return Result; +} + +int DOKAN_CALLBACK MxfsFindFiles( + LPCWSTR PathName, + PFillFindData Callback, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %p", PathName, FileInfo); + ASSERT(FileInfo->IsDirectory); + + int Result; + minix_dir_entry Entries[MINIX_BLOCK_SIZE / sizeof(minix_dir_entry)]; + unsigned TotalEntries, FileIdx, Offset, i, j; + minix_inode DirInfo, Info; + + MINIX_FS *FileSys = (MINIX_FS*)(LONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(LONG_PTR)FileInfo->Context; + ASSERT(FileCtx); + + Result = MxfsReadInode(FileSys, FileCtx->Index, &DirInfo); + if(Result < 0) + return Result; + + TotalEntries = DirInfo.d2_size / sizeof(minix_dir_entry); + if(TotalEntries == 0) + return 0; // empty dir + + WIN32_FIND_DATAW wfd; + ZeroMemory(&wfd, sizeof(wfd)); + + for(i = 0, j = 0; i < TotalEntries; ++i, ++j) + { + if(i % COUNTOF(Entries) == 0) + { + Offset = i * sizeof(minix_dir_entry); + Result = MxfsGetBlockFromFileOffset(FileSys, FileCtx->Index, &DirInfo, Offset, FALSE); + if(Result < 0) + return Result; + + Result = MxfsCacheRead(FileSys, Entries, Result, 0, MINIX_BLOCK_SIZE); + if(Result < 0) + return Result; + + j = 0; + } + + FileIdx = Entries[j].inode; + if(!FileIdx) + continue; + + if(MxfsReadInode(FileSys, FileIdx, &Info) < 0) + { + WARN("Invalid node in directory\n"); + continue; + } + + MxfsFillFindData(&wfd, &Entries[j], &Info); + Callback(&wfd, FileInfo); + } + + return 0; +} diff --git a/dir.h b/dir.h new file mode 100644 index 0000000..18b52e4 --- /dev/null +++ b/dir.h @@ -0,0 +1,15 @@ +#ifndef DIR_H_INCLUDED +#define DIR_H_INCLUDED + +#include +#include "dokan.h" +#include "general.h" + +int DOKAN_CALLBACK MxfsFindFiles( + LPCWSTR PathName, + PFillFindData Callback, + PDOKAN_FILE_INFO FileInfo); + +int MxfsParsePath(MINIX_FS *pFileSys, const char *pszPath, BOOL Parent); + +#endif // DIR_H_INCLUDED diff --git a/disk_image.c b/disk_image.c new file mode 100644 index 0000000..b0f6af2 --- /dev/null +++ b/disk_image.c @@ -0,0 +1,68 @@ +#include +#include "disk_image.h" +#include "general.h" + +DISK_IMG *ImgOpen(const char *pszPath) +{ + HANDLE FileHandle = CreateFileA(pszPath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if(FileHandle == INVALID_HANDLE_VALUE) + return NULL; + + DISK_IMG *pImg = MxfsAlloc(sizeof(DISK_IMG)); + if(!pImg) + return NULL; + + pImg->FileHandle = FileHandle; + InitializeCriticalSection(&pImg->Lock); + return pImg; +} + +void ImgClose(DISK_IMG *pImg) +{ + if(!pImg) return; + CloseHandle(pImg->FileHandle); + MxfsFree(pImg); +} + +int ImgRead(DISK_IMG *pImg, unsigned uOffset, unsigned cBytes, PVOID pBuf) +{ + DWORD dwBytesRead; + BOOL bSuccess; + + EnterCriticalSection(&pImg->Lock); + SetFilePointer(pImg->FileHandle, uOffset, NULL, FILE_BEGIN); + bSuccess = ReadFile(pImg->FileHandle, pBuf, cBytes, &dwBytesRead, NULL); + LeaveCriticalSection(&pImg->Lock); + + if(!bSuccess || dwBytesRead != cBytes) + return -ERROR_READ_FAULT; + + return 0; +} + +int ImgWrite(DISK_IMG *pImg, unsigned uOffset, unsigned cBytes, PVOID pBuf) +{ + DWORD dwBytesWritten = 0; + BOOL bSuccess = FALSE; + + EnterCriticalSection(&pImg->Lock); + if(uOffset + cBytes <= GetFileSize(pImg->FileHandle, NULL)) + { + SetFilePointer(pImg->FileHandle, uOffset, NULL, FILE_BEGIN); + bSuccess = WriteFile(pImg->FileHandle, pBuf, cBytes, &dwBytesWritten, NULL); + } + LeaveCriticalSection(&pImg->Lock); + + if(!bSuccess || dwBytesWritten != cBytes) + return -ERROR_WRITE_FAULT; + + return 0; +} + +unsigned ImgGetSize(DISK_IMG *pImg) +{ + EnterCriticalSection(&pImg->Lock); + unsigned uSize = GetFileSize(pImg->FileHandle, NULL); + LeaveCriticalSection(&pImg->Lock); + return uSize; +} diff --git a/disk_image.h b/disk_image.h new file mode 100644 index 0000000..2e5beb3 --- /dev/null +++ b/disk_image.h @@ -0,0 +1,19 @@ +#ifndef DISK_IMAGE_H_INCLUDED +#define DISK_IMAGE_H_INCLUDED + +#include +#include + +typedef struct +{ + CRITICAL_SECTION Lock; + HANDLE FileHandle; +} DISK_IMG; + +DISK_IMG *ImgOpen(const char *pszPath); +void ImgClose(DISK_IMG *pImg); +int ImgRead(DISK_IMG *pImg, unsigned uOffset, unsigned cBytes, PVOID pBuf); +int ImgWrite(DISK_IMG *pImg, unsigned uOffset, unsigned cBytes, PVOID pBuf); +unsigned ImgGetSize(DISK_IMG *pImg); + +#endif // DISK_IMAGE_H_INCLUDED diff --git a/dokan.h b/dokan.h new file mode 100644 index 0000000..987b639 --- /dev/null +++ b/dokan.h @@ -0,0 +1,335 @@ +/* + Dokan : user-mode file system library for Windows + + Copyright (C) 2008 Hiroki Asakawa info@dokan-dev.net + + http://dokan-dev.net/en + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +*/ + + +#ifndef _DOKAN_H_ +#define _DOKAN_H_ + +#define DOKAN_DRIVER_NAME L"dokan.sys" + +#ifndef _M_X64 + #ifdef _EXPORTING + #define DOKANAPI __declspec(dllimport) __stdcall + #else + #define DOKANAPI __declspec(dllexport) __stdcall + #endif +#else + #define DOKANAPI +#endif + +#define DOKAN_CALLBACK __stdcall + +#ifdef __cplusplus +extern "C" { +#endif + +// The current Dokan version (ver 0.6.0). Please set this constant on DokanOptions->Version. +#define DOKAN_VERSION 600 + +#define DOKAN_OPTION_DEBUG 1 // ouput debug message +#define DOKAN_OPTION_STDERR 2 // ouput debug message to stderr +#define DOKAN_OPTION_ALT_STREAM 4 // use alternate stream +#define DOKAN_OPTION_KEEP_ALIVE 8 // use auto unmount +#define DOKAN_OPTION_NETWORK 16 // use network drive, you need to install Dokan network provider. +#define DOKAN_OPTION_REMOVABLE 32 // use removable drive + +typedef struct _DOKAN_OPTIONS { + USHORT Version; // Supported Dokan Version, ex. "530" (Dokan ver 0.5.3) + USHORT ThreadCount; // number of threads to be used + ULONG Options; // combination of DOKAN_OPTIONS_* + ULONG64 GlobalContext; // FileSystem can use this variable + LPCWSTR MountPoint; // mount point "M:\" (drive letter) or "C:\mount\dokan" (path in NTFS) +} DOKAN_OPTIONS, *PDOKAN_OPTIONS; + +typedef struct _DOKAN_FILE_INFO { + ULONG64 Context; // FileSystem can use this variable + ULONG64 DokanContext; // Don't touch this + PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS which was passed to DokanMain. + ULONG ProcessId; // process id for the thread that originally requested a given I/O operation + UCHAR IsDirectory; // requesting a directory file + UCHAR DeleteOnClose; // Delete on when "cleanup" is called + UCHAR PagingIo; // Read or write is paging IO. + UCHAR SynchronousIo; // Read or write is synchronous IO. + UCHAR Nocache; + UCHAR WriteToEndOfFile; // If true, write to the current end of file instead of Offset parameter. + +} DOKAN_FILE_INFO, *PDOKAN_FILE_INFO; + + +// FillFileData +// add an entry in FindFiles +// return 1 if buffer is full, otherwise 0 +// (currently never return 1) +typedef int (WINAPI *PFillFindData) (PWIN32_FIND_DATAW, PDOKAN_FILE_INFO); + +typedef struct _DOKAN_OPERATIONS { + + // When an error occurs, return negative value. + // Usually you should return GetLastError() * -1. + + + // CreateFile + // If file is a directory, CreateFile (not OpenDirectory) may be called. + // In this case, CreateFile should return 0 when that directory can be opened. + // You should set TRUE on DokanFileInfo->IsDirectory when file is a directory. + // When CreationDisposition is CREATE_ALWAYS or OPEN_ALWAYS and a file already exists, + // you should return ERROR_ALREADY_EXISTS(183) (not negative value) + int (DOKAN_CALLBACK *CreateFile) ( + LPCWSTR, // FileName + DWORD, // DesiredAccess + DWORD, // ShareMode + DWORD, // CreationDisposition + DWORD, // FlagsAndAttributes + PDOKAN_FILE_INFO); + + int (DOKAN_CALLBACK *OpenDirectory) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + int (DOKAN_CALLBACK *CreateDirectory) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + // When FileInfo->DeleteOnClose is true, you must delete the file in Cleanup. + int (DOKAN_CALLBACK *Cleanup) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + int (DOKAN_CALLBACK *CloseFile) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + int (DOKAN_CALLBACK *ReadFile) ( + LPCWSTR, // FileName + LPVOID, // Buffer + DWORD, // NumberOfBytesToRead + LPDWORD, // NumberOfBytesRead + LONGLONG, // Offset + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *WriteFile) ( + LPCWSTR, // FileName + LPCVOID, // Buffer + DWORD, // NumberOfBytesToWrite + LPDWORD, // NumberOfBytesWritten + LONGLONG, // Offset + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *FlushFileBuffers) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *GetFileInformation) ( + LPCWSTR, // FileName + LPBY_HANDLE_FILE_INFORMATION, // Buffer + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *FindFiles) ( + LPCWSTR, // PathName + PFillFindData, // call this function with PWIN32_FIND_DATAW + PDOKAN_FILE_INFO); // (see PFillFindData definition) + + + // You should implement either FindFiles or FindFilesWithPattern + int (DOKAN_CALLBACK *FindFilesWithPattern) ( + LPCWSTR, // PathName + LPCWSTR, // SearchPattern + PFillFindData, // call this function with PWIN32_FIND_DATAW + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *SetFileAttributes) ( + LPCWSTR, // FileName + DWORD, // FileAttributes + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *SetFileTime) ( + LPCWSTR, // FileName + CONST FILETIME*, // CreationTime + CONST FILETIME*, // LastAccessTime + CONST FILETIME*, // LastWriteTime + PDOKAN_FILE_INFO); + + + // You should not delete file on DeleteFile or DeleteDirectory. + // When DeleteFile or DeleteDirectory, you must check whether + // you can delete the file or not, and return 0 (when you can delete it) + // or appropriate error codes such as -ERROR_DIR_NOT_EMPTY, + // -ERROR_SHARING_VIOLATION. + // When you return 0 (ERROR_SUCCESS), you get Cleanup with + // FileInfo->DeleteOnClose set TRUE and you have to delete the + // file in Close. + int (DOKAN_CALLBACK *DeleteFile) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + int (DOKAN_CALLBACK *DeleteDirectory) ( + LPCWSTR, // FileName + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *MoveFile) ( + LPCWSTR, // ExistingFileName + LPCWSTR, // NewFileName + BOOL, // ReplaceExisiting + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *SetEndOfFile) ( + LPCWSTR, // FileName + LONGLONG, // Length + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *SetAllocationSize) ( + LPCWSTR, // FileName + LONGLONG, // Length + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *LockFile) ( + LPCWSTR, // FileName + LONGLONG, // ByteOffset + LONGLONG, // Length + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *UnlockFile) ( + LPCWSTR, // FileName + LONGLONG,// ByteOffset + LONGLONG,// Length + PDOKAN_FILE_INFO); + + + // Neither GetDiskFreeSpace nor GetVolumeInformation + // save the DokanFileContext->Context. + // Before these methods are called, CreateFile may not be called. + // (ditto CloseFile and Cleanup) + + // see Win32 API GetDiskFreeSpaceEx + int (DOKAN_CALLBACK *GetDiskFreeSpace) ( + PULONGLONG, // FreeBytesAvailable + PULONGLONG, // TotalNumberOfBytes + PULONGLONG, // TotalNumberOfFreeBytes + PDOKAN_FILE_INFO); + + + // see Win32 API GetVolumeInformation + int (DOKAN_CALLBACK *GetVolumeInformation) ( + LPWSTR, // VolumeNameBuffer + DWORD, // VolumeNameSize in num of chars + LPDWORD,// VolumeSerialNumber + LPDWORD,// MaximumComponentLength in num of chars + LPDWORD,// FileSystemFlags + LPWSTR, // FileSystemNameBuffer + DWORD, // FileSystemNameSize in num of chars + PDOKAN_FILE_INFO); + + + int (DOKAN_CALLBACK *Unmount) ( + PDOKAN_FILE_INFO); + + + // Suported since 0.6.0. You must specify the version at DOKAN_OPTIONS.Version. + int (DOKAN_CALLBACK *GetFileSecurity) ( + LPCWSTR, // FileName + PSECURITY_INFORMATION, // A pointer to SECURITY_INFORMATION value being requested + PSECURITY_DESCRIPTOR, // A pointer to SECURITY_DESCRIPTOR buffer to be filled + ULONG, // length of Security descriptor buffer + PULONG, // LengthNeeded + PDOKAN_FILE_INFO); + + int (DOKAN_CALLBACK *SetFileSecurity) ( + LPCWSTR, // FileName + PSECURITY_INFORMATION, + PSECURITY_DESCRIPTOR, // SecurityDescriptor + ULONG, // SecurityDescriptor length + PDOKAN_FILE_INFO); + + +} DOKAN_OPERATIONS, *PDOKAN_OPERATIONS; + + + +/* DokanMain returns error codes */ +#define DOKAN_SUCCESS 0 +#define DOKAN_ERROR -1 /* General Error */ +#define DOKAN_DRIVE_LETTER_ERROR -2 /* Bad Drive letter */ +#define DOKAN_DRIVER_INSTALL_ERROR -3 /* Can't install driver */ +#define DOKAN_START_ERROR -4 /* Driver something wrong */ +#define DOKAN_MOUNT_ERROR -5 /* Can't assign a drive letter or mount point */ +#define DOKAN_MOUNT_POINT_ERROR -6 /* Mount point is invalid */ + +int DOKANAPI +DokanMain( + PDOKAN_OPTIONS DokanOptions, + PDOKAN_OPERATIONS DokanOperations); + + +BOOL DOKANAPI +DokanUnmount( + WCHAR DriveLetter); + +BOOL DOKANAPI +DokanRemoveMountPoint( + LPCWSTR MountPoint); + + +// DokanIsNameInExpression +// check whether Name can match Expression +// Expression can contain wildcard characters (? and *) +BOOL DOKANAPI +DokanIsNameInExpression( + LPCWSTR Expression, // matching pattern + LPCWSTR Name, // file name + BOOL IgnoreCase); + + +ULONG DOKANAPI +DokanVersion(); + +ULONG DOKANAPI +DokanDriverVersion(); + +// DokanResetTimeout +// extends the time out of the current IO operation in driver. +BOOL DOKANAPI +DokanResetTimeout( + ULONG Timeout, // timeout in millisecond + PDOKAN_FILE_INFO DokanFileInfo); + +// Get the handle to Access Token +// This method needs be called in CreateFile, OpenDirectory or CreateDirectly callback. +// The caller must call CloseHandle for the returned handle. +HANDLE DOKANAPI +DokanOpenRequestorToken( + PDOKAN_FILE_INFO DokanFileInfo); + +#ifdef __cplusplus +} +#endif + + +#endif // _DOKAN_H_ diff --git a/dokan.lib b/dokan.lib new file mode 100644 index 0000000000000000000000000000000000000000..c1636e5a934bb73ca8c7060caffc5261d10ea172 GIT binary patch literal 4822 zcmcIo&2HO95FSZM6v>h;s|5Jy*JFL55El|xRK*j}1utFyP;>zmC7`}cdTz5Z^` z96Bch8%(o%&~8tQ;b$iIrTuRYx)06fz5YEgn{-%bVA;kt4z|XpW|YO`2lm)GvpqwP zs7PF<)tYn%W74u8T%5XOGO--n*r-KDBxtbF`@}uBj&bt zm{7(>91@o;Z9TFwiOaN_u60gai7T0AO2$U?$b@)(YfPLoPqC#cV)|X;{p7eOd#-bC z4T&rE9bHzkQrsK|w#BWNI!i>2&9hIuCi(8{hmJFxHeyu7^gGlW?mM>UIwQsCs)*_D zo)Wu9emo-+&vEq)`p`IPZIxi>+9R~AUCu#Iq1OMPCf$|k?hH=4QbxIy)WYZ zYwY|YK6qoqtVV;$fhB%w$7258T*#K7E8 zpr23(HuV|2Rl(#1bT;}>dEuhkv5sA5;(YIEpZD%)?N;|8lPWLFXlx*z05AbzfrAi$ zX@~X#3^0zJ@hPseaPcd=el5VLK7HuIvLLo=0I;P#5*)TMy1bm==u`(85a`rH4&{G9 zU;Lr718ngx+GL@N@2e|_ior%_^pKjqEqO?mdC2cmfrX3SW|kYzi**kl(}y zZ>fj;4<|p%iN23ZNGvYrw%L|0+02iszYxGb-guj(L{(jwi8>*TD*2J3N*8>p_ow*a z?G@T~7JI&juve6$>Y{wnjxb%&kZfmYp3>OPN(mJ~aAsj>&*<@W;qVIHr-+xqs>S~S z5r5geIjX+NT}DBD2T`*DD!)KOn7QM*z7dae3vqHGPV`n@0I!jNcL(vV1$fFWF7C_q z@bF;noi-9IdKZzeCn3xA!vebwe`QB~_7lXu5n#s_C5xbn%UtoHj}bK=pvF`>i(n?4 zFZHLJ5@w{%T?8*B8x#UOwI&MzgWjy!K%{cTrXS({iYlJ)TdeZ}oUk`5Vv0qVB*aMB nAHvNYC%X9KN7>=KxWiI_7Z#);RM4AoXK6^~04XeTgZbfKMfsSH literal 0 HcmV?d00001 diff --git a/file.c b/file.c new file mode 100644 index 0000000..40d3a24 --- /dev/null +++ b/file.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include "file.h" +#include "dir.h" +#include "debug.h" +#include "internal.h" +#include "inode.h" + +int DOKAN_CALLBACK MxfsCreateFile( + LPCWSTR FileName, + DWORD DesiredAccess, + DWORD ShareMode, + DWORD CreationDisposition, + DWORD FlagsAndAttributes, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("FileName %ls DesiredAccess %lx CreationDisposition %ld FileInfo %p\n", FileName, DesiredAccess, CreationDisposition, FileInfo); + + char Path[MAX_PATH]; + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + + wcstombs(Path, FileName, sizeof(Path)); + int FileIdx = MxfsParsePath(FileSys, Path, FALSE); + if(FileIdx < 0) + { + INFO("Failed to create file %s\n", Path); + return -ERROR_FILE_NOT_FOUND; + } + + minix_inode Info; + int Result = MxfsReadInode(FileSys, FileIdx, &Info); + if(Result < 0) + return Result; + + if(CreationDisposition == CREATE_NEW) + { + INFO("Already exists %s\n", Path); + return -ERROR_ALREADY_EXISTS; + } + + FILE_CTX *FileCtx = MxfsAlloc(sizeof(FILE_CTX)); + if(!FileCtx) + return -ERROR_NOT_ENOUGH_MEMORY; + + FileCtx->Index = FileIdx; + FileCtx->Write = (DesiredAccess & (GENERIC_WRITE|GENERIC_ALL|FILE_WRITE_DATA)) ? TRUE : FALSE; + FileCtx->Changed = FALSE; +#ifndef NDEBUG + wcscpy_s(FileCtx->DbgBuf, sizeof(FileCtx->DbgBuf), FileName); +#endif // NDEBUG + + if(FileInfo->Context) + WARN("MxfsCreateFile: Context %I64u\n", FileInfo->Context); + FileInfo->Context = (LONG64)(ULONG_PTR)FileCtx; + FileInfo->IsDirectory = (Info.d2_mode & I_DIRECTORY) ? TRUE : FALSE; + + FileCtx->Length = Info.d2_size; + if(CreationDisposition == TRUNCATE_EXISTING || CreationDisposition == CREATE_ALWAYS) + FileCtx->Length = 0; + if(FileCtx->Write) + INFO("CreationDisposition %ld\n", CreationDisposition); + + return 0; +} + +int DOKAN_CALLBACK MxfsOpenDirectory( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %p", FileName, FileInfo); + + char Path[MAX_PATH]; + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + + wcstombs(Path, FileName, sizeof(Path)); + int FileIdx = MxfsParsePath(FileSys, Path, FALSE); + if(FileIdx < 0) + { + INFO("Failed to open dir %s\n", Path); + return -ERROR_FILE_NOT_FOUND; + } + + minix_inode Info; + int Result = MxfsReadInode(FileSys, FileIdx, &Info); + if(Result < 0) + return Result; + + FILE_CTX *FileCtx = MxfsAlloc(sizeof(FILE_CTX)); + if(!FileCtx) + return -ERROR_NOT_ENOUGH_MEMORY; + + FileCtx->Index = FileIdx; + FileCtx->Length = Info.d2_size; + FileCtx->Changed = FALSE; +#ifndef NDEBUG + wcscpy_s(FileCtx->DbgBuf, sizeof(FileCtx->DbgBuf), FileName); +#endif // NDEBUG + + if(!(Info.d2_mode & I_DIRECTORY)) + { + MxfsFree(FileCtx); + INFO("Failed to open dir %s\n", Path); + return -ERROR_INVALID_PARAMETER; + } + + if(FileInfo->Context) + WARN("MxfsOpenDirectory: Context %I64u\n", FileInfo->Context); + + FileInfo->IsDirectory = TRUE; + FileInfo->Context = (ULONG64)(ULONG_PTR)FileCtx; + + return 0; +} + +int DOKAN_CALLBACK MxfsCreateDirectory( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %p", FileName, FileInfo); + + char Path[MAX_PATH]; + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + + wcstombs(Path, FileName, sizeof(Path)); + int FileIdx = MxfsParsePath(FileSys, Path, FALSE); + if(FileIdx > 0) + return -ERROR_ALREADY_EXISTS; + + UNIMPLEMENTED("%ls", FileName); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +// When FileInfo->DeleteOnClose is true, you must delete the file in Cleanup. +int DOKAN_CALLBACK MxfsCleanup( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %p", FileName, FileInfo); + + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(ULONG_PTR)FileInfo->Context; + ASSERT(FileCtx); + + minix_inode Info; + if(MxfsReadInode(FileSys, FileCtx->Index, &Info) >= 0) + { + Info.d2_atime = time(NULL); + + if(FileCtx->Changed) + { + Info.d2_mtime = time(NULL); + Info.d2_ctime = time(NULL); + } + + MxfsWriteInode(FileSys, FileCtx->Index, &Info); + } + + if(FileCtx->Changed) + MxfsSetEndOfFile(FileName, FileCtx->Length, FileInfo); + + MxfsFree(FileCtx); + FileInfo->Context = 0; + + return 0; +} + +int DOKAN_CALLBACK MxfsCloseFile( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %p", FileName, FileInfo); + + ASSERT(FileInfo->Context == 0); + + return 0; +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..9573ea3 --- /dev/null +++ b/file.h @@ -0,0 +1,43 @@ +#ifndef FILE_H_INCLUDED +#define FILE_H_INCLUDED + +#include +#include "dokan.h" +#include "internal.h" + +int DOKAN_CALLBACK MxfsCreateFile( + LPCWSTR FileName, + DWORD DesiredAccess, + DWORD ShareMode, + DWORD CreationDisposition, + DWORD FlagsAndAttributes, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsCreateDirectory( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsOpenDirectory( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsCleanup( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsCloseFile( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +typedef struct +{ + int Index; + unsigned Length; + BOOL Write, Changed; + //minix_inode Info; +#ifndef NDEBUG + WCHAR DbgBuf[256]; +#endif +} FILE_CTX; + +#endif // FILE_H_INCLUDED diff --git a/file_info.c b/file_info.c new file mode 100644 index 0000000..275a12c --- /dev/null +++ b/file_info.c @@ -0,0 +1,107 @@ +#include "file_info.h" +#include "file.h" +#include "general.h" +#include "debug.h" +#include "inode.h" + +void MxfsFileTimeFromTimeT(LPFILETIME FileTime, time_t t) +{ + LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000ull; + FileTime->dwLowDateTime = (DWORD)ll; + FileTime->dwHighDateTime = ll >> 32; +} + +time_t MxfsTimeTFromFileTime(CONST FILETIME *FileTime) +{ + ULARGE_INTEGER ull; + ull.LowPart = FileTime->dwLowDateTime; + ull.HighPart = FileTime->dwHighDateTime; + return ull.QuadPart / 10000000ULL - 11644473600ULL; +} + +int DOKAN_CALLBACK MxfsGetFileInformation( + LPCWSTR FileName, + LPBY_HANDLE_FILE_INFORMATION Buffer, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %p", FileName, FileInfo); + + MINIX_FS *FileSys = (MINIX_FS*)(LONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(LONG_PTR)FileInfo->Context; + ASSERT(FileCtx); + + minix_inode Info; + MxfsReadInode(FileSys, FileCtx->Index, &Info); + + ZeroMemory(Buffer, sizeof(*Buffer)); + Buffer->dwFileAttributes = FileInfo->IsDirectory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; + MxfsFileTimeFromTimeT(&Buffer->ftCreationTime, Info.d2_mtime); + MxfsFileTimeFromTimeT(&Buffer->ftLastAccessTime, Info.d2_atime); + MxfsFileTimeFromTimeT(&Buffer->ftLastWriteTime, Info.d2_mtime); + Buffer->dwVolumeSerialNumber = MINIX_VOLUME_ID; + Buffer->nFileSizeLow = Info.d2_size; + Buffer->nNumberOfLinks = 1; + Buffer->nFileIndexLow = FileCtx->Index; + + return 0; +} + +int DOKAN_CALLBACK MxfsSetFileTime( + LPCWSTR FileName, + CONST FILETIME *CreationTime, + CONST FILETIME *LastAccessTime, + CONST FILETIME *LastWriteTime, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls", FileName); + + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(ULONG_PTR)FileInfo->Context; + ASSERT(FileCtx); + + minix_inode Info; + int Result = MxfsReadInode(FileSys, FileCtx->Index, &Info); + if(Result < 0) + return Result; + + if(LastAccessTime && (LastAccessTime->dwHighDateTime || LastAccessTime->dwLowDateTime)) + { + if(LastAccessTime->dwHighDateTime == 0xFFFFFFFF && LastAccessTime->dwLowDateTime == 0xFFFFFFFF) + WARN("TODO: Support -1!\n"); // preserve access time + else + Info.d2_atime = MxfsTimeTFromFileTime(LastAccessTime); + } + + if(LastWriteTime && (LastWriteTime->dwHighDateTime || LastWriteTime->dwLowDateTime)) + { + if(LastWriteTime->dwHighDateTime == 0xFFFFFFFF && LastWriteTime->dwLowDateTime == 0xFFFFFFFF) + WARN("TODO: Support -1!\n"); // preserve write time + else + Info.d2_mtime = MxfsTimeTFromFileTime(LastWriteTime); + } + + return MxfsWriteInode(FileSys, FileCtx->Index, &Info); +} + +void MxfsFillFindData(WIN32_FIND_DATAW *pFindData, const minix_dir_entry *pDirEntry, const minix_inode *pInfo) +{ + /* Attributes */ + if(pInfo->d2_mode & I_DIRECTORY) + pFindData->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + else + pFindData->dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + + /* Times */ + MxfsFileTimeFromTimeT(&pFindData->ftCreationTime, pInfo->d2_mtime); + MxfsFileTimeFromTimeT(&pFindData->ftLastAccessTime, pInfo->d2_atime); + MxfsFileTimeFromTimeT(&pFindData->ftLastWriteTime, pInfo->d2_mtime); + + /* Size */ + pFindData->nFileSizeLow = pInfo->d2_size; + + /* Name */ + int NameLen = strnlen(pDirEntry->name, MAX_NAME_LEN); + MultiByteToWideChar(CP_ACP, 0, pDirEntry->name, NameLen, pFindData->cFileName, COUNTOF(pFindData->cFileName)); + pFindData->cFileName[min(NameLen, COUNTOF(pFindData->cFileName))] = 0; + wcscpy_s(pFindData->cAlternateFileName, sizeof(pFindData->cAlternateFileName), pFindData->cFileName); +} diff --git a/file_info.h b/file_info.h new file mode 100644 index 0000000..8e3cf2f --- /dev/null +++ b/file_info.h @@ -0,0 +1,24 @@ +#ifndef FILE_INFO_H_INCLUDED +#define FILE_INFO_H_INCLUDED + +#include +#include "dokan.h" +#include "internal.h" + +int DOKAN_CALLBACK MxfsGetFileInformation( + LPCWSTR FileName, + LPBY_HANDLE_FILE_INFORMATION Buffer, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsSetFileTime( + LPCWSTR FileName, + CONST FILETIME* CreationTime, + CONST FILETIME* LastAccessTime, + CONST FILETIME* LastWriteTime, + PDOKAN_FILE_INFO FileInfo); + +void MxfsFillFindData(WIN32_FIND_DATAW *pFindData, const minix_dir_entry *pDirEntry, const minix_inode *pInfo); +void MxfsFileTimeFromTimeT(LPFILETIME FileTime, time_t t); +time_t MxfsTimeTFromFileTime(CONST FILETIME *FileTime); + +#endif // FILE_INFO_H_INCLUDED diff --git a/general.c b/general.c new file mode 100644 index 0000000..66e2341 --- /dev/null +++ b/general.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include "general.h" +#include "internal.h" +#include "debug.h" +#include "read_write.h" + +MINIX_FS *MxfsOpen(DISK_IMG *pImg, unsigned uOffset, unsigned cbLen) +{ + MINIX_FS *FileSys; + + TRACE(""); + assert(sizeof(d2_inode) == 64); + + FileSys = MxfsAlloc(sizeof(*FileSys)); + if(!FileSys) + return NULL; + + memset(FileSys, 0, sizeof(*FileSys)); + FileSys->pImg = pImg; + FileSys->uOffset = uOffset; + FileSys->cbLen = cbLen; + InitializeCriticalSection(&FileSys->Lock); + MxfsCacheInit(FileSys); + + if(MxfsCacheRead(FileSys, &FileSys->Super, SUPER_BLOCK, 0, sizeof(FileSys->Super)) != 0) + { + MxfsClose(FileSys); + return NULL; + } + + if(FileSys->Super.s_magic != SUPER_V2_REV) + { + ERR("Invalid magic in super block %x\n", FileSys->Super.s_magic); + MxfsClose(FileSys); + return NULL; + } + + unsigned InodeMapSize = FileSys->Super.s_imap_blocks * MINIX_BLOCK_SIZE; + uint32_t *InodeMapBuf = MxfsAlloc(InodeMapSize); + if(!InodeMapBuf || MxfsReadBlocks(FileSys, InodeMapBuf, SUPER_BLOCK + 1, FileSys->Super.s_imap_blocks) != 0) + { + MxfsFree(InodeMapBuf); + MxfsClose(FileSys); + return NULL; + } + MxfsInitBitmap(&FileSys->InodeMap, InodeMapBuf, InodeMapSize * 8); + + unsigned ZoneMapBlock = SUPER_BLOCK + 1 + FileSys->Super.s_imap_blocks; + unsigned ZoneMapSize = FileSys->Super.s_zmap_blocks * MINIX_BLOCK_SIZE; + uint32_t *ZoneMapBuf = MxfsAlloc(ZoneMapSize); + if(!ZoneMapBuf || MxfsReadBlocks(FileSys, ZoneMapBuf, ZoneMapBlock, FileSys->Super.s_zmap_blocks) != 0) + { + MxfsFree(ZoneMapBuf); + MxfsClose(FileSys); + return NULL; + } + MxfsInitBitmap(&FileSys->ZoneMap, ZoneMapBuf, ZoneMapSize * 8); + + return FileSys; +} + +void MxfsClose(MINIX_FS *FileSys) +{ + TRACE(""); + if(!FileSys) return; + MxfsCacheDestroy(FileSys); // destroy cache first (flush) + MxfsDestroyBitmap(&FileSys->InodeMap); + MxfsDestroyBitmap(&FileSys->ZoneMap); + memset(FileSys, 0, sizeof(*FileSys)); + MxfsFree(FileSys); +} diff --git a/general.h b/general.h new file mode 100644 index 0000000..fe42620 --- /dev/null +++ b/general.h @@ -0,0 +1,42 @@ +#ifndef GENERAL_H_INCLUDED +#define GENERAL_H_INCLUDED + +#include +#include +#include "internal.h" +#include "disk_image.h" +#include "cache.h" +#include "bitmap.h" + +#define ALIGN_UP(addr, n) (((addr) + (n) - 1)/(n)*(n)) +#define ALIGN_DOWN(addr, n) ((addr)/(n)*(n)) +#define COUNTOF(arr) (sizeof(arr)/sizeof((arr)[0])) + +typedef struct _MINIX_FS +{ + DISK_IMG *pImg; + unsigned uOffset, cbLen; + struct super_block Super; + MX_BITMAP InodeMap; + MX_BITMAP ZoneMap; + CRITICAL_SECTION Lock; + MX_BLOCK_CACHE Cache; +} MINIX_FS; + +MINIX_FS *MxfsOpen(DISK_IMG *pImg, unsigned uOffset, unsigned uLen); +void MxfsClose(MINIX_FS *pFileSys); + +FORCEINLINE void *MxfsAlloc(unsigned uSize) +{ + return HeapAlloc(GetProcessHeap(), 0, uSize); +} + +FORCEINLINE void MxfsFree(void *pMem) +{ + if(pMem) + HeapFree(GetProcessHeap(), 0, pMem); +} + +#define MINIX_VOLUME_ID 0x5F7AC349 + +#endif // GENERAL_H_INCLUDED diff --git a/inode.c b/inode.c new file mode 100644 index 0000000..7be3a5d --- /dev/null +++ b/inode.c @@ -0,0 +1,285 @@ +#include "debug.h" +#include "general.h" +#include "file.h" +#include "bitmap.h" +#include "inode.h" + +int MxfsReadInode(MINIX_FS *FileSys, int FileIdx, minix_inode *Result) +{ + if(FileIdx <= 0 || FileIdx > FileSys->Super.s_ninodes) + { + ERR("MxfsReadInode: Invalid inode %d\n", FileIdx); + return -ERROR_INVALID_INDEX; + } + + --FileIdx; // make it zero based + unsigned InodesFirstBlock = SUPER_BLOCK + 1 + FileSys->Super.s_imap_blocks + FileSys->Super.s_zmap_blocks; + unsigned Block = InodesFirstBlock + FileIdx * sizeof(minix_inode) / MINIX_BLOCK_SIZE; + unsigned Offset = (FileIdx * sizeof(minix_inode)) % MINIX_BLOCK_SIZE; + return MxfsCacheRead(FileSys, Result, Block, Offset, sizeof(minix_inode)); +} + +int MxfsWriteInode(MINIX_FS *FileSys, int FileIdx, minix_inode *Info) +{ + if(FileIdx <= 0 || FileIdx > FileSys->Super.s_ninodes) + { + ERR("MxfsWriteInode: Invalid inode %d\n", FileIdx); + return -ERROR_INVALID_INDEX; + } + + // Update inode modified time + Info->d2_ctime = time(NULL); + + --FileIdx; // make it zero based + unsigned InodesFirstBlock = SUPER_BLOCK + 1 + FileSys->Super.s_imap_blocks + FileSys->Super.s_zmap_blocks; + unsigned Block = InodesFirstBlock + FileIdx * sizeof(minix_inode) / MINIX_BLOCK_SIZE; + unsigned Offset = (FileIdx * sizeof(minix_inode)) % MINIX_BLOCK_SIZE; + return MxfsCacheWrite(FileSys, Info, Block, Offset, sizeof(minix_inode)); +} + +int MxfsAllocZone(MINIX_FS *FileSys) +{ + int Bit = MxfsFindClearBit(&FileSys->ZoneMap); + if(Bit < 0) + { + WARN("MxfsAllocZone: disk is full\n"); + return -ERROR_DISK_FULL; + } + + unsigned Zone = MxfsZoneFromBit(FileSys, Bit); + if(Zone > FileSys->Super.s_zones) + { + WARN("MxfsAllocZone: invalid zone %u > %lu\n", Zone, FileSys->Super.s_zones); + return -ERROR_DISK_FULL; + } + + int Result = MxfsSetBit(&FileSys->ZoneMap, Bit); + ASSERT(Result >= 0); + + unsigned BitmapOffset = ALIGN_DOWN(Bit/8, MINIX_BLOCK_SIZE); + unsigned BlockIdx = SUPER_BLOCK + 1 + FileSys->Super.s_imap_blocks + BitmapOffset/MINIX_BLOCK_SIZE; + BYTE *Buffer = ((BYTE*)FileSys->ZoneMap.Buffer) + BitmapOffset; + Result = MxfsCacheWrite(FileSys, Buffer, BlockIdx, 0, MINIX_BLOCK_SIZE); + if(Result < 0) + { + WARN("MxfsAllocZone: MxfsCacheWrite failed\n"); + return Result; + } + + WARN("Alloc zone %u\n", Zone); + return Zone; +} + +int MxfsFreeZone(MINIX_FS *FileSys, unsigned Zone) +{ + WARN("Free zone %u\n", Zone); + + if(Zone > FileSys->Super.s_zones) + { + WARN("MxfsFreeZone: invalid zone %u > %lu\n", Zone, FileSys->Super.s_zones); + return -ERROR_INVALID_PARAMETER; + } + + unsigned Bit = MxfsBitFromZone(FileSys, Zone); + int Result = MxfsClearBit(&FileSys->ZoneMap, Bit); + if(Result < 0) + { + WARN("MxfsFreeZone: MxfsClearBit failed\n"); + return Result; + } + + unsigned BitmapOffset = ALIGN_DOWN(Bit/8, MINIX_BLOCK_SIZE); + unsigned BlockIdx = SUPER_BLOCK + 1 + FileSys->Super.s_imap_blocks + BitmapOffset/MINIX_BLOCK_SIZE; + BYTE *Buffer = ((BYTE*)FileSys->ZoneMap.Buffer) + BitmapOffset; + return MxfsCacheWrite(FileSys, Buffer, BlockIdx, 0, MINIX_BLOCK_SIZE); +} + +int MxfsGetBlockFromFileOffset(MINIX_FS *FileSys, unsigned FileIdx, minix_inode *FileInfo, unsigned Offset, BOOL Alloc) +{ + unsigned Block; + zone_t Zone; + unsigned OffsetInBlocks = Offset / MINIX_BLOCK_SIZE; + unsigned OffsetInZones = OffsetInBlocks >> FileSys->Super.s_log_zone_size; + unsigned FirstZoneBlockOffset = OffsetInZones << FileSys->Super.s_log_zone_size; + + if(OffsetInZones < V2_NR_DZONES) + { + // Use direct zone + Zone = FileInfo->d2_zone[OffsetInZones]; + if(!Zone && Alloc) + { + Zone = MxfsAllocZone(FileSys); + if(Zone) + { + FileInfo->d2_zone[OffsetInZones] = Zone; + MxfsWriteInode(FileSys, FileIdx, FileInfo); + } + } + } + else + { + zone_t IndirectZone; + unsigned IndirectBlock, IndIndex; + int Result; + + IndIndex = OffsetInZones - V2_NR_DZONES; + + if(IndIndex < V2_INDIRECTS) + { + // Use single indirect zone + IndirectZone = FileInfo->d2_zone[V2_NR_DZONES]; + if(!IndirectZone && Alloc) + { + IndirectZone = MxfsAllocZone(FileSys); + if(IndirectZone) + { + MxfsCacheZero(FileSys, IndirectZone << FileSys->Super.s_log_zone_size); + FileInfo->d2_zone[V2_NR_DZONES] = IndirectZone; + MxfsWriteInode(FileSys, FileIdx, FileInfo); + } + } + } + else // use double-indirect + { + zone_t DblIndZone; + unsigned DblIndBlock, DblIndIndex; + + DblIndZone = FileInfo->d2_zone[V2_NR_DZONES + 1]; + if(!DblIndZone && Alloc) + { + DblIndZone = MxfsAllocZone(FileSys); + if(DblIndZone) + { + MxfsCacheZero(FileSys, DblIndZone << FileSys->Super.s_log_zone_size); + FileInfo->d2_zone[V2_NR_DZONES + 1] = DblIndZone; + MxfsWriteInode(FileSys, FileIdx, FileInfo); + } + } + + if(!DblIndZone) + return -ERROR_NOT_FOUND; + + DblIndBlock = DblIndZone << FileSys->Super.s_log_zone_size; + IndIndex -= V2_INDIRECTS; + DblIndIndex = IndIndex / V2_INDIRECTS; + IndIndex = IndIndex % V2_INDIRECTS; + + if(DblIndIndex > V2_INDIRECTS) + { + ERR("Too big offset!\n"); + return -ERROR_INVALID_PARAMETER; + } + + Result = MxfsCacheRead(FileSys, &IndirectZone, DblIndBlock, DblIndIndex*sizeof(zone_t), sizeof(zone_t)); + if(Result < 0) + return Result; + + if(!IndirectZone && Alloc) + { + IndirectZone = MxfsAllocZone(FileSys); + if(IndirectZone) + { + MxfsCacheZero(FileSys, IndirectZone << FileSys->Super.s_log_zone_size); + MxfsCacheWrite(FileSys, &IndirectZone, DblIndBlock, DblIndIndex*sizeof(zone_t), sizeof(zone_t)); + } + } + } + + if(!IndirectZone) + return -ERROR_NOT_FOUND; + + IndirectBlock = IndirectZone << FileSys->Super.s_log_zone_size; + Result = MxfsCacheRead(FileSys, &Zone, IndirectBlock, IndIndex*sizeof(zone_t), sizeof(zone_t)); + if(Result < 0) + return Result; + + if(!Zone && Alloc) + { + Zone = MxfsAllocZone(FileSys); + if(Zone) + { + MxfsCacheZero(FileSys, Zone << FileSys->Super.s_log_zone_size); + MxfsCacheWrite(FileSys, &Zone, IndirectBlock, IndIndex*sizeof(zone_t), sizeof(zone_t)); + } + } + } + + if(!Zone) + return -ERROR_NOT_FOUND; + + unsigned Bit = MxfsBitFromZone(FileSys, Zone); + if(!MxfsGetBit(&FileSys->ZoneMap, Bit)) + { + WARN("NOT GOOD! Bit %u is not set\n", Bit); + } + + Block = Zone << FileSys->Super.s_log_zone_size; + Block += OffsetInBlocks - FirstZoneBlockOffset; // add offset in zone + return Block; +} + +int DOKAN_CALLBACK MxfsSetEndOfFile( + LPCWSTR FileName, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls", FileName); + + MINIX_FS *FileSys = (MINIX_FS*)(LONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(LONG_PTR)FileInfo->Context; + ASSERT(FileCtx && Length >= 0); + + minix_inode Info; + int Result = MxfsReadInode(FileSys, FileCtx->Index, &Info); + if(Result < 0) + return Result; + + unsigned ZoneSize = MINIX_BLOCK_SIZE << FileSys->Super.s_log_zone_size; + unsigned NewFirstFree = ALIGN_UP(Length, ZoneSize)/ZoneSize; + unsigned FirstFree = ALIGN_UP(Info.d2_size, ZoneSize)/ZoneSize; + + if(NewFirstFree >= FirstFree) + return 0; // done + + unsigned i = NewFirstFree; + while(i < V2_NR_DZONES) + { + if(Info.d2_zone[i]) + MxfsFreeZone(FileSys, Info.d2_zone[i]); + Info.d2_zone[i] = 0; + ++i; + } + + i -= V2_NR_DZONES; + unsigned IndirZone = Info.d2_zone[V2_NR_DZONES]; + if(IndirZone && i < V2_INDIRECTS) + { + unsigned IndirBlock = IndirZone << FileSys->Super.s_log_zone_size; + zone_t IndirTbl[V2_INDIRECTS]; + BOOL FreeIndirZone = (i == 0); + + Result = MxfsCacheRead(FileSys, IndirTbl, IndirBlock, 0, MINIX_BLOCK_SIZE); + if(Result >= 0) + { + while(i < V2_INDIRECTS) + { + if(IndirTbl[i]) + MxfsFreeZone(FileSys, IndirTbl[i]); + IndirTbl[i] = 0; + ++i; + } + if(FreeIndirZone) + MxfsFreeZone(FileSys, IndirZone); + else + MxfsCacheWrite(FileSys, IndirTbl, IndirBlock, 0, MINIX_BLOCK_SIZE); + } else + ERR("Failed to read indirect block\n"); + } + + // TODO: Free dbl indirect block zones! + Info.d2_size = Length; + Info.d2_mtime = time(NULL); + MxfsWriteInode(FileSys, FileCtx->Index, &Info); + + return 0; +} diff --git a/inode.h b/inode.h new file mode 100644 index 0000000..a529f07 --- /dev/null +++ b/inode.h @@ -0,0 +1,28 @@ +#ifndef INODE_H_INCLUDED +#define INODE_H_INCLUDED + +#include +#include "dokan.h" +#include "internal.h" + +int MxfsReadInode(MINIX_FS *FileSys, int FileIdx, minix_inode *Result); +int MxfsWriteInode(MINIX_FS *FileSys, int FileIdx, minix_inode *Result); +int MxfsAllocZone(MINIX_FS *FileSys); +int MxfsGetBlockFromFileOffset(MINIX_FS *FileSys, unsigned FileIdx, minix_inode *FileInfo, unsigned Offset, BOOL Alloc); + +int DOKAN_CALLBACK MxfsSetEndOfFile( + LPCWSTR FileName, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo); + +FORCEINLINE int MxfsZoneFromBit(MINIX_FS *FileSys, unsigned Bit) +{ + return FileSys->Super.s_firstdatazone + Bit - 1; +} + +FORCEINLINE int MxfsBitFromZone(MINIX_FS *FileSys, unsigned Zone) +{ + return Zone - FileSys->Super.s_firstdatazone + 1; +} + +#endif // INODE_H_INCLUDED diff --git a/internal.h b/internal.h new file mode 100644 index 0000000..54deab4 --- /dev/null +++ b/internal.h @@ -0,0 +1,148 @@ +#ifndef INTERNAL_H_INCLUDED +#define INTERNAL_H_INCLUDED + +#include +#include +#include + +/* Super block table. The root file system and every mounted file system + * has an entry here. The entry holds information about the sizes of the bit + * maps and inodes. The s_ninodes field gives the number of inodes available + * for files and directories, including the root directory. Inode 0 is + * on the disk, but not used. Thus s_ninodes = 4 means that 5 bits will be + * used in the bit map, bit 0, which is always 1 and not used, and bits 1-4 + * for files and directories. The disk layout is: + * + * Item # blocks + * boot block 1 + * super block 1 + * inode map s_imap_blocks + * zone map s_zmap_blocks + * inodes (s_ninodes + 'inodes per block' - 1)/'inodes per block' + * unused whatever is needed to fill out the current zone + * data zones (s_zones - s_firstdatazone) << s_log_zone_size + * + * A super_block slot is free if s_dev == NO_DEV. + */ + +#define MINIX_BLOCK_SIZE 1024 +#define BLOCK_SIZE MINIX_BLOCK_SIZE +#define usizeof sizeof + +#define SUPER_V2_REV 0x2468 /* V2 magic written on PC, read on 68K or vv */ + +/* Tables sizes */ +#define V1_NR_DZONES 7 /* # direct zone numbers in a V1 inode */ +#define V1_NR_TZONES 9 /* total # zone numbers in a V1 inode */ +#define V2_NR_DZONES 7 /* # direct zone numbers in a V2 inode */ +#define V2_NR_TZONES 10 /* total # zone numbers in a V2 inode */ + +/* Types used in disk, inode, etc. data structures. */ +typedef short dev_t; /* holds (major|minor) device pair */ +typedef char gid_t; /* group id */ +typedef unsigned short ino_t; /* i-node number */ +typedef unsigned short mode_t; /* file type and permissions bits */ +typedef char nlink_t; /* number of links to a file */ +typedef unsigned long off_t; /* offset within a file */ +typedef int pid_t; /* process id (must be signed) */ +typedef short uid_t; /* user id */ +typedef unsigned long zone_t; /* zone number */ +typedef unsigned long block_t; /* block number */ +typedef unsigned long bit_t; /* bit number in a bit map */ +typedef unsigned short zone1_t; /* zone number for V1 file systems */ +typedef unsigned short bitchunk_t; /* collection of bits in a bitmap */ + +typedef unsigned char u8_t; /* 8 bit type */ +typedef unsigned short u16_t; /* 16 bit type */ +typedef unsigned long u32_t; /* 32 bit type */ + +typedef char i8_t; /* 8 bit signed type */ +typedef short i16_t; /* 16 bit signed type */ +typedef long i32_t; /* 32 bit signed type */ + +#define ROOT_INODE 1 /* inode number for root directory */ +#define BOOT_BLOCK ((block_t) 0) /* block number of boot block */ +#define SUPER_BLOCK ((block_t) 1) /* block number of super block */ + +#define DIR_ENTRY_SIZE usizeof (struct direct) /* # bytes/dir entry */ +#define NR_DIR_ENTRIES (BLOCK_SIZE/DIR_ENTRY_SIZE) /* # dir entries/blk */ +#define SUPER_SIZE usizeof (struct super_block) /* super_block size */ +#define PIPE_SIZE (V1_NR_DZONES*BLOCK_SIZE) /* pipe size in bytes */ +#define BITMAP_CHUNKS (BLOCK_SIZE/usizeof (bitchunk_t))/* # map chunks/blk */ + +/* Derived sizes pertaining to the V1 file system. */ +#define V1_ZONE_NUM_SIZE usizeof (zone1_t) /* # bytes in V1 zone */ +#define V1_INODE_SIZE usizeof (d1_inode) /* bytes in V1 dsk ino */ +#define V1_INDIRECTS (BLOCK_SIZE/V1_ZONE_NUM_SIZE) /* # zones/indir block */ +#define V1_INODES_PER_BLOCK (BLOCK_SIZE/V1_INODE_SIZE)/* # V1 dsk inodes/blk */ + +/* Derived sizes pertaining to the V2 file system. */ +#define V2_ZONE_NUM_SIZE usizeof (zone_t) /* # bytes in V2 zone */ +#define V2_INODE_SIZE usizeof (d2_inode) /* bytes in V2 dsk ino */ +#define V2_INDIRECTS (BLOCK_SIZE/V2_ZONE_NUM_SIZE) /* # zones/indir block */ +#define V2_INODES_PER_BLOCK (BLOCK_SIZE/V2_INODE_SIZE)/* # V2 dsk inodes/blk */ + +struct super_block +{ + ino_t s_ninodes; /* # usable inodes on the minor device */ + zone1_t s_nzones; /* total device size, including bit maps etc */ + short s_imap_blocks; /* # of blocks used by inode bit map */ + short s_zmap_blocks; /* # of blocks used by zone bit map */ + zone1_t s_firstdatazone; /* number of first data zone */ + short s_log_zone_size; /* log2 of blocks/zone */ + off_t s_max_size; /* maximum file size on this device */ + short s_magic; /* magic number to recognize super-blocks */ + short s_pad; /* try to avoid compiler-dependent padding */ + zone_t s_zones; /* number of zones (replaces s_nzones in V2) */ +}; + +/* Declaration of the V1 inode as it is on the disk (not in core). */ +typedef struct { /* V1.x disk inode */ + mode_t d1_mode; /* file type, protection, etc. */ + uid_t d1_uid; /* user id of the file's owner */ + off_t d1_size; /* current file size in bytes */ + time_t d1_mtime; /* when was file data last changed */ + gid_t d1_gid; /* group number */ + nlink_t d1_nlinks; /* how many links to this file */ + u16_t d1_zone[V1_NR_TZONES]; /* block nums for direct, ind, and dbl ind */ +} d1_inode; + +/* Declaration of the V2 inode as it is on the disk (not in core). */ +typedef struct { /* V2.x disk inode */ + mode_t d2_mode; /* file type, protection, etc. */ + u16_t d2_nlinks; /* how many links to this file. HACK! */ + uid_t d2_uid; /* user id of the file's owner. */ + u16_t d2_gid; /* group number HACK! */ + off_t d2_size; /* current file size in bytes */ + time_t d2_atime; /* when was file data last accessed */ + time_t d2_mtime; /* when was file data last changed */ + time_t d2_ctime; /* when was inode data last changed */ + zone_t d2_zone[V2_NR_TZONES]; /* block nums for direct, ind, and dbl ind */ +} d2_inode; + +/* Flag bits for i_mode in the inode. */ +#define I_TYPE 0170000 /* this field gives inode type */ +#define I_REGULAR 0100000 /* regular file, not dir or special */ +#define I_BLOCK_SPECIAL 0060000 /* block special file */ +#define I_DIRECTORY 0040000 /* file is a directory */ +#define I_CHAR_SPECIAL 0020000 /* character special file */ +#define I_NAMED_PIPE 0010000 /* named pipe (FIFO) */ +#define I_SET_UID_BIT 0004000 /* set effective uid_t on exec */ +#define I_SET_GID_BIT 0002000 /* set effective gid_t on exec */ +#define ALL_MODES 0006777 /* all bits for user, group and others */ +#define RWX_MODES 0000777 /* mode bits for RWX only */ +#define R_BIT 0000004 /* Rwx protection bit */ +#define W_BIT 0000002 /* rWx protection bit */ +#define X_BIT 0000001 /* rwX protection bit */ +#define I_NOT_ALLOC 0000000 /* this inode is free */ + +#define MAX_NAME_LEN 14 + +typedef struct { /* V2.x disk inode */ + u16_t inode; + char name[MAX_NAME_LEN]; +} minix_dir_entry; + +typedef d2_inode minix_inode; + +#endif // INTERNAL_H_INCLUDED diff --git a/list.h b/list.h new file mode 100644 index 0000000..43f7c30 --- /dev/null +++ b/list.h @@ -0,0 +1,120 @@ +#ifndef LIST_H_INCLUDED +#define LIST_H_INCLUDED + +#include + +#ifndef FORCEINLINE +#define FORCEINLINE __inline__ +#endif // FORCEINLINE + +#if !defined(_LIST_ENTRY_DEFINED) && !defined(_MSC_VER) +typedef struct _LIST_ENTRY { + struct _LIST_ENTRY *Flink; + struct _LIST_ENTRY *Blink; +} LIST_ENTRY, *PLIST_ENTRY; +#endif // _LIST_ENTRY_DEFINED + +FORCEINLINE +VOID +InitializeListHead( + OUT PLIST_ENTRY ListHead) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +FORCEINLINE +BOOLEAN +IsListEmpty( + IN CONST LIST_ENTRY * ListHead) +{ + return (BOOLEAN)(ListHead->Flink == ListHead); +} + +FORCEINLINE +BOOLEAN +RemoveEntryList( + IN PLIST_ENTRY Entry) +{ + PLIST_ENTRY OldFlink; + PLIST_ENTRY OldBlink; + + OldFlink = Entry->Flink; + OldBlink = Entry->Blink; + OldFlink->Blink = OldBlink; + OldBlink->Flink = OldFlink; + return (BOOLEAN)(OldFlink == OldBlink); +} + +FORCEINLINE +PLIST_ENTRY +RemoveHeadList( + IN OUT PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Flink; + PLIST_ENTRY Entry; + + Entry = ListHead->Flink; + Flink = Entry->Flink; + ListHead->Flink = Flink; + Flink->Blink = ListHead; + return Entry; +} + +FORCEINLINE +PLIST_ENTRY +RemoveTailList( + IN OUT PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Blink; + PLIST_ENTRY Entry; + + Entry = ListHead->Blink; + Blink = Entry->Blink; + ListHead->Blink = Blink; + Blink->Flink = ListHead; + return Entry; +} + +FORCEINLINE +VOID +InsertTailList( + IN OUT PLIST_ENTRY ListHead, + IN OUT PLIST_ENTRY Entry) +{ + PLIST_ENTRY OldBlink; + OldBlink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = OldBlink; + OldBlink->Flink = Entry; + ListHead->Blink = Entry; +} + +FORCEINLINE +VOID +InsertHeadList( + IN OUT PLIST_ENTRY ListHead, + IN OUT PLIST_ENTRY Entry) +{ + PLIST_ENTRY OldFlink; + OldFlink = ListHead->Flink; + Entry->Flink = OldFlink; + Entry->Blink = ListHead; + OldFlink->Blink = Entry; + ListHead->Flink = Entry; +} + +FORCEINLINE +VOID +AppendTailList( + IN OUT PLIST_ENTRY ListHead, + IN OUT PLIST_ENTRY ListToAppend) +{ + PLIST_ENTRY ListEnd = ListHead->Blink; + + ListHead->Blink->Flink = ListToAppend; + ListHead->Blink = ListToAppend->Blink; + ListToAppend->Blink->Flink = ListHead; + ListToAppend->Blink = ListEnd; +} + +#endif // LIST_H_INCLUDED diff --git a/main.c b/main.c new file mode 100644 index 0000000..a34f49d --- /dev/null +++ b/main.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include "partitions.h" +#include "general.h" +#include "debug.h" +#include "dokan.h" +#include "stubs.h" +#include "file.h" +#include "file_info.h" +#include "dir.h" +#include "read_write.h" +#include "inode.h" +#include "volume_info.h" +#include "disk_image.h" + +#define VERSION "0.4" + +typedef struct +{ + unsigned Part; + unsigned Subpart; + const char *Path; + SUBPARTITION *pSubpart; + DISK_IMG *pImg; + HANDLE hEvent; + volatile BOOL bMounted; +} MOUNT_REQ; + +static DOKAN_OPERATIONS DokanCallbacks = { + MxfsCreateFile, // CreateFile + MxfsOpenDirectory, // OpenDirectory + MxfsCreateDirectory, // CreateDirectory + MxfsCleanup, // Cleanup + MxfsCloseFile, // CloseFile + MxfsReadFile, // ReadFile + MxfsWriteFile, // WriteFile + MxfsFlushFileBuffers, // FlushFileBuffers + MxfsGetFileInformation, // GetFileInformation, + MxfsFindFiles, // FindFiles + NULL, // FindFilesWithPattern + MxfsSetFileAttributes, // SetFileAttributes + MxfsSetFileTime, // SetFileTime + MxfsDeleteFile, // DeleteFile + MxfsDeleteDirectory, // DeleteDirectory + MxfsMoveFile, // MoveFile + MxfsSetEndOfFile, // SetEndOfFile + MxfsSetAllocationSize, // SetAllocationSize + MxfsLockFile, // LockFile + MxfsUnlockFile, // UnlockFile + MxfsGetDiskFreeSpace, // GetDiskFreeSpace + MxfsGetVolumeInformation, // GetVolumeInformation + MxfsUnmount, // Unmount + MxfsGetFileSecurity, // GetFileSecurity + MxfsSetFileSecurity, // SetFileSecurity +}; + +static HANDLE g_Threads[16]; +static unsigned g_cThreads = 0; +static MOUNT_REQ g_MountReq[16]; +static unsigned g_cMountReq = 0; + +static DWORD WINAPI MxfsThread(LPVOID lpParameter) +{ + MOUNT_REQ *pReq = (MOUNT_REQ*)lpParameter; + MINIX_FS *pFileSys; + WCHAR wszMountPath[MAX_PATH]; + + mbstowcs(wszMountPath, pReq->Path, sizeof(wszMountPath)/sizeof(wszMountPath[0])); + + pFileSys = MxfsOpen(pReq->pImg, pReq->pSubpart->Offset, pReq->pSubpart->Size); + if(!pFileSys) + { + ERR("Failed to open Minix filesystem\n"); + SetEvent(pReq->hEvent); + return (DWORD)-1; + } + + DOKAN_OPTIONS Opts = { + DOKAN_VERSION, // Version + 1, // ThreadCount, TODO: support many threads... + DOKAN_OPTION_DEBUG, // Options + (ULONG64)(LONG_PTR)pFileSys, // GlobalContext + wszMountPath, // MountPoint + }; + + SetEvent(pReq->hEvent); + pReq->bMounted = TRUE; + int err = DokanMain(&Opts, &DokanCallbacks); + if(err < 0) + ERR("DokanMain failed: %d\n", err); + pReq->bMounted = FALSE; + MxfsClose(pFileSys); + return 0; +} + +static int MxfsMount(MOUNT_REQ *pReq) +{ + pReq->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + HANDLE hThread = CreateThread(NULL, 0, MxfsThread, pReq, 0, NULL); + if(!hThread) + return -1; + + WaitForSingleObject(pReq->hEvent, INFINITE); + CloseHandle(pReq->hEvent); + Sleep(10); + + g_Threads[g_cThreads++] = hThread; + return 0; +} + +static LONG WINAPI ExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) +{ + ERR("Exception!!!\n"); + return EXCEPTION_EXECUTE_HANDLER; +} + +static BOOL WINAPI CtrlHandler(DWORD dwCtrlType) +{ + printf("Console Ctrl Handler: unmounting...\n"); + unsigned i; + WCHAR wszMountPath[MAX_PATH]; + + for(i = 0; i < g_cMountReq; ++i) + { + if(!g_MountReq[i].bMounted) + continue; + + mbstowcs(wszMountPath, g_MountReq[i].Path, sizeof(wszMountPath)/sizeof(wszMountPath[0])); + BOOL Result = DokanRemoveMountPoint(wszMountPath); + if(!Result) + ERR("DokanRemoveMountPoint(%ls) failed!\n", wszMountPath); + } + + return TRUE; +} + +static void DisplayHelp(const char *pProcName) +{ + printf("Usage: %s [-l] [-f img_path p0s0=mount_path1 p0s1=mount_path2 ...]\n", pProcName); + printf("-l\t\t\tLists all partitions and subpartitions\n"); + printf("-f img_path\t\tSets disk image path\n"); + printf("p0s0=mount_path\t\tMounts subpartition 0 on partition 0 in given path\n"); +} + +static void Test() +{ +#ifndef NDEBUG + FILETIME ft; + time_t t = time(NULL); + MxfsFileTimeFromTimeT(&ft, t); + assert(t == MxfsTimeTFromFileTime(&ft)); +#endif +} + +int main(int argc, char *argv[]) +{ + const char *pszPath = NULL; + unsigned i; + BOOL bHelp = FALSE, bList = FALSE; + + printf("MINIX Filesystem Driver " VERSION " for Windows (c) 2013 Rafal Harabien\n"); + + Test(); + + for(i = 1; i < (unsigned)argc; ++i) + { + if(!strcmp(argv[i], "-f") && i + 1 < (unsigned)argc) + pszPath = argv[++i]; + else if(!strcmp(argv[i], "-l")) + bList = TRUE; + else if(!strcmp(argv[i], "-h")) + { + bHelp = TRUE; + break; + } + else + { + unsigned uPart, uSubpart; + if(sscanf(argv[i], "p%us%u=", &uPart, &uSubpart) == 2 && g_cMountReq < COUNTOF(g_MountReq)) + { + const char *pszMount = strchr(argv[i], '=') + 1; + g_MountReq[g_cMountReq].Part = uPart; + g_MountReq[g_cMountReq].Subpart = uSubpart; + g_MountReq[g_cMountReq].Path = pszMount; + g_MountReq[g_cMountReq].bMounted = FALSE; + ++g_cMountReq; + } else + printf("Invalid argument: %s\n", argv[i]); + } + } + + if(!pszPath) + bHelp = TRUE; + if(bHelp) + { + DisplayHelp(argv[0]); + return 0; + } + + DISK_IMG *pImg = ImgOpen(pszPath); + if(!pImg) + { + ERR("Failed to open image: %s\n", pszPath); + return -1; + } + + SUBPART_VECTOR SubpartVect; + int err = FindSubpartitions(pImg, &SubpartVect); + if(err < 0) + { + ERR("FindSubpartitions failed!\n"); + ImgClose(pImg); + return err; + } + + if(bList) + { + printf("Subpartitions:\n"); + for(i = 0; i < SubpartVect.Count; ++i) + { + SUBPARTITION *pSubpart = &SubpartVect.Entries[i]; + printf("p%us%u - offset 0x%x size 0x%x\n", pSubpart->PartIdx, pSubpart->SubpartIdx, pSubpart->Offset, pSubpart->Size); + } + } + + SetUnhandledExceptionFilter(ExceptionHandler); + + SetConsoleCtrlHandler(CtrlHandler, TRUE); + + for(i = 0; i < g_cMountReq; ++i) + { + unsigned j; + for(j = 0; j < SubpartVect.Count; ++j) + { + if(g_MountReq[i].Part == SubpartVect.Entries[j].PartIdx && g_MountReq[i].Subpart == SubpartVect.Entries[j].SubpartIdx) + break; + } + + if(j < SubpartVect.Count) + { + printf("Mounting p%us%u in %s...\n", g_MountReq[i].Part, g_MountReq[i].Subpart, g_MountReq[i].Path); + g_MountReq[i].pSubpart = &SubpartVect.Entries[j]; + g_MountReq[i].pImg = pImg; + MxfsMount(&g_MountReq[i]); + } + } + + WaitForMultipleObjects(g_cThreads, g_Threads, TRUE, INFINITE); + for(i = 0; i < g_cThreads; ++i) + CloseHandle(g_Threads[i]); + + MxfsFree(SubpartVect.Entries); + ImgClose(pImg); + + printf("Done!\n"); + return 0; +} diff --git a/mbr.h b/mbr.h new file mode 100644 index 0000000..a317d96 --- /dev/null +++ b/mbr.h @@ -0,0 +1,43 @@ +#ifndef MBR_H_INCLUDED +#define MBR_H_INCLUDED + +#include + +#define MBR_BOOT_SIG 0xAA55 +#define MBR_PART_COUNT 4 + +typedef enum +{ + MBR_PART_FREE = 0x00, + MBR_PART_MINIX14 = 0x81, +} MBR_PART_TYPE; + +#pragma pack(push, 1) + +typedef struct +{ + uint8_t uHead; + uint8_t uCylHiSect; + uint8_t uCylLo; +} MBR_CHS; + +typedef struct +{ + uint8_t uStatus; + MBR_CHS FirstSectChs; + MBR_PART_TYPE Type: 8; + MBR_CHS LastSectChs; + uint32_t uFirstSectLba; + uint32_t cSect; +} MBR_PARTITION; + +typedef struct +{ + char Bootstrap[446]; + MBR_PARTITION Partitions[MBR_PART_COUNT]; + uint16_t uBootSig; +} MBR; + +#pragma pack(pop) + +#endif // MBR_H_INCLUDED diff --git a/partitions.c b/partitions.c new file mode 100644 index 0000000..59769d9 --- /dev/null +++ b/partitions.c @@ -0,0 +1,106 @@ +#include +#include +#include "partitions.h" +#include "debug.h" + +int LoadMBR(DISK_IMG *pImg, MBR *pMbr) +{ + assert(sizeof(*pMbr) == 512); + + int err = ImgRead(pImg, 0, sizeof(*pMbr), pMbr); + if(err < 0) + { + ERR("Failed to load MBR\n"); + return err; + } + + if(pMbr->uBootSig != MBR_BOOT_SIG) + { + WARN("Invalid signature: %u\n", pMbr->uBootSig); + return -1; + } + + return 0; +} + +void AddSubpartToVect(SUBPART_VECTOR *pVect, unsigned Offset, unsigned Size, unsigned PartIdx, unsigned SubpartIdx) +{ + if(pVect->Count % 4 == 0) + pVect->Entries = realloc(pVect->Entries, (pVect->Count + 4) * sizeof(SUBPARTITION)); + + SUBPARTITION *pSubpart = &pVect->Entries[pVect->Count++]; + pSubpart->Offset = Offset; + pSubpart->Size = Size; + pSubpart->PartIdx = PartIdx; + pSubpart->SubpartIdx = SubpartIdx; +} + +int FindSubpartsOnPart(DISK_IMG *pImg, unsigned Offset, unsigned Size, unsigned PartIdx, SUBPART_VECTOR *pVect) +{ + MBR Ebr; + + if(Size > 512) + { + int err = ImgRead(pImg, Offset, sizeof(Ebr), &Ebr); + if(err < 0) + return err; + } else + Ebr.uBootSig = 0; + + if(Ebr.uBootSig != MBR_BOOT_SIG) + { + // No subpartitions + AddSubpartToVect(pVect, Offset, Size, PartIdx, 0); + return 0; + } + + unsigned i; + for(i = 0; i < 4; ++i) + { + MBR_PARTITION *pPart = &Ebr.Partitions[i]; + if(pPart->Type == MBR_PART_FREE) continue; + + unsigned SubpartOffset = pPart->uFirstSectLba * 512; + unsigned SubpartSize = pPart->cSect * 512; + AddSubpartToVect(pVect, SubpartOffset, SubpartSize, PartIdx, i); + + TRACE("EBR[%u]: Type 0x%x uFirstSectLba 0x%x cSect 0x%x off 0x%x\n", i, pPart->Type, pPart->uFirstSectLba, pPart->cSect, pPart->uFirstSectLba*512); + } + + return 0; +} + +int FindSubpartitions(DISK_IMG *pImg, SUBPART_VECTOR *pVect) +{ + pVect->Count = 0; + pVect->Entries = NULL; + + MBR Mbr; + int iRet = LoadMBR(pImg, &Mbr); + if(iRet < 0) + { + // No partitions + AddSubpartToVect(pVect, 0, ImgGetSize(pImg), 0, 0); + return 0; + } + + unsigned i; + for(i = 0; i < MBR_PART_COUNT; ++i) + { + MBR_PARTITION *pPart = &Mbr.Partitions[i]; + if(pPart->Type == MBR_PART_MINIX14) + { + TRACE("Found MINIX partition: %u. MBR entry, lba 0x%x, size 0x%x\n", i, pPart->uFirstSectLba, pPart->cSect); + unsigned Offset = pPart->uFirstSectLba * 512; + unsigned Size = pPart->cSect * 512; + FindSubpartsOnPart(pImg, Offset, Size, i, pVect); + } + else if(pPart->Type != MBR_PART_FREE) + INFO("Unknown partition %u: type 0x%x, lba 0x%x, size 0x%x\n", i, pPart->Type, pPart->uFirstSectLba, pPart->cSect); + } + + if(pVect->Count == 0) + ERR("Failed to find a MINIX partition\n"); + + return 0; +} diff --git a/partitions.h b/partitions.h new file mode 100644 index 0000000..f3d57ad --- /dev/null +++ b/partitions.h @@ -0,0 +1,25 @@ +#ifndef PARTITIONS_H_INCLUDED +#define PARTITIONS_H_INCLUDED + +#include +#include "mbr.h" +#include "disk_image.h" + +typedef struct +{ + unsigned PartIdx; + unsigned SubpartIdx; + unsigned Offset; + unsigned Size; +} SUBPARTITION; + +typedef struct +{ + SUBPARTITION *Entries; + unsigned Count; +} SUBPART_VECTOR; + +int LoadMBR(DISK_IMG *pImg, MBR *pMbr); +int FindSubpartitions(DISK_IMG *pImg, SUBPART_VECTOR *pVect); + +#endif // PARTITIONS_H_INCLUDED diff --git a/read_write.c b/read_write.c new file mode 100644 index 0000000..641a376 --- /dev/null +++ b/read_write.c @@ -0,0 +1,131 @@ +#include +#include "debug.h" +#include "file.h" +#include "read_write.h" +#include "inode.h" + +int MxfsReadBlocks(MINIX_FS *FileSys, void *Buffer, unsigned FirstBlock, unsigned BlocksCount) +{ + while(BlocksCount > 0) + { + int Result = MxfsCacheRead(FileSys, Buffer, FirstBlock, 0, MINIX_BLOCK_SIZE); + if(Result < 0) + return Result; + + Buffer = ((PBYTE)Buffer) + MINIX_BLOCK_SIZE; + ++FirstBlock; + --BlocksCount; + } + return 0; +} + +int DOKAN_CALLBACK MxfsReadFile( + LPCWSTR FileName, + LPVOID Buffer, + DWORD NumberOfBytesToRead, + LPDWORD NumberOfBytesRead, + LONGLONG Offset, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %lu %I64u", FileName, NumberOfBytesToRead, Offset); + + MINIX_FS *FileSys = (MINIX_FS*)(LONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(LONG_PTR)FileInfo->Context; + if(!FileCtx || Offset < 0) + { + ERR("Invalid params %p %I64d\n", FileCtx, Offset); + return -ERROR_INVALID_PARAMETER; + } + ASSERT(FileCtx && Offset >= 0); + + minix_inode Info; + int Result = MxfsReadInode(FileSys, FileCtx->Index, &Info); + if(Result < 0) + return Result; + + *NumberOfBytesRead = 0; + + if(Offset > FileCtx->Length) + Offset = FileCtx->Length; + if(Offset + NumberOfBytesToRead > FileCtx->Length) + NumberOfBytesToRead = FileCtx->Length - Offset; + + while(NumberOfBytesToRead > 0) + { + int Block = MxfsGetBlockFromFileOffset(FileSys, FileCtx->Index, &Info, Offset, FALSE); + if(Block < 0) + return Block; + + unsigned ChunkOffset = Offset % MINIX_BLOCK_SIZE; + unsigned ChunkLength = min(NumberOfBytesToRead, MINIX_BLOCK_SIZE - ChunkOffset); + + int Result = MxfsCacheRead(FileSys, Buffer, Block, ChunkOffset, ChunkLength); + if(Result < 0) + return Result; + + Buffer = ((PBYTE)Buffer) + ChunkLength; + Offset += ChunkLength; + NumberOfBytesToRead -= ChunkLength; + *NumberOfBytesRead += ChunkLength; + } + + return 0; +} + +int DOKAN_CALLBACK MxfsWriteFile( + LPCWSTR FileName, + LPCVOID Buffer, + DWORD NumberOfBytesToWrite, + LPDWORD NumberOfBytesWritten, + LONGLONG Offset, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls %lu %I64d\n", FileName, NumberOfBytesToWrite, Offset); + + MINIX_FS *FileSys = (MINIX_FS*)(LONG_PTR)FileInfo->DokanOptions->GlobalContext; + FILE_CTX *FileCtx = (FILE_CTX*)(LONG_PTR)FileInfo->Context; + ASSERT(FileCtx && Offset >= 0); + + minix_inode Info; + int Result = MxfsReadInode(FileSys, FileCtx->Index, &Info); + if(Result < 0) + return Result; + + *NumberOfBytesWritten = 0; + + if(Offset > FileCtx->Length) + Offset = FileCtx->Length; + + while(NumberOfBytesToWrite > 0) + { + int Block = MxfsGetBlockFromFileOffset(FileSys, FileCtx->Index, &Info, Offset, TRUE); + if(Block < 0) + { + ERR("MxfsGetBlockFromFileOffset failed %I64d\n", Offset); + Result = Block; + break; + } + + unsigned ChunkOffset = Offset % MINIX_BLOCK_SIZE; + unsigned ChunkLength = min(NumberOfBytesToWrite, MINIX_BLOCK_SIZE - ChunkOffset); + + Result = MxfsCacheWrite(FileSys, Buffer, Block, ChunkOffset, ChunkLength); + if(Result < 0) + break; + + Buffer = ((PBYTE)Buffer) + ChunkLength; + Offset += ChunkLength; + NumberOfBytesToWrite -= ChunkLength; + *NumberOfBytesWritten += ChunkLength; + } + + if(*NumberOfBytesWritten > 0) + { + if(Offset > FileCtx->Length) + FileCtx->Length = Offset; + + FileCtx->Changed = TRUE; + } + + return Result; +} diff --git a/read_write.h b/read_write.h new file mode 100644 index 0000000..c9c4027 --- /dev/null +++ b/read_write.h @@ -0,0 +1,25 @@ +#ifndef READ_WRITE_H_INCLUDED +#define READ_WRITE_H_INCLUDED + +#include "general.h" +#include "dokan.h" + +int MxfsReadBlocks(MINIX_FS *FileSys, void *pBuffer, unsigned Block, unsigned Count); + +int DOKAN_CALLBACK MxfsReadFile( + LPCWSTR FileName, + LPVOID Buffer, + DWORD NumberOfBytesToRead, + LPDWORD NumberOfBytesRead, + LONGLONG Offset, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsWriteFile( + LPCWSTR FileName, + LPCVOID Buffer, + DWORD NumberOfBytesToWrite, + LPDWORD NumberOfBytesWritten, + LONGLONG Offset, + PDOKAN_FILE_INFO FileInfo); + +#endif // READ_WRITE_H_INCLUDED diff --git a/stubs.c b/stubs.c new file mode 100644 index 0000000..19901f2 --- /dev/null +++ b/stubs.c @@ -0,0 +1,118 @@ +#include "stubs.h" +#include "general.h" +#include "debug.h" +#include "internal.h" + +int DOKAN_CALLBACK MxfsMoveFile( + LPCWSTR ExistingFileName, + LPCWSTR NewFileName, + BOOL ReplaceExisiting, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls %ls %u", ExistingFileName, NewFileName, ReplaceExisiting ? 1 : 0); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +int DOKAN_CALLBACK MxfsLockFile( + LPCWSTR FileName, + LONGLONG ByteOffset, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls %I64u %I64u", FileName, ByteOffset, Length); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +int DOKAN_CALLBACK MxfsUnlockFile( + LPCWSTR FileName, + LONGLONG ByteOffset, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls %I64u %I64u", FileName, ByteOffset, Length); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +int DOKAN_CALLBACK MxfsUnmount( + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED(""); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +// Optional +int DOKAN_CALLBACK MxfsFlushFileBuffers( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls", FileName); + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + MxfsCacheFlush(FileSys); // flush entire FS... + return 0; +} + +int DOKAN_CALLBACK MxfsSetFileAttributes( + LPCWSTR FileName, + DWORD FileAttributes, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE("%ls", FileName); + return 0; +} + +// You should not delete file on DeleteFile or DeleteDirectory. +// When DeleteFile or DeleteDirectory, you must check whether +// you can delete the file or not, and return 0 (when you can delete it) +// or appropriate error codes such as -ERROR_DIR_NOT_EMPTY, +// -ERROR_SHARING_VIOLATION. +// When you return 0 (ERROR_SUCCESS), you get Cleanup with +// FileInfo->DeleteOnClose set TRUE and you have to delete the +// file in Close. +int DOKAN_CALLBACK MxfsDeleteFile( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls", FileName); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +int DOKAN_CALLBACK MxfsDeleteDirectory( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls", FileName); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +int DOKAN_CALLBACK MxfsSetAllocationSize( + LPCWSTR FileName, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls", FileName); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +// Suported since 0.6.0. You must specify the version at DOKAN_OPTIONS.Version. +int DOKAN_CALLBACK MxfsGetFileSecurity( + LPCWSTR FileName, + PSECURITY_INFORMATION SecurityInfo, // A pointer to SECURITY_INFORMATION value being requested + PSECURITY_DESCRIPTOR SecurityDescriptor, // A pointer to SECURITY_DESCRIPTOR buffer to be filled + ULONG SecurityDescriptorLength, // length of Security descriptor buffer + PULONG LengthNeeded, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls", FileName); + return -ERROR_CALL_NOT_IMPLEMENTED; +} + +int DOKAN_CALLBACK MxfsSetFileSecurity( + LPCWSTR FileName, + PSECURITY_INFORMATION SecurityInfo, + PSECURITY_DESCRIPTOR SecurityDescriptor, + ULONG SecurityDescriptorLength, + PDOKAN_FILE_INFO FileInfo) +{ + UNIMPLEMENTED("%ls", FileName); + return -ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/stubs.h b/stubs.h new file mode 100644 index 0000000..37ce953 --- /dev/null +++ b/stubs.h @@ -0,0 +1,75 @@ +#ifndef STUBS_H_INCLUDED +#define STUBS_H_INCLUDED + +#include +#include "dokan.h" + +int DOKAN_CALLBACK MxfsMoveFile( + LPCWSTR ExistingFileName, + LPCWSTR NewFileName, + BOOL ReplaceExisiting, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsLockFile( + LPCWSTR FileName, + LONGLONG ByteOffset, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsUnlockFile( + LPCWSTR FileName, + LONGLONG ByteOffset, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsUnmount( + PDOKAN_FILE_INFO FileInfo); + +// Optional +int DOKAN_CALLBACK MxfsFlushFileBuffers( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsSetFileAttributes( + LPCWSTR FileName, + DWORD FileAttributes, + PDOKAN_FILE_INFO FileInfo); + +// You should not delete file on DeleteFile or DeleteDirectory. +// When DeleteFile or DeleteDirectory, you must check whether +// you can delete the file or not, and return 0 (when you can delete it) +// or appropriate error codes such as -ERROR_DIR_NOT_EMPTY, +// -ERROR_SHARING_VIOLATION. +// When you return 0 (ERROR_SUCCESS), you get Cleanup with +// FileInfo->DeleteOnClose set TRUE and you have to delete the +// file in Close. +int DOKAN_CALLBACK MxfsDeleteFile( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsDeleteDirectory( + LPCWSTR FileName, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsSetAllocationSize( + LPCWSTR FileName, + LONGLONG Length, + PDOKAN_FILE_INFO FileInfo); + +// Suported since 0.6.0. You must specify the version at DOKAN_OPTIONS.Version. +int DOKAN_CALLBACK MxfsGetFileSecurity( + LPCWSTR FileName, + PSECURITY_INFORMATION SecurityInfo, // A pointer to SECURITY_INFORMATION value being requested + PSECURITY_DESCRIPTOR SecurityDescriptor, // A pointer to SECURITY_DESCRIPTOR buffer to be filled + ULONG SecurityDescriptorLength, // length of Security descriptor buffer + PULONG LengthNeeded, + PDOKAN_FILE_INFO FileInfo); + +int DOKAN_CALLBACK MxfsSetFileSecurity( + LPCWSTR FileName, + PSECURITY_INFORMATION SecurityInfo, + PSECURITY_DESCRIPTOR SecurityDescriptor, + ULONG SecurityDescriptorLength, + PDOKAN_FILE_INFO FileInfo); + +#endif // STUBS_H_INCLUDED diff --git a/volume_info.c b/volume_info.c new file mode 100644 index 0000000..66e924e --- /dev/null +++ b/volume_info.c @@ -0,0 +1,66 @@ +#include "volume_info.h" +#include "debug.h" +#include "general.h" + +unsigned MxfsGetFreeSpace(MINIX_FS *FileSys) +{ + unsigned UsedZones = MxfsCountBitsSet(&FileSys->ZoneMap); + unsigned TotalZones = FileSys->Super.s_zones; + unsigned FreeBlocks = (TotalZones - UsedZones) << FileSys->Super.s_log_zone_size; + return FreeBlocks * MINIX_BLOCK_SIZE; +} + +int DOKAN_CALLBACK MxfsGetDiskFreeSpace( + PULONGLONG FreeBytesAvailable, + PULONGLONG TotalNumberOfBytes, + PULONGLONG TotalNumberOfFreeBytes, + PDOKAN_FILE_INFO FileInfo) +{ + TRACE(""); + + MINIX_FS *FileSys = (MINIX_FS*)(ULONG_PTR)FileInfo->DokanOptions->GlobalContext; + ASSERT(FileSys); + + unsigned FreeBytes = 0; + if(FreeBytesAvailable || TotalNumberOfFreeBytes) + FreeBytes = MxfsGetFreeSpace(FileSys); + + if(FreeBytesAvailable) + *FreeBytesAvailable = FreeBytes; + if(TotalNumberOfBytes) + *TotalNumberOfBytes = FileSys->cbLen; + if(TotalNumberOfFreeBytes) + *TotalNumberOfFreeBytes = FreeBytes; + return 0; +} + +// see Win32 API GetVolumeInformation +int DOKAN_CALLBACK MxfsGetVolumeInformation( + LPWSTR VolumeNameBuffer, + DWORD VolumeNameSize, // in num of chars + LPDWORD VolumeSerialNumber, + LPDWORD MaximumComponentLength, // in num of chars + LPDWORD FileSystemFlags, + LPWSTR FileSystemNameBuffer, + DWORD FileSystemNameSize, // in num of chars (this is not true?) + PDOKAN_FILE_INFO FileInfo) +{ + TRACE(""); + + if(VolumeNameSize > 0 && VolumeNameBuffer) + wcscpy_s(VolumeNameBuffer, VolumeNameSize, L""); + + if(VolumeSerialNumber) + *VolumeSerialNumber = MINIX_VOLUME_ID; + + if(MaximumComponentLength) + *MaximumComponentLength = MAX_NAME_LEN; + + if(FileSystemFlags) + *FileSystemFlags = FILE_CASE_SENSITIVE_SEARCH; + + if(FileSystemNameSize > 0 && FileSystemNameBuffer) + wcscpy_s(FileSystemNameBuffer, FileSystemNameSize, L"Minix"); + + return 0; +} diff --git a/volume_info.h b/volume_info.h new file mode 100644 index 0000000..5fc2c77 --- /dev/null +++ b/volume_info.h @@ -0,0 +1,31 @@ +#ifndef VOLUME_INFO_H_INCLUDED +#define VOLUME_INFO_H_INCLUDED + +#include +#include "dokan.h" + +// Neither GetDiskFreeSpace nor GetVolumeInformation +// save the DokanFileContext->Context. +// Before these methods are called, CreateFile may not be called. +// (ditto CloseFile and Cleanup) + +// see Win32 API GetDiskFreeSpaceEx +int DOKAN_CALLBACK MxfsGetDiskFreeSpace( + PULONGLONG FreeBytesAvailable, + PULONGLONG TotalNumberOfBytes, + PULONGLONG TotalNumberOfFreeBytes, + PDOKAN_FILE_INFO FileInfo); + + +// see Win32 API GetVolumeInformation +int DOKAN_CALLBACK MxfsGetVolumeInformation( + LPWSTR VolumeNameBuffer, + DWORD VolumeNameSize, // in num of chars + LPDWORD VolumeSerialNumber, + LPDWORD MaximumComponentLength, // in num of chars + LPDWORD FileSystemFlags, + LPWSTR FileSystemNameBuffer, + DWORD FileSystemNameSize, // in num of chars + PDOKAN_FILE_INFO FileInfo); + +#endif // VOLUME_INFO_H_INCLUDED