winfsp/tst/airfs/airfs.cpp
2022-01-07 17:30:49 +00:00

1449 lines
44 KiB
C++

/**
* @file airfs.cpp
*
* @copyright 2015-2022 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);
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////