mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-23 00:43:00 -05:00
2068 lines
60 KiB
C++
2068 lines
60 KiB
C++
/**
|
|
* @file airfs.cpp
|
|
*
|
|
* @copyright 2015-2019 Bill Zissimopoulos
|
|
*/
|
|
/*
|
|
* This file is part of WinFsp.
|
|
*
|
|
* You can redistribute it and/or modify it under the terms of the GNU
|
|
* General Public License version 3 as published by the Free Software
|
|
* Foundation.
|
|
*
|
|
* Licensees holding a valid commercial license may use this software
|
|
* in accordance with the commercial license agreement provided in
|
|
* conjunction with the software. The terms and conditions of any such
|
|
* commercial license agreement shall govern, supersede, and render
|
|
* ineffective any application of the GPLv3 license to this software,
|
|
* notwithstanding of any reference thereto in the software or
|
|
* associated repository.
|
|
*/
|
|
/*
|
|
* Airfs is based on Memfs with changes contributed by John Oberschelp.
|
|
* The contributed changes are under joint copyright by Bill Zissimopoulos
|
|
* and John Oberschelp per the Contributor Agreement found at the
|
|
* root of this project.
|
|
*/
|
|
|
|
#include <winfsp/winfsp.h>
|
|
#include <sddl.h>
|
|
#include <VersionHelpers.h>
|
|
#include <cassert>
|
|
#include <set>
|
|
#include <mutex>
|
|
#include <thread> // Used by SLOWIO.
|
|
|
|
#define AIRFS_MAX_PATH 512
|
|
FSP_FSCTL_STATIC_ASSERT(AIRFS_MAX_PATH > MAX_PATH,
|
|
"AIRFS_MAX_PATH must be greater than MAX_PATH.");
|
|
|
|
#define AIRFS_NAME_NORMALIZATION // Include name normalization support.
|
|
#define AIRFS_REPARSE_POINTS // Include reparse points support.
|
|
#define AIRFS_NAMED_STREAMS // Include alternate data streams support.
|
|
#define AIRFS_DIRINFO_BY_NAME // Include GetDirInfoByName.
|
|
#define AIRFS_SLOWIO // Include delayed I/O response support.
|
|
#define AIRFS_CONTROL // Include DeviceIoControl support.
|
|
|
|
|
|
#define SECTOR_SIZE 512
|
|
#define SECTORS_PER_ALLOCATION_UNIT 1
|
|
#define ALLOCATION_UNIT ( SECTOR_SIZE * SECTORS_PER_ALLOCATION_UNIT )
|
|
#define IN_ALLOCATION_UNITS(bytes) (((bytes) + ALLOCATION_UNIT - 1) / ALLOCATION_UNIT * ALLOCATION_UNIT)
|
|
|
|
enum
|
|
{
|
|
AirfsDisk = 0x00000000,
|
|
AirfsNet = 0x00000001,
|
|
AirfsDeviceMask = 0x0000000f,
|
|
AirfsCaseInsensitive = 0x80000000,
|
|
AirfsFlushAndPurgeOnCleanup = 0x40000000,
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// Heap Support
|
|
|
|
static HANDLE AirfsHeap = 0;
|
|
std::once_flag AirfsHeapInitOnceFlag;
|
|
|
|
static inline BOOLEAN AirfsHeapInitialize()
|
|
{
|
|
std::call_once(AirfsHeapInitOnceFlag, [](){ AirfsHeap = HeapCreate(0, 0, 0); });
|
|
return AirfsHeap != 0;
|
|
}
|
|
|
|
static inline PVOID AirfsHeapAlloc(SIZE_T Size)
|
|
{
|
|
return HeapAlloc(AirfsHeap, 0, Size);
|
|
}
|
|
|
|
static inline PVOID AirfsHeapRealloc(PVOID Pointer, SIZE_T RequestedSize)
|
|
{
|
|
if (!Pointer)
|
|
{
|
|
if (!RequestedSize) return 0;
|
|
return HeapAlloc(AirfsHeap, 0, RequestedSize);
|
|
}
|
|
if (!RequestedSize) return HeapFree(AirfsHeap, 0, Pointer), 0;
|
|
return HeapReAlloc(AirfsHeap, 0, Pointer, RequestedSize);
|
|
}
|
|
|
|
static inline void AirfsHeapFree(PVOID Pointer)
|
|
{
|
|
if (Pointer) HeapFree(AirfsHeap, 0, Pointer);
|
|
}
|
|
|
|
static inline SIZE_T AirfsHeapSize(PVOID Pointer)
|
|
{
|
|
if (!Pointer) return 0;
|
|
return HeapSize(AirfsHeap, 0, Pointer);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct NODE NODE, *NODE_;
|
|
|
|
struct NODE_LESS
|
|
{
|
|
bool operator() (const NODE_ NodeA, const NODE_ NodeB) const
|
|
{
|
|
WCHAR* a = (WCHAR*) NodeA;
|
|
WCHAR* b = (WCHAR*) NodeB;
|
|
return (CaseInsensitive ? _wcsicmp(a, b) : wcscmp(a, b)) < 0;
|
|
}
|
|
NODE_LESS(BOOLEAN Insensitive) : CaseInsensitive(Insensitive){}
|
|
BOOLEAN CaseInsensitive;
|
|
};
|
|
|
|
typedef std::set<NODE_, NODE_LESS> NODES, *NODES_;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
struct NODE
|
|
{
|
|
WCHAR Name[AIRFS_MAX_PATH];
|
|
NODE_ Parent;
|
|
NODES_ Children;
|
|
FSP_FSCTL_FILE_INFO FileInfo;
|
|
SIZE_T SecurityDescriptorSize;
|
|
PVOID SecurityDescriptor;
|
|
PVOID FileData;
|
|
#if defined(AIRFS_REPARSE_POINTS)
|
|
SIZE_T ReparseDataSize;
|
|
PVOID ReparseData;
|
|
#endif
|
|
volatile LONG RefCount;
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
NODES_ Streams;
|
|
BOOLEAN IsAStream;
|
|
#endif
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct
|
|
{
|
|
FSP_FILE_SYSTEM *FileSystem;
|
|
NODE_ Root;
|
|
ULONG NumNodes;
|
|
ULONG MaxNodes;
|
|
ULONG MaxFileSize;
|
|
#ifdef AIRFS_SLOWIO
|
|
ULONG SlowioMaxDelay;
|
|
ULONG SlowioPercentDelay;
|
|
ULONG SlowioRarefyDelay;
|
|
volatile LONG SlowioThreadsRunning;
|
|
#endif
|
|
UINT16 VolumeLabelLength;
|
|
WCHAR VolumeLabel[32];
|
|
BOOLEAN CaseInsensitive;
|
|
} AIRFS, *AIRFS_;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
inline UINT64 GetSystemTime()
|
|
{
|
|
FILETIME FileTime;
|
|
GetSystemTimeAsFileTime(&FileTime);
|
|
return ((PLARGE_INTEGER)&FileTime)->QuadPart;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS CreateNode(AIRFS_ Airfs, PWSTR Name, NODE_ *PNode)
|
|
{
|
|
static UINT64 IndexNumber = 1;
|
|
NODE_ Node = (NODE_) malloc(sizeof *Node);
|
|
if (!Node)
|
|
{
|
|
*PNode = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
memset(Node, 0, sizeof *Node);
|
|
wcscpy_s(Node->Name, sizeof Node->Name / sizeof(WCHAR), Name);
|
|
Node->FileInfo.CreationTime =
|
|
Node->FileInfo.LastAccessTime =
|
|
Node->FileInfo.LastWriteTime =
|
|
Node->FileInfo.ChangeTime = GetSystemTime();
|
|
Node->FileInfo.IndexNumber = IndexNumber++;
|
|
|
|
*PNode = Node;
|
|
Airfs->NumNodes++;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void DeleteNode(AIRFS_ Airfs, NODE_ Node)
|
|
{
|
|
#if defined(AIRFS_REPARSE_POINTS)
|
|
free(Node->ReparseData);
|
|
#endif
|
|
AirfsHeapFree(Node->FileData);
|
|
free(Node->SecurityDescriptor);
|
|
|
|
if (Node->Children)
|
|
{
|
|
assert(Node->Children->empty());
|
|
delete Node->Children;
|
|
}
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->Streams)
|
|
{
|
|
assert(Node->Streams->empty());
|
|
delete Node->Streams;
|
|
}
|
|
#endif
|
|
|
|
free(Node);
|
|
Airfs->NumNodes--;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
inline void ReferenceNode(NODE_ Node)
|
|
{
|
|
InterlockedIncrement(&Node->RefCount);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
inline void DereferenceNode(AIRFS_ Airfs, NODE_ Node)
|
|
{
|
|
if (InterlockedDecrement(&Node->RefCount) == 0)
|
|
DeleteNode(Airfs, Node);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void GetFileInfo(NODE_ Node, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream)
|
|
{
|
|
*FileInfo = Node->Parent->FileInfo;
|
|
FileInfo->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
|
|
// Named streams cannot be directories.
|
|
FileInfo->AllocationSize = Node->FileInfo.AllocationSize;
|
|
FileInfo->FileSize = Node->FileInfo.FileSize;
|
|
}
|
|
else
|
|
#endif
|
|
*FileInfo = Node->FileInfo;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void DumpNodes(NODE_ Node, int indent=0)
|
|
{
|
|
UINT32 dir = FILE_ATTRIBUTE_DIRECTORY & Node->FileInfo.FileAttributes;
|
|
printf("%c%3d ", dir ? 'd' : 'f', (int)Node->RefCount);
|
|
for (int i = indent; i; i--) putchar('\t');
|
|
printf("\"%S\"\n", Node->Name);
|
|
if (Node->Children)
|
|
for (auto& Child : *Node->Children)
|
|
DumpNodes(Child, indent+1);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS CreateNodeSet(BOOLEAN CaseInsensitive, NODES_ *PNodeSet)
|
|
{
|
|
try
|
|
{
|
|
*PNodeSet = new NODES(NODE_LESS(CaseInsensitive));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
catch (...)
|
|
{
|
|
*PNodeSet = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void DeleteAllNodes(AIRFS_ Airfs)
|
|
{
|
|
NODE_ Node = Airfs->Root;
|
|
while (Node)
|
|
{
|
|
if (!Node->Children->empty())
|
|
{
|
|
Node = *Node->Children->begin();
|
|
}
|
|
else
|
|
{
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->Streams)
|
|
{
|
|
for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); )
|
|
{
|
|
NODE_ Stream = *Iter++;
|
|
DeleteNode(Airfs, Stream);
|
|
}
|
|
delete Node->Streams;
|
|
}
|
|
#endif
|
|
NODE_ Parent = Node->Parent;
|
|
DeleteNode(Airfs, Node);
|
|
Node = Parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName,
|
|
NODE_ *PParent, NODE_ *PNode)
|
|
{
|
|
// Special case root.
|
|
if (Name[0] == 0 || Name[1] == 0)
|
|
{
|
|
if (BaseName) *BaseName = Name;
|
|
if (PParent) *PParent = Airfs->Root;
|
|
*PNode = Airfs->Root;
|
|
return 0;
|
|
}
|
|
|
|
WCHAR ParsedName[AIRFS_MAX_PATH];
|
|
wcscpy_s(ParsedName, sizeof ParsedName / sizeof(WCHAR), Name);
|
|
|
|
// From root, for each ancestor...
|
|
NODE_ Ancestor = Airfs->Root;
|
|
WCHAR* fm;
|
|
WCHAR* to = ParsedName;
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
WCHAR* Colon = 0;
|
|
#endif
|
|
for (;;)
|
|
{
|
|
// Isolate the next base name.
|
|
for (fm = to+1; *fm == L'\\'; fm++) {}
|
|
for (to = fm; *to != L'\0' && *to != L'\\'; to++)
|
|
if (*to == ':'){Colon = to; break;}
|
|
if (*to == 0) break;
|
|
*to = 0;
|
|
|
|
// Find this name.
|
|
auto Iter = Ancestor->Children->find((NODE_)fm);
|
|
if (Iter == Ancestor->Children->end())
|
|
{
|
|
if (PParent) *PParent = 0;
|
|
*PNode = 0;
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Colon) return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
#endif
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
Ancestor = *Iter;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Colon)
|
|
{
|
|
fm = to+1;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (!(Ancestor->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
if (PParent) *PParent = 0;
|
|
*PNode = 0;
|
|
return STATUS_NOT_A_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
if (BaseName)
|
|
*BaseName = Name + ( ( (UINT_PTR)fm - (UINT_PTR)ParsedName ) / sizeof(WCHAR));
|
|
|
|
if (PParent)
|
|
*PParent = Ancestor;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Colon)
|
|
{
|
|
// Find the stream, if it exists.
|
|
if (!Ancestor->Streams)
|
|
{
|
|
*PNode = 0;
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
auto Iter = Ancestor->Streams->find((NODE_)fm);
|
|
if (Iter == Ancestor->Streams->end())
|
|
{
|
|
*PNode = 0;
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
*PNode = *Iter;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// Find the directory entry, if it exists.
|
|
auto Iter = Ancestor->Children->find((NODE_)fm);
|
|
if (Iter == Ancestor->Children->end())
|
|
{
|
|
*PNode = 0;
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
*PNode = *Iter;
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
|
|
BOOLEAN AddStreamInfo(NODE_ Node, PWSTR StreamName, PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
|
{
|
|
UINT8 StreamInfoBuf[sizeof(FSP_FSCTL_STREAM_INFO) + sizeof Node->Name];
|
|
FSP_FSCTL_STREAM_INFO *StreamInfo = (FSP_FSCTL_STREAM_INFO *)StreamInfoBuf;
|
|
|
|
StreamInfo->Size = (UINT16)(sizeof(FSP_FSCTL_STREAM_INFO) + wcslen(StreamName) * sizeof(WCHAR));
|
|
StreamInfo->StreamSize = Node->FileInfo.FileSize;
|
|
StreamInfo->StreamAllocationSize = Node->FileInfo.AllocationSize;
|
|
memcpy(StreamInfo->StreamNameBuf, StreamName, StreamInfo->Size - sizeof(FSP_FSCTL_STREAM_INFO));
|
|
|
|
return FspFileSystemAddStreamInfo(StreamInfo, Buffer, Length, PBytesTransferred);
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
inline void TouchNode(NODE_ Node)
|
|
{
|
|
Node->FileInfo.LastAccessTime =
|
|
Node->FileInfo.LastWriteTime =
|
|
Node->FileInfo.ChangeTime = GetSystemTime();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS InsertNode(AIRFS_ Airfs, NODE_ Parent, NODE_ Node, PBOOLEAN PInserted)
|
|
{
|
|
try
|
|
{
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream)
|
|
{
|
|
if (!Parent->Streams)
|
|
{
|
|
NTSTATUS Result = CreateNodeSet(Airfs->CaseInsensitive, &Parent->Streams);
|
|
if (Result) return Result;
|
|
}
|
|
*PInserted = Parent->Streams->insert(Node).second;
|
|
}
|
|
else
|
|
#endif
|
|
*PInserted = Parent->Children->insert(Node).second;
|
|
if (*PInserted)
|
|
{
|
|
Node->Parent = Parent;
|
|
ReferenceNode(Node);
|
|
TouchNode(Parent);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
catch (...)
|
|
{
|
|
*PInserted = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void RemoveNode(AIRFS_ Airfs, NODE_ Node)
|
|
{
|
|
NODE_ Parent = Node->Parent;
|
|
TouchNode(Parent);
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream)
|
|
{
|
|
if (Parent->Streams)
|
|
{
|
|
auto found = Parent->Streams->find(Node);
|
|
if (found != Parent->Streams->end())
|
|
Parent->Streams->erase(found);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
Parent->Children->erase(Node);
|
|
|
|
DereferenceNode(Airfs, Node);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
inline BOOLEAN NodeHasChildren(NODE_ Node)
|
|
{
|
|
return Node->Children && !Node->Children->empty();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef AIRFS_SLOWIO
|
|
/*
|
|
* SLOWIO
|
|
*
|
|
* This is included for two uses:
|
|
*
|
|
* 1) For testing winfsp, by allowing Airfs to act more like a non-ram file system,
|
|
* with some IO taking many milliseconds, and some IO completion delayed.
|
|
*
|
|
* 2) As sample code for how to use winfsp's STATUS_PENDING capabilities.
|
|
*
|
|
*/
|
|
|
|
inline UINT64 Hash(UINT64 x)
|
|
{
|
|
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ull;
|
|
x = (x ^ (x >> 27)) * 0x94d049bb133111ebull;
|
|
x = x ^ (x >> 31);
|
|
return x;
|
|
}
|
|
|
|
inline ULONG PseudoRandom(ULONG to)
|
|
{
|
|
// John Oberschelp's PRNG
|
|
static UINT64 spin = 0;
|
|
InterlockedIncrement(&spin);
|
|
return Hash(spin) % to;
|
|
}
|
|
|
|
inline BOOLEAN SlowioReturnPending(FSP_FILE_SYSTEM *FileSystem)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
if (!Airfs->SlowioMaxDelay)
|
|
return FALSE;
|
|
return PseudoRandom(100) < Airfs->SlowioPercentDelay;
|
|
}
|
|
|
|
inline void SlowioSnooze(FSP_FILE_SYSTEM *FileSystem)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
if (!Airfs->SlowioMaxDelay)
|
|
return;
|
|
ULONG millis = PseudoRandom(Airfs->SlowioMaxDelay + 1) >> PseudoRandom(Airfs->SlowioRarefyDelay + 1);
|
|
Sleep(millis);
|
|
}
|
|
|
|
void SlowioReadThread(
|
|
FSP_FILE_SYSTEM *FileSystem,
|
|
NODE_ Node,
|
|
PVOID Buffer,
|
|
UINT64 Offset,
|
|
UINT64 EndOffset,
|
|
UINT64 RequestHint)
|
|
{
|
|
SlowioSnooze(FileSystem);
|
|
|
|
memcpy(Buffer, (PUINT8)Node->FileData + Offset, (size_t)(EndOffset - Offset));
|
|
UINT32 BytesTransferred = (ULONG)(EndOffset - Offset);
|
|
|
|
FSP_FSCTL_TRANSACT_RSP ResponseBuf;
|
|
memset(&ResponseBuf, 0, sizeof ResponseBuf);
|
|
ResponseBuf.Size = sizeof ResponseBuf;
|
|
ResponseBuf.Kind = FspFsctlTransactReadKind;
|
|
ResponseBuf.Hint = RequestHint; // IRP that is being completed
|
|
ResponseBuf.IoStatus.Status = STATUS_SUCCESS;
|
|
ResponseBuf.IoStatus.Information = BytesTransferred; // bytes read
|
|
FspFileSystemSendResponse(FileSystem, &ResponseBuf);
|
|
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
InterlockedDecrement(&Airfs->SlowioThreadsRunning);
|
|
}
|
|
|
|
void SlowioWriteThread(
|
|
FSP_FILE_SYSTEM *FileSystem,
|
|
NODE_ Node,
|
|
PVOID Buffer,
|
|
UINT64 Offset,
|
|
UINT64 EndOffset,
|
|
UINT64 RequestHint)
|
|
{
|
|
SlowioSnooze(FileSystem);
|
|
|
|
memcpy((PUINT8)Node->FileData + Offset, Buffer, (size_t)(EndOffset - Offset));
|
|
UINT32 BytesTransferred = (ULONG)(EndOffset - Offset);
|
|
|
|
FSP_FSCTL_TRANSACT_RSP ResponseBuf;
|
|
memset(&ResponseBuf, 0, sizeof ResponseBuf);
|
|
ResponseBuf.Size = sizeof ResponseBuf;
|
|
ResponseBuf.Kind = FspFsctlTransactWriteKind;
|
|
ResponseBuf.Hint = RequestHint; // IRP that is being completed
|
|
ResponseBuf.IoStatus.Status = STATUS_SUCCESS;
|
|
ResponseBuf.IoStatus.Information = BytesTransferred; // bytes written
|
|
GetFileInfo(Node, &ResponseBuf.Rsp.Write.FileInfo);
|
|
FspFileSystemSendResponse(FileSystem, &ResponseBuf);
|
|
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
InterlockedDecrement(&Airfs->SlowioThreadsRunning);
|
|
}
|
|
|
|
void SlowioReadDirectoryThread(
|
|
FSP_FILE_SYSTEM *FileSystem,
|
|
ULONG BytesTransferred,
|
|
UINT64 RequestHint)
|
|
{
|
|
SlowioSnooze(FileSystem);
|
|
|
|
FSP_FSCTL_TRANSACT_RSP ResponseBuf;
|
|
memset(&ResponseBuf, 0, sizeof ResponseBuf);
|
|
ResponseBuf.Size = sizeof ResponseBuf;
|
|
ResponseBuf.Kind = FspFsctlTransactQueryDirectoryKind;
|
|
ResponseBuf.Hint = RequestHint; // IRP that is being completed
|
|
ResponseBuf.IoStatus.Status = STATUS_SUCCESS;
|
|
ResponseBuf.IoStatus.Information = BytesTransferred; // bytes of directory info read
|
|
FspFileSystemSendResponse(FileSystem, &ResponseBuf);
|
|
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
InterlockedDecrement(&Airfs->SlowioThreadsRunning);
|
|
}
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
#if defined(AIRFS_REPARSE_POINTS)
|
|
|
|
NTSTATUS GetReparsePointByName(FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
|
PWSTR Name, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
// GetReparsePointByName will never receive a named stream.
|
|
assert(wcschr(Name, L':') == 0);
|
|
#endif
|
|
|
|
NTSTATUS Result = FindNode(Airfs, Name, 0, 0, &Node);
|
|
|
|
if (!Node)
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
|
|
return STATUS_NOT_A_REPARSE_POINT;
|
|
|
|
if (Buffer)
|
|
{
|
|
if (Node->ReparseDataSize > *PSize)
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
*PSize = Node->ReparseDataSize;
|
|
memcpy(Buffer, Node->ReparseData, Node->ReparseDataSize);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS SetAllocSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node,
|
|
UINT64 RequestedAllocSize)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
|
|
RequestedAllocSize = IN_ALLOCATION_UNITS(RequestedAllocSize);
|
|
|
|
if (Node->FileInfo.AllocationSize != RequestedAllocSize)
|
|
{
|
|
if (RequestedAllocSize > Airfs->MaxFileSize) return STATUS_DISK_FULL;
|
|
|
|
// Reallocate only if the file is made smaller, or if it will not fit in the actual memory footprint.
|
|
size_t ActualSize = AirfsHeapSize(Node->FileData);
|
|
if (RequestedAllocSize < Node->FileInfo.AllocationSize || RequestedAllocSize > ActualSize)
|
|
{
|
|
// If the file grow request was modest, guess that it might happen again, and grow the file by 50%.
|
|
if (RequestedAllocSize > Node->FileInfo.AllocationSize && RequestedAllocSize <= ActualSize + ActualSize / 8)
|
|
RequestedAllocSize = IN_ALLOCATION_UNITS(ActualSize + ActualSize / 2);
|
|
|
|
PVOID FileData = AirfsHeapRealloc(Node->FileData, (size_t)RequestedAllocSize);
|
|
if (!FileData && RequestedAllocSize > 0)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Node->FileData = FileData;
|
|
}
|
|
|
|
Node->FileInfo.AllocationSize = RequestedAllocSize;
|
|
if (Node->FileInfo.FileSize > RequestedAllocSize)
|
|
Node->FileInfo.FileSize = RequestedAllocSize;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, NODE_ Node,
|
|
UINT64 RequestedFileSize)
|
|
{
|
|
if (Node->FileInfo.FileSize != RequestedFileSize)
|
|
{
|
|
if (Node->FileInfo.AllocationSize < RequestedFileSize)
|
|
{
|
|
NTSTATUS Result = SetAllocSize(FileSystem, Node, RequestedFileSize);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
}
|
|
|
|
if (Node->FileInfo.FileSize < RequestedFileSize)
|
|
memset((PUINT8)Node->FileData + Node->FileInfo.FileSize, 0,
|
|
(size_t)(RequestedFileSize - Node->FileInfo.FileSize));
|
|
Node->FileInfo.FileSize = RequestedFileSize;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
BOOLEAN AddDirInfo(NODE_ Node, PWSTR Name, PVOID Buffer, ULONG Length,
|
|
PULONG PBytesTransferred)
|
|
{
|
|
UINT8 DirInfoBuf[sizeof(FSP_FSCTL_DIR_INFO) + sizeof Node->Name];
|
|
FSP_FSCTL_DIR_INFO *DirInfo = (FSP_FSCTL_DIR_INFO *)DirInfoBuf;
|
|
WCHAR Root[2] = L"\\";
|
|
PWSTR Remain, Suffix;
|
|
|
|
if (!Name)
|
|
{
|
|
FspPathSuffix(Node->Name, &Remain, &Suffix, Root);
|
|
Name = Suffix;
|
|
FspPathCombine(Node->Name, Suffix);
|
|
}
|
|
|
|
memset(DirInfo->Padding, 0, sizeof DirInfo->Padding);
|
|
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(Name) * sizeof(WCHAR));
|
|
DirInfo->FileInfo = Node->FileInfo;
|
|
memcpy(DirInfo->FileNameBuf, Name, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO));
|
|
|
|
return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiGetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
|
|
VolumeInfo->TotalSize = Airfs->MaxNodes * (UINT64)Airfs->MaxFileSize;
|
|
VolumeInfo->FreeSize = (Airfs->MaxNodes - Airfs->NumNodes) *
|
|
(UINT64)Airfs->MaxFileSize;
|
|
VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength;
|
|
memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiSetVolumeLabel(FSP_FILE_SYSTEM *FileSystem, PWSTR VolumeLabel,
|
|
FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
|
|
Airfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof(WCHAR));
|
|
if (Airfs->VolumeLabelLength > sizeof Airfs->VolumeLabel)
|
|
Airfs->VolumeLabelLength = sizeof Airfs->VolumeLabel;
|
|
memcpy(Airfs->VolumeLabel, VolumeLabel, Airfs->VolumeLabelLength);
|
|
|
|
VolumeInfo->TotalSize = Airfs->MaxNodes * Airfs->MaxFileSize;
|
|
VolumeInfo->FreeSize =
|
|
(Airfs->MaxNodes - Airfs->NumNodes) * Airfs->MaxFileSize;
|
|
VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength;
|
|
memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, PUINT32 PFileAttributes,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node;
|
|
NTSTATUS Result = FindNode(Airfs, Name, 0, 0, &Node);
|
|
if (!Node)
|
|
{
|
|
#if defined(AIRFS_REPARSE_POINTS)
|
|
if (FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0,
|
|
Name, PFileAttributes))
|
|
{
|
|
return STATUS_REPARSE;
|
|
}
|
|
#endif
|
|
return Result;
|
|
}
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
UINT32 FileAttributesMask = ~(UINT32)0;
|
|
if (Node->IsAStream)
|
|
{
|
|
FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY;
|
|
Node = Node->Parent;
|
|
}
|
|
|
|
if (PFileAttributes)
|
|
*PFileAttributes = Node->FileInfo.FileAttributes & FileAttributesMask;
|
|
#else
|
|
if (PFileAttributes)
|
|
*PFileAttributes = Node->FileInfo.FileAttributes;
|
|
#endif
|
|
|
|
if (PSecurityDescriptorSize)
|
|
{
|
|
if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize)
|
|
{
|
|
*PSecurityDescriptorSize = Node->SecurityDescriptorSize;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
*PSecurityDescriptorSize = Node->SecurityDescriptorSize;
|
|
if (SecurityDescriptor)
|
|
memcpy(SecurityDescriptor, Node->SecurityDescriptor, Node->SecurityDescriptorSize);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiCreate(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions,
|
|
UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
UINT64 AllocationSize, PVOID *PNode, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node;
|
|
NODE_ Parent;
|
|
NTSTATUS Result;
|
|
BOOLEAN Inserted;
|
|
PWSTR BaseName;
|
|
|
|
if (AIRFS_MAX_PATH <= wcslen(Name))
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
|
|
if (CreateOptions & FILE_DIRECTORY_FILE)
|
|
AllocationSize = 0;
|
|
|
|
Result = FindNode(Airfs, Name, &BaseName, &Parent, &Node);
|
|
|
|
if (Node)
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
if (!Parent)
|
|
return Result;
|
|
|
|
if (Airfs->NumNodes >= Airfs->MaxNodes)
|
|
return STATUS_CANNOT_MAKE;
|
|
|
|
if (AllocationSize > Airfs->MaxFileSize)
|
|
return STATUS_DISK_FULL;
|
|
|
|
Result = CreateNode(Airfs, BaseName, &Node);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
Node->IsAStream = BaseName[-1] == L':';
|
|
#endif
|
|
|
|
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
Result = CreateNodeSet(Airfs->CaseInsensitive, &Node->Children);
|
|
if (Result)
|
|
{
|
|
DeleteNode(Airfs, Node);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
Node->FileInfo.FileAttributes = (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
|
|
FileAttributes : FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
if (SecurityDescriptor)
|
|
{
|
|
Node->SecurityDescriptorSize = GetSecurityDescriptorLength(SecurityDescriptor);
|
|
Node->SecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(Node->SecurityDescriptorSize);
|
|
if (!Node->SecurityDescriptor)
|
|
{
|
|
DeleteNode(Airfs, Node);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
memcpy(Node->SecurityDescriptor, SecurityDescriptor, Node->SecurityDescriptorSize);
|
|
}
|
|
|
|
Node->FileInfo.AllocationSize = AllocationSize;
|
|
if (Node->FileInfo.AllocationSize)
|
|
{
|
|
Node->FileData = AirfsHeapAlloc((size_t)Node->FileInfo.AllocationSize);
|
|
if (!Node->FileData)
|
|
{
|
|
DeleteNode(Airfs, Node);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
Result = InsertNode(Airfs, Parent, Node, &Inserted);
|
|
if (!NT_SUCCESS(Result) || !Inserted)
|
|
{
|
|
DeleteNode(Airfs, Node);
|
|
if (NT_SUCCESS(Result))
|
|
Result = STATUS_OBJECT_NAME_COLLISION; // Should not happen!
|
|
return Result;
|
|
}
|
|
|
|
ReferenceNode(Node);
|
|
*PNode = Node;
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
#if defined(AIRFS_NAME_NORMALIZATION)
|
|
if (Airfs->CaseInsensitive)
|
|
{
|
|
int ParentPathNumBytes = (int)( (char*)BaseName - (char*)Name );
|
|
int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof(WCHAR) );
|
|
|
|
FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo);
|
|
memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes);
|
|
memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof(WCHAR), Node->Name, NodeNameNumBytes);
|
|
OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes;
|
|
}
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiOpen(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions,
|
|
UINT32 GrantedAccess, PVOID *PNode, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node;
|
|
NTSTATUS Result;
|
|
|
|
if (AIRFS_MAX_PATH <= wcslen(Name))
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
|
|
PWSTR baseName;
|
|
Result = FindNode(Airfs, Name, &baseName, 0, &Node);
|
|
if (Result) return Result;
|
|
if (!Node)
|
|
{
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
ReferenceNode(Node);
|
|
*PNode = Node;
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
#if defined(AIRFS_NAME_NORMALIZATION)
|
|
if (Airfs->CaseInsensitive)
|
|
{
|
|
int ParentPathNumBytes = (int)( (char*)baseName - (char*)Name );
|
|
int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof(WCHAR) );
|
|
|
|
FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo);
|
|
memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes);
|
|
memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof(WCHAR), Node->Name, NodeNameNumBytes);
|
|
OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes;
|
|
}
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiOverwrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttributes,
|
|
BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node = (NODE_) Node0;
|
|
NTSTATUS Result;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->Streams)
|
|
{
|
|
for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); )
|
|
{
|
|
NODE_ Stream = *Iter++;
|
|
LONG RefCount = Stream->RefCount;
|
|
MemoryBarrier();
|
|
if (RefCount <= 1)
|
|
{
|
|
RemoveNode(Airfs, Stream);
|
|
}
|
|
}
|
|
if (Node->Streams->empty())
|
|
{
|
|
delete Node->Streams;
|
|
Node->Streams = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Result = SetAllocSize(FileSystem, Node, AllocationSize);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
if (ReplaceFileAttributes)
|
|
Node->FileInfo.FileAttributes = FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
|
|
else
|
|
Node->FileInfo.FileAttributes |= FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
Node->FileInfo.FileSize = 0;
|
|
Node->FileInfo.LastAccessTime =
|
|
Node->FileInfo.LastWriteTime =
|
|
Node->FileInfo.ChangeTime = GetSystemTime();
|
|
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void ApiCleanup(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, ULONG Flags)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node = (NODE_) Node0;
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
NODE_ MainNode = Node->IsAStream ? Node->Parent : Node;
|
|
#else
|
|
NODE_ MainNode = Node;
|
|
#endif
|
|
|
|
assert(Flags); // FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this.
|
|
|
|
if (Flags & FspCleanupSetArchiveBit)
|
|
{
|
|
if (!(MainNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
MainNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|
}
|
|
|
|
if (Flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime | FspCleanupSetChangeTime))
|
|
{
|
|
UINT64 SystemTime = GetSystemTime();
|
|
|
|
if (Flags & FspCleanupSetLastAccessTime)
|
|
MainNode->FileInfo.LastAccessTime = SystemTime;
|
|
if (Flags & FspCleanupSetLastWriteTime)
|
|
MainNode->FileInfo.LastWriteTime = SystemTime;
|
|
if (Flags & FspCleanupSetChangeTime)
|
|
MainNode->FileInfo.ChangeTime = SystemTime;
|
|
}
|
|
|
|
if (Flags & FspCleanupSetAllocationSize)
|
|
{
|
|
SetAllocSize(FileSystem, Node, Node->FileInfo.FileSize);
|
|
}
|
|
|
|
if ((Flags & FspCleanupDelete) && !NodeHasChildren(Node))
|
|
{
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->Streams)
|
|
{
|
|
for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); )
|
|
{
|
|
NODE_ Stream = *Iter++;
|
|
DeleteNode(Airfs, Stream);
|
|
}
|
|
delete Node->Streams;
|
|
Node->Streams = 0;
|
|
}
|
|
#endif
|
|
|
|
RemoveNode(Airfs, Node);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void ApiClose(FSP_FILE_SYSTEM *FileSystem, PVOID Node0)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node = (NODE_) Node0;
|
|
DereferenceNode(Airfs, Node);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiRead(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer,
|
|
UINT64 Offset, ULONG Length, PULONG PBytesTransferred)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
UINT64 EndOffset;
|
|
|
|
if (Offset >= Node->FileInfo.FileSize)
|
|
return STATUS_END_OF_FILE;
|
|
|
|
EndOffset = Offset + Length;
|
|
if (EndOffset > Node->FileInfo.FileSize)
|
|
EndOffset = Node->FileInfo.FileSize;
|
|
|
|
#ifdef AIRFS_SLOWIO
|
|
if (SlowioReturnPending(FileSystem))
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
try
|
|
{
|
|
InterlockedIncrement(&Airfs->SlowioThreadsRunning);
|
|
std::thread(SlowioReadThread,
|
|
FileSystem, Node, Buffer, Offset, EndOffset,
|
|
FspFileSystemGetOperationContext()->Request->Hint).
|
|
detach();
|
|
return STATUS_PENDING;
|
|
}
|
|
catch (...)
|
|
{
|
|
InterlockedDecrement(&Airfs->SlowioThreadsRunning);
|
|
}
|
|
}
|
|
SlowioSnooze(FileSystem);
|
|
#endif
|
|
|
|
memcpy(Buffer, (PUINT8)Node->FileData + Offset, (size_t)(EndOffset - Offset));
|
|
|
|
*PBytesTransferred = (ULONG)(EndOffset - Offset);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiWrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer,
|
|
UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile,
|
|
BOOLEAN ConstrainedIo, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
UINT64 EndOffset;
|
|
NTSTATUS Result;
|
|
|
|
if (ConstrainedIo)
|
|
{
|
|
if (Offset >= Node->FileInfo.FileSize)
|
|
return STATUS_SUCCESS;
|
|
EndOffset = Offset + Length;
|
|
if (EndOffset > Node->FileInfo.FileSize)
|
|
EndOffset = Node->FileInfo.FileSize;
|
|
}
|
|
else
|
|
{
|
|
if (WriteToEndOfFile)
|
|
Offset = Node->FileInfo.FileSize;
|
|
EndOffset = Offset + Length;
|
|
if (EndOffset > Node->FileInfo.FileSize)
|
|
{
|
|
Result = SetFileSize(FileSystem, Node, EndOffset);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
#ifdef AIRFS_SLOWIO
|
|
if (SlowioReturnPending(FileSystem))
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
try
|
|
{
|
|
InterlockedIncrement(&Airfs->SlowioThreadsRunning);
|
|
std::thread(SlowioWriteThread,
|
|
FileSystem, Node, Buffer, Offset, EndOffset,
|
|
FspFileSystemGetOperationContext()->Request->Hint).
|
|
detach();
|
|
return STATUS_PENDING;
|
|
}
|
|
catch (...)
|
|
{
|
|
InterlockedDecrement(&Airfs->SlowioThreadsRunning);
|
|
}
|
|
}
|
|
SlowioSnooze(FileSystem);
|
|
#endif
|
|
|
|
memcpy((PUINT8)Node->FileData + Offset, Buffer, (size_t)(EndOffset - Offset));
|
|
|
|
*PBytesTransferred = (ULONG)(EndOffset - Offset);
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiFlush(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
// Nothing to flush, since we do not cache anything.
|
|
|
|
if (Node)
|
|
{
|
|
#if 0
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream)
|
|
Node->MainNode->FileInfo.LastAccessTime =
|
|
Node->MainNode->FileInfo.LastWriteTime =
|
|
Node->MainNode->FileInfo.ChangeTime = GetSystemTime();
|
|
else
|
|
#endif
|
|
Node->FileInfo.LastAccessTime =
|
|
Node->FileInfo.LastWriteTime =
|
|
Node->FileInfo.ChangeTime = GetSystemTime();
|
|
#endif
|
|
|
|
GetFileInfo(Node, FileInfo);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiGetFileInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiSetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttributes,
|
|
UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime,
|
|
FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
#endif
|
|
|
|
if (INVALID_FILE_ATTRIBUTES != FileAttributes)
|
|
Node->FileInfo.FileAttributes = FileAttributes;
|
|
if (CreationTime ) Node->FileInfo.CreationTime = CreationTime;
|
|
if (LastAccessTime ) Node->FileInfo.LastAccessTime = LastAccessTime;
|
|
if (LastWriteTime ) Node->FileInfo.LastWriteTime = LastWriteTime;
|
|
if (ChangeTime ) Node->FileInfo.ChangeTime = ChangeTime;
|
|
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiSetFileSize(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
NTSTATUS Result = SetAllocationSize
|
|
? SetAllocSize(FileSystem, Node, NewSize)
|
|
: SetFileSize(FileSystem, Node, NewSize);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
GetFileInfo(Node, FileInfo);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiCanDelete(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
if (NodeHasChildren(Node))
|
|
return STATUS_DIRECTORY_NOT_EMPTY;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiRename(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name,
|
|
PWSTR NewFileName, BOOLEAN ReplaceIfExists)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node = (NODE_) Node0;
|
|
NODE_ NewNode;
|
|
BOOLEAN Inserted;
|
|
NTSTATUS Result;
|
|
PWSTR newBaseName;
|
|
NODE_ NewParent;
|
|
Result = FindNode(Airfs, NewFileName, &newBaseName, &NewParent, &NewNode);
|
|
|
|
if (NewNode && Node != NewNode)
|
|
{
|
|
if (!ReplaceIfExists)
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
if (NewNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
ReferenceNode(NewNode);
|
|
RemoveNode(Airfs, NewNode);
|
|
DereferenceNode(Airfs, NewNode);
|
|
}
|
|
|
|
ReferenceNode(Node);
|
|
RemoveNode(Airfs, Node);
|
|
wcscpy_s(Node->Name, sizeof Node->Name / sizeof(WCHAR), newBaseName);
|
|
Result = InsertNode(Airfs, NewParent, Node, &Inserted);
|
|
DereferenceNode(Airfs, Node);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiGetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
#endif
|
|
|
|
if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize)
|
|
{
|
|
*PSecurityDescriptorSize = Node->SecurityDescriptorSize;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
*PSecurityDescriptorSize = Node->SecurityDescriptorSize;
|
|
if (SecurityDescriptor)
|
|
memcpy(SecurityDescriptor, Node->SecurityDescriptor, Node->SecurityDescriptorSize);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiSetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
PSECURITY_DESCRIPTOR NewSecurityDescriptor, SecurityDescriptor;
|
|
SIZE_T SecurityDescriptorSize;
|
|
NTSTATUS Result;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
#endif
|
|
|
|
Result = FspSetSecurityDescriptor(
|
|
Node->SecurityDescriptor,
|
|
SecurityInformation,
|
|
ModificationDescriptor,
|
|
&NewSecurityDescriptor);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
SecurityDescriptorSize = GetSecurityDescriptorLength(NewSecurityDescriptor);
|
|
SecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SecurityDescriptorSize);
|
|
if (!SecurityDescriptor)
|
|
{
|
|
FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
memcpy(SecurityDescriptor, NewSecurityDescriptor, SecurityDescriptorSize);
|
|
FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor);
|
|
|
|
free(Node->SecurityDescriptor);
|
|
Node->SecurityDescriptorSize = SecurityDescriptorSize;
|
|
Node->SecurityDescriptor = SecurityDescriptor;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
PWSTR Pattern, PWSTR Marker, PVOID Buffer, ULONG Length,
|
|
PULONG PBytesTransferred)
|
|
{
|
|
assert(!Pattern);
|
|
|
|
AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext;
|
|
NODE_ Node = (NODE_) Node0;
|
|
NODE_ Parent = Node->Parent;
|
|
if (Parent)
|
|
{
|
|
// If this is not the root directory, add the dot entries.
|
|
if (!Marker)
|
|
{
|
|
if (!AddDirInfo(Node, L".", Buffer, Length, PBytesTransferred))
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if (!Marker || (L'.' == Marker[0] && L'\0' == Marker[1]))
|
|
{
|
|
if (!AddDirInfo(Parent, L"..", Buffer, Length, PBytesTransferred))
|
|
return STATUS_SUCCESS;
|
|
Marker = 0;
|
|
}
|
|
}
|
|
|
|
auto Iter = Marker ? Node->Children->upper_bound((NODE_)Marker) : Node->Children->begin();
|
|
for (; Iter != Node->Children->end(); ++Iter)
|
|
{
|
|
NODE_ Node = *Iter;
|
|
if (!AddDirInfo(Node, Node->Name, Buffer, Length, PBytesTransferred))
|
|
{
|
|
goto bufferReady;
|
|
}
|
|
}
|
|
FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred);
|
|
|
|
bufferReady:
|
|
|
|
#ifdef AIRFS_SLOWIO
|
|
if (SlowioReturnPending(FileSystem))
|
|
{
|
|
try
|
|
{
|
|
InterlockedIncrement(&Airfs->SlowioThreadsRunning);
|
|
std::thread(SlowioReadDirectoryThread,
|
|
FileSystem, *PBytesTransferred,
|
|
FspFileSystemGetOperationContext()->Request->Hint).
|
|
detach();
|
|
return STATUS_PENDING;
|
|
}
|
|
catch (...)
|
|
{
|
|
InterlockedDecrement(&Airfs->SlowioThreadsRunning);
|
|
}
|
|
}
|
|
SlowioSnooze(FileSystem);
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
#if defined(AIRFS_DIRINFO_BY_NAME)
|
|
|
|
NTSTATUS ApiGetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, PVOID ParentNode0,
|
|
PWSTR Name, FSP_FSCTL_DIR_INFO *DirInfo)
|
|
{
|
|
NODE_ Parent = (NODE_) ParentNode0;
|
|
NODE_ Node;
|
|
auto Iter = Parent->Children->find((NODE_)Name);
|
|
if (Iter == Parent->Children->end())
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
Node = *Iter;
|
|
|
|
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(Node->Name) * sizeof(WCHAR));
|
|
DirInfo->FileInfo = Node->FileInfo;
|
|
memcpy(DirInfo->FileNameBuf, Node->Name, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
#if defined(AIRFS_REPARSE_POINTS)
|
|
|
|
NTSTATUS ApiResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PWSTR Name,
|
|
UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent,
|
|
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize)
|
|
{
|
|
return FspFileSystemResolveReparsePoints(FileSystem, GetReparsePointByName, 0,
|
|
Name, ReparsePointIndex, ResolveLastPathComponent,
|
|
PIoStatus, Buffer, PSize);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiGetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
PWSTR Name, PVOID Buffer, PSIZE_T PSize)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
#endif
|
|
|
|
if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
|
|
return STATUS_NOT_A_REPARSE_POINT;
|
|
|
|
if (Node->ReparseDataSize > *PSize)
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
*PSize = Node->ReparseDataSize;
|
|
memcpy(Buffer, Node->ReparseData, Node->ReparseDataSize);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiSetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
PWSTR Name, PVOID Buffer, SIZE_T Size)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
PVOID ReparseData;
|
|
NTSTATUS Result;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
#endif
|
|
|
|
if (NodeHasChildren(Node))
|
|
return STATUS_DIRECTORY_NOT_EMPTY;
|
|
|
|
if (Node->ReparseData)
|
|
{
|
|
Result = FspFileSystemCanReplaceReparsePoint(
|
|
Node->ReparseData, Node->ReparseDataSize,
|
|
Buffer, Size);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
}
|
|
|
|
ReparseData = realloc(Node->ReparseData, Size);
|
|
if (!ReparseData && Size)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Node->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
|
|
Node->FileInfo.ReparseTag = *(PULONG)Buffer;
|
|
// The first field in a reparse buffer is the reparse tag.
|
|
Node->ReparseDataSize = Size;
|
|
Node->ReparseData = ReparseData;
|
|
memcpy(Node->ReparseData, Buffer, Size);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS ApiDeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
PWSTR Name, PVOID Buffer, SIZE_T Size)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
NTSTATUS Result;
|
|
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
#endif
|
|
|
|
if (Node->ReparseData)
|
|
{
|
|
Result = FspFileSystemCanReplaceReparsePoint(
|
|
Node->ReparseData, Node->ReparseDataSize,
|
|
Buffer, Size);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
}
|
|
else
|
|
return STATUS_NOT_A_REPARSE_POINT;
|
|
|
|
free(Node->ReparseData);
|
|
|
|
Node->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
|
|
Node->FileInfo.ReparseTag = 0;
|
|
Node->ReparseDataSize = 0;
|
|
Node->ReparseData = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
|
|
NTSTATUS ApiGetStreamInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0,
|
|
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
|
{
|
|
NODE_ Node = (NODE_) Node0;
|
|
|
|
if (Node->IsAStream) Node = Node->Parent;
|
|
|
|
if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
if (!AddStreamInfo(Node, L"", Buffer, Length, PBytesTransferred))
|
|
return STATUS_SUCCESS;
|
|
|
|
if (Node->Streams)
|
|
{
|
|
// TODO: how to handle out-of-response-buffer-space condition?
|
|
for (auto Iter = Node->Streams->begin(); Iter != Node->Streams->end(); ++Iter)
|
|
{
|
|
BOOLEAN added = AddStreamInfo(*Iter, (*Iter)->Name, Buffer, Length, PBytesTransferred);
|
|
if (!added) goto done;
|
|
}
|
|
}
|
|
FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred);
|
|
|
|
done:
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
#if defined(AIRFS_CONTROL)
|
|
|
|
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;
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
FSP_FILE_SYSTEM_INTERFACE AirfsInterface =
|
|
{
|
|
ApiGetVolumeInfo,
|
|
ApiSetVolumeLabel,
|
|
ApiGetSecurityByName,
|
|
ApiCreate,
|
|
ApiOpen,
|
|
ApiOverwrite,
|
|
ApiCleanup,
|
|
ApiClose,
|
|
ApiRead,
|
|
ApiWrite,
|
|
ApiFlush,
|
|
ApiGetFileInfo,
|
|
ApiSetBasicInfo,
|
|
ApiSetFileSize,
|
|
ApiCanDelete,
|
|
ApiRename,
|
|
ApiGetSecurity,
|
|
ApiSetSecurity,
|
|
ApiReadDirectory,
|
|
#if defined(AIRFS_REPARSE_POINTS)
|
|
ApiResolveReparsePoints,
|
|
ApiGetReparsePoint,
|
|
ApiSetReparsePoint,
|
|
ApiDeleteReparsePoint,
|
|
#else
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
#endif
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
ApiGetStreamInfo,
|
|
#else
|
|
0,
|
|
#endif
|
|
#if defined(AIRFS_DIRINFO_BY_NAME)
|
|
ApiGetDirInfoByName,
|
|
#else
|
|
0,
|
|
#endif
|
|
#if defined(AIRFS_CONTROL)
|
|
ApiControl,
|
|
#else
|
|
0,
|
|
#endif
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void AirfsDelete(AIRFS_ Airfs)
|
|
{
|
|
FspFileSystemDelete(Airfs->FileSystem);
|
|
|
|
DeleteAllNodes(Airfs);
|
|
|
|
free(Airfs);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS AirfsStart(AIRFS_ Airfs)
|
|
{
|
|
#ifdef AIRFS_SLOWIO
|
|
Airfs->SlowioThreadsRunning = 0;
|
|
#endif
|
|
|
|
return FspFileSystemStartDispatcher(Airfs->FileSystem, 0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void AirfsStop(AIRFS_ Airfs)
|
|
{
|
|
FspFileSystemStopDispatcher(Airfs->FileSystem);
|
|
|
|
#ifdef AIRFS_SLOWIO
|
|
while (Airfs->SlowioThreadsRunning)
|
|
Sleep(1);
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS AirfsCreate(
|
|
ULONG Flags,
|
|
ULONG FileInfoTimeout,
|
|
ULONG MaxNodes,
|
|
ULONG MaxFileSize,
|
|
ULONG SlowioMaxDelay,
|
|
ULONG SlowioPercentDelay,
|
|
ULONG SlowioRarefyDelay,
|
|
PWSTR FileSystemName,
|
|
PWSTR VolumePrefix,
|
|
PWSTR RootSddl,
|
|
AIRFS_ *PAirfs)
|
|
{
|
|
NTSTATUS Result;
|
|
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
|
BOOLEAN CaseInsensitive = !!(Flags & AirfsCaseInsensitive);
|
|
BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & AirfsFlushAndPurgeOnCleanup);
|
|
PWSTR DevicePath = AirfsNet == (Flags & AirfsDeviceMask) ?
|
|
L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME;
|
|
AIRFS_ Airfs;
|
|
NODE_ RootNode;
|
|
PSECURITY_DESCRIPTOR RootSecurity;
|
|
ULONG RootSecuritySize;
|
|
|
|
*PAirfs = 0;
|
|
|
|
if (!AirfsHeapInitialize())
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (!RootSddl)
|
|
RootSddl = L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)";
|
|
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(RootSddl, SDDL_REVISION_1,
|
|
&RootSecurity, &RootSecuritySize))
|
|
return FspNtStatusFromWin32(GetLastError());
|
|
|
|
Airfs = (AIRFS_) malloc(sizeof *Airfs);
|
|
if (!Airfs)
|
|
{
|
|
LocalFree(RootSecurity);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
memset(Airfs, 0, sizeof *Airfs);
|
|
Airfs->MaxNodes = MaxNodes;
|
|
Airfs->MaxFileSize = IN_ALLOCATION_UNITS(MaxFileSize);
|
|
|
|
#ifdef AIRFS_SLOWIO
|
|
Airfs->SlowioMaxDelay = SlowioMaxDelay;
|
|
Airfs->SlowioPercentDelay = SlowioPercentDelay;
|
|
Airfs->SlowioRarefyDelay = SlowioRarefyDelay;
|
|
#endif
|
|
|
|
Airfs->CaseInsensitive = CaseInsensitive;
|
|
|
|
memset(&VolumeParams, 0, sizeof VolumeParams);
|
|
VolumeParams.Version = sizeof FSP_FSCTL_VOLUME_PARAMS;
|
|
VolumeParams.SectorSize = SECTOR_SIZE;
|
|
VolumeParams.SectorsPerAllocationUnit = SECTORS_PER_ALLOCATION_UNIT;
|
|
VolumeParams.VolumeCreationTime = GetSystemTime();
|
|
VolumeParams.VolumeSerialNumber = (UINT32)(GetSystemTime() / (10000 * 1000));
|
|
VolumeParams.FileInfoTimeout = FileInfoTimeout;
|
|
VolumeParams.CaseSensitiveSearch = !CaseInsensitive;
|
|
VolumeParams.CasePreservedNames = 1;
|
|
VolumeParams.UnicodeOnDisk = 1;
|
|
VolumeParams.PersistentAcls = 1;
|
|
VolumeParams.ReparsePoints = 1;
|
|
VolumeParams.ReparsePointsAccessCheck = 0;
|
|
#if defined(AIRFS_NAMED_STREAMS)
|
|
VolumeParams.NamedStreams = 1;
|
|
#endif
|
|
VolumeParams.PostCleanupWhenModifiedOnly = 1;
|
|
#if defined(AIRFS_DIRINFO_BY_NAME)
|
|
VolumeParams.PassQueryDirectoryFileName = 1;
|
|
#endif
|
|
VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup;
|
|
#if defined(AIRFS_CONTROL)
|
|
VolumeParams.DeviceControl = 1;
|
|
#endif
|
|
if (VolumePrefix)
|
|
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
|
|
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),
|
|
FileSystemName ? FileSystemName : L"-AIRFS");
|
|
|
|
Result = FspFileSystemCreate(DevicePath, &VolumeParams, &AirfsInterface, &Airfs->FileSystem);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
DeleteAllNodes(Airfs);
|
|
free(Airfs);
|
|
LocalFree(RootSecurity);
|
|
return Result;
|
|
}
|
|
|
|
Airfs->FileSystem->UserContext = Airfs;
|
|
Airfs->VolumeLabelLength = sizeof L"AIRFS" - sizeof(WCHAR);
|
|
memcpy(Airfs->VolumeLabel, L"AIRFS", Airfs->VolumeLabelLength);
|
|
|
|
#if 0
|
|
FspFileSystemSetOperationGuardStrategy(Airfs->FileSystem,
|
|
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE);
|
|
#endif
|
|
|
|
// Create the root directory.
|
|
Result = CreateNode(Airfs, L"", &RootNode);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
AirfsDelete(Airfs);
|
|
LocalFree(RootSecurity);
|
|
return Result;
|
|
}
|
|
Result = CreateNodeSet(Airfs->CaseInsensitive, &RootNode->Children);
|
|
if (Result)
|
|
{
|
|
AirfsDelete(Airfs);
|
|
LocalFree(RootSecurity);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RootNode->FileInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
RootNode->SecurityDescriptor = malloc(RootSecuritySize);
|
|
if (!RootNode->SecurityDescriptor)
|
|
{
|
|
DeleteNode(Airfs, RootNode);
|
|
AirfsDelete(Airfs);
|
|
LocalFree(RootSecurity);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RootNode->SecurityDescriptorSize = RootSecuritySize;
|
|
memcpy(RootNode->SecurityDescriptor, RootSecurity, RootSecuritySize);
|
|
Airfs->Root = RootNode;
|
|
ReferenceNode(RootNode);
|
|
|
|
LocalFree(RootSecurity);
|
|
*PAirfs = Airfs;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#define PROGNAME "airfs"
|
|
|
|
#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE , format, __VA_ARGS__)
|
|
#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE , format, __VA_ARGS__)
|
|
#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE , format, __VA_ARGS__)
|
|
|
|
#define argtos(v) if (arge > ++argp) v = *argp; else goto usage
|
|
#define argtol(v) if (arge > ++argp) v = wcstol_default(*argp, v); else goto usage
|
|
|
|
static ULONG wcstol_default(wchar_t *w, ULONG deflt)
|
|
{
|
|
wchar_t *endp;
|
|
ULONG ul = wcstol(w, &endp, 0);
|
|
return L'\0' != w[0] && L'\0' == *endp ? ul : deflt;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
|
|
{
|
|
wchar_t **argp, **arge;
|
|
ULONG DebugFlags = 0;
|
|
PWSTR DebugLogFile = 0;
|
|
ULONG Flags = AirfsDisk;
|
|
ULONG OtherFlags = 0;
|
|
ULONG FileInfoTimeout = INFINITE;
|
|
ULONG MaxNodes = 1024;
|
|
ULONG MaxFileSize = 16 * 1024 * 1024;
|
|
ULONG SlowioMaxDelay = 0; // -M: maximum slow IO delay in milliseconds
|
|
ULONG SlowioPercentDelay = 0; // -P: percent of slow IO to make pending
|
|
ULONG SlowioRarefyDelay = 0; // -R: adjust the rarity of pending slow IO
|
|
PWSTR FileSystemName = 0;
|
|
PWSTR MountPoint = 0;
|
|
PWSTR VolumePrefix = 0;
|
|
PWSTR RootSddl = 0;
|
|
HANDLE DebugLogHandle = INVALID_HANDLE_VALUE;
|
|
AIRFS_ Airfs = 0;
|
|
NTSTATUS Result;
|
|
|
|
for (argp = argv + 1, arge = argv + argc; arge > argp; argp++)
|
|
{
|
|
if (L'-' != argp[0][0])
|
|
break;
|
|
switch (argp[0][1])
|
|
{
|
|
case L'?': goto usage;
|
|
case L'd': argtol(DebugFlags); break;
|
|
case L'D': argtos(DebugLogFile); break;
|
|
case L'f': OtherFlags = AirfsFlushAndPurgeOnCleanup; break;
|
|
case L'F': argtos(FileSystemName); break;
|
|
case L'i': OtherFlags = AirfsCaseInsensitive; break;
|
|
case L'm': argtos(MountPoint); break;
|
|
case L'M': argtol(SlowioMaxDelay); break;
|
|
case L'n': argtol(MaxNodes); break;
|
|
case L'P': argtol(SlowioPercentDelay); break;
|
|
case L'R': argtol(SlowioRarefyDelay); break;
|
|
case L'S': argtos(RootSddl); break;
|
|
case L's': argtol(MaxFileSize); break;
|
|
case L't': argtol(FileInfoTimeout); break;
|
|
case L'u':
|
|
argtos(VolumePrefix);
|
|
if (VolumePrefix && L'\0' != VolumePrefix[0])
|
|
Flags = AirfsNet;
|
|
break;
|
|
default:
|
|
goto usage;
|
|
}
|
|
}
|
|
|
|
if (arge > argp)
|
|
goto usage;
|
|
|
|
if (AirfsDisk == Flags && !MountPoint)
|
|
goto usage;
|
|
|
|
if (DebugLogFile)
|
|
{
|
|
if (!wcscmp(L"-", DebugLogFile))
|
|
DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE);
|
|
else
|
|
DebugLogHandle = CreateFileW(
|
|
DebugLogFile,
|
|
FILE_APPEND_DATA,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
0,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0);
|
|
if (INVALID_HANDLE_VALUE == DebugLogHandle)
|
|
{
|
|
fail(L"cannot open debug log file");
|
|
goto usage;
|
|
}
|
|
|
|
FspDebugLogSetHandle(DebugLogHandle);
|
|
}
|
|
|
|
Result = AirfsCreate(
|
|
Flags | OtherFlags,
|
|
FileInfoTimeout,
|
|
MaxNodes,
|
|
MaxFileSize,
|
|
SlowioMaxDelay,
|
|
SlowioPercentDelay,
|
|
SlowioRarefyDelay,
|
|
FileSystemName,
|
|
VolumePrefix,
|
|
RootSddl,
|
|
&Airfs);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
fail(L"cannot create AIRFS");
|
|
goto exit;
|
|
}
|
|
|
|
FspFileSystemSetDebugLog(Airfs->FileSystem, DebugFlags);
|
|
|
|
if (MountPoint && L'\0' != MountPoint[0])
|
|
{
|
|
Result = FspFileSystemSetMountPoint(Airfs->FileSystem,
|
|
L'*' == MountPoint[0] && L'\0' == MountPoint[1] ? 0 : MountPoint);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
fail(L"cannot mount AIRFS");
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
Result = AirfsStart(Airfs);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
fail(L"cannot start AIRFS");
|
|
goto exit;
|
|
}
|
|
|
|
MountPoint = FspFileSystemMountPoint(Airfs->FileSystem);
|
|
|
|
info(L"%s -t %ld -n %ld -s %ld%s%s%s%s%s%s",
|
|
L"" PROGNAME, FileInfoTimeout, MaxNodes, MaxFileSize,
|
|
RootSddl ? L" -S " : L"", RootSddl ? RootSddl : L"",
|
|
VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"",
|
|
VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"",
|
|
MountPoint ? L" -m " : L"", MountPoint ? MountPoint : L"");
|
|
|
|
Service->UserContext = Airfs;
|
|
Result = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (!NT_SUCCESS(Result) && Airfs)
|
|
AirfsDelete(Airfs);
|
|
|
|
return Result;
|
|
|
|
usage:
|
|
static wchar_t usage[] = L""
|
|
"usage: %s OPTIONS\n"
|
|
"\n"
|
|
"options:\n"
|
|
" -d DebugFlags [-1: enable all debug logs]\n"
|
|
" -D DebugLogFile [file path; use - for stderr]\n"
|
|
" -i [case insensitive file system]\n"
|
|
" -f [flush and purge cache on cleanup]\n"
|
|
" -t FileInfoTimeout [millis]\n"
|
|
" -n MaxNodes\n"
|
|
" -s MaxFileSize [bytes]\n"
|
|
" -M MaxDelay [maximum slow IO delay in millis]\n"
|
|
" -P PercentDelay [percent of slow IO to make pending]\n"
|
|
" -R RarefyDelay [adjust the rarity of pending slow IO]\n"
|
|
" -F FileSystemName\n"
|
|
" -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n"
|
|
" -u \\Server\\Share [UNC prefix (single backslash)]\n"
|
|
" -m MountPoint [X:|* (required if no UNC prefix)]\n";
|
|
|
|
fail(usage, L"" PROGNAME);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS SvcStop(FSP_SERVICE *Service)
|
|
{
|
|
AIRFS_ Airfs = (AIRFS_) Service->UserContext;
|
|
|
|
AirfsStop(Airfs);
|
|
AirfsDelete(Airfs);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
int wmain(int argc, wchar_t **argv)
|
|
{
|
|
if (!NT_SUCCESS(FspLoad(0)))
|
|
return ERROR_DELAY_LOAD_FAILED;
|
|
|
|
return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|