/** * @file airfs.cpp * * @copyright 2015-2024 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 "common.h" enum { AirfsDisk = 0x00000000, AirfsNet = 0x00000001, AirfsDeviceMask = 0x0000000f, AirfsCaseInsensitive = 0x80000000, AirfsFlushAndPurgeOnCleanup = 0x40000000, }; ////////////////////////////////////////////////////////////////////// NTSTATUS CreateNode(AIRFS_ Airfs, PWSTR Name, NODE_ *PNode) { static UINT64 IndexNumber = 1; NODE_ Node = (NODE_) StorageAllocate(Airfs, sizeof NODE); if (!Node) { *PNode = 0; return STATUS_INSUFFICIENT_RESOURCES; } 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 = SystemTime(); Node->FileInfo.IndexNumber = IndexNumber++; *PNode = Node; return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// void DeleteNode(AIRFS_ Airfs, NODE_ Node) { NODE_ Block; while (Block = Node->FileBlocks) { Detach(Node->FileBlocks, Block); StorageFree(Airfs, Block); } StorageFree(Airfs, Node->Name); StorageFree(Airfs, Node->ReparseData); StorageFree(Airfs, Node->SecurityDescriptor); StorageFree(Airfs, Node); } ////////////////////////////////////////////////////////////////////// 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 (Node->IsAStream) { *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; } ////////////////////////////////////////////////////////////////////// NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, NODE_ *PParent, NODE_ *PNode) { CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; // 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; WCHAR* Colon = 0; 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. NODE_ Child = Find(Ancestor->Children, fm, NodeCmp); if (!Child) { if (PParent) *PParent = 0; *PNode = 0; if (Colon) return STATUS_OBJECT_NAME_NOT_FOUND; return STATUS_OBJECT_PATH_NOT_FOUND; } Ancestor = Child; if (Colon) { fm = to+1; break; } 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 (Colon) { // Find the stream, if it exists. if (!Ancestor->Streams) { *PNode = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } NODE_ Stream = Find(Ancestor->Streams, fm, NodeCmp); if (!Stream) { *PNode = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } *PNode = Stream; return 0; } // Find the directory entry, if it exists. NODE_ Found = Find(Ancestor->Children, fm, NodeCmp); if (!Found) { *PNode = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } *PNode = Found; return 0; } ////////////////////////////////////////////////////////////////////// BOOLEAN AddStreamInfo(NODE_ Node, PWSTR StreamName, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) { 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->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); } ////////////////////////////////////////////////////////////////////// inline void TouchNode(NODE_ Node) { Node->FileInfo.LastAccessTime = Node->FileInfo.LastWriteTime = Node->FileInfo.ChangeTime = SystemTime(); } ////////////////////////////////////////////////////////////////////// void InsertNode(AIRFS_ Airfs, NODE_ Parent, NODE_ Node) { 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); } ////////////////////////////////////////////////////////////////////// void RemoveNode(AIRFS_ Airfs, NODE_ Node) { NODE_ Parent = Node->Parent; TouchNode(Parent); if (Node->IsAStream) { if (Parent->Streams) Detach(Parent->Streams, Node); } else Detach(Parent->Children, Node); DereferenceNode(Airfs, Node); } ////////////////////////////////////////////////////////////////////// NTSTATUS GetReparsePointByName(FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR Name, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize) { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node; 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.Address(), Node->ReparseDataSize); } return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// NTSTATUS SetAllocSize(AIRFS_ Airfs, NODE_ Node, UINT64 RequestedAllocSize) { NTSTATUS Result = StorageSetFileCapacity(Airfs, Node, RequestedAllocSize); if (!Result) { Node->FileInfo.AllocationSize = RequestedAllocSize; if (Node->FileInfo.FileSize > Node->FileInfo.AllocationSize) Node->FileInfo.FileSize = Node->FileInfo.AllocationSize; } return Result; } ////////////////////////////////////////////////////////////////////// NTSTATUS SetFileSize(AIRFS_ Airfs, NODE_ Node, UINT64 RequestedFileSize) { if (Node->FileInfo.FileSize != RequestedFileSize) { if (Node->FileInfo.AllocationSize < RequestedFileSize) { NTSTATUS Result = SetAllocSize(Airfs, Node, RequestedFileSize); if (!NT_SUCCESS(Result)) return Result; } if (Node->FileInfo.FileSize < RequestedFileSize) StorageAccessFile(ZERO, Node, Node->FileInfo.FileSize, RequestedFileSize - Node->FileInfo.FileSize, 0); Node->FileInfo.FileSize = RequestedFileSize; } return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// BOOLEAN AddDirInfo(NODE_ Node, PWSTR Name, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) { 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; DirInfo->Size = (UINT16)(sizeof FSP_FSCTL_DIR_INFO + NameBytes); DirInfo->FileInfo = Node->FileInfo; memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); memcpy(DirInfo->FileNameBuf, Name, NameBytes + sizeof WCHAR); 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->VolumeSize; VolumeInfo->FreeSize = Airfs->FreeSize; 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->VolumeSize; VolumeInfo->FreeSize = Airfs->FreeSize; 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 (FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0, Name, PFileAttributes)) { return STATUS_REPARSE; } return Result; } UINT32 FileAttributesMask = ~(UINT32)0; if (Node->IsAStream) { FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY; Node = Node->Parent; } if (PFileAttributes) *PFileAttributes = Node->FileInfo.FileAttributes & FileAttributesMask; if (PSecurityDescriptorSize) { if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) { *PSecurityDescriptorSize = Node->SecurityDescriptorSize; return STATUS_BUFFER_OVERFLOW; } *PSecurityDescriptorSize = Node->SecurityDescriptorSize; if (SecurityDescriptor) memcpy(SecurityDescriptor, Node->SecurityDescriptor.Address(), 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; 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; Result = CreateNode(Airfs, BaseName, &Node); if (!NT_SUCCESS(Result)) return Result; Node->IsAStream = BaseName[-1] == L':'; Node->FileInfo.FileAttributes = (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FileAttributes : FileAttributes | FILE_ATTRIBUTE_ARCHIVE; if (SecurityDescriptor) { Node->SecurityDescriptorSize = GetSecurityDescriptorLength(SecurityDescriptor); Node->SecurityDescriptor = StorageAllocate(Airfs, (int)Node->SecurityDescriptorSize); if (!Node->SecurityDescriptor) { DeleteNode(Airfs, Node); return STATUS_INSUFFICIENT_RESOURCES; } memcpy(Node->SecurityDescriptor.Address(), SecurityDescriptor, Node->SecurityDescriptorSize); } if (AllocationSize) { NTSTATUS Result = SetAllocSize(Airfs, Node, AllocationSize); if (Result) { DeleteNode(Airfs, Node); return Result; } } InsertNode(Airfs, Parent, Node); ReferenceNode(Node); *PNode = Node; GetFileInfo(Node, FileInfo); 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; } 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 (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; } 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; for (NODE_ Stream = First(Node->Streams); Stream; ) { NODE_ NextStream = Next(Stream); LONG RefCount = Stream->RefCount; MemoryBarrier(); if (RefCount <= 1) { RemoveNode(Airfs, Stream); } Stream = NextStream; } 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; Node->FileInfo.FileSize = 0; Node->FileInfo.LastAccessTime = Node->FileInfo.LastWriteTime = Node->FileInfo.ChangeTime = SystemTime(); 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; NODE_ MainNode = Node->IsAStream ? Node->Parent : Node; if (Flags & FspCleanupSetArchiveBit) { if (!(MainNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) MainNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; } if (Flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime | FspCleanupSetChangeTime)) { 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(Airfs, Node, Node->FileInfo.FileSize); } if ((Flags & FspCleanupDelete) && !Node->Children) { NODE_ Stream; while (Stream = Node->Streams) { Detach(Node->Streams, Stream); DeleteNode(Airfs, Stream); } 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 BegOffset, ULONG Length, PULONG PBytesTransferred) { NODE_ Node = (NODE_) Node0; UINT64 EndOffset; if (BegOffset >= Node->FileInfo.FileSize) return STATUS_END_OF_FILE; EndOffset = BegOffset + Length; if (EndOffset > Node->FileInfo.FileSize) EndOffset = Node->FileInfo.FileSize; StorageAccessFile(READ, Node, BegOffset, EndOffset - BegOffset, (char*)Buffer); *PBytesTransferred = (ULONG)(EndOffset - BegOffset); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// NTSTATUS ApiWrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, 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 (BegOffset >= Node->FileInfo.FileSize) return STATUS_SUCCESS; EndOffset = BegOffset + Length; if (EndOffset > Node->FileInfo.FileSize) EndOffset = Node->FileInfo.FileSize; } else { if (WriteToEndOfFile) BegOffset = Node->FileInfo.FileSize; EndOffset = BegOffset + Length; if (EndOffset > Node->FileInfo.FileSize) { Result = SetFileSize(Airfs, Node, EndOffset); if (!NT_SUCCESS(Result)) return Result; } } StorageAccessFile(WRITE, Node, BegOffset, EndOffset - BegOffset, (char*)Buffer); *PBytesTransferred = (ULONG)(EndOffset - BegOffset); GetFileInfo(Node, FileInfo); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// NTSTATUS ApiFlush(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, FSP_FSCTL_FILE_INFO *FileInfo) { NODE_ Node = (NODE_) Node0; if (Node) 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 (Node->IsAStream) Node = Node->Parent; 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) { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; NODE_ Node = (NODE_) Node0; NTSTATUS Result = SetAllocationSize ? SetAllocSize (Airfs, Node, NewSize) : SetFileSize (Airfs, 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 (Node->Children) 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; NTSTATUS Result; PWSTR NewBaseName; NODE_ NewParent; 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); DereferenceNode(Airfs, NewNode); } ReferenceNode(Node); RemoveNode(Airfs, Node); StorageFree(Airfs, Node->Name); Node->Name = NewBaseNameAlloc; InsertNode(Airfs, NewParent, Node); 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 (Node->IsAStream) Node = Node->Parent; if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) { *PSecurityDescriptorSize = Node->SecurityDescriptorSize; return STATUS_BUFFER_OVERFLOW; } *PSecurityDescriptorSize = Node->SecurityDescriptorSize; if (SecurityDescriptor) memcpy(SecurityDescriptor, Node->SecurityDescriptor.Address(), Node->SecurityDescriptorSize); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// 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, MallocSecurityDescriptor; if (Node->IsAStream) Node = Node->Parent; NTSTATUS Result = FspSetSecurityDescriptor( Node->SecurityDescriptor.Address(), SecurityInformation, ModificationDescriptor, &NewSecurityDescriptor); if (!NT_SUCCESS(Result)) return Result; uint64_t SecurityDescriptorSize = GetSecurityDescriptorLength(NewSecurityDescriptor); MallocSecurityDescriptor = (PSECURITY_DESCRIPTOR) malloc(SecurityDescriptorSize); if (!MallocSecurityDescriptor) { FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); return STATUS_INSUFFICIENT_RESOURCES; } memcpy(MallocSecurityDescriptor, NewSecurityDescriptor, SecurityDescriptorSize); FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); 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) { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; 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; } } // 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)) { if (!AddDirInfo(Child, Child->Name, Buffer, Length, PBytesTransferred)) goto bufferReady; } FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); bufferReady: return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// NTSTATUS ApiGetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, PVOID ParentNode0, PWSTR Name, FSP_FSCTL_DIR_INFO *DirInfo) { AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; 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); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// 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 (Node->IsAStream) Node = Node->Parent; 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.Address(), Node->ReparseDataSize); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// NTSTATUS ApiSetReparsePoint(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 (Node->IsAStream) Node = Node->Parent; if (Node->Children) return STATUS_DIRECTORY_NOT_EMPTY; if (Node->ReparseData) { Result = FspFileSystemCanReplaceReparsePoint( Node->ReparseData.Address(), Node->ReparseDataSize, Buffer, Size); if (!NT_SUCCESS(Result)) return Result; } NODE_ ReparseData = (NODE_) StorageReallocate(Airfs, 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.Address(), Buffer, Size); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// 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 (Node->IsAStream) Node = Node->Parent; if (Node->ReparseData) { Result = FspFileSystemCanReplaceReparsePoint( Node->ReparseData.Address(), Node->ReparseDataSize, Buffer, Size); if (!NT_SUCCESS(Result)) return Result; } else return STATUS_NOT_A_REPARSE_POINT; StorageFree(Airfs, Node->ReparseData); Node->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; Node->FileInfo.ReparseTag = 0; Node->ReparseDataSize = 0; Node->ReparseData = 0; return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// 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)) if (!AddStreamInfo(Node, L"", Buffer, Length, PBytesTransferred)) return STATUS_SUCCESS; for (NODE_ Stream = First(Node->Streams); Stream; Stream = Next(Stream)) { BOOLEAN added = AddStreamInfo(Stream, Stream->Name, Buffer, Length, PBytesTransferred); if (!added) goto done; } FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred); done: return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// NTSTATUS ApiControl(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, UINT32 ControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) { // Trivial example: Perform a ROT13 translation on alphas. if (CTL_CODE(0x8000 + 'M', 'R', METHOD_BUFFERED, FILE_ANY_ACCESS) == ControlCode) { if (OutputBufferLength != InputBufferLength) return STATUS_INVALID_PARAMETER; for (ULONG i = 0; i < InputBufferLength; i++) { char c = ((char*)InputBuffer)[i]; if (('A' <= c && c <= 'M') || ('a' <= c && c <= 'm')) c += 13; else if (('N' <= c && c <= 'Z') || ('n' <= c && c <= 'z')) c -= 13; ((char*)OutputBuffer)[i] = c; } *PBytesTransferred = InputBufferLength; return STATUS_SUCCESS; } return STATUS_INVALID_DEVICE_REQUEST; } ////////////////////////////////////////////////////////////////////// FSP_FILE_SYSTEM_INTERFACE AirfsInterface = { ApiGetVolumeInfo, ApiSetVolumeLabel, ApiGetSecurityByName, ApiCreate, ApiOpen, ApiOverwrite, ApiCleanup, ApiClose, ApiRead, ApiWrite, ApiFlush, ApiGetFileInfo, ApiSetBasicInfo, ApiSetFileSize, ApiCanDelete, ApiRename, ApiGetSecurity, ApiSetSecurity, ApiReadDirectory, ApiResolveReparsePoints, ApiGetReparsePoint, ApiSetReparsePoint, ApiDeleteReparsePoint, ApiGetStreamInfo, ApiGetDirInfoByName, ApiControl, }; ////////////////////////////////////////////////////////////////////// void AirfsDelete(AIRFS_ Airfs) { FspFileSystemDelete(Airfs->FileSystem); StorageShutdown(Airfs); } ////////////////////////////////////////////////////////////////////// NTSTATUS AirfsCreate( PWSTR StorageFileName, PWSTR MapName, ULONG Flags, ULONG FileInfoTimeout, UINT64 VolumeSize, PWSTR FileSystemName, PWSTR VolumePrefix, PWSTR RootSddl, AIRFS_ *PAirfs) { NTSTATUS Result; 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; PSECURITY_DESCRIPTOR RootSecurity = 0; ULONG RootSecuritySize; *PAirfs = 0; boolean StorageFileExists = *StorageFileName && (_waccess(StorageFileName, 0) != -1); Result = StorageStartup(Airfs, MapName, StorageFileName, VolumeSize); if (Result) return Result; boolean ShouldFormat = !StorageFileExists || memcmp(Airfs->Signature, "Airfs\0\0\0", 8); 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->VolumeSize = ROUND_DOWN(VolumeSize, ALLOCATION_UNIT); Airfs->CaseInsensitive = CaseInsensitive; Airfs->VolumeLabelLength = sizeof L"AIRFS" - sizeof WCHAR; memcpy(Airfs->VolumeLabel, L"AIRFS", Airfs->VolumeLabelLength); 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; // 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); } Result = FspFileSystemCreate(DevicePath, &Airfs->VolumeParams, &AirfsInterface, &Airfs->FileSystem); if (!NT_SUCCESS(Result)) { LocalFree(RootSecurity); return Result; } Airfs->FileSystem->UserContext = Airfs; *PAirfs = Airfs; return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// 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; PWSTR StorageFileName = L""; PWSTR MapName = L""; UINT64 VolumeSize = 16LL * 1024 * 1024; PWSTR FileSystemName = 0; PWSTR MountPoint = 0; PWSTR VolumePrefix = L""; 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': ARG_TO_4(DebugFlags); break; case L'D': ARG_TO_S(DebugLogFile); break; case L'f': OtherFlags = AirfsFlushAndPurgeOnCleanup; break; case L'F': ARG_TO_S(FileSystemName); break; case L'i': OtherFlags = AirfsCaseInsensitive; break; case L'm': ARG_TO_S(MountPoint); break; case L'N': ARG_TO_S(StorageFileName); 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': ARG_TO_S(VolumePrefix); if (*VolumePrefix) 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( StorageFileName, MapName, Flags | OtherFlags, FileInfoTimeout, VolumeSize, 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 = FspFileSystemStartDispatcher(Airfs->FileSystem, 0); if (!NT_SUCCESS(Result)) { FAIL(L"cannot start AIRFS"); goto exit; } MountPoint = FspFileSystemMountPoint(Airfs->FileSystem); WCHAR buffer[1024]; _snwprintf_s(buffer, 1024, L"%S%S%s%S%s -t %ld -s %lld%S%s%S%s%S%s", PROGNAME, *StorageFileName ? " -N " : "", *StorageFileName ? StorageFileName : 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" " -f [flush and purge cache on cleanup]\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 StorageFileName [\"\": in memory only]\n" " -s VolumeSize [bytes]\n" " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" " -t FileInfoTimeout [millis]\n" " -u \\Server\\Share [UNC prefix (single backslash)]\n"; FAIL(usage, L"" PROGNAME); return STATUS_UNSUCCESSFUL; } ////////////////////////////////////////////////////////////////////// NTSTATUS SvcStop(FSP_SERVICE *Service) { AIRFS_ Airfs = (AIRFS_) Service->UserContext; FspFileSystemStopDispatcher(Airfs->FileSystem); AirfsDelete(Airfs); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////// int wmain(int argc, wchar_t **argv) { if (!NT_SUCCESS(FspLoad(0))) return ERROR_DELAY_LOAD_FAILED; return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); } ////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////