mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 16:33:02 -05:00
1449 lines
44 KiB
C++
1449 lines
44 KiB
C++
/**
|
|
* @file airfs.cpp
|
|
*
|
|
* @copyright 2015-2020 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);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////
|