From 14a2004437d999a11b57eeed465a75d9d6ba2136 Mon Sep 17 00:00:00 2001 From: John Oberschelp Date: Sat, 23 Jun 2018 16:36:31 -0700 Subject: [PATCH 1/3] Create airfs.cpp --- tst/airfs/airfs.cpp | 2031 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2031 insertions(+) create mode 100644 tst/airfs/airfs.cpp diff --git a/tst/airfs/airfs.cpp b/tst/airfs/airfs.cpp new file mode 100644 index 00000000..122c1f1a --- /dev/null +++ b/tst/airfs/airfs.cpp @@ -0,0 +1,2031 @@ +/** + * @file airfs.cpp + * + * @copyright 2015-2018 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 file in + * accordance with the commercial license agreement provided with the + * software. + */ + +#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_SECTOR_SIZE 512 +#define AIRFS_SECTORS_PER_ALLOCATION_UNIT 1 + +enum +{ + AirfsDisk = 0x00000000, + AirfsNet = 0x00000001, + AirfsDeviceMask = 0x0000000f, + AirfsCaseInsensitive = 0x80000000, + AirfsFlushAndPurgeOnCleanup = 0x40000000, +}; + +////////////////////////////////////////////////////////////////////// + +// 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); + if (!Node) + { + *PNode = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + memset(Node, 0, sizeof *Node); + wcscpy_s(Node->Name, sizeof Node->Name / sizeof(WCHAR), Name); + Node->FileInfo.CreationTime = + Node->FileInfo.LastAccessTime = + Node->FileInfo.LastWriteTime = + Node->FileInfo.ChangeTime = GetSystemTime(); + Node->FileInfo.IndexNumber = IndexNumber++; + + *PNode = Node; + Airfs->NumNodes++; + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +void DeleteNode(AIRFS_ Airfs, NODE_ Node) +{ +#if defined(AIRFS_REPARSE_POINTS) + free(Node->ReparseData); +#endif + AirfsHeapFree(Node->FileData); + free(Node->SecurityDescriptor); + + if (Node->Children) + { + assert(Node->Children->empty()); + delete Node->Children; + } + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->Streams) + { + assert(Node->Streams->empty()); + delete Node->Streams; + } +#endif + + free(Node); + Airfs->NumNodes--; +} + +////////////////////////////////////////////////////////////////////// + +inline void ReferenceNode(NODE_ Node) +{ + InterlockedIncrement(&Node->RefCount); +} + +////////////////////////////////////////////////////////////////////// + +inline void DereferenceNode(AIRFS_ Airfs, NODE_ Node) +{ + if (InterlockedDecrement(&Node->RefCount) == 0) + DeleteNode(Airfs, Node); +} + +////////////////////////////////////////////////////////////////////// + +void GetFileInfo(NODE_ Node, FSP_FSCTL_FILE_INFO *FileInfo) +{ +#if defined(AIRFS_NAMED_STREAMS) + if (!Node->IsAStream) + *FileInfo = Node->FileInfo; + else + { + *FileInfo = Node->Parent->FileInfo; + FileInfo->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; + // Named streams cannot be directories. + FileInfo->AllocationSize = Node->FileInfo.AllocationSize; + FileInfo->FileSize = Node->FileInfo.FileSize; + } +#else + *FileInfo = Node->FileInfo; +#endif +} + +////////////////////////////////////////////////////////////////////// + +void DumpNodes(NODE_ Node, int indent=0) +{ + 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); +} + +////////////////////////////////////////////////////////////////////// + +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; + 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) + { + if (BaseName) *BaseName = Name; + if (PParent) *PParent = Airfs->Root; + *PNode = Airfs->Root; + return 0; + } + + WCHAR ParsedName[AIRFS_MAX_PATH]; + 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 == 0) break; + *to = 0; + + // Find this name. + auto Iter = Ancestor->Children->find((NODE_)fm); + if (Iter == Ancestor->Children->end()) + { + 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; + +#if defined(AIRFS_NAMED_STREAMS) + if (Colon) + { + fm = to+1; + break; + } +#endif + + if (!(Ancestor->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if (PParent) *PParent = 0; + *PNode = 0; + return STATUS_NOT_A_DIRECTORY; + } + } + + if (BaseName) + *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. + if (!Ancestor->Streams) + { + *PNode = 0; + return STATUS_OBJECT_NAME_NOT_FOUND; + } + auto Iter = Ancestor->Streams->find((NODE_)fm); + if (Iter == Ancestor->Streams->end()) + { + *PNode = 0; + return STATUS_OBJECT_NAME_NOT_FOUND; + } + *PNode = *Iter; + return 0; + } +#endif + + // Find the directory entry, if it exists. + auto Iter = Ancestor->Children->find((NODE_)fm); + if (Iter == Ancestor->Children->end()) + { + *PNode = 0; + return STATUS_OBJECT_NAME_NOT_FOUND; + } + *PNode = *Iter; + return 0; +} + +////////////////////////////////////////////////////////////////////// +#if defined(AIRFS_NAMED_STREAMS) + +BOOLEAN AddStreamInfo(NODE_ Node, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + UINT8 StreamInfoBuf[sizeof(FSP_FSCTL_STREAM_INFO) + sizeof Node->Name]; + FSP_FSCTL_STREAM_INFO *StreamInfo = (FSP_FSCTL_STREAM_INFO *)StreamInfoBuf; + PWSTR StreamName; + + StreamName = wcschr(Node->Name, L':'); + if (StreamName) + StreamName++; + else + StreamName = L""; + + 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)); + + return FspFileSystemAddStreamInfo(StreamInfo, Buffer, Length, PBytesTransferred); +} + +#endif +////////////////////////////////////////////////////////////////////// + +inline void TouchNode(NODE_ Node) +{ + Node->FileInfo.LastAccessTime = + Node->FileInfo.LastWriteTime = + Node->FileInfo.ChangeTime = GetSystemTime(); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS InsertNode(AIRFS_ Airfs, NODE_ Parent, NODE_ Node, PBOOLEAN PInserted) +{ + 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; + } +} + +////////////////////////////////////////////////////////////////////// + +void RemoveNode(AIRFS_ Airfs, NODE_ Node) +{ + NODE_ Parent = Node->Parent; + TouchNode(Parent); + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->IsAStream) + Parent->Streams->erase(Node); + else +#endif + Parent->Children->erase(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 (Buffer) + { + if (Node->ReparseDataSize > *PSize) + return STATUS_BUFFER_TOO_SMALL; + + *PSize = Node->ReparseDataSize; + memcpy(Buffer, Node->ReparseData, Node->ReparseDataSize); + } + + return STATUS_SUCCESS; +} + +#endif +////////////////////////////////////////////////////////////////////// + +NTSTATUS SetAllocSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node, + UINT64 RequestedAllocSize) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + if (Node->FileInfo.AllocationSize != RequestedAllocSize) + { + 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 = (ActualSize + ActualSize / 2 + 7) ^ 7; + + 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; + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node, + UINT64 RequestedFileSize) +{ + if (Node->FileInfo.FileSize != RequestedFileSize) + { + if (Node->FileInfo.AllocationSize < RequestedFileSize) + { + UINT64 AllocationUnit = AIRFS_SECTOR_SIZE * AIRFS_SECTORS_PER_ALLOCATION_UNIT; + UINT64 AllocationSize = (RequestedFileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + + NTSTATUS Result = SetAllocSize(FileSystem, Node, AllocationSize); + 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)); + Node->FileInfo.FileSize = RequestedFileSize; + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +BOOLEAN AddDirInfo(NODE_ Node, PWSTR Name, PVOID Buffer, ULONG Length, + PULONG PBytesTransferred) +{ + UINT8 DirInfoBuf[sizeof(FSP_FSCTL_DIR_INFO) + sizeof Node->Name]; + 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->FileInfo = Node->FileInfo; + memcpy(DirInfo->FileNameBuf, Name, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + + return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + VolumeInfo->TotalSize = Airfs->MaxNodes * (UINT64)Airfs->MaxFileSize; + VolumeInfo->FreeSize = (Airfs->MaxNodes - Airfs->NumNodes) * + (UINT64)Airfs->MaxFileSize; + VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength); + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetVolumeLabel(FSP_FILE_SYSTEM *FileSystem, PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + 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->VolumeLabelLength = Airfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, PUINT32 PFileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + 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) + { + FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY; + Node = Node->Parent; + } + + if (PFileAttributes) + *PFileAttributes = Node->FileInfo.FileAttributes & FileAttributesMask; +#else + if (PFileAttributes) + *PFileAttributes = Node->FileInfo.FileAttributes; +#endif + + if (PSecurityDescriptorSize) + { + if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + if (SecurityDescriptor) + memcpy(SecurityDescriptor, Node->SecurityDescriptor, Node->SecurityDescriptorSize); + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiCreate(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions, + UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, + UINT64 AllocationSize, PVOID *PNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + NODE_ Parent; + NTSTATUS Result; + BOOLEAN Inserted; + PWSTR BaseName; + + if (AIRFS_MAX_PATH <= wcslen(Name)) + return STATUS_OBJECT_NAME_INVALID; + + if (CreateOptions & FILE_DIRECTORY_FILE) + 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; + + Result = CreateNode(Airfs, BaseName, &Node); + 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; + + if (SecurityDescriptor) + { + Node->SecurityDescriptorSize = GetSecurityDescriptorLength(SecurityDescriptor); + Node->SecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(Node->SecurityDescriptorSize); + if (!Node->SecurityDescriptor) + { + DeleteNode(Airfs, Node); + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(Node->SecurityDescriptor, SecurityDescriptor, Node->SecurityDescriptorSize); + } + + Node->FileInfo.AllocationSize = AllocationSize; + if (Node->FileInfo.AllocationSize) + { + Node->FileData = AirfsHeapAlloc((size_t)Node->FileInfo.AllocationSize); + if (!Node->FileData) + { + DeleteNode(Airfs, Node); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + 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; + } + + 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) ); + + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes); + memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof(WCHAR), Node->Name, NodeNameNumBytes); + OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes; + } +#endif + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiOpen(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions, + UINT32 GrantedAccess, PVOID *PNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + NTSTATUS Result; + + if (AIRFS_MAX_PATH <= wcslen(Name)) + return STATUS_OBJECT_NAME_INVALID; + + PWSTR baseName; + Result = FindNode(Airfs, Name, &baseName, 0, &Node); + if (Result) return Result; + if (!Node) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + 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) ); + + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes); + memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof(WCHAR), Node->Name, NodeNameNumBytes); + OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes; + } +#endif + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiOverwrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttributes, + BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + NTSTATUS Result; + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->Streams) + { + for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ) + { + NODE_ Stream = *Iter; + LONG RefCount = Stream->RefCount; + MemoryBarrier(); + Iter++; + if (RefCount <= 2) + { + RemoveNode(Airfs, Stream); + } + } + if (Node->Streams->empty()) + { + delete Node->Streams; + Node->Streams = 0; + } + } +#endif + + Result = SetAllocSize(FileSystem, 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; + + Node->FileInfo.FileSize = 0; + Node->FileInfo.LastAccessTime = + Node->FileInfo.LastWriteTime = + Node->FileInfo.ChangeTime = GetSystemTime(); + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +void ApiCleanup(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, ULONG Flags) +{ + 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. + + if (Flags & FspCleanupSetArchiveBit) + { + if (!(MainNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + 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; + } + + if (Flags & FspCleanupSetAllocationSize) + { + UINT64 AllocationUnit = AIRFS_SECTOR_SIZE * AIRFS_SECTORS_PER_ALLOCATION_UNIT; + UINT64 AllocationSize = (Node->FileInfo.FileSize + AllocationUnit - 1) / + AllocationUnit * AllocationUnit; + + SetAllocSize(FileSystem, Node, AllocationSize); + } + + if ((Flags & FspCleanupDelete) && !NodeHasChildren(Node)) + { + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->Streams) + { + for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ) + { + NODE_ Stream = *Iter; + Iter++; + DeleteNode(Airfs, Stream); + } + delete Node->Streams; + Node->Streams = 0; + } +#endif + + RemoveNode(Airfs, Node); + } +} + +////////////////////////////////////////////////////////////////////// + +void ApiClose(FSP_FILE_SYSTEM *FileSystem, PVOID Node0) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + DereferenceNode(Airfs, Node); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiRead(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, + UINT64 Offset, ULONG Length, PULONG PBytesTransferred) +{ + NODE_ Node = (NODE_) Node0; + UINT64 EndOffset; + + if (Offset >= Node->FileInfo.FileSize) + return STATUS_END_OF_FILE; + + EndOffset = Offset + 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 + + memcpy(Buffer, (PUINT8)Node->FileData + Offset, (size_t)(EndOffset - Offset)); + + *PBytesTransferred = (ULONG)(EndOffset - Offset); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiWrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, + UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile, + BOOLEAN ConstrainedIo, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) +{ + NODE_ Node = (NODE_) Node0; + UINT64 EndOffset; + NTSTATUS Result; + + if (ConstrainedIo) + { + if (Offset >= Node->FileInfo.FileSize) + return STATUS_SUCCESS; + EndOffset = Offset + Length; + if (EndOffset > Node->FileInfo.FileSize) + EndOffset = Node->FileInfo.FileSize; + } + else + { + if (WriteToEndOfFile) + Offset = Node->FileInfo.FileSize; + EndOffset = Offset + Length; + if (EndOffset > Node->FileInfo.FileSize) + { + Result = SetFileSize(FileSystem, 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 + + memcpy((PUINT8)Node->FileData + Offset, Buffer, (size_t)(EndOffset - Offset)); + + *PBytesTransferred = (ULONG)(EndOffset - Offset); + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +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); + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetFileInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + NODE_ Node = (NODE_) Node0; + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + 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; + if (CreationTime ) Node->FileInfo.CreationTime = CreationTime; + if (LastAccessTime ) Node->FileInfo.LastAccessTime = LastAccessTime; + if (LastWriteTime ) Node->FileInfo.LastWriteTime = LastWriteTime; + if (ChangeTime ) Node->FileInfo.ChangeTime = ChangeTime; + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetFileSize(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO *FileInfo) +{ + NODE_ Node = (NODE_) Node0; + NTSTATUS Result = SetAllocationSize + ? SetAllocSize(FileSystem, Node, NewSize) + : SetFileSize(FileSystem, Node, NewSize); + if (!NT_SUCCESS(Result)) + return Result; + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiCanDelete(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name) +{ + NODE_ Node = (NODE_) Node0; + + if (NodeHasChildren(Node)) + return STATUS_DIRECTORY_NOT_EMPTY; + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiRename(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, + PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + NODE_ NewNode; + BOOLEAN Inserted; + NTSTATUS Result; + PWSTR newBaseName; + NODE_ NewParent; + Result = FindNode(Airfs, NewFileName, &newBaseName, &NewParent, &NewNode); + + if (NewNode && Node != NewNode) + { + if (!ReplaceIfExists) + return STATUS_OBJECT_NAME_COLLISION; + + if (NewNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return STATUS_ACCESS_DENIED; + } + + if (NewNode && Node != NewNode) + { + ReferenceNode(NewNode); + RemoveNode(Airfs, NewNode); + DereferenceNode(Airfs, NewNode); + } + + ReferenceNode(Node); + RemoveNode(Airfs, Node); + wcscpy_s(Node->Name, sizeof Node->Name / sizeof(WCHAR), newBaseName); + Result = InsertNode(Airfs, NewParent, Node, &Inserted); + DereferenceNode(Airfs, Node); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + NODE_ Node = (NODE_) Node0; + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->IsAStream) Node = Node->Parent; +#endif + + if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + if (SecurityDescriptor) + memcpy(SecurityDescriptor, Node->SecurityDescriptor, Node->SecurityDescriptorSize); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) +{ + NODE_ Node = (NODE_) Node0; + PSECURITY_DESCRIPTOR NewSecurityDescriptor, SecurityDescriptor; + SIZE_T SecurityDescriptorSize; + NTSTATUS Result; + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->IsAStream) Node = Node->Parent; +#endif + + Result = FspSetSecurityDescriptor( + Node->SecurityDescriptor, + SecurityInformation, + ModificationDescriptor, + &NewSecurityDescriptor); + if (!NT_SUCCESS(Result)) + return Result; + + SecurityDescriptorSize = GetSecurityDescriptorLength(NewSecurityDescriptor); + SecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SecurityDescriptorSize); + if (!SecurityDescriptor) + { + FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(SecurityDescriptor, NewSecurityDescriptor, SecurityDescriptorSize); + FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); + + free(Node->SecurityDescriptor); + Node->SecurityDescriptorSize = SecurityDescriptorSize; + Node->SecurityDescriptor = SecurityDescriptor; + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +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; + NODE_ Node = (NODE_) Node0; + NODE_ Parent = Node->Parent; + if (Parent) + { + // If this is not the root directory, add the dot entries. + if (!Marker) + { + if (!AddDirInfo(Node, L".", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + } + if (!Marker || (L'.' == Marker[0] && L'\0' == Marker[1])) + { + if (!AddDirInfo(Parent, L"..", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + Marker = 0; + } + } + + auto Iter = Marker ? Node->Children->upper_bound((NODE_)Marker) : Node->Children->begin(); + for (; Iter != Node->Children->end(); ++Iter) + { + NODE_ Node = *Iter; + if (!AddDirInfo(Node, Node->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; + + 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)); + + return STATUS_SUCCESS; +} + +#endif +////////////////////////////////////////////////////////////////////// +#if defined(AIRFS_REPARSE_POINTS) + +NTSTATUS ApiResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, + UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) +{ + return FspFileSystemResolveReparsePoints(FileSystem, GetReparsePointByName, 0, + Name, ReparsePointIndex, ResolveLastPathComponent, + PIoStatus, Buffer, PSize); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PWSTR Name, PVOID Buffer, PSIZE_T PSize) +{ + 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; + + if (Node->ReparseDataSize > *PSize) + return STATUS_BUFFER_TOO_SMALL; + + *PSize = Node->ReparseDataSize; + memcpy(Buffer, Node->ReparseData, Node->ReparseDataSize); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PWSTR Name, PVOID Buffer, SIZE_T Size) +{ + NODE_ Node = (NODE_) Node0; + PVOID ReparseData; + NTSTATUS Result; + +#if defined(AIRFS_NAMED_STREAMS) + if (Node->IsAStream) Node = Node->Parent; +#endif + + if (NodeHasChildren(Node)) + return STATUS_DIRECTORY_NOT_EMPTY; + + if (Node->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + Node->ReparseData, Node->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + + ReparseData = realloc(Node->ReparseData, Size); + if (!ReparseData && Size) + return STATUS_INSUFFICIENT_RESOURCES; + + Node->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + Node->FileInfo.ReparseTag = *(PULONG)Buffer; + // The first field in a reparse buffer is the reparse tag. + Node->ReparseDataSize = Size; + Node->ReparseData = ReparseData; + memcpy(Node->ReparseData, Buffer, Size); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiDeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PWSTR Name, PVOID Buffer, SIZE_T Size) +{ + 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, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + else + return STATUS_NOT_A_REPARSE_POINT; + + free(Node->ReparseData); + + Node->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; + Node->FileInfo.ReparseTag = 0; + Node->ReparseDataSize = 0; + Node->ReparseData = 0; + + return STATUS_SUCCESS; +} + +#endif +////////////////////////////////////////////////////////////////////// +#if defined(AIRFS_NAMED_STREAMS) + +NTSTATUS ApiGetStreamInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + NODE_ Node = (NODE_) Node0; + + if (Node->IsAStream) + Node = Node->Parent; + + if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !AddStreamInfo(Node, Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + + // TODO: how to handle out-of-response-buffer-space condition? + if (Node->Streams) + { + for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ) + { + BOOLEAN added = AddStreamInfo(*Iter, Buffer, Length, PBytesTransferred); + if (!added) goto done; + } + } + FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred); + + done: + + return STATUS_SUCCESS; +} + +#endif +////////////////////////////////////////////////////////////////////// + +FSP_FILE_SYSTEM_INTERFACE AirfsInterface = +{ + ApiGetVolumeInfo, + ApiSetVolumeLabel, + ApiGetSecurityByName, + ApiCreate, + ApiOpen, + ApiOverwrite, + ApiCleanup, + ApiClose, + ApiRead, + ApiWrite, + ApiFlush, + ApiGetFileInfo, + ApiSetBasicInfo, + ApiSetFileSize, + ApiCanDelete, + ApiRename, + 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 +}; + +////////////////////////////////////////////////////////////////////// + +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 +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS AirfsCreate( + ULONG Flags, + ULONG FileInfoTimeout, + ULONG MaxNodes, + ULONG MaxFileSize, + ULONG SlowioMaxDelay, + ULONG SlowioPercentDelay, + ULONG SlowioRarefyDelay, + 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; + UINT64 AllocationUnit; + AIRFS_ Airfs; + NODE_ RootNode; + PSECURITY_DESCRIPTOR RootSecurity; + ULONG RootSecuritySize; + + *PAirfs = 0; + + if (!AirfsHeapInitialize()) + return STATUS_INSUFFICIENT_RESOURCES; + + 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()); + + Airfs = (AIRFS_) malloc(sizeof *Airfs); + if (!Airfs) + { + LocalFree(RootSecurity); + return STATUS_INSUFFICIENT_RESOURCES; + } + + memset(Airfs, 0, sizeof *Airfs); + Airfs->MaxNodes = MaxNodes; + AllocationUnit = AIRFS_SECTOR_SIZE * AIRFS_SECTORS_PER_ALLOCATION_UNIT; + Airfs->MaxFileSize = (ULONG)((MaxFileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit); + +#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 = AIRFS_SECTOR_SIZE; + VolumeParams.SectorsPerAllocationUnit = AIRFS_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 (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); + 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; + ULONG DebugFlags = 0; + PWSTR DebugLogFile = 0; + 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 FileSystemName = 0; + PWSTR MountPoint = 0; + PWSTR VolumePrefix = 0; + PWSTR RootSddl = 0; + HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; + AIRFS_ Airfs = 0; + NTSTATUS Result; + + for (argp = argv + 1, arge = argv + argc; arge > argp; argp++) + { + if (L'-' != argp[0][0]) + break; + switch (argp[0][1]) + { + case L'?': goto usage; + case L'd': argtol(DebugFlags); break; + case L'D': argtos(DebugLogFile); break; + case L'f': OtherFlags = AirfsFlushAndPurgeOnCleanup; break; + case L'F': argtos(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'u': + argtos(VolumePrefix); + if (VolumePrefix && L'\0' != VolumePrefix[0]) + Flags = AirfsNet; + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (AirfsDisk == Flags && !MountPoint) + goto usage; + + if (DebugLogFile) + { + if (!wcscmp(L"-", DebugLogFile)) + DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE); + else + DebugLogHandle = CreateFileW( + DebugLogFile, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (INVALID_HANDLE_VALUE == DebugLogHandle) + { + fail(L"cannot open debug log file"); + goto usage; + } + + FspDebugLogSetHandle(DebugLogHandle); + } + + Result = AirfsCreate( + Flags | OtherFlags, + FileInfoTimeout, + MaxNodes, + MaxFileSize, + SlowioMaxDelay, + SlowioPercentDelay, + SlowioRarefyDelay, + FileSystemName, + VolumePrefix, + RootSddl, + &Airfs); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot create AIRFS"); + goto exit; + } + + FspFileSystemSetDebugLog(Airfs->FileSystem, DebugFlags); + + if (MountPoint && L'\0' != MountPoint[0]) + { + Result = FspFileSystemSetMountPoint(Airfs->FileSystem, + L'*' == MountPoint[0] && L'\0' == MountPoint[1] ? 0 : MountPoint); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot mount AIRFS"); + goto exit; + } + } + + Result = AirfsStart(Airfs); + if (!NT_SUCCESS(Result)) + { + 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""); + + 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" + " -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"; + + fail(usage, L"" PROGNAME); + + return STATUS_UNSUCCESSFUL; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + AIRFS_ Airfs = (AIRFS_) Service->UserContext; + + AirfsStop(Airfs); + AirfsDelete(Airfs); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +int wmain(int argc, wchar_t **argv) +{ + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); +} + +////////////////////////////////////////////////////////////////////// From 1906772aa276641d0c1100fbb6080be38fb96a41 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 5 Jul 2018 14:45:44 -0700 Subject: [PATCH 2/3] airfs: add VS project --- tst/airfs/.gitignore | 5 + tst/airfs/airfs.sln | 28 +++++ tst/airfs/airfs.vcxproj | 178 ++++++++++++++++++++++++++++++++ tst/airfs/airfs.vcxproj.filters | 14 +++ 4 files changed, 225 insertions(+) create mode 100644 tst/airfs/.gitignore create mode 100644 tst/airfs/airfs.sln create mode 100644 tst/airfs/airfs.vcxproj create mode 100644 tst/airfs/airfs.vcxproj.filters diff --git a/tst/airfs/.gitignore b/tst/airfs/.gitignore new file mode 100644 index 00000000..c78dfb19 --- /dev/null +++ b/tst/airfs/.gitignore @@ -0,0 +1,5 @@ +build +*.ncb +*.suo +*.vcproj.* +*.vcxproj.user diff --git a/tst/airfs/airfs.sln b/tst/airfs/airfs.sln new file mode 100644 index 00000000..15c9d006 --- /dev/null +++ b/tst/airfs/airfs.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "airfs", "airfs.vcxproj", "{CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x64.ActiveCfg = Debug|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x64.Build.0 = Debug|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x86.ActiveCfg = Debug|Win32 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x86.Build.0 = Debug|Win32 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x64.ActiveCfg = Release|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x64.Build.0 = Release|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x86.ActiveCfg = Release|Win32 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tst/airfs/airfs.vcxproj b/tst/airfs/airfs.vcxproj new file mode 100644 index 00000000..9210e1b1 --- /dev/null +++ b/tst/airfs/airfs.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0} + Win32Proj + airfs + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + \ No newline at end of file diff --git a/tst/airfs/airfs.vcxproj.filters b/tst/airfs/airfs.vcxproj.filters new file mode 100644 index 00000000..f67a722d --- /dev/null +++ b/tst/airfs/airfs.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + \ No newline at end of file From 5a8aad60b39aa97acbb63d710077da175033f445 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 5 Jul 2018 14:59:24 -0700 Subject: [PATCH 3/3] airfs: add notice re: contributions --- tst/airfs/airfs.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tst/airfs/airfs.cpp b/tst/airfs/airfs.cpp index 122c1f1a..4d83b658 100644 --- a/tst/airfs/airfs.cpp +++ b/tst/airfs/airfs.cpp @@ -14,6 +14,12 @@ * accordance with the commercial license agreement provided with the * software. */ +/* + * 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