From f181593f49725e7816c53813b41b9275e145f6c8 Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Sat, 3 Aug 2019 11:52:47 -0700 Subject: [PATCH 1/7] Add persistence to Airfs Adds persistence to Airfs; stores the volume in a file. The interface has changed slightly. Pass... -N VolumeName ( for example C:\Users\foo\Desktop\test.air ) -n MapName ( for example Local\Airfs ) ... in place of the no longer used... -n MaxFileNodes --- tst/airfs/airfs.cpp | 1342 ++++++++++++------------------------------- 1 file changed, 363 insertions(+), 979 deletions(-) diff --git a/tst/airfs/airfs.cpp b/tst/airfs/airfs.cpp index 32347909..1b1ea50d 100644 --- a/tst/airfs/airfs.cpp +++ b/tst/airfs/airfs.cpp @@ -25,30 +25,7 @@ * root of this project. */ -#include -#include -#include -#include -#include -#include -#include // Used by SLOWIO. - -#define AIRFS_MAX_PATH 512 -FSP_FSCTL_STATIC_ASSERT(AIRFS_MAX_PATH > MAX_PATH, - "AIRFS_MAX_PATH must be greater than MAX_PATH."); - -#define AIRFS_NAME_NORMALIZATION // Include name normalization support. -#define AIRFS_REPARSE_POINTS // Include reparse points support. -#define AIRFS_NAMED_STREAMS // Include alternate data streams support. -#define AIRFS_DIRINFO_BY_NAME // Include GetDirInfoByName. -#define AIRFS_SLOWIO // Include delayed I/O response support. -#define AIRFS_CONTROL // Include DeviceIoControl support. - - -#define SECTOR_SIZE 512 -#define SECTORS_PER_ALLOCATION_UNIT 1 -#define ALLOCATION_UNIT ( SECTOR_SIZE * SECTORS_PER_ALLOCATION_UNIT ) -#define IN_ALLOCATION_UNITS(bytes) (((bytes) + ALLOCATION_UNIT - 1) / ALLOCATION_UNIT * ALLOCATION_UNIT) +#include "common.h" enum { @@ -61,135 +38,38 @@ enum ////////////////////////////////////////////////////////////////////// -// Heap Support - -static HANDLE AirfsHeap = 0; -std::once_flag AirfsHeapInitOnceFlag; - -static inline BOOLEAN AirfsHeapInitialize() -{ - std::call_once(AirfsHeapInitOnceFlag, [](){ AirfsHeap = HeapCreate(0, 0, 0); }); - return AirfsHeap != 0; -} - -static inline PVOID AirfsHeapAlloc(SIZE_T Size) -{ - return HeapAlloc(AirfsHeap, 0, Size); -} - -static inline PVOID AirfsHeapRealloc(PVOID Pointer, SIZE_T RequestedSize) -{ - if (!Pointer) - { - if (!RequestedSize) return 0; - return HeapAlloc(AirfsHeap, 0, RequestedSize); - } - if (!RequestedSize) return HeapFree(AirfsHeap, 0, Pointer), 0; - return HeapReAlloc(AirfsHeap, 0, Pointer, RequestedSize); -} - -static inline void AirfsHeapFree(PVOID Pointer) -{ - if (Pointer) HeapFree(AirfsHeap, 0, Pointer); -} - -static inline SIZE_T AirfsHeapSize(PVOID Pointer) -{ - if (!Pointer) return 0; - return HeapSize(AirfsHeap, 0, Pointer); -} - -////////////////////////////////////////////////////////////////////// - -typedef struct NODE NODE, *NODE_; - -struct NODE_LESS -{ - bool operator() (const NODE_ NodeA, const NODE_ NodeB) const - { - WCHAR* a = (WCHAR*) NodeA; - WCHAR* b = (WCHAR*) NodeB; - return (CaseInsensitive ? _wcsicmp(a, b) : wcscmp(a, b)) < 0; - } - NODE_LESS(BOOLEAN Insensitive) : CaseInsensitive(Insensitive){} - BOOLEAN CaseInsensitive; -}; - -typedef std::set NODES, *NODES_; - -////////////////////////////////////////////////////////////////////// - -struct NODE -{ - WCHAR Name[AIRFS_MAX_PATH]; - NODE_ Parent; - NODES_ Children; - FSP_FSCTL_FILE_INFO FileInfo; - SIZE_T SecurityDescriptorSize; - PVOID SecurityDescriptor; - PVOID FileData; -#if defined(AIRFS_REPARSE_POINTS) - SIZE_T ReparseDataSize; - PVOID ReparseData; -#endif - volatile LONG RefCount; -#if defined(AIRFS_NAMED_STREAMS) - NODES_ Streams; - BOOLEAN IsAStream; -#endif -}; - -////////////////////////////////////////////////////////////////////// - -typedef struct -{ - FSP_FILE_SYSTEM *FileSystem; - NODE_ Root; - ULONG NumNodes; - ULONG MaxNodes; - ULONG MaxFileSize; -#ifdef AIRFS_SLOWIO - ULONG SlowioMaxDelay; - ULONG SlowioPercentDelay; - ULONG SlowioRarefyDelay; - volatile LONG SlowioThreadsRunning; -#endif - UINT16 VolumeLabelLength; - WCHAR VolumeLabel[32]; - BOOLEAN CaseInsensitive; -} AIRFS, *AIRFS_; - -////////////////////////////////////////////////////////////////////// - -inline UINT64 GetSystemTime() -{ - FILETIME FileTime; - GetSystemTimeAsFileTime(&FileTime); - return ((PLARGE_INTEGER)&FileTime)->QuadPart; -} - -////////////////////////////////////////////////////////////////////// - NTSTATUS CreateNode(AIRFS_ Airfs, PWSTR Name, NODE_ *PNode) { static UINT64 IndexNumber = 1; - NODE_ Node = (NODE_) malloc(sizeof *Node); + + NODE_ Node = (NODE_) StorageAllocate(Airfs, sizeof NODE); + if (!Node) { *PNode = 0; return STATUS_INSUFFICIENT_RESOURCES; } - memset(Node, 0, sizeof *Node); - wcscpy_s(Node->Name, sizeof Node->Name / sizeof(WCHAR), Name); + memset(Node, 0, sizeof NODE); + + size_t NameNumBytes = (wcslen(Name)+1) * sizeof WCHAR; + WCHAR* NameAllocation = (WCHAR*) StorageAllocate(Airfs, NameNumBytes); + if (!NameAllocation) + { + StorageFree(Airfs, Node); + *PNode = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(NameAllocation, Name, NameNumBytes); + Node->Name = NameAllocation; + Node->FileInfo.CreationTime = Node->FileInfo.LastAccessTime = Node->FileInfo.LastWriteTime = - Node->FileInfo.ChangeTime = GetSystemTime(); + Node->FileInfo.ChangeTime = SystemTime(); Node->FileInfo.IndexNumber = IndexNumber++; *PNode = Node; - Airfs->NumNodes++; return STATUS_SUCCESS; } @@ -197,28 +77,17 @@ NTSTATUS CreateNode(AIRFS_ Airfs, PWSTR Name, NODE_ *PNode) void DeleteNode(AIRFS_ Airfs, NODE_ Node) { -#if defined(AIRFS_REPARSE_POINTS) - free(Node->ReparseData); -#endif - AirfsHeapFree(Node->FileData); - free(Node->SecurityDescriptor); - - if (Node->Children) + NODE_ Block; + while (Block = Node->FileBlocks) { - assert(Node->Children->empty()); - delete Node->Children; + Detach(Node->FileBlocks, Block); + StorageFree(Airfs, Block); } -#if defined(AIRFS_NAMED_STREAMS) - if (Node->Streams) - { - assert(Node->Streams->empty()); - delete Node->Streams; - } -#endif - - free(Node); - Airfs->NumNodes--; + StorageFree(Airfs, Node->Name); + StorageFree(Airfs, Node->ReparseData); + StorageFree(Airfs, Node->SecurityDescriptor); + StorageFree(Airfs, Node); } ////////////////////////////////////////////////////////////////////// @@ -240,7 +109,6 @@ inline void DereferenceNode(AIRFS_ Airfs, NODE_ Node) void GetFileInfo(NODE_ Node, FSP_FSCTL_FILE_INFO *FileInfo) { -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) { *FileInfo = Node->Parent->FileInfo; @@ -250,75 +118,15 @@ void GetFileInfo(NODE_ Node, FSP_FSCTL_FILE_INFO *FileInfo) FileInfo->FileSize = Node->FileInfo.FileSize; } else -#endif *FileInfo = Node->FileInfo; } ////////////////////////////////////////////////////////////////////// -void DumpNodes(NODE_ Node, int indent=0) +NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, NODE_ *PParent, NODE_ *PNode) { - UINT32 dir = FILE_ATTRIBUTE_DIRECTORY & Node->FileInfo.FileAttributes; - printf("%c%3d ", dir ? 'd' : 'f', (int)Node->RefCount); - for (int i = indent; i; i--) putchar('\t'); - printf("\"%S\"\n", Node->Name); - if (Node->Children) - for (auto& Child : *Node->Children) - DumpNodes(Child, indent+1); -} + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; -////////////////////////////////////////////////////////////////////// - -NTSTATUS CreateNodeSet(BOOLEAN CaseInsensitive, NODES_ *PNodeSet) -{ - try - { - *PNodeSet = new NODES(NODE_LESS(CaseInsensitive)); - return STATUS_SUCCESS; - } - catch (...) - { - *PNodeSet = 0; - return STATUS_INSUFFICIENT_RESOURCES; - } -} - -////////////////////////////////////////////////////////////////////// - -void DeleteAllNodes(AIRFS_ Airfs) -{ - NODE_ Node = Airfs->Root; - while (Node) - { - if (!Node->Children->empty()) - { - Node = *Node->Children->begin(); - } - else - { -#if defined(AIRFS_NAMED_STREAMS) - if (Node->Streams) - { - for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ) - { - NODE_ Stream = *Iter++; - DeleteNode(Airfs, Stream); - } - delete Node->Streams; - } -#endif - NODE_ Parent = Node->Parent; - DeleteNode(Airfs, Node); - Node = Parent; - } - } -} - -////////////////////////////////////////////////////////////////////// - -NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, - NODE_ *PParent, NODE_ *PNode) -{ // Special case root. if (Name[0] == 0 || Name[1] == 0) { @@ -329,45 +137,39 @@ NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, } WCHAR ParsedName[AIRFS_MAX_PATH]; - wcscpy_s(ParsedName, sizeof ParsedName / sizeof(WCHAR), Name); + wcscpy_s(ParsedName, sizeof ParsedName / sizeof WCHAR, Name); // From root, for each ancestor... NODE_ Ancestor = Airfs->Root; WCHAR* fm; WCHAR* to = ParsedName; -#if defined(AIRFS_NAMED_STREAMS) WCHAR* Colon = 0; -#endif for (;;) { // Isolate the next base name. for (fm = to+1; *fm == L'\\'; fm++) {} for (to = fm; *to != L'\0' && *to != L'\\'; to++) - if (*to == ':'){Colon = to; break;} + if (*to == ':') {Colon = to; break;} if (*to == 0) break; *to = 0; // Find this name. - auto Iter = Ancestor->Children->find((NODE_)fm); - if (Iter == Ancestor->Children->end()) + NODE_ Child = Find(Ancestor->Children, fm, NodeCmp); + if (!Child) { if (PParent) *PParent = 0; *PNode = 0; -#if defined(AIRFS_NAMED_STREAMS) if (Colon) return STATUS_OBJECT_NAME_NOT_FOUND; -#endif return STATUS_OBJECT_PATH_NOT_FOUND; } - Ancestor = *Iter; + Ancestor = Child; -#if defined(AIRFS_NAMED_STREAMS) if (Colon) { fm = to+1; break; } -#endif if (!(Ancestor->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { @@ -378,12 +180,11 @@ NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, } if (BaseName) - *BaseName = Name + ( ( (UINT_PTR)fm - (UINT_PTR)ParsedName ) / sizeof(WCHAR)); + *BaseName = Name + ( ( (UINT_PTR)fm - (UINT_PTR)ParsedName ) / sizeof WCHAR); if (PParent) *PParent = Ancestor; -#if defined(AIRFS_NAMED_STREAMS) if (Colon) { // Find the stream, if it exists. @@ -392,86 +193,64 @@ NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, *PNode = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } - auto Iter = Ancestor->Streams->find((NODE_)fm); - if (Iter == Ancestor->Streams->end()) + NODE_ Stream = Find(Ancestor->Streams, fm, NodeCmp); + if (!Stream) { *PNode = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } - *PNode = *Iter; + *PNode = Stream; return 0; } -#endif // Find the directory entry, if it exists. - auto Iter = Ancestor->Children->find((NODE_)fm); - if (Iter == Ancestor->Children->end()) + NODE_ Found = Find(Ancestor->Children, fm, NodeCmp); + if (!Found) { *PNode = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } - *PNode = *Iter; + *PNode = Found; return 0; } ////////////////////////////////////////////////////////////////////// -#if defined(AIRFS_NAMED_STREAMS) BOOLEAN AddStreamInfo(NODE_ Node, PWSTR StreamName, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) { - UINT8 StreamInfoBuf[sizeof(FSP_FSCTL_STREAM_INFO) + sizeof Node->Name]; + UINT8 StreamInfoBuf[sizeof FSP_FSCTL_STREAM_INFO + AIRFS_MAX_PATH * sizeof WCHAR]; FSP_FSCTL_STREAM_INFO *StreamInfo = (FSP_FSCTL_STREAM_INFO *)StreamInfoBuf; - StreamInfo->Size = (UINT16)(sizeof(FSP_FSCTL_STREAM_INFO) + wcslen(StreamName) * sizeof(WCHAR)); + StreamInfo->Size = (UINT16)(sizeof FSP_FSCTL_STREAM_INFO + wcslen(StreamName) * sizeof WCHAR); StreamInfo->StreamSize = Node->FileInfo.FileSize; StreamInfo->StreamAllocationSize = Node->FileInfo.AllocationSize; - memcpy(StreamInfo->StreamNameBuf, StreamName, StreamInfo->Size - sizeof(FSP_FSCTL_STREAM_INFO)); + memcpy(StreamInfo->StreamNameBuf, StreamName, StreamInfo->Size - sizeof FSP_FSCTL_STREAM_INFO); return FspFileSystemAddStreamInfo(StreamInfo, Buffer, Length, PBytesTransferred); } -#endif ////////////////////////////////////////////////////////////////////// inline void TouchNode(NODE_ Node) { Node->FileInfo.LastAccessTime = Node->FileInfo.LastWriteTime = - Node->FileInfo.ChangeTime = GetSystemTime(); + Node->FileInfo.ChangeTime = SystemTime(); } ////////////////////////////////////////////////////////////////////// -NTSTATUS InsertNode(AIRFS_ Airfs, NODE_ Parent, NODE_ Node, PBOOLEAN PInserted) +void InsertNode(AIRFS_ Airfs, NODE_ Parent, NODE_ Node) { - try - { -#if defined(AIRFS_NAMED_STREAMS) - if (Node->IsAStream) - { - if (!Parent->Streams) - { - NTSTATUS Result = CreateNodeSet(Airfs->CaseInsensitive, &Parent->Streams); - if (Result) return Result; - } - *PInserted = Parent->Streams->insert(Node).second; - } - else -#endif - *PInserted = Parent->Children->insert(Node).second; - if (*PInserted) - { - Node->Parent = Parent; - ReferenceNode(Node); - TouchNode(Parent); - } - return STATUS_SUCCESS; - } - catch (...) - { - *PInserted = 0; - return STATUS_INSUFFICIENT_RESOURCES; - } + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + + WCHAR* key = Node->Name; + if (Node->IsAStream) Attach(Parent->Streams , Node, NodeCmp, key); + else Attach(Parent->Children , Node, NodeCmp, key); + + Node->Parent = Parent; + ReferenceNode(Node); + TouchNode(Parent); } ////////////////////////////////////////////////////////////////////// @@ -481,239 +260,70 @@ void RemoveNode(AIRFS_ Airfs, NODE_ Node) NODE_ Parent = Node->Parent; TouchNode(Parent); -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) { - if (Parent->Streams) - { - auto found = Parent->Streams->find(Node); - if (found != Parent->Streams->end()) - Parent->Streams->erase(found); - } + if (Parent->Streams) Detach(Parent->Streams, Node); } else -#endif - Parent->Children->erase(Node); + Detach(Parent->Children, Node); DereferenceNode(Airfs, Node); } ////////////////////////////////////////////////////////////////////// -inline BOOLEAN NodeHasChildren(NODE_ Node) -{ - return Node->Children && !Node->Children->empty(); -} - -////////////////////////////////////////////////////////////////////// - -#ifdef AIRFS_SLOWIO -/* - * SLOWIO - * - * This is included for two uses: - * - * 1) For testing winfsp, by allowing Airfs to act more like a non-ram file system, - * with some IO taking many milliseconds, and some IO completion delayed. - * - * 2) As sample code for how to use winfsp's STATUS_PENDING capabilities. - * - */ - -inline UINT64 Hash(UINT64 x) -{ - x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ull; - x = (x ^ (x >> 27)) * 0x94d049bb133111ebull; - x = x ^ (x >> 31); - return x; -} - -inline ULONG PseudoRandom(ULONG to) -{ - // John Oberschelp's PRNG - static UINT64 spin = 0; - InterlockedIncrement(&spin); - return Hash(spin) % to; -} - -inline BOOLEAN SlowioReturnPending(FSP_FILE_SYSTEM *FileSystem) -{ - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - if (!Airfs->SlowioMaxDelay) - return FALSE; - return PseudoRandom(100) < Airfs->SlowioPercentDelay; -} - -inline void SlowioSnooze(FSP_FILE_SYSTEM *FileSystem) -{ - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - if (!Airfs->SlowioMaxDelay) - return; - ULONG millis = PseudoRandom(Airfs->SlowioMaxDelay + 1) >> PseudoRandom(Airfs->SlowioRarefyDelay + 1); - Sleep(millis); -} - -void SlowioReadThread( - FSP_FILE_SYSTEM *FileSystem, - NODE_ Node, - PVOID Buffer, - UINT64 Offset, - UINT64 EndOffset, - UINT64 RequestHint) -{ - SlowioSnooze(FileSystem); - - memcpy(Buffer, (PUINT8)Node->FileData + Offset, (size_t)(EndOffset - Offset)); - UINT32 BytesTransferred = (ULONG)(EndOffset - Offset); - - FSP_FSCTL_TRANSACT_RSP ResponseBuf; - memset(&ResponseBuf, 0, sizeof ResponseBuf); - ResponseBuf.Size = sizeof ResponseBuf; - ResponseBuf.Kind = FspFsctlTransactReadKind; - ResponseBuf.Hint = RequestHint; // IRP that is being completed - ResponseBuf.IoStatus.Status = STATUS_SUCCESS; - ResponseBuf.IoStatus.Information = BytesTransferred; // bytes read - FspFileSystemSendResponse(FileSystem, &ResponseBuf); - - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - InterlockedDecrement(&Airfs->SlowioThreadsRunning); -} - -void SlowioWriteThread( - FSP_FILE_SYSTEM *FileSystem, - NODE_ Node, - PVOID Buffer, - UINT64 Offset, - UINT64 EndOffset, - UINT64 RequestHint) -{ - SlowioSnooze(FileSystem); - - memcpy((PUINT8)Node->FileData + Offset, Buffer, (size_t)(EndOffset - Offset)); - UINT32 BytesTransferred = (ULONG)(EndOffset - Offset); - - FSP_FSCTL_TRANSACT_RSP ResponseBuf; - memset(&ResponseBuf, 0, sizeof ResponseBuf); - ResponseBuf.Size = sizeof ResponseBuf; - ResponseBuf.Kind = FspFsctlTransactWriteKind; - ResponseBuf.Hint = RequestHint; // IRP that is being completed - ResponseBuf.IoStatus.Status = STATUS_SUCCESS; - ResponseBuf.IoStatus.Information = BytesTransferred; // bytes written - GetFileInfo(Node, &ResponseBuf.Rsp.Write.FileInfo); - FspFileSystemSendResponse(FileSystem, &ResponseBuf); - - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - InterlockedDecrement(&Airfs->SlowioThreadsRunning); -} - -void SlowioReadDirectoryThread( - FSP_FILE_SYSTEM *FileSystem, - ULONG BytesTransferred, - UINT64 RequestHint) -{ - SlowioSnooze(FileSystem); - - FSP_FSCTL_TRANSACT_RSP ResponseBuf; - memset(&ResponseBuf, 0, sizeof ResponseBuf); - ResponseBuf.Size = sizeof ResponseBuf; - ResponseBuf.Kind = FspFsctlTransactQueryDirectoryKind; - ResponseBuf.Hint = RequestHint; // IRP that is being completed - ResponseBuf.IoStatus.Status = STATUS_SUCCESS; - ResponseBuf.IoStatus.Information = BytesTransferred; // bytes of directory info read - FspFileSystemSendResponse(FileSystem, &ResponseBuf); - - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - InterlockedDecrement(&Airfs->SlowioThreadsRunning); -} -#endif -////////////////////////////////////////////////////////////////////// -#if defined(AIRFS_REPARSE_POINTS) - NTSTATUS GetReparsePointByName(FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR Name, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize) { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node; -#if defined(AIRFS_NAMED_STREAMS) - // GetReparsePointByName will never receive a named stream. - assert(wcschr(Name, L':') == 0); -#endif - NTSTATUS Result = FindNode(Airfs, Name, 0, 0, &Node); - if (!Node) - return STATUS_OBJECT_NAME_NOT_FOUND; - - if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) - return STATUS_NOT_A_REPARSE_POINT; + if (!Node) return STATUS_OBJECT_NAME_NOT_FOUND; + if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) return STATUS_NOT_A_REPARSE_POINT; if (Buffer) { - if (Node->ReparseDataSize > *PSize) - return STATUS_BUFFER_TOO_SMALL; - + if (Node->ReparseDataSize > *PSize) return STATUS_BUFFER_TOO_SMALL; *PSize = Node->ReparseDataSize; - memcpy(Buffer, Node->ReparseData, Node->ReparseDataSize); + memcpy(Buffer, Node->ReparseData.Address(), Node->ReparseDataSize); } return STATUS_SUCCESS; } -#endif ////////////////////////////////////////////////////////////////////// -NTSTATUS SetAllocSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node, - UINT64 RequestedAllocSize) +NTSTATUS SetAllocSize(AIRFS_ Airfs, NODE_ Node, UINT64 RequestedAllocSize) { - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - - RequestedAllocSize = IN_ALLOCATION_UNITS(RequestedAllocSize); - - if (Node->FileInfo.AllocationSize != RequestedAllocSize) + NTSTATUS Result = StorageSetFileCapacity(Airfs, Node, RequestedAllocSize); + if (!Result) { - if (RequestedAllocSize > Airfs->MaxFileSize) return STATUS_DISK_FULL; - - // Reallocate only if the file is made smaller, or if it will not fit in the actual memory footprint. - size_t ActualSize = AirfsHeapSize(Node->FileData); - if (RequestedAllocSize < Node->FileInfo.AllocationSize || RequestedAllocSize > ActualSize) - { - // If the file grow request was modest, guess that it might happen again, and grow the file by 50%. - if (RequestedAllocSize > Node->FileInfo.AllocationSize && RequestedAllocSize <= ActualSize + ActualSize / 8) - RequestedAllocSize = IN_ALLOCATION_UNITS(ActualSize + ActualSize / 2); - - PVOID FileData = AirfsHeapRealloc(Node->FileData, (size_t)RequestedAllocSize); - if (!FileData && RequestedAllocSize > 0) - return STATUS_INSUFFICIENT_RESOURCES; - - Node->FileData = FileData; - } - Node->FileInfo.AllocationSize = RequestedAllocSize; - if (Node->FileInfo.FileSize > RequestedAllocSize) - Node->FileInfo.FileSize = RequestedAllocSize; - } + if (Node->FileInfo.FileSize > Node->FileInfo.AllocationSize) + Node->FileInfo.FileSize = Node->FileInfo.AllocationSize; - return STATUS_SUCCESS; + } + return Result; } ////////////////////////////////////////////////////////////////////// -NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node, - UINT64 RequestedFileSize) +NTSTATUS SetFileSize(AIRFS_ Airfs, NODE_ Node, UINT64 RequestedFileSize) { if (Node->FileInfo.FileSize != RequestedFileSize) { if (Node->FileInfo.AllocationSize < RequestedFileSize) { - NTSTATUS Result = SetAllocSize(FileSystem, Node, RequestedFileSize); + NTSTATUS Result = SetAllocSize(Airfs, Node, RequestedFileSize); if (!NT_SUCCESS(Result)) return Result; } if (Node->FileInfo.FileSize < RequestedFileSize) - memset((PUINT8)Node->FileData + Node->FileInfo.FileSize, 0, - (size_t)(RequestedFileSize - Node->FileInfo.FileSize)); + StorageAccessFile(ZERO, Node, Node->FileInfo.FileSize, RequestedFileSize - Node->FileInfo.FileSize, 0); + Node->FileInfo.FileSize = RequestedFileSize; } @@ -722,25 +332,15 @@ NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node, ////////////////////////////////////////////////////////////////////// -BOOLEAN AddDirInfo(NODE_ Node, PWSTR Name, PVOID Buffer, ULONG Length, - PULONG PBytesTransferred) +BOOLEAN AddDirInfo(NODE_ Node, PWSTR Name, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) { - UINT8 DirInfoBuf[sizeof(FSP_FSCTL_DIR_INFO) + sizeof Node->Name]; + size_t NameBytes = wcslen(Name) * sizeof WCHAR; + UINT8 DirInfoBuf[sizeof FSP_FSCTL_DIR_INFO + AIRFS_MAX_PATH * sizeof WCHAR]; FSP_FSCTL_DIR_INFO *DirInfo = (FSP_FSCTL_DIR_INFO *)DirInfoBuf; - WCHAR Root[2] = L"\\"; - PWSTR Remain, Suffix; - - if (!Name) - { - FspPathSuffix(Node->Name, &Remain, &Suffix, Root); - Name = Suffix; - FspPathCombine(Node->Name, Suffix); - } - - memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); - DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(Name) * sizeof(WCHAR)); + DirInfo->Size = (UINT16)(sizeof FSP_FSCTL_DIR_INFO + NameBytes); DirInfo->FileInfo = Node->FileInfo; - memcpy(DirInfo->FileNameBuf, Name, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); + memcpy(DirInfo->FileNameBuf, Name, NameBytes + sizeof WCHAR); return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred); } @@ -751,11 +351,11 @@ NTSTATUS ApiGetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *Vo { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - VolumeInfo->TotalSize = Airfs->MaxNodes * (UINT64)Airfs->MaxFileSize; - VolumeInfo->FreeSize = (Airfs->MaxNodes - Airfs->NumNodes) * - (UINT64)Airfs->MaxFileSize; + VolumeInfo->TotalSize = Airfs->VolumeSize; + VolumeInfo->FreeSize = Airfs->FreeSize; VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength; memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength); + return STATUS_SUCCESS; } @@ -766,14 +366,13 @@ NTSTATUS ApiSetVolumeLabel(FSP_FILE_SYSTEM *FileSystem, PWSTR VolumeLabel, { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - Airfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof(WCHAR)); + Airfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof WCHAR); if (Airfs->VolumeLabelLength > sizeof Airfs->VolumeLabel) Airfs->VolumeLabelLength = sizeof Airfs->VolumeLabel; memcpy(Airfs->VolumeLabel, VolumeLabel, Airfs->VolumeLabelLength); - VolumeInfo->TotalSize = Airfs->MaxNodes * Airfs->MaxFileSize; - VolumeInfo->FreeSize = - (Airfs->MaxNodes - Airfs->NumNodes) * Airfs->MaxFileSize; + VolumeInfo->TotalSize = Airfs->VolumeSize; + VolumeInfo->FreeSize = Airfs->FreeSize; VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength; memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength); @@ -790,17 +389,14 @@ NTSTATUS ApiGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, PUINT32 P NTSTATUS Result = FindNode(Airfs, Name, 0, 0, &Node); if (!Node) { -#if defined(AIRFS_REPARSE_POINTS) if (FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0, Name, PFileAttributes)) { return STATUS_REPARSE; } -#endif return Result; } -#if defined(AIRFS_NAMED_STREAMS) UINT32 FileAttributesMask = ~(UINT32)0; if (Node->IsAStream) { @@ -810,10 +406,6 @@ NTSTATUS ApiGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, PUINT32 P if (PFileAttributes) *PFileAttributes = Node->FileInfo.FileAttributes & FileAttributesMask; -#else - if (PFileAttributes) - *PFileAttributes = Node->FileInfo.FileAttributes; -#endif if (PSecurityDescriptorSize) { @@ -825,7 +417,7 @@ NTSTATUS ApiGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, PUINT32 P *PSecurityDescriptorSize = Node->SecurityDescriptorSize; if (SecurityDescriptor) - memcpy(SecurityDescriptor, Node->SecurityDescriptor, Node->SecurityDescriptorSize); + memcpy(SecurityDescriptor, Node->SecurityDescriptor.Address(), Node->SecurityDescriptorSize); } return STATUS_SUCCESS; @@ -841,7 +433,6 @@ NTSTATUS ApiCreate(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions NODE_ Node; NODE_ Parent; NTSTATUS Result; - BOOLEAN Inserted; PWSTR BaseName; if (AIRFS_MAX_PATH <= wcslen(Name)) @@ -851,36 +442,13 @@ NTSTATUS ApiCreate(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions AllocationSize = 0; Result = FindNode(Airfs, Name, &BaseName, &Parent, &Node); - - if (Node) - return STATUS_OBJECT_NAME_COLLISION; - - if (!Parent) - return Result; - - if (Airfs->NumNodes >= Airfs->MaxNodes) - return STATUS_CANNOT_MAKE; - - if (AllocationSize > Airfs->MaxFileSize) - return STATUS_DISK_FULL; + if (Node) return STATUS_OBJECT_NAME_COLLISION; + if (!Parent) return Result; Result = CreateNode(Airfs, BaseName, &Node); - if (!NT_SUCCESS(Result)) - return Result; + if (!NT_SUCCESS(Result)) return Result; -#if defined(AIRFS_NAMED_STREAMS) Node->IsAStream = BaseName[-1] == L':'; -#endif - - if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - Result = CreateNodeSet(Airfs->CaseInsensitive, &Node->Children); - if (Result) - { - DeleteNode(Airfs, Node); - return STATUS_INSUFFICIENT_RESOURCES; - } - } Node->FileInfo.FileAttributes = (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FileAttributes : FileAttributes | FILE_ATTRIBUTE_ARCHIVE; @@ -888,51 +456,40 @@ NTSTATUS ApiCreate(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions if (SecurityDescriptor) { Node->SecurityDescriptorSize = GetSecurityDescriptorLength(SecurityDescriptor); - Node->SecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(Node->SecurityDescriptorSize); + Node->SecurityDescriptor = StorageAllocate(Airfs, (int)Node->SecurityDescriptorSize); if (!Node->SecurityDescriptor) { DeleteNode(Airfs, Node); return STATUS_INSUFFICIENT_RESOURCES; } - memcpy(Node->SecurityDescriptor, SecurityDescriptor, Node->SecurityDescriptorSize); + memcpy(Node->SecurityDescriptor.Address(), SecurityDescriptor, Node->SecurityDescriptorSize); } - Node->FileInfo.AllocationSize = AllocationSize; - if (Node->FileInfo.AllocationSize) + if (AllocationSize) { - Node->FileData = AirfsHeapAlloc((size_t)Node->FileInfo.AllocationSize); - if (!Node->FileData) + NTSTATUS Result = SetAllocSize(Airfs, Node, AllocationSize); + if (Result) { DeleteNode(Airfs, Node); - return STATUS_INSUFFICIENT_RESOURCES; + return Result; } } - Result = InsertNode(Airfs, Parent, Node, &Inserted); - if (!NT_SUCCESS(Result) || !Inserted) - { - DeleteNode(Airfs, Node); - if (NT_SUCCESS(Result)) - Result = STATUS_OBJECT_NAME_COLLISION; // Should not happen! - return Result; - } - + InsertNode(Airfs, Parent, Node); ReferenceNode(Node); *PNode = Node; GetFileInfo(Node, FileInfo); -#if defined(AIRFS_NAME_NORMALIZATION) if (Airfs->CaseInsensitive) { int ParentPathNumBytes = (int)( (char*)BaseName - (char*)Name ); - int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof(WCHAR) ); + int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof WCHAR ); FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes); - memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof(WCHAR), Node->Name, NodeNameNumBytes); + memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof WCHAR, Node->Name, NodeNameNumBytes); OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes; } -#endif return STATUS_SUCCESS; } @@ -961,18 +518,16 @@ NTSTATUS ApiOpen(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions, *PNode = Node; GetFileInfo(Node, FileInfo); -#if defined(AIRFS_NAME_NORMALIZATION) if (Airfs->CaseInsensitive) { int ParentPathNumBytes = (int)( (char*)baseName - (char*)Name ); - int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof(WCHAR) ); + int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof WCHAR ); FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes); - memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof(WCHAR), Node->Name, NodeNameNumBytes); + memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof WCHAR, Node->Name, NodeNameNumBytes); OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes; } -#endif return STATUS_SUCCESS; } @@ -984,42 +539,30 @@ NTSTATUS ApiOverwrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttri { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; - NTSTATUS Result; -#if defined(AIRFS_NAMED_STREAMS) - if (Node->Streams) + for (NODE_ Stream = First(Node->Streams); Stream; ) { - for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ) + NODE_ NextStream = Next(Stream); + LONG RefCount = Stream->RefCount; + MemoryBarrier(); + if (RefCount <= 1) { - NODE_ Stream = *Iter++; - LONG RefCount = Stream->RefCount; - MemoryBarrier(); - if (RefCount <= 1) - { - RemoveNode(Airfs, Stream); - } - } - if (Node->Streams->empty()) - { - delete Node->Streams; - Node->Streams = 0; + RemoveNode(Airfs, Stream); } + Stream = NextStream; } -#endif - Result = SetAllocSize(FileSystem, Node, AllocationSize); + NTSTATUS Result = SetAllocSize(Airfs, Node, AllocationSize); if (!NT_SUCCESS(Result)) return Result; - if (ReplaceFileAttributes) - Node->FileInfo.FileAttributes = FileAttributes | FILE_ATTRIBUTE_ARCHIVE; - else - Node->FileInfo.FileAttributes |= FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + if (ReplaceFileAttributes) Node->FileInfo.FileAttributes = FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + else Node->FileInfo.FileAttributes |= FileAttributes | FILE_ATTRIBUTE_ARCHIVE; Node->FileInfo.FileSize = 0; Node->FileInfo.LastAccessTime = Node->FileInfo.LastWriteTime = - Node->FileInfo.ChangeTime = GetSystemTime(); + Node->FileInfo.ChangeTime = SystemTime(); GetFileInfo(Node, FileInfo); @@ -1032,53 +575,36 @@ void ApiCleanup(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, ULONG Flag { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; -#if defined(AIRFS_NAMED_STREAMS) - NODE_ MainNode = Node->IsAStream ? Node->Parent : Node; -#else - NODE_ MainNode = Node; -#endif - assert(Flags); // FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this. + NODE_ MainNode = Node->IsAStream ? Node->Parent : Node; if (Flags & FspCleanupSetArchiveBit) { if (!(MainNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - MainNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + MainNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; } if (Flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime | FspCleanupSetChangeTime)) { - UINT64 SystemTime = GetSystemTime(); - - if (Flags & FspCleanupSetLastAccessTime) - MainNode->FileInfo.LastAccessTime = SystemTime; - if (Flags & FspCleanupSetLastWriteTime) - MainNode->FileInfo.LastWriteTime = SystemTime; - if (Flags & FspCleanupSetChangeTime) - MainNode->FileInfo.ChangeTime = SystemTime; + UINT64 Time = SystemTime(); + if (Flags & FspCleanupSetLastAccessTime ) MainNode->FileInfo.LastAccessTime = Time; + if (Flags & FspCleanupSetLastWriteTime ) MainNode->FileInfo.LastWriteTime = Time; + if (Flags & FspCleanupSetChangeTime ) MainNode->FileInfo.ChangeTime = Time; } if (Flags & FspCleanupSetAllocationSize) { - SetAllocSize(FileSystem, Node, Node->FileInfo.FileSize); + SetAllocSize(Airfs, Node, Node->FileInfo.FileSize); } - if ((Flags & FspCleanupDelete) && !NodeHasChildren(Node)) + if ((Flags & FspCleanupDelete) && !Node->Children) { - -#if defined(AIRFS_NAMED_STREAMS) - if (Node->Streams) + NODE_ Stream; + while (Stream = Node->Streams) { - for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ) - { - NODE_ Stream = *Iter++; - DeleteNode(Airfs, Stream); - } - delete Node->Streams; - Node->Streams = 0; + Detach(Node->Streams, Stream); + DeleteNode(Airfs, Stream); } -#endif - RemoveNode(Airfs, Node); } } @@ -1095,42 +621,21 @@ void ApiClose(FSP_FILE_SYSTEM *FileSystem, PVOID Node0) ////////////////////////////////////////////////////////////////////// NTSTATUS ApiRead(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, - UINT64 Offset, ULONG Length, PULONG PBytesTransferred) + UINT64 BegOffset, ULONG Length, PULONG PBytesTransferred) { NODE_ Node = (NODE_) Node0; UINT64 EndOffset; - if (Offset >= Node->FileInfo.FileSize) + if (BegOffset >= Node->FileInfo.FileSize) return STATUS_END_OF_FILE; - EndOffset = Offset + Length; + EndOffset = BegOffset + Length; if (EndOffset > Node->FileInfo.FileSize) EndOffset = Node->FileInfo.FileSize; -#ifdef AIRFS_SLOWIO - if (SlowioReturnPending(FileSystem)) - { - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - try - { - InterlockedIncrement(&Airfs->SlowioThreadsRunning); - std::thread(SlowioReadThread, - FileSystem, Node, Buffer, Offset, EndOffset, - FspFileSystemGetOperationContext()->Request->Hint). - detach(); - return STATUS_PENDING; - } - catch (...) - { - InterlockedDecrement(&Airfs->SlowioThreadsRunning); - } - } - SlowioSnooze(FileSystem); -#endif + StorageAccessFile(READ, Node, BegOffset, EndOffset - BegOffset, (char*)Buffer); - memcpy(Buffer, (PUINT8)Node->FileData + Offset, (size_t)(EndOffset - Offset)); - - *PBytesTransferred = (ULONG)(EndOffset - Offset); + *PBytesTransferred = (ULONG)(EndOffset - BegOffset); return STATUS_SUCCESS; } @@ -1138,58 +643,38 @@ NTSTATUS ApiRead(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, ////////////////////////////////////////////////////////////////////// NTSTATUS ApiWrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, - UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile, + UINT64 BegOffset, ULONG Length, BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) { + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; UINT64 EndOffset; NTSTATUS Result; if (ConstrainedIo) { - if (Offset >= Node->FileInfo.FileSize) + if (BegOffset >= Node->FileInfo.FileSize) return STATUS_SUCCESS; - EndOffset = Offset + Length; + EndOffset = BegOffset + Length; if (EndOffset > Node->FileInfo.FileSize) EndOffset = Node->FileInfo.FileSize; } else { if (WriteToEndOfFile) - Offset = Node->FileInfo.FileSize; - EndOffset = Offset + Length; + BegOffset = Node->FileInfo.FileSize; + EndOffset = BegOffset + Length; if (EndOffset > Node->FileInfo.FileSize) { - Result = SetFileSize(FileSystem, Node, EndOffset); + Result = SetFileSize(Airfs, Node, EndOffset); if (!NT_SUCCESS(Result)) return Result; } } -#ifdef AIRFS_SLOWIO - if (SlowioReturnPending(FileSystem)) - { - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - try - { - InterlockedIncrement(&Airfs->SlowioThreadsRunning); - std::thread(SlowioWriteThread, - FileSystem, Node, Buffer, Offset, EndOffset, - FspFileSystemGetOperationContext()->Request->Hint). - detach(); - return STATUS_PENDING; - } - catch (...) - { - InterlockedDecrement(&Airfs->SlowioThreadsRunning); - } - } - SlowioSnooze(FileSystem); -#endif + StorageAccessFile(WRITE, Node, BegOffset, EndOffset - BegOffset, (char*)Buffer); - memcpy((PUINT8)Node->FileData + Offset, Buffer, (size_t)(EndOffset - Offset)); - - *PBytesTransferred = (ULONG)(EndOffset - Offset); + *PBytesTransferred = (ULONG)(EndOffset - BegOffset); GetFileInfo(Node, FileInfo); return STATUS_SUCCESS; @@ -1197,30 +682,11 @@ NTSTATUS ApiWrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, ////////////////////////////////////////////////////////////////////// -NTSTATUS ApiFlush(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, - FSP_FSCTL_FILE_INFO *FileInfo) +NTSTATUS ApiFlush(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, FSP_FSCTL_FILE_INFO *FileInfo) { NODE_ Node = (NODE_) Node0; - // Nothing to flush, since we do not cache anything. - - if (Node) - { -#if 0 -#if defined(AIRFS_NAMED_STREAMS) - if (Node->IsAStream) - Node->MainNode->FileInfo.LastAccessTime = - Node->MainNode->FileInfo.LastWriteTime = - Node->MainNode->FileInfo.ChangeTime = GetSystemTime(); - else -#endif - Node->FileInfo.LastAccessTime = - Node->FileInfo.LastWriteTime = - Node->FileInfo.ChangeTime = GetSystemTime(); -#endif - - GetFileInfo(Node, FileInfo); - } + if (Node) GetFileInfo(Node, FileInfo); return STATUS_SUCCESS; } @@ -1245,9 +711,7 @@ NTSTATUS ApiSetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAt { NODE_ Node = (NODE_) Node0; -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) Node = Node->Parent; -#endif if (INVALID_FILE_ATTRIBUTES != FileAttributes) Node->FileInfo.FileAttributes = FileAttributes; @@ -1266,10 +730,11 @@ NTSTATUS ApiSetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAt NTSTATUS ApiSetFileSize(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO *FileInfo) { + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; NTSTATUS Result = SetAllocationSize - ? SetAllocSize(FileSystem, Node, NewSize) - : SetFileSize(FileSystem, Node, NewSize); + ? SetAllocSize (Airfs, Node, NewSize) + : SetFileSize (Airfs, Node, NewSize); if (!NT_SUCCESS(Result)) return Result; @@ -1284,7 +749,7 @@ NTSTATUS ApiCanDelete(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name) { NODE_ Node = (NODE_) Node0; - if (NodeHasChildren(Node)) + if (Node->Children) return STATUS_DIRECTORY_NOT_EMPTY; return STATUS_SUCCESS; @@ -1298,19 +763,33 @@ NTSTATUS ApiRename(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; NODE_ NewNode; - BOOLEAN Inserted; NTSTATUS Result; - PWSTR newBaseName; + PWSTR NewBaseName; NODE_ NewParent; - Result = FindNode(Airfs, NewFileName, &newBaseName, &NewParent, &NewNode); + Result = FindNode(Airfs, NewFileName, &NewBaseName, &NewParent, &NewNode); + + // Create the replacement name. + size_t NewBaseNameNumBytes = (wcslen(NewBaseName)+1) * sizeof WCHAR; + WCHAR* NewBaseNameAlloc = (WCHAR*) StorageAllocate(Airfs, NewBaseNameNumBytes); + if (!NewBaseNameAlloc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(NewBaseNameAlloc, NewBaseName, NewBaseNameNumBytes); if (NewNode && Node != NewNode) { if (!ReplaceIfExists) + { + StorageFree(Airfs, NewBaseNameAlloc); return STATUS_OBJECT_NAME_COLLISION; + } if (NewNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + StorageFree(Airfs, NewBaseNameAlloc); return STATUS_ACCESS_DENIED; + } ReferenceNode(NewNode); RemoveNode(Airfs, NewNode); @@ -1319,8 +798,11 @@ NTSTATUS ApiRename(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, ReferenceNode(Node); RemoveNode(Airfs, Node); - wcscpy_s(Node->Name, sizeof Node->Name / sizeof(WCHAR), newBaseName); - Result = InsertNode(Airfs, NewParent, Node, &Inserted); + + StorageFree(Airfs, Node->Name); + Node->Name = NewBaseNameAlloc; + + InsertNode(Airfs, NewParent, Node); DereferenceNode(Airfs, Node); return STATUS_SUCCESS; @@ -1333,9 +815,7 @@ NTSTATUS ApiGetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, { NODE_ Node = (NODE_) Node0; -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) Node = Node->Parent; -#endif if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) { @@ -1345,7 +825,7 @@ NTSTATUS ApiGetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, *PSecurityDescriptorSize = Node->SecurityDescriptorSize; if (SecurityDescriptor) - memcpy(SecurityDescriptor, Node->SecurityDescriptor, Node->SecurityDescriptorSize); + memcpy(SecurityDescriptor, Node->SecurityDescriptor.Address(), Node->SecurityDescriptorSize); return STATUS_SUCCESS; } @@ -1355,49 +835,58 @@ NTSTATUS ApiGetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, NTSTATUS ApiSetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) { + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; - PSECURITY_DESCRIPTOR NewSecurityDescriptor, SecurityDescriptor; - SIZE_T SecurityDescriptorSize; - NTSTATUS Result; + PSECURITY_DESCRIPTOR NewSecurityDescriptor, MallocSecurityDescriptor; -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) Node = Node->Parent; -#endif - Result = FspSetSecurityDescriptor( - Node->SecurityDescriptor, + NTSTATUS Result = FspSetSecurityDescriptor( + Node->SecurityDescriptor.Address(), SecurityInformation, ModificationDescriptor, &NewSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; - SecurityDescriptorSize = GetSecurityDescriptorLength(NewSecurityDescriptor); - SecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SecurityDescriptorSize); - if (!SecurityDescriptor) + uint64_t SecurityDescriptorSize = GetSecurityDescriptorLength(NewSecurityDescriptor); + MallocSecurityDescriptor = (PSECURITY_DESCRIPTOR) malloc(SecurityDescriptorSize); + if (!MallocSecurityDescriptor) { FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); return STATUS_INSUFFICIENT_RESOURCES; } - memcpy(SecurityDescriptor, NewSecurityDescriptor, SecurityDescriptorSize); + memcpy(MallocSecurityDescriptor, NewSecurityDescriptor, SecurityDescriptorSize); FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); - free(Node->SecurityDescriptor); - Node->SecurityDescriptorSize = SecurityDescriptorSize; - Node->SecurityDescriptor = SecurityDescriptor; + NODE_ NewHunk = (NODE_) StorageReallocate(Airfs, Node->SecurityDescriptor, SecurityDescriptorSize); + if (!NewHunk && SecurityDescriptorSize) + { + free(MallocSecurityDescriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + Node->SecurityDescriptorSize = SecurityDescriptorSize; + if (!SecurityDescriptorSize) Node->SecurityDescriptor = 0; + else + { + Node->SecurityDescriptor = NewHunk; + memcpy(Node->SecurityDescriptor.Address(), MallocSecurityDescriptor, SecurityDescriptorSize); + } + + free(MallocSecurityDescriptor); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// -NTSTATUS ApiReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, - PWSTR Pattern, PWSTR Marker, PVOID Buffer, ULONG Length, - PULONG PBytesTransferred) +NTSTATUS ApiReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Pattern, + PWSTR Marker, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) { - assert(!Pattern); - AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + NODE_ Node = (NODE_) Node0; NODE_ Parent = Node->Parent; if (Parent) @@ -1416,65 +905,45 @@ NTSTATUS ApiReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, } } - auto Iter = Marker ? Node->Children->upper_bound((NODE_)Marker) : Node->Children->begin(); - for (; Iter != Node->Children->end(); ++Iter) + // Find the next Child, even if the Marker child no longer exists. + NODE_ Child; + if (!Marker) Child = First(Node->Children); + else Child = Near(Node->Children, Marker, NodeCmp, GT); + + for (; Child; Child = Next(Child)) { - NODE_ Node = *Iter; - if (!AddDirInfo(Node, Node->Name, Buffer, Length, PBytesTransferred)) - { + if (!AddDirInfo(Child, Child->Name, Buffer, Length, PBytesTransferred)) goto bufferReady; - } } FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); bufferReady: -#ifdef AIRFS_SLOWIO - if (SlowioReturnPending(FileSystem)) - { - try - { - InterlockedIncrement(&Airfs->SlowioThreadsRunning); - std::thread(SlowioReadDirectoryThread, - FileSystem, *PBytesTransferred, - FspFileSystemGetOperationContext()->Request->Hint). - detach(); - return STATUS_PENDING; - } - catch (...) - { - InterlockedDecrement(&Airfs->SlowioThreadsRunning); - } - } - SlowioSnooze(FileSystem); -#endif - return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// -#if defined(AIRFS_DIRINFO_BY_NAME) NTSTATUS ApiGetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, PVOID ParentNode0, PWSTR Name, FSP_FSCTL_DIR_INFO *DirInfo) { - NODE_ Parent = (NODE_) ParentNode0; - NODE_ Node; - auto Iter = Parent->Children->find((NODE_)Name); - if (Iter == Parent->Children->end()) - return STATUS_OBJECT_NAME_NOT_FOUND; - Node = *Iter; + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; - DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(Node->Name) * sizeof(WCHAR)); + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + + NODE_ Parent = (NODE_) ParentNode0; + NODE_ Node = Find(Parent->Children, Name, NodeCmp); + if (!Node) + return STATUS_OBJECT_NAME_NOT_FOUND; + + DirInfo->Size = (UINT16)(sizeof FSP_FSCTL_DIR_INFO + wcslen(Node->Name) * sizeof WCHAR); DirInfo->FileInfo = Node->FileInfo; - memcpy(DirInfo->FileNameBuf, Node->Name, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + memcpy(DirInfo->FileNameBuf, Node->Name, DirInfo->Size - sizeof FSP_FSCTL_DIR_INFO); return STATUS_SUCCESS; } -#endif ////////////////////////////////////////////////////////////////////// -#if defined(AIRFS_REPARSE_POINTS) NTSTATUS ApiResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, @@ -1492,9 +961,7 @@ NTSTATUS ApiGetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, { NODE_ Node = (NODE_) Node0; -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) Node = Node->Parent; -#endif if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) return STATUS_NOT_A_REPARSE_POINT; @@ -1503,7 +970,7 @@ NTSTATUS ApiGetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, return STATUS_BUFFER_TOO_SMALL; *PSize = Node->ReparseDataSize; - memcpy(Buffer, Node->ReparseData, Node->ReparseDataSize); + memcpy(Buffer, Node->ReparseData.Address(), Node->ReparseDataSize); return STATUS_SUCCESS; } @@ -1513,27 +980,25 @@ NTSTATUS ApiGetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, NTSTATUS ApiSetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, PVOID Buffer, SIZE_T Size) { + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; - PVOID ReparseData; NTSTATUS Result; -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) Node = Node->Parent; -#endif - if (NodeHasChildren(Node)) + if (Node->Children) return STATUS_DIRECTORY_NOT_EMPTY; if (Node->ReparseData) { Result = FspFileSystemCanReplaceReparsePoint( - Node->ReparseData, Node->ReparseDataSize, + Node->ReparseData.Address(), Node->ReparseDataSize, Buffer, Size); if (!NT_SUCCESS(Result)) return Result; } - ReparseData = realloc(Node->ReparseData, Size); + NODE_ ReparseData = (NODE_) StorageReallocate(Airfs, Node->ReparseData, Size); if (!ReparseData && Size) return STATUS_INSUFFICIENT_RESOURCES; @@ -1542,7 +1007,7 @@ NTSTATUS ApiSetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, // The first field in a reparse buffer is the reparse tag. Node->ReparseDataSize = Size; Node->ReparseData = ReparseData; - memcpy(Node->ReparseData, Buffer, Size); + memcpy(Node->ReparseData.Address(), Buffer, Size); return STATUS_SUCCESS; } @@ -1552,17 +1017,16 @@ NTSTATUS ApiSetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, NTSTATUS ApiDeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, PVOID Buffer, SIZE_T Size) { + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; NTSTATUS Result; -#if defined(AIRFS_NAMED_STREAMS) if (Node->IsAStream) Node = Node->Parent; -#endif if (Node->ReparseData) { Result = FspFileSystemCanReplaceReparsePoint( - Node->ReparseData, Node->ReparseDataSize, + Node->ReparseData.Address(), Node->ReparseDataSize, Buffer, Size); if (!NT_SUCCESS(Result)) return Result; @@ -1570,7 +1034,7 @@ NTSTATUS ApiDeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, else return STATUS_NOT_A_REPARSE_POINT; - free(Node->ReparseData); + StorageFree(Airfs, Node->ReparseData); Node->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; Node->FileInfo.ReparseTag = 0; @@ -1580,9 +1044,7 @@ NTSTATUS ApiDeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, return STATUS_SUCCESS; } -#endif ////////////////////////////////////////////////////////////////////// -#if defined(AIRFS_NAMED_STREAMS) NTSTATUS ApiGetStreamInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) @@ -1595,15 +1057,12 @@ NTSTATUS ApiGetStreamInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, if (!AddStreamInfo(Node, L"", Buffer, Length, PBytesTransferred)) return STATUS_SUCCESS; - if (Node->Streams) + for (NODE_ Stream = First(Node->Streams); Stream; Stream = Next(Stream)) { - // TODO: how to handle out-of-response-buffer-space condition? - for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ++Iter) - { - BOOLEAN added = AddStreamInfo(*Iter, (*Iter)->Name, Buffer, Length, PBytesTransferred); - if (!added) goto done; - } + BOOLEAN added = AddStreamInfo(Stream, Stream->Name, Buffer, Length, PBytesTransferred); + if (!added) goto done; } + FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred); done: @@ -1611,9 +1070,7 @@ NTSTATUS ApiGetStreamInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, return STATUS_SUCCESS; } -#endif ////////////////////////////////////////////////////////////////////// -#if defined(AIRFS_CONTROL) NTSTATUS ApiControl(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, UINT32 ControlCode, @@ -1639,7 +1096,6 @@ NTSTATUS ApiControl(FSP_FILE_SYSTEM *FileSystem, return STATUS_INVALID_DEVICE_REQUEST; } -#endif ////////////////////////////////////////////////////////////////////// FSP_FILE_SYSTEM_INTERFACE AirfsInterface = @@ -1663,32 +1119,13 @@ FSP_FILE_SYSTEM_INTERFACE AirfsInterface = ApiGetSecurity, ApiSetSecurity, ApiReadDirectory, -#if defined(AIRFS_REPARSE_POINTS) ApiResolveReparsePoints, ApiGetReparsePoint, ApiSetReparsePoint, ApiDeleteReparsePoint, -#else - 0, - 0, - 0, - 0, -#endif -#if defined(AIRFS_NAMED_STREAMS) ApiGetStreamInfo, -#else - 0, -#endif -#if defined(AIRFS_DIRINFO_BY_NAME) ApiGetDirInfoByName, -#else - 0, -#endif -#if defined(AIRFS_CONTROL) ApiControl, -#else - 0, -#endif }; ////////////////////////////////////////////////////////////////////// @@ -1696,192 +1133,143 @@ FSP_FILE_SYSTEM_INTERFACE AirfsInterface = void AirfsDelete(AIRFS_ Airfs) { FspFileSystemDelete(Airfs->FileSystem); - - DeleteAllNodes(Airfs); - - free(Airfs); -} - -////////////////////////////////////////////////////////////////////// - -NTSTATUS AirfsStart(AIRFS_ Airfs) -{ -#ifdef AIRFS_SLOWIO - Airfs->SlowioThreadsRunning = 0; -#endif - - return FspFileSystemStartDispatcher(Airfs->FileSystem, 0); -} - -////////////////////////////////////////////////////////////////////// - -void AirfsStop(AIRFS_ Airfs) -{ - FspFileSystemStopDispatcher(Airfs->FileSystem); - -#ifdef AIRFS_SLOWIO - while (Airfs->SlowioThreadsRunning) - Sleep(1); -#endif + StorageShutdown(Airfs); } ////////////////////////////////////////////////////////////////////// NTSTATUS AirfsCreate( + PWSTR VolumeName, + PWSTR MapName, ULONG Flags, ULONG FileInfoTimeout, - ULONG MaxNodes, - ULONG MaxFileSize, - ULONG SlowioMaxDelay, - ULONG SlowioPercentDelay, - ULONG SlowioRarefyDelay, + UINT64 VolumeSize, PWSTR FileSystemName, PWSTR VolumePrefix, PWSTR RootSddl, AIRFS_ *PAirfs) { NTSTATUS Result; - FSP_FSCTL_VOLUME_PARAMS VolumeParams; BOOLEAN CaseInsensitive = !!(Flags & AirfsCaseInsensitive); BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & AirfsFlushAndPurgeOnCleanup); PWSTR DevicePath = AirfsNet == (Flags & AirfsDeviceMask) ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME; AIRFS_ Airfs; - NODE_ RootNode; - PSECURITY_DESCRIPTOR RootSecurity; + PSECURITY_DESCRIPTOR RootSecurity = 0; ULONG RootSecuritySize; *PAirfs = 0; - if (!AirfsHeapInitialize()) - return STATUS_INSUFFICIENT_RESOURCES; + boolean VolumeExists = *VolumeName && (_waccess(VolumeName, 0) != -1); - if (!RootSddl) - RootSddl = L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; - if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(RootSddl, SDDL_REVISION_1, - &RootSecurity, &RootSecuritySize)) - return FspNtStatusFromWin32(GetLastError()); + Result = StorageStartup(Airfs, MapName, VolumeName, VolumeSize); + if (Result) return Result; - Airfs = (AIRFS_) malloc(sizeof *Airfs); - if (!Airfs) + boolean ShouldFormat = !VolumeExists || memcmp(Airfs->VolumeLabel, L"AIRFS", 10); + + if (ShouldFormat) { + memcpy(Airfs->Signature,"Airfs\0\0\0" "\1\0\0\0" "\0\0\0\0", 16); + + if (!RootSddl) + RootSddl = L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(RootSddl, SDDL_REVISION_1, + &RootSecurity, &RootSecuritySize)) + return GetLastErrorAsStatus(); + + Airfs->VolumeName[0] = 0; + Airfs->VolumeSize = ROUND_DOWN(VolumeSize, ALLOCATION_UNIT); + + Airfs->CaseInsensitive = CaseInsensitive; + + FSP_FSCTL_VOLUME_PARAMS V; + memset(&V, 0, sizeof V); + V.Version = sizeof FSP_FSCTL_VOLUME_PARAMS; + V.SectorSize = SECTOR_SIZE; + V.SectorsPerAllocationUnit = SECTORS_PER_ALLOCATION_UNIT; + V.VolumeCreationTime = SystemTime(); + V.VolumeSerialNumber = (UINT32)(SystemTime() / (10000 * 1000)); + V.FileInfoTimeout = FileInfoTimeout; + V.CaseSensitiveSearch = !CaseInsensitive; + V.CasePreservedNames = 1; + V.UnicodeOnDisk = 1; + V.PersistentAcls = 1; + V.ReparsePoints = 1; + V.ReparsePointsAccessCheck = 0; + V.NamedStreams = 1; + V.PostCleanupWhenModifiedOnly = 1; + V.PassQueryDirectoryFileName = 1; + V.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; + V.DeviceControl = 1; + wcscpy_s(V.Prefix, sizeof V.Prefix / sizeof WCHAR, VolumePrefix); + wcscpy_s(V.FileSystemName, sizeof V.FileSystemName / sizeof WCHAR, + FileSystemName ? FileSystemName : L"-AIRFS"); + Airfs->VolumeParams = V; + + Airfs->VolumeLabelLength = sizeof L"AIRFS" - sizeof WCHAR; + memcpy(Airfs->VolumeLabel, L"AIRFS", Airfs->VolumeLabelLength); + + // Set up the available storage in chunks. + Airfs->FreeSize = 0; + Airfs->Available = 0; + uint64_t to = 4096; + for (;;) + { + uint64_t fm = to + sizeof int32_t; + to = fm + MAXIMUM_ALLOCSIZE; + if (to > Airfs->VolumeSize - MINIMUM_ALLOCSIZE) to = Airfs->VolumeSize; + int32_t Size = (int32_t)( to - fm ); + char* Addr = (char*)Airfs + fm;// + sizeof int32_t; + NODE_ NewItem = (NODE_) Addr; + ((int32_t*)NewItem)[-1] = Size; + Attach(Airfs->Available, NewItem, SizeCmp, &Size); + Airfs->FreeSize += Size; + if (to == Airfs->VolumeSize) break; + } + + // Create the root directory. + Airfs->Root = 0; + NODE_ RootNode; + Result = CreateNode(Airfs, L"", &RootNode); + if (!NT_SUCCESS(Result)) + { + AirfsDelete(Airfs); + LocalFree(RootSecurity); + return Result; + } + RootNode->P = RootNode->L = RootNode->R = RootNode->E = RootNode->Parent = 0; + RootNode->FileInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + RootNode->SecurityDescriptor = StorageAllocate(Airfs, RootSecuritySize); + if (!RootNode->SecurityDescriptor) + { + DeleteNode(Airfs, RootNode); + AirfsDelete(Airfs); + LocalFree(RootSecurity); + return STATUS_INSUFFICIENT_RESOURCES; + } + RootNode->SecurityDescriptorSize = RootSecuritySize; + memcpy(RootNode->SecurityDescriptor.Address(), RootSecurity, RootSecuritySize); + Airfs->Root = RootNode; + ReferenceNode(RootNode); LocalFree(RootSecurity); - return STATUS_INSUFFICIENT_RESOURCES; } - memset(Airfs, 0, sizeof *Airfs); - Airfs->MaxNodes = MaxNodes; - Airfs->MaxFileSize = IN_ALLOCATION_UNITS(MaxFileSize); - -#ifdef AIRFS_SLOWIO - Airfs->SlowioMaxDelay = SlowioMaxDelay; - Airfs->SlowioPercentDelay = SlowioPercentDelay; - Airfs->SlowioRarefyDelay = SlowioRarefyDelay; -#endif - - Airfs->CaseInsensitive = CaseInsensitive; - - memset(&VolumeParams, 0, sizeof VolumeParams); - VolumeParams.Version = sizeof FSP_FSCTL_VOLUME_PARAMS; - VolumeParams.SectorSize = SECTOR_SIZE; - VolumeParams.SectorsPerAllocationUnit = SECTORS_PER_ALLOCATION_UNIT; - VolumeParams.VolumeCreationTime = GetSystemTime(); - VolumeParams.VolumeSerialNumber = (UINT32)(GetSystemTime() / (10000 * 1000)); - VolumeParams.FileInfoTimeout = FileInfoTimeout; - VolumeParams.CaseSensitiveSearch = !CaseInsensitive; - VolumeParams.CasePreservedNames = 1; - VolumeParams.UnicodeOnDisk = 1; - VolumeParams.PersistentAcls = 1; - VolumeParams.ReparsePoints = 1; - VolumeParams.ReparsePointsAccessCheck = 0; -#if defined(AIRFS_NAMED_STREAMS) - VolumeParams.NamedStreams = 1; -#endif - VolumeParams.PostCleanupWhenModifiedOnly = 1; -#if defined(AIRFS_DIRINFO_BY_NAME) - VolumeParams.PassQueryDirectoryFileName = 1; -#endif - VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; -#if defined(AIRFS_CONTROL) - VolumeParams.DeviceControl = 1; -#endif - if (VolumePrefix) - wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); - wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), - FileSystemName ? FileSystemName : L"-AIRFS"); - - Result = FspFileSystemCreate(DevicePath, &VolumeParams, &AirfsInterface, &Airfs->FileSystem); + Result = FspFileSystemCreate(DevicePath, &Airfs->VolumeParams, &AirfsInterface, &Airfs->FileSystem); if (!NT_SUCCESS(Result)) { - DeleteAllNodes(Airfs); - free(Airfs); LocalFree(RootSecurity); return Result; } Airfs->FileSystem->UserContext = Airfs; - Airfs->VolumeLabelLength = sizeof L"AIRFS" - sizeof(WCHAR); - memcpy(Airfs->VolumeLabel, L"AIRFS", Airfs->VolumeLabelLength); -#if 0 - FspFileSystemSetOperationGuardStrategy(Airfs->FileSystem, - FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE); -#endif - - // Create the root directory. - Result = CreateNode(Airfs, L"", &RootNode); - if (!NT_SUCCESS(Result)) - { - AirfsDelete(Airfs); - LocalFree(RootSecurity); - return Result; - } - Result = CreateNodeSet(Airfs->CaseInsensitive, &RootNode->Children); - if (Result) - { - AirfsDelete(Airfs); - LocalFree(RootSecurity); - return STATUS_INSUFFICIENT_RESOURCES; - } - RootNode->FileInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY; - RootNode->SecurityDescriptor = malloc(RootSecuritySize); - if (!RootNode->SecurityDescriptor) - { - DeleteNode(Airfs, RootNode); - AirfsDelete(Airfs); - LocalFree(RootSecurity); - return STATUS_INSUFFICIENT_RESOURCES; - } - RootNode->SecurityDescriptorSize = RootSecuritySize; - memcpy(RootNode->SecurityDescriptor, RootSecurity, RootSecuritySize); - Airfs->Root = RootNode; - ReferenceNode(RootNode); - - LocalFree(RootSecurity); *PAirfs = Airfs; + return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// -#define PROGNAME "airfs" - -#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE , format, __VA_ARGS__) -#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE , format, __VA_ARGS__) -#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE , format, __VA_ARGS__) - -#define argtos(v) if (arge > ++argp) v = *argp; else goto usage -#define argtol(v) if (arge > ++argp) v = wcstol_default(*argp, v); else goto usage - -static ULONG wcstol_default(wchar_t *w, ULONG deflt) -{ - wchar_t *endp; - ULONG ul = wcstol(w, &endp, 0); - return L'\0' != w[0] && L'\0' == *endp ? ul : deflt; -} - -////////////////////////////////////////////////////////////////////// - NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) { wchar_t **argp, **arge; @@ -1890,14 +1278,12 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) ULONG Flags = AirfsDisk; ULONG OtherFlags = 0; ULONG FileInfoTimeout = INFINITE; - ULONG MaxNodes = 1024; - ULONG MaxFileSize = 16 * 1024 * 1024; - ULONG SlowioMaxDelay = 0; // -M: maximum slow IO delay in milliseconds - ULONG SlowioPercentDelay = 0; // -P: percent of slow IO to make pending - ULONG SlowioRarefyDelay = 0; // -R: adjust the rarity of pending slow IO + PWSTR VolumeName = L""; + PWSTR MapName = L""; + UINT64 VolumeSize = 16LL * 1024 * 1024; PWSTR FileSystemName = 0; PWSTR MountPoint = 0; - PWSTR VolumePrefix = 0; + PWSTR VolumePrefix = L""; PWSTR RootSddl = 0; HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; AIRFS_ Airfs = 0; @@ -1910,23 +1296,20 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) switch (argp[0][1]) { case L'?': goto usage; - case L'd': argtol(DebugFlags); break; - case L'D': argtos(DebugLogFile); break; + case L'd': ARG_TO_4(DebugFlags); break; + case L'D': ARG_TO_S(DebugLogFile); break; case L'f': OtherFlags = AirfsFlushAndPurgeOnCleanup; break; - case L'F': argtos(FileSystemName); break; + case L'F': ARG_TO_S(FileSystemName); break; case L'i': OtherFlags = AirfsCaseInsensitive; break; - case L'm': argtos(MountPoint); break; - case L'M': argtol(SlowioMaxDelay); break; - case L'n': argtol(MaxNodes); break; - case L'P': argtol(SlowioPercentDelay); break; - case L'R': argtol(SlowioRarefyDelay); break; - case L'S': argtos(RootSddl); break; - case L's': argtol(MaxFileSize); break; - case L't': argtol(FileInfoTimeout); break; + case L'm': ARG_TO_S(MountPoint); break; + case L'N': ARG_TO_S(VolumeName); break; + case L'n': ARG_TO_S(MapName); break; + case L'S': ARG_TO_S(RootSddl); break; + case L's': ARG_TO_8(VolumeSize); break; + case L't': ARG_TO_4(FileInfoTimeout); break; case L'u': - argtos(VolumePrefix); - if (VolumePrefix && L'\0' != VolumePrefix[0]) - Flags = AirfsNet; + ARG_TO_S(VolumePrefix); + if (*VolumePrefix) Flags = AirfsNet; break; default: goto usage; @@ -1954,7 +1337,7 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) 0); if (INVALID_HANDLE_VALUE == DebugLogHandle) { - fail(L"cannot open debug log file"); + FAIL(L"cannot open debug log file"); goto usage; } @@ -1962,20 +1345,19 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) } Result = AirfsCreate( + VolumeName, + MapName, Flags | OtherFlags, FileInfoTimeout, - MaxNodes, - MaxFileSize, - SlowioMaxDelay, - SlowioPercentDelay, - SlowioRarefyDelay, + VolumeSize, FileSystemName, VolumePrefix, RootSddl, &Airfs); + if (!NT_SUCCESS(Result)) { - fail(L"cannot create AIRFS"); + FAIL(L"cannot create AIRFS"); goto exit; } @@ -1987,57 +1369,60 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) L'*' == MountPoint[0] && L'\0' == MountPoint[1] ? 0 : MountPoint); if (!NT_SUCCESS(Result)) { - fail(L"cannot mount AIRFS"); + FAIL(L"cannot mount AIRFS"); goto exit; } } - Result = AirfsStart(Airfs); + Result = FspFileSystemStartDispatcher(Airfs->FileSystem, 0); if (!NT_SUCCESS(Result)) { - fail(L"cannot start AIRFS"); + FAIL(L"cannot start AIRFS"); goto exit; } MountPoint = FspFileSystemMountPoint(Airfs->FileSystem); - info(L"%s -t %ld -n %ld -s %ld%s%s%s%s%s%s", - L"" PROGNAME, FileInfoTimeout, MaxNodes, MaxFileSize, - RootSddl ? L" -S " : L"", RootSddl ? RootSddl : L"", - VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"", - VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"", - MountPoint ? L" -m " : L"", MountPoint ? MountPoint : L""); + WCHAR buffer[1024]; + _snwprintf_s(buffer, 1024, L"%S%S%s%S%s -t %ld -s %lld%S%s%S%s%S%s", + PROGNAME, + *VolumeName ? " -N " : "", *VolumeName ? VolumeName : L"", + *MapName ? " -n " : "", *MapName ? MapName : L"", + FileInfoTimeout, VolumeSize, + RootSddl ? " -S " : "", RootSddl ? RootSddl : L"", + *VolumePrefix ? " -u " : "", *VolumePrefix ? VolumePrefix : L"", + MountPoint ? " -m " : "", MountPoint ? MountPoint : L""); + INFO(buffer); Service->UserContext = Airfs; Result = STATUS_SUCCESS; exit: + if (!NT_SUCCESS(Result) && Airfs) AirfsDelete(Airfs); - return Result; usage: + static wchar_t usage[] = L"" "usage: %s OPTIONS\n" "\n" "options:\n" " -d DebugFlags [-1: enable all debug logs]\n" " -D DebugLogFile [file path; use - for stderr]\n" - " -i [case insensitive file system]\n" " -f [flush and purge cache on cleanup]\n" - " -t FileInfoTimeout [millis]\n" - " -n MaxNodes\n" - " -s MaxFileSize [bytes]\n" - " -M MaxDelay [maximum slow IO delay in millis]\n" - " -P PercentDelay [percent of slow IO to make pending]\n" - " -R RarefyDelay [adjust the rarity of pending slow IO]\n" " -F FileSystemName\n" + " -i [case insensitive file system]\n" + " -m MountPoint [X:|* (required if no UNC prefix)]\n" + " -n MapName [(ex) \"Local\\Airfs\"]\n" + " -N VolumeName [\"\": in memory only]\n" + " -s VolumeSize [bytes]\n" " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" - " -u \\Server\\Share [UNC prefix (single backslash)]\n" - " -m MountPoint [X:|* (required if no UNC prefix)]\n"; + " -t FileInfoTimeout [millis]\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n"; - fail(usage, L"" PROGNAME); + FAIL(usage, L"" PROGNAME); return STATUS_UNSUCCESSFUL; } @@ -2047,10 +1432,8 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) NTSTATUS SvcStop(FSP_SERVICE *Service) { AIRFS_ Airfs = (AIRFS_) Service->UserContext; - - AirfsStop(Airfs); + FspFileSystemStopDispatcher(Airfs->FileSystem); AirfsDelete(Airfs); - return STATUS_SUCCESS; } @@ -2065,3 +1448,4 @@ int wmain(int argc, wchar_t **argv) } ////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// From 16b1b2b34958594b5708f5c2c6bbaa246f57c7c3 Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Sat, 3 Aug 2019 12:19:56 -0700 Subject: [PATCH 2/7] Create persistence.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create persistence.cpp to supply functionality needed for volume persistence within a memory-mapped file: memory management sorted sets offsets that don’t use a pointer --- tst/airfs/persistence.cpp | 597 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 597 insertions(+) create mode 100644 tst/airfs/persistence.cpp diff --git a/tst/airfs/persistence.cpp b/tst/airfs/persistence.cpp new file mode 100644 index 00000000..85cd6d77 --- /dev/null +++ b/tst/airfs/persistence.cpp @@ -0,0 +1,597 @@ +/** + * @file persistence.cpp + * + * @copyright 2015-2019 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ +/* + * Airfs is based on Memfs with changes contributed by John Oberschelp. + * The contributed changes are under joint copyright by Bill Zissimopoulos + * and John Oberschelp per the Contributor Agreement found at the + * root of this project. + */ +/* + * Airfs uses a memory-mapped file per volume to achieve persistence. + * The primary advantage of this is that volume loads and saves are automatic. + * The two primary disadvantages, and our workarounds are: + * 1. We can't use standard containers or memory management, + * so the below Rubbertree and Storage functions are used instead. + * 2. Each process will map the volume to an arbitrary address, + * so Where offsets are used in place of pointers. + */ + +#include "common.h" + +SpinLock StorageLock, AirprintLock, SetLock; + +int SizeCmp ( void* key, NODE_ x) +{ + return *(int32_t*)key - ((int32_t*)x)[-1]; +} + +int BlockCmp ( void* key, NODE_ x) +{ + int64_t left = *(int64_t*)key; + int64_t right = x->FileOffset; + return left == right ? 0 : (left < right ? -1 : 1); +} + +int CaselessNameCmp ( void* key, NODE_ x) +{ + WCHAR* c1 = (WCHAR*) key; + WCHAR* c2 = x->Name; + return _wcsicmp(c1,c2); +} + +int ExactNameCmp ( void* key, NODE_ x) +{ + WCHAR* c1 = (WCHAR*) key; + WCHAR* c2 = x->Name; + return wcscmp(c1,c2); +} + +////////////////////////////////////////////////////////////////////// + +void Airprint (const char * format, ...) +{ + AirprintLock.Acquire(); + va_list args; + va_start(args, format); + char szBuffer[512]; + sprintf_s(szBuffer, 511, "Airfs %5.5f ----", SystemTime() / 10'000'000.0); + vsnprintf(szBuffer+25, 511-25, format, args); + OutputDebugStringA(szBuffer); + va_end(args); + AirprintLock.Release(); +} + +////////////////////////////////////////////////////////////////////// +// +// Rubbertree (because it is flexible!) +// Implements a sorted set of elements, using a binary tree. +// Has a function, Near, that finds nodes at or adjacent to a key. +// Has an equal branch for trees that require handling equivalent +// nodes, like Airfs' memory heap manager. +// Attach, Find, and Near use splay to improve random access times. +// First, Last, Next, and Prev do not, to improve sequential access times. +// Replacing Where with NODE_ would make it a pointer-based tree. + +//-------------------------------------------------------------------- + +inline void rotateL(Where &root, NODE_ x) +{ + NODE_ y = x->R, p = x->P; + if (x->R = y->L) y->L->P = x; + if (!(y->P = p)) + root = y; + else + { + if (x == p->L) p->L = y; + else p->R = y; + } + (y->L = x)->P = y; +} + +//-------------------------------------------------------------------- + +inline void rotateR(Where &root, NODE_ y) +{ + NODE_ x = y->L, p = y->P; + if (y->L = x->R) x->R->P = y; + if (!(x->P = p)) + root = x; + else + { + if (y == p->L) p->L = x; + else p->R = x; + } + (x->R = y)->P = x; +} + +//-------------------------------------------------------------------- + +static void splay(Where &root, NODE_ x) +{ + while (NODE_ p = x->P) + { + if (!p->P) + { + if (p->L == x) rotateR(root, p); + else rotateL(root, p); + } + else + { + if (p == p->P->R) + { + if (p->R == x) rotateL(root, p->P); + else rotateR(root, p); + rotateL(root, x->P); + } + else + { + if (p->L == x) rotateR(root, p->P); + else rotateL(root, p); + rotateR(root, x->P); + } + } + } +} + +//-------------------------------------------------------------------- + +inline int seek(Where &root, NODE_ &x, void* key, CompareFunction CMP) +{ + x = root; + for (;;) + { + int diff = CMP(key, x); + if (diff < 0) + { + if (!x->L) return -1; + x = x->L; + } + else if (diff > 0) + { + if (!x->R) return 1; + x = x->R; + } + else return 0; + } +} + +//-------------------------------------------------------------------- + +inline NODE_ next(NODE_ x) +{ + if (x->R) { x = x->R; while (x->L) x = x->L; return x; } + NODE_ p = x->P; + while (p && x == p->R) { x = p; p = p->P; } + return p; +} + +//-------------------------------------------------------------------- + +inline NODE_ prev(NODE_ x) +{ + if (x->L) { x = x->L; while (x->R) x = x->R; return x; } + NODE_ p = x->P; + while (p && x == p->L) { x = p; p = p->P; } + return p; +} + +//-------------------------------------------------------------------- + +NODE_ First(NODE_ x) +{ + SetLock.Acquire(); + if (x) while (x->L) x = x->L; + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Last(NODE_ x) +{ + SetLock.Acquire(); + if (x) while (x->R) x = x->R; + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Next(NODE_ x) +{ + SetLock.Acquire(); + x = next(x); + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Prev(NODE_ x) +{ + SetLock.Acquire(); + x = prev(x); + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Near(Where &root, void* key, CompareFunction CMP, Neighbor want) +{ + // Return a node relative to (just <, <=, ==, >=, or >) a key. + if (!root) return 0; + SetLock.Acquire(); + NODE_ x; + int dir = seek(root, x, key, CMP); + if ((dir == 0 && want == GT) || (dir > 0 && want >= GE)) x = next(x); + else + if ((dir == 0 && want == LT) || (dir < 0 && want <= LE)) x = prev(x); + else + if (dir != 0 && want == EQ) x = 0; + if (x) splay(root, x); + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Find(Where &root, void* key, CompareFunction CMP) +{ + if (!root) return 0; + SetLock.Acquire(); + NODE_ x; + int direction = seek(root, x, key, CMP); + splay(root, x); + SetLock.Release(); + return direction?0:x; +} + +//-------------------------------------------------------------------- + +void Attach(Where &root, NODE_ x, CompareFunction CMP, void* key) +{ + SetLock.Acquire(); + if (!root) + { + root = x; + x->P = x->L = x->R = x->E = 0; + SetLock.Release(); + return; + } + NODE_ f; + int diff = seek(root, f, key, CMP); + if (!diff) + { + if (x->L = f->L) x->L->P = x; + if (x->R = f->R) x->R->P = x; + NODE_ p = f->P; + if (x->P = p) { if (p->L == f) p->L = x; else p->R = x; } + else root = x; + (x->E = f)->P = x; + f->L = f->R = 0; + splay(root, x); + SetLock.Release(); + return; + } + if (diff < 0) f->L = x; else f->R = x; + x->P = f; + x->L = x->R = x->E = 0; + splay(root, x); + SetLock.Release(); +} + +//-------------------------------------------------------------------- + +void Detach(Where &root, NODE_ x) +{ + SetLock.Acquire(); + NODE_ e = x->E, p = x->P; + if (p && p->E == x) { if (p->E = e) e->P = p; } + else if (e) + { + if (e->L = x->L) e->L->P = e; + if (e->R = x->R) e->R->P = e; + if (e->P = p) { if (p->L == x) p->L = e; else p->R = e; } + else root = e; + } + else if (!x->L) + { + if (p) { if ( p->L == x) p->L = x->R; else p->R = x->R; } + else root = x->R; + if (x->R) x->R->P = p; + } + else if (!x->R) + { + if (p) { if ( p->L == x) p->L = x->L; else p->R = x->L; } + else root = x->L; + if (x->L) x->L->P = p; + } + else + { + e = x->L; + if (e->R) + { + do { e = e->R; } while (e->R); + if (e->P->R = e->L) e->L->P = e->P; + (e->L = x->L)->P = e; + } + (e->R = x->R)->P = e; + if (e->P = x->P) { if (e->P->L == x) e->P->L = e; else e->P->R = e; } + else root = e; + } + SetLock.Release(); +} + +////////////////////////////////////////////////////////////////////// +// +// Storage Functions for our memory-mapped file-based persistent volumes + +//-------------------------------------------------------------------- + +void* StorageAllocate(AIRFS_ Airfs, int64_t RequestedSize) +{ + if (!RequestedSize) return 0; + if (RequestedSize + sizeof int32_t > MAXIMUM_ALLOCSIZE) return 0; + + StorageLock.Acquire(); + int32_t RoundedSize = (int32_t) ROUND_UP(RequestedSize, MINIMUM_ALLOCSIZE - sizeof int32_t); + int32_t SplitableSize = RoundedSize + MINIMUM_ALLOCSIZE; + + // See if we have a freed node of the size we requested. + NODE_ NewItem = Near(Airfs->Available, &RoundedSize, SizeCmp, GE); + if (NewItem) + { + int32_t FoundSize = ((int32_t*)NewItem)[-1]; + if (FoundSize < SplitableSize) + { + Detach(Airfs->Available, NewItem); + Airfs->FreeSize -= FoundSize; + StorageLock.Release(); + return NewItem; + } + } + + // If not, see if we can downsize a larger freed element. + NewItem = Near(Airfs->Available, &SplitableSize, SizeCmp, GE); + if (NewItem) + { + int32_t FoundSize = ((int32_t*)NewItem)[-1]; + Detach(Airfs->Available, NewItem); + Airfs->FreeSize -= FoundSize; + char* Addr = (char*)NewItem + RoundedSize + sizeof int32_t; + NODE_ Remainder = (NODE_) Addr; + int32_t RemainderSize = FoundSize - (RoundedSize + sizeof int32_t); + ((int32_t*)Remainder)[-1] = RemainderSize; + Attach(Airfs->Available, Remainder, SizeCmp, &RemainderSize); + Airfs->FreeSize += RemainderSize; + ((int32_t*)NewItem)[-1] = RoundedSize; + StorageLock.Release(); + return NewItem; + } + + // If not, give up. + StorageLock.Release(); + return 0; +} + +//-------------------------------------------------------------------- + +void* StorageReallocate(AIRFS_ Airfs, void* OldAlloc, int64_t RequestedSize) +{ + if (!OldAlloc) + { + return StorageAllocate(Airfs, RequestedSize); + } + + if (!RequestedSize) + { + StorageFree(Airfs, OldAlloc); + return 0; + } + + int32_t OldSize = ((int32_t*)OldAlloc)[-1]; + void* NewAlloc = StorageAllocate(Airfs, RequestedSize); + if (!NewAlloc) return 0; + memcpy(NewAlloc, OldAlloc, min(RequestedSize, OldSize)); + StorageFree(Airfs, OldAlloc); + return NewAlloc; +} + +//-------------------------------------------------------------------- + +void StorageFree(AIRFS_ Airfs, void* r) +{ + if (!r) return; + StorageLock.Acquire(); + NODE_ release = (NODE_) r; + int32_t Size = ((int32_t*)r)[-1]; + Attach(Airfs->Available, release, SizeCmp, &Size); + Airfs->FreeSize += Size; + StorageLock.Release(); +} + +//-------------------------------------------------------------------- + +void StorageAccessFile(StorageFileAccessType Type, NODE_ Node, int64_t AccessOffset, int64_t NumBytes, char* MemoryAddress) +{ + StorageLock.Acquire(); + + NODE_ Block = Near(Node->FileBlocks, &AccessOffset, BlockCmp, LE); + for (;;) + { + int32_t BlockSize = ((int32_t*)Block)[-1]; + int64_t BlockOffset = Block->FileOffset; + int64_t BlockIndex = AccessOffset - BlockOffset + FILEBLOCK_OVERHEAD; + int64_t BlockNum = min(BlockSize-BlockIndex, NumBytes); + + switch (Type) + { + case ZERO : { memset((char*)Block + BlockIndex, 0, BlockNum); break; } + case READ : { memcpy(MemoryAddress, (char*)Block + BlockIndex, BlockNum); break; } + case WRITE : { memcpy((char*)Block + BlockIndex, MemoryAddress, BlockNum); break; } + } + NumBytes -= BlockNum; + if (!NumBytes) break; + MemoryAddress += BlockNum; + AccessOffset += BlockNum; + Block = Next(Block); + } + + StorageLock.Release(); +} + +//-------------------------------------------------------------------- + +NTSTATUS StorageSetFileCapacity(AIRFS_ Airfs, NODE_ Node, int64_t minimumRequiredCapacity) +{ + StorageLock.Acquire(); + + int64_t TargetCapacity = ROUND_UP(minimumRequiredCapacity, ALLOCATION_UNIT); + NODE_ Block = Last(Node->FileBlocks); + int32_t BlockSize = Block ? ((int32_t*)Block)[-1] : 0; + int64_t CurrentCapacity = Block ? Block->FileOffset + BlockSize - FILEBLOCK_OVERHEAD: 0; + int64_t Add = TargetCapacity - CurrentCapacity; + + while (Add > 0) + { + // Add a block if we can, preferably as large or larger than we need. + Add += FILEBLOCK_OVERHEAD; + Block = Near(Airfs->Available, &Add, SizeCmp, GE); + if (!Block) Block = Near(Airfs->Available, &Add, SizeCmp, LT); + Add -= FILEBLOCK_OVERHEAD; + if (Block) + { + Detach(Airfs->Available, Block); + BlockSize = ((int32_t*)Block)[-1]; + Airfs->FreeSize -= BlockSize; + Block->FileOffset = CurrentCapacity; + Attach(Node->FileBlocks, Block, BlockCmp, &CurrentCapacity); + CurrentCapacity += BlockSize - FILEBLOCK_OVERHEAD; + Add -= BlockSize - FILEBLOCK_OVERHEAD; + continue; + } + + StorageLock.Release(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Throw away any trailing blocks that are no longer needed. + while (Add < 0) + { + Block = Last(Node->FileBlocks); + BlockSize = ((int32_t*)Block)[-1]; + if (BlockSize - FILEBLOCK_OVERHEAD > -Add) break; + Add += BlockSize - FILEBLOCK_OVERHEAD; + Detach(Node->FileBlocks, Block); + Attach(Airfs->Available, Block, SizeCmp, &BlockSize); + Airfs->FreeSize += BlockSize; + } + + // Possibly downsize the last block. + if (Add < 0) + { + Block = Last(Node->FileBlocks); + int32_t OldBlockSize = ((int32_t*)Block)[-1]; + int32_t NewBlockSize = OldBlockSize - (int32_t) ROUND_DOWN(-Add, MINIMUM_ALLOCSIZE); + if (NewBlockSize < MINIMUM_ALLOCSIZE) NewBlockSize = MINIMUM_ALLOCSIZE; + int32_t RemainderBlockSize = OldBlockSize - NewBlockSize - sizeof int32_t; + if (RemainderBlockSize >= MINIMUM_ALLOCSIZE) // i.e. if not too near the end + { + char* Addr = (char*)Block + NewBlockSize + sizeof int32_t; + NODE_ Remainder = (NODE_) Addr; + ((int32_t*)Remainder)[-1] = RemainderBlockSize; + Attach(Airfs->Available, Remainder, SizeCmp, &RemainderBlockSize); + Airfs->FreeSize += RemainderBlockSize; + ((int32_t*)Block)[-1] = NewBlockSize; + } + } + + StorageLock.Release(); + return 0; +} + +//-------------------------------------------------------------------- + +NTSTATUS StorageStartup(AIRFS_ &Airfs, WCHAR* MapName, WCHAR* VolumeName, int64_t VolumeLength) +{ + HANDLE MapFileHandle = INVALID_HANDLE_VALUE; + Airfs = 0; + + // Open. + if (*VolumeName) + { + MapFileHandle = CreateFileW(VolumeName, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, OPEN_ALWAYS, NULL, NULL); + if (MapFileHandle == INVALID_HANDLE_VALUE) return GetLastErrorAsStatus(); + } + + // Map. + HANDLE MapHandle = CreateFileMappingW(MapFileHandle, NULL, PAGE_EXECUTE_READWRITE, VolumeLength>>32, VolumeLength & 0xFFFFFFFF, MapName); + if (!MapHandle) return GetLastErrorAsStatus(); + + // Point. + char* MappedAddress = (char*) MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, VolumeLength); + if (!MappedAddress) return GetLastErrorAsStatus(); + + // Keep. + Airfs = (AIRFS_) MappedAddress; + Airfs->MapFileHandle = MapFileHandle; + Airfs->MapHandle = MapHandle; + wcscpy_s(Airfs->VolumeName, 256, VolumeName); // Use "" for a memory-only page file. + wcscpy_s(Airfs->MapName, 256, MapName); + Airfs->VolumeLength = VolumeLength; + + return 0; +} + +//-------------------------------------------------------------------- + +NTSTATUS StorageShutdown(AIRFS_ Airfs) +{ + BOOL Ok; + NTSTATUS Result = 0; + HANDLE M = Airfs->MapHandle; + HANDLE F = Airfs->MapFileHandle; + + if (Airfs) + { + Ok = FlushViewOfFile(Airfs, 0); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + if (F != INVALID_HANDLE_VALUE) + { + Ok = FlushFileBuffers(F); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + } + Ok = UnmapViewOfFile(Airfs); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + } + + if (M) + { + Ok = CloseHandle(M); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + } + if (F != INVALID_HANDLE_VALUE) + { + Ok = CloseHandle(F); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + } + + return Result; +} + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// From 6a7b6c77c6c636d2028dd0fb0ec14d50b0cbe027 Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Sat, 3 Aug 2019 12:22:57 -0700 Subject: [PATCH 3/7] Create common.h --- tst/airfs/common.h | 212 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 tst/airfs/common.h diff --git a/tst/airfs/common.h b/tst/airfs/common.h new file mode 100644 index 00000000..57adff35 --- /dev/null +++ b/tst/airfs/common.h @@ -0,0 +1,212 @@ +/** + * @file common.h + * + * @copyright 2015-2019 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ +/* + * Airfs is based on Memfs with changes contributed by John Oberschelp. + * The contributed changes are under joint copyright by Bill Zissimopoulos + * and John Oberschelp per the Contributor Agreement found at the + * root of this project. + */ + +#include +#include +#include +#include +#include + +#define PROGNAME "airfs" +#define ROUND_UP( bytes, units ) (((bytes) + (units) - 1) / (units) * (units)) +#define ROUND_DOWN( bytes, units ) (((bytes) ) / (units) * (units)) +#define MINIMUM_ALLOCSIZE 196 +#define MAXIMUM_ALLOCSIZE ROUND_DOWN(10*1024*1024, MINIMUM_ALLOCSIZE) +#define SECTOR_SIZE 512 +#define SECTORS_PER_ALLOCATION_UNIT 1 +#define ALLOCATION_UNIT ( SECTOR_SIZE * SECTORS_PER_ALLOCATION_UNIT ) +#define INFO(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE , format, __VA_ARGS__) +#define WARN(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE , format, __VA_ARGS__) +#define FAIL(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE , format, __VA_ARGS__) +#define AIRFS_MAX_PATH 512 +#define FILEBLOCK_OVERHEAD 40 // size of ( P + E + L + R + FileOffset ) = 8 * 5 = 40 +#define ARG_TO_S(v) if (arge > ++argp) v = *argp; else goto usage +#define ARG_TO_4(v) if (arge > ++argp) v = (int32_t) wcstoll_default(*argp, v); else goto usage +#define ARG_TO_8(v) if (arge > ++argp) v = wcstoll_default(*argp, v); else goto usage + +enum StorageFileAccessType {ZERO=0,READ,WRITE}; +enum Neighbor {LT=-2,LE=-1,EQ=0,GE=1,GT=2}; + +struct NODE; +typedef NODE* NODE_; + +typedef int CompareFunction (void* key, NODE_); + +inline NTSTATUS GetLastErrorAsStatus() +{ + return FspNtStatusFromWin32(GetLastError()); +} + +inline UINT64 SystemTime() +{ + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + return ((PLARGE_INTEGER)&FileTime)->QuadPart; +} + +static int64_t wcstoll_default(wchar_t *w, int64_t deflt) +{ + wchar_t *endp; + int64_t i = wcstoll(w, &endp, 0); + return L'\0' != w[0] && L'\0' == *endp ? i : deflt; +} + +////////////////////////////////////////////////////////////////////// +// +// Where Class: This class manages an offset within our memory-mapped +// volume to another location within our memory-mapped volume. Because it is +// a self-relative offset, this delta is constant regardless of where in +// memory the file system is mapped, so we can always reoptain its address. +// A delta of 0 is the special case for "null". +// + +template class Where +{ + int64_t delta; + + public: + + Where() = default; + ~Where() = default; + Where(T t) : delta( t ?( (char*)t -(char*)this ):0) {} + Where(Where& w) : delta( w.delta?( ((char*)&w+w.delta)-(char*)this ):0) {} + + operator bool () { return delta != 0; } + operator T () { return (T) ( delta?( (char*)this+delta ):0); } + T operator -> () { return (T) ( delta?( (char*)this+delta ):0); } + operator void* () { return (void*)( delta?( (char*)this+delta ):0); } + + bool operator == (Where& rhs) { return (char*)this+delta == (char*)&rhs+rhs.delta; } + bool operator != (Where& rhs) { return (char*)this+delta != (char*)&rhs+rhs.delta; } + bool operator == (T rhs) { return (char*)this+delta == (char*)rhs; } + bool operator != (T rhs) { return (char*)this+delta != (char*)rhs; } + + Where& operator = (Where& rhs) { delta = rhs.delta?( ((char*)&rhs+rhs.delta) - ((char*)this) ):0; return *this; } + Where& operator = (void* rhs) { delta = rhs ?( (char*)rhs - ((char*)this) ):0; return *this; } + + char* Address () { return (char*)this+delta; } +}; + +////////////////////////////////////////////////////////////////////// +// +// The header for an Airfs volume +// + +typedef struct +{ + char Signature[8]; // Airfs\0\0\0 + char MapFormatVersion[4]; // Major.Minor.Patch.Build + char filler[4]; + Where Root; + Where Available; + UINT64 VolumeSize; + UINT64 FreeSize; + WCHAR VolumeLabel[32]; + UINT16 VolumeLabelLength; + UINT16 filler1,filler2,filler3; + UINT32 CaseInsensitive; + UINT32 filler4; + WCHAR MapName[256]; + WCHAR VolumeName[256]; // Use "" for a memory-only page file. + int64_t VolumeLength; + FSP_FSCTL_VOLUME_PARAMS VolumeParams; + FSP_FILE_SYSTEM *FileSystem; + HANDLE MapFileHandle; + HANDLE MapHandle; +} AIRFS, *AIRFS_; + +////////////////////////////////////////////////////////////////////// +// +// Information per file or directory +// +struct NODE +{ + Where P,L,R,E; // Sorted sibling tree: Parent, Left, Right, and Equal + union + { + Where Name; + int64_t FileOffset; + }; + Where Parent; + Where Children; + FSP_FSCTL_FILE_INFO FileInfo; + uint64_t SecurityDescriptorSize; + Where SecurityDescriptor; + Where FileBlocks; + uint64_t ReparseDataSize; + Where ReparseData; + volatile LONG RefCount; + Where Streams; + BOOLEAN IsAStream; +}; + +////////////////////////////////////////////////////////////////////// + +class SpinLock +{ + LONG C; // Counter + HANDLE S; // Semaphore + + public: + + SpinLock() { C = 0; S = CreateSemaphore(NULL, 0, 1, NULL); } + ~SpinLock() { CloseHandle(S); } + + void Acquire() { if (_InterlockedIncrement(&C) > 1) WaitForSingleObject(S, INFINITE); } + void Release() { if (_InterlockedDecrement(&C) > 0) ReleaseSemaphore(S, 1, NULL); } +}; + +////////////////////////////////////////////////////////////////////// + +void Airprint (const char * format, ...); + +int SizeCmp (void* key, NODE_); +int ExactNameCmp (void* key, NODE_); +int CaselessNameCmp (void* key, NODE_); + +NODE_ Find (Where &root, void* key, CompareFunction); +NODE_ Near (Where &root, void* key, CompareFunction, Neighbor); +void Attach (Where &root, NODE_ attach, CompareFunction, void* key); +void Detach (Where &root, NODE_ detach); +NODE_ First (NODE_ start); +NODE_ Last (NODE_ start); +NODE_ Next (NODE_); +NODE_ Prev (NODE_); + +NTSTATUS StorageStartup (AIRFS_ &, WCHAR* MapName, WCHAR* VolumeName, int64_t Length); +NTSTATUS StorageShutdown (AIRFS_); +void* StorageAllocate (AIRFS_ Airfs, int64_t RequestedSize); +void* StorageReallocate (AIRFS_ Airfs, void* Reallocate, int64_t RequestedSize); +void StorageFree (AIRFS_ Airfs, void* Release); +NTSTATUS StorageSetFileCapacity (AIRFS_, NODE_, int64_t MinimumRequiredCapacity); +void StorageAccessFile (StorageFileAccessType, NODE_, int64_t Offset, int64_t NumBytes, char* Address); + +static_assert(AIRFS_MAX_PATH > MAX_PATH, "AIRFS_MAX_PATH must be greater than MAX_PATH."); +static_assert(sizeof NODE + sizeof int32_t == MINIMUM_ALLOCSIZE, "MINIMUM_ALLOCSIZE should be 196."); + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// From d60b1de430f5095acbed34559faaea7a7cf2c1c0 Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Sat, 3 Aug 2019 17:54:26 -0700 Subject: [PATCH 4/7] Add persistence.cpp and common.h --- tst/airfs/airfs.vcxproj.filters | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tst/airfs/airfs.vcxproj.filters b/tst/airfs/airfs.vcxproj.filters index f67a722d..8ade23f2 100644 --- a/tst/airfs/airfs.vcxproj.filters +++ b/tst/airfs/airfs.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -10,5 +10,13 @@ Source + + Source + - \ No newline at end of file + + + Source + + + From 19c320350fce1985efed3ef30de89e80000c521d Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Sat, 3 Aug 2019 17:55:29 -0700 Subject: [PATCH 5/7] Add persistence.cpp and common.h --- tst/airfs/airfs.vcxproj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tst/airfs/airfs.vcxproj b/tst/airfs/airfs.vcxproj index f21301e1..3cd9bf9e 100644 --- a/tst/airfs/airfs.vcxproj +++ b/tst/airfs/airfs.vcxproj @@ -1,4 +1,4 @@ - + @@ -171,8 +171,12 @@ + + + + - \ No newline at end of file + From af8c74378e6a7aa32728d1df67de54a52ebfca1a Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Wed, 7 Aug 2019 22:53:21 -0700 Subject: [PATCH 6/7] appveyor: hack to make WDK 1903 work on VS2015 --- appveyor.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index ba087bbe..77957461 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,6 +15,13 @@ init: #- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: +- ps: | + # Hack to make WDK 1903 work on VS2015. + # See https://github.com/appveyor-tests/WDK-10.0.14393.0/blob/31cf12217fe0c92b218c70d7027dfe145be4f4cb/appveyor.yml#L7 + [xml]$targets = get-content "C:\Program Files (x86)\Windows Kits\10\build\WindowsDriver.Common.targets" + $usingTask = $targets.ChildNodes[1].UsingTask | ? {$_.TaskName -eq "ValidateNTTargetVersion"} + $usingTask.AssemblyFile = '$(WDKContentRoot)build\bin\Microsoft.DriverKit.Build.Tasks.16.0.dll' + $targets.Save("C:\Program Files (x86)\Windows Kits\10\build\WindowsDriver.Common.targets") - git submodule update --init --recursive - appveyor AddMessage "Change boot configuration and reboot" -Category Information - bcdedit /set testsigning on From 2c64d590018c854f3323e44c61f427767fbe1da0 Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Fri, 9 Aug 2019 15:58:12 -0700 Subject: [PATCH 7/7] Add common.h & persistence.cpp for Airfs to Product.wxs --- build/VStudio/installer/Product.wxs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build/VStudio/installer/Product.wxs b/build/VStudio/installer/Product.wxs index 86c89dd1..b10f28ba 100644 --- a/build/VStudio/installer/Product.wxs +++ b/build/VStudio/installer/Product.wxs @@ -391,6 +391,12 @@ + + + + + + @@ -587,6 +593,8 @@ + + @@ -750,4 +758,4 @@ - \ No newline at end of file +