mirror of
				https://github.com/winfsp/winfsp.git
				synced 2025-10-30 19:48:38 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			1112 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1112 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * @file memfs.cpp
 | |
|  *
 | |
|  * @copyright 2015-2016 Bill Zissimopoulos
 | |
|  */
 | |
| /*
 | |
|  * This file is part of WinFsp.
 | |
|  *
 | |
|  * You can redistribute it and/or modify it under the terms of the
 | |
|  * GNU Affero General Public License version 3 as published by the
 | |
|  * Free Software Foundation.
 | |
|  *
 | |
|  * Licensees holding a valid commercial license may use this file in
 | |
|  * accordance with the commercial license agreement provided with the
 | |
|  * software.
 | |
|  */
 | |
| 
 | |
| #undef _DEBUG
 | |
| #include "memfs.h"
 | |
| #include <sddl.h>
 | |
| #include <map>
 | |
| #include <cassert>
 | |
| 
 | |
| /*
 | |
|  * Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
 | |
|  * a check for the Write buffer to ensure that it is read-only.
 | |
|  */
 | |
| #if !defined(NDEBUG)
 | |
| #define DEBUG_BUFFER_CHECK
 | |
| #endif
 | |
| 
 | |
| #define MEMFS_SECTOR_SIZE               512
 | |
| #define MEMFS_SECTORS_PER_ALLOCATION_UNIT 1
 | |
| 
 | |
| static inline
 | |
| UINT64 MemfsGetSystemTime(VOID)
 | |
| {
 | |
|     FILETIME FileTime;
 | |
|     GetSystemTimeAsFileTime(&FileTime);
 | |
|     return ((PLARGE_INTEGER)&FileTime)->QuadPart;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| int MemfsFileNameCompare(PWSTR a, PWSTR b)
 | |
| {
 | |
|     return wcscmp(a, b);
 | |
| }
 | |
| 
 | |
| static inline
 | |
| BOOLEAN MemfsFileNameHasPrefix(PWSTR a, PWSTR b)
 | |
| {
 | |
|     return 0 == wcsncmp(a, b, wcslen(b));
 | |
| }
 | |
| 
 | |
| typedef struct _MEMFS_FILE_NODE
 | |
| {
 | |
|     WCHAR FileName[MAX_PATH];
 | |
|     FSP_FSCTL_FILE_INFO FileInfo;
 | |
|     SIZE_T FileSecuritySize;
 | |
|     PVOID FileSecurity;
 | |
|     PVOID FileData;
 | |
|     ULONG RefCount;
 | |
| } MEMFS_FILE_NODE;
 | |
| 
 | |
| struct MEMFS_FILE_NODE_LESS
 | |
| {
 | |
|     bool operator()(PWSTR a, PWSTR b) const
 | |
|     {
 | |
|         return 0 > MemfsFileNameCompare(a, b);
 | |
|     }
 | |
| };
 | |
| typedef std::map<PWSTR, MEMFS_FILE_NODE *, MEMFS_FILE_NODE_LESS> MEMFS_FILE_NODE_MAP;
 | |
| 
 | |
| typedef struct _MEMFS
 | |
| {
 | |
|     FSP_FILE_SYSTEM *FileSystem;
 | |
|     MEMFS_FILE_NODE_MAP *FileNodeMap;
 | |
|     ULONG MaxFileNodes;
 | |
|     ULONG MaxFileSize;
 | |
|     UINT16 VolumeLabelLength;
 | |
|     WCHAR VolumeLabel[32];
 | |
|     CRITICAL_SECTION Lock;
 | |
| } MEMFS;
 | |
| 
 | |
| static inline
 | |
| NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode)
 | |
| {
 | |
|     static UINT64 IndexNumber = 1;
 | |
|     MEMFS_FILE_NODE *FileNode;
 | |
| 
 | |
|     *PFileNode = 0;
 | |
| 
 | |
|     if (MAX_PATH <= wcslen(FileName))
 | |
|         return STATUS_OBJECT_NAME_INVALID;
 | |
| 
 | |
|     FileNode = (MEMFS_FILE_NODE *)malloc(sizeof *FileNode);
 | |
|     if (0 == FileNode)
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
| 
 | |
|     memset(FileNode, 0, sizeof *FileNode);
 | |
|     wcscpy_s(FileNode->FileName, sizeof FileNode->FileName / sizeof(WCHAR), FileName);
 | |
|     FileNode->FileInfo.CreationTime =
 | |
|     FileNode->FileInfo.LastAccessTime =
 | |
|     FileNode->FileInfo.LastWriteTime =
 | |
|     FileNode->FileInfo.ChangeTime = MemfsGetSystemTime();
 | |
|     FileNode->FileInfo.IndexNumber = IndexNumber++;
 | |
| 
 | |
|     *PFileNode = FileNode;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode)
 | |
| {
 | |
|     free(FileNode->FileData);
 | |
|     free(FileNode->FileSecurity);
 | |
|     free(FileNode);
 | |
| }
 | |
| 
 | |
| static inline
 | |
| VOID MemfsFileNodeMapDump(MEMFS_FILE_NODE_MAP *FileNodeMap)
 | |
| {
 | |
|     for (MEMFS_FILE_NODE_MAP::iterator p = FileNodeMap->begin(), q = FileNodeMap->end(); p != q; ++p)
 | |
|         FspDebugLog("%c %04lx %6lu %S\n",
 | |
|             FILE_ATTRIBUTE_DIRECTORY & p->second->FileInfo.FileAttributes ? 'd' : 'f',
 | |
|             (ULONG)p->second->FileInfo.FileAttributes,
 | |
|             (ULONG)p->second->FileInfo.FileSize,
 | |
|             p->second->FileName);
 | |
| }
 | |
| 
 | |
| static inline
 | |
| NTSTATUS MemfsFileNodeMapCreate(MEMFS_FILE_NODE_MAP **PFileNodeMap)
 | |
| {
 | |
|     *PFileNodeMap = 0;
 | |
|     try
 | |
|     {
 | |
|         *PFileNodeMap = new MEMFS_FILE_NODE_MAP;
 | |
|         return STATUS_SUCCESS;
 | |
|     }
 | |
|     catch (...)
 | |
|     {
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static inline
 | |
| VOID MemfsFileNodeMapDelete(MEMFS_FILE_NODE_MAP *FileNodeMap)
 | |
| {
 | |
|     for (MEMFS_FILE_NODE_MAP::iterator p = FileNodeMap->begin(), q = FileNodeMap->end(); p != q; ++p)
 | |
|         MemfsFileNodeDelete(p->second);
 | |
| 
 | |
|     delete FileNodeMap;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| SIZE_T MemfsFileNodeMapCount(MEMFS_FILE_NODE_MAP *FileNodeMap)
 | |
| {
 | |
|     return FileNodeMap->size();
 | |
| }
 | |
| 
 | |
| static inline
 | |
| MEMFS_FILE_NODE *MemfsFileNodeMapGet(MEMFS_FILE_NODE_MAP *FileNodeMap, PWSTR FileName)
 | |
| {
 | |
|     MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->find(FileName);
 | |
|     if (iter == FileNodeMap->end())
 | |
|         return 0;
 | |
|     return iter->second;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| MEMFS_FILE_NODE *MemfsFileNodeMapGetParent(MEMFS_FILE_NODE_MAP *FileNodeMap, PWSTR FileName0,
 | |
|     PNTSTATUS PResult)
 | |
| {
 | |
|     WCHAR Root[2] = L"\\";
 | |
|     PWSTR Remain, Suffix;
 | |
|     WCHAR FileName[MAX_PATH];
 | |
|     wcscpy_s(FileName, sizeof FileName / sizeof(WCHAR), FileName0);
 | |
|     FspPathSuffix(FileName, &Remain, &Suffix, Root);
 | |
|     MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->find(Remain);
 | |
|     FspPathCombine(FileName, Suffix);
 | |
|     if (iter == FileNodeMap->end())
 | |
|     {
 | |
|         *PResult = STATUS_OBJECT_PATH_NOT_FOUND;
 | |
|         return 0;
 | |
|     }
 | |
|     if (0 == (iter->second->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
 | |
|     {
 | |
|         *PResult = STATUS_NOT_A_DIRECTORY;
 | |
|         return 0;
 | |
|     }
 | |
|     return iter->second;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| NTSTATUS MemfsFileNodeMapInsert(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode,
 | |
|     PBOOLEAN PInserted)
 | |
| {
 | |
|     *PInserted = 0;
 | |
|     try
 | |
|     {
 | |
|         *PInserted = FileNodeMap->insert(MEMFS_FILE_NODE_MAP::value_type(FileNode->FileName, FileNode)).second;
 | |
|         if (*PInserted)
 | |
|             FileNode->RefCount++;
 | |
|         return STATUS_SUCCESS;
 | |
|     }
 | |
|     catch (...)
 | |
|     {
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static inline
 | |
| VOID MemfsFileNodeMapRemove(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode)
 | |
| {
 | |
|     --FileNode->RefCount;
 | |
|     FileNodeMap->erase(FileNode->FileName);
 | |
| }
 | |
| 
 | |
| static inline
 | |
| BOOLEAN MemfsFileNodeMapHasChild(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode)
 | |
| {
 | |
|     BOOLEAN Result;
 | |
|     WCHAR Root[2] = L"\\";
 | |
|     PWSTR Remain, Suffix;
 | |
|     MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->upper_bound(FileNode->FileName);
 | |
|     if (iter == FileNodeMap->end())
 | |
|         return FALSE;
 | |
|     FspPathSuffix(iter->second->FileName, &Remain, &Suffix, Root);
 | |
|     Result = 0 == MemfsFileNameCompare(Remain, FileNode->FileName);
 | |
|     FspPathCombine(iter->second->FileName, Suffix);
 | |
|     return Result;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| BOOLEAN MemfsFileNodeMapEnumerateChildren(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode,
 | |
|     BOOLEAN (*EnumFn)(MEMFS_FILE_NODE *, PVOID), PVOID Context)
 | |
| {
 | |
|     WCHAR Root[2] = L"\\";
 | |
|     PWSTR Remain, Suffix;
 | |
|     MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->upper_bound(FileNode->FileName);
 | |
|     BOOLEAN Equal;
 | |
|     for (; FileNodeMap->end() != iter; ++iter)
 | |
|     {
 | |
|         if (!MemfsFileNameHasPrefix(iter->second->FileName, FileNode->FileName))
 | |
|             break;
 | |
|         FspPathSuffix(iter->second->FileName, &Remain, &Suffix, Root);
 | |
|         Equal = 0 == MemfsFileNameCompare(Remain, FileNode->FileName);
 | |
|         FspPathCombine(iter->second->FileName, Suffix);
 | |
|         if (Equal)
 | |
|         {
 | |
|             if (!EnumFn(iter->second, Context))
 | |
|                 return FALSE;
 | |
|         }
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static inline
 | |
| BOOLEAN MemfsFileNodeMapEnumerateDescendants(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode,
 | |
|     BOOLEAN (*EnumFn)(MEMFS_FILE_NODE *, PVOID), PVOID Context)
 | |
| {
 | |
|     WCHAR Root[2] = L"\\";
 | |
|     MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->lower_bound(FileNode->FileName);
 | |
|     for (; FileNodeMap->end() != iter; ++iter)
 | |
|     {
 | |
|         if (!MemfsFileNameHasPrefix(iter->second->FileName, FileNode->FileName))
 | |
|             break;
 | |
|         if (!EnumFn(iter->second, Context))
 | |
|             return FALSE;
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, UINT64 FileSize,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo);
 | |
| 
 | |
| static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     FSP_FSCTL_VOLUME_INFO *VolumeInfo)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
| 
 | |
|     VolumeInfo->TotalSize = Memfs->MaxFileNodes * (UINT64)Memfs->MaxFileSize;
 | |
|     VolumeInfo->FreeSize = (Memfs->MaxFileNodes - MemfsFileNodeMapCount(Memfs->FileNodeMap)) *
 | |
|         (UINT64)Memfs->MaxFileSize;
 | |
|     VolumeInfo->VolumeLabelLength = Memfs->VolumeLabelLength;
 | |
|     memcpy(VolumeInfo->VolumeLabel, Memfs->VolumeLabel, Memfs->VolumeLabelLength);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS SetVolumeLabel(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PWSTR VolumeLabel,
 | |
|     FSP_FSCTL_VOLUME_INFO *VolumeInfo)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
| 
 | |
|     Memfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof(WCHAR));
 | |
|     if (Memfs->VolumeLabelLength > sizeof Memfs->VolumeLabel)
 | |
|         Memfs->VolumeLabelLength = sizeof Memfs->VolumeLabel;
 | |
|     memcpy(Memfs->VolumeLabel, VolumeLabel, Memfs->VolumeLabelLength);
 | |
| 
 | |
|     VolumeInfo->TotalSize = Memfs->MaxFileNodes * Memfs->MaxFileSize;
 | |
|     VolumeInfo->FreeSize =
 | |
|         (Memfs->MaxFileNodes - MemfsFileNodeMapCount(Memfs->FileNodeMap)) * Memfs->MaxFileSize;
 | |
|     VolumeInfo->VolumeLabelLength = Memfs->VolumeLabelLength;
 | |
|     memcpy(VolumeInfo->VolumeLabel, Memfs->VolumeLabel, Memfs->VolumeLabelLength);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PWSTR FileName, PUINT32 PFileAttributes,
 | |
|     PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName);
 | |
|     if (0 == FileNode)
 | |
|     {
 | |
|         Result = STATUS_OBJECT_NAME_NOT_FOUND;
 | |
|         MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result);
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     if (0 != PFileAttributes)
 | |
|         *PFileAttributes = FileNode->FileInfo.FileAttributes;
 | |
| 
 | |
|     if (0 != PSecurityDescriptorSize)
 | |
|     {
 | |
|         if (FileNode->FileSecuritySize > *PSecurityDescriptorSize)
 | |
|         {
 | |
|             *PSecurityDescriptorSize = FileNode->FileSecuritySize;
 | |
|             return STATUS_BUFFER_OVERFLOW;
 | |
|         }
 | |
| 
 | |
|         *PSecurityDescriptorSize = FileNode->FileSecuritySize;
 | |
|         if (0 != SecurityDescriptor)
 | |
|             memcpy(SecurityDescriptor, FileNode->FileSecurity, FileNode->FileSecuritySize);
 | |
|     }
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PWSTR FileName, BOOLEAN CaseSensitive, UINT32 CreateOptions,
 | |
|     UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
 | |
|     PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode;
 | |
|     NTSTATUS Result;
 | |
|     BOOLEAN Inserted;
 | |
| 
 | |
|     if (CreateOptions & FILE_DIRECTORY_FILE)
 | |
|         AllocationSize = 0;
 | |
| 
 | |
|     FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName);
 | |
|     if (0 != FileNode)
 | |
|         return STATUS_OBJECT_NAME_COLLISION;
 | |
| 
 | |
|     if (!MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result))
 | |
|         return Result;
 | |
| 
 | |
|     if (MemfsFileNodeMapCount(Memfs->FileNodeMap) >= Memfs->MaxFileNodes)
 | |
|         return STATUS_CANNOT_MAKE;
 | |
| 
 | |
|     if (AllocationSize > Memfs->MaxFileSize)
 | |
|         return STATUS_DISK_FULL;
 | |
| 
 | |
|     Result = MemfsFileNodeCreate(FileName, &FileNode);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|         return Result;
 | |
| 
 | |
|     FileNode->FileInfo.FileAttributes = FileAttributes;
 | |
| 
 | |
|     if (0 != SecurityDescriptor)
 | |
|     {
 | |
|         FileNode->FileSecuritySize = GetSecurityDescriptorLength(SecurityDescriptor);
 | |
|         FileNode->FileSecurity = (PSECURITY_DESCRIPTOR)malloc(FileNode->FileSecuritySize);
 | |
|         if (0 == FileNode->FileSecurity)
 | |
|         {
 | |
|             MemfsFileNodeDelete(FileNode);
 | |
|             return STATUS_INSUFFICIENT_RESOURCES;
 | |
|         }
 | |
|         memcpy(FileNode->FileSecurity, SecurityDescriptor, FileNode->FileSecuritySize);
 | |
|     }
 | |
| 
 | |
|     FileNode->FileInfo.AllocationSize = AllocationSize;
 | |
|     if (0 != FileNode->FileInfo.AllocationSize)
 | |
|     {
 | |
|         FileNode->FileData = malloc((size_t)FileNode->FileInfo.AllocationSize);
 | |
|         if (0 == FileNode->FileData)
 | |
|         {
 | |
|             MemfsFileNodeDelete(FileNode);
 | |
|             return STATUS_INSUFFICIENT_RESOURCES;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     Result = MemfsFileNodeMapInsert(Memfs->FileNodeMap, FileNode, &Inserted);
 | |
|     if (!NT_SUCCESS(Result) || !Inserted)
 | |
|     {
 | |
|         MemfsFileNodeDelete(FileNode);
 | |
|         if (NT_SUCCESS(Result))
 | |
|             Result = STATUS_OBJECT_NAME_COLLISION; /* should not happen! */
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     FileNode->RefCount++;
 | |
|     *PFileNode = FileNode;
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PWSTR FileName, BOOLEAN CaseSensitive, UINT32 CreateOptions,
 | |
|     PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName);
 | |
|     if (0 == FileNode)
 | |
|     {
 | |
|         Result = STATUS_OBJECT_NAME_NOT_FOUND;
 | |
|         MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result);
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     FileNode->FileInfo.LastAccessTime = MemfsGetSystemTime();
 | |
| 
 | |
|     FileNode->RefCount++;
 | |
|     *PFileNode = FileNode;
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     if (ReplaceFileAttributes)
 | |
|         FileNode->FileInfo.FileAttributes = FileAttributes;
 | |
|     else
 | |
|         FileNode->FileInfo.FileAttributes |= FileAttributes;
 | |
| 
 | |
|     FileNode->FileInfo.FileSize = 0;
 | |
|     FileNode->FileInfo.LastWriteTime =
 | |
|     FileNode->FileInfo.LastAccessTime = MemfsGetSystemTime();
 | |
| 
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, PWSTR FileName, BOOLEAN Delete)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     assert(0 == FileName || 0 == wcscmp(FileNode->FileName, FileName));
 | |
| 
 | |
|     if (Delete && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
 | |
|         MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
 | |
| }
 | |
| 
 | |
| static VOID Close(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     if (0 == --FileNode->RefCount)
 | |
|         MemfsFileNodeDelete(FileNode);
 | |
| }
 | |
| 
 | |
| static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length,
 | |
|     PULONG PBytesTransferred)
 | |
| {
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
|     UINT64 EndOffset;
 | |
| 
 | |
|     if (Offset >= FileNode->FileInfo.FileSize)
 | |
|         return STATUS_END_OF_FILE;
 | |
| 
 | |
|     EndOffset = Offset + Length;
 | |
|     if (EndOffset > FileNode->FileInfo.FileSize)
 | |
|         EndOffset = FileNode->FileInfo.FileSize;
 | |
| 
 | |
|     memcpy(Buffer, (PUINT8)FileNode->FileData + Offset, (size_t)(EndOffset - Offset));
 | |
| 
 | |
|     *PBytesTransferred = (ULONG)(EndOffset - Offset);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length,
 | |
|     BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo,
 | |
|     PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
| #if defined(DEBUG_BUFFER_CHECK)
 | |
|     SYSTEM_INFO SystemInfo;
 | |
|     GetSystemInfo(&SystemInfo);
 | |
|     for (PUINT8 P = (PUINT8)Buffer, EndP = P + Length; EndP > P; P += SystemInfo.dwPageSize)
 | |
|         __try
 | |
|         {
 | |
|             *P = *P | 0;
 | |
|             assert(0);
 | |
|         }
 | |
|         __except (EXCEPTION_EXECUTE_HANDLER)
 | |
|         {
 | |
|             /* ignore! */
 | |
|         }
 | |
| #endif
 | |
| 
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
|     UINT64 EndOffset;
 | |
| 
 | |
|     if (ConstrainedIo)
 | |
|     {
 | |
|         if (Offset >= FileNode->FileInfo.FileSize)
 | |
|             return STATUS_SUCCESS;
 | |
|         EndOffset = Offset + Length;
 | |
|         if (EndOffset > FileNode->FileInfo.FileSize)
 | |
|             EndOffset = FileNode->FileInfo.FileSize;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         if (WriteToEndOfFile)
 | |
|             Offset = FileNode->FileInfo.FileSize;
 | |
|         EndOffset = Offset + Length;
 | |
|         if (EndOffset > FileNode->FileInfo.FileSize)
 | |
|             SetFileSize(FileSystem, Request, FileNode, EndOffset, FileInfo);
 | |
|     }
 | |
| 
 | |
|     memcpy((PUINT8)FileNode->FileData + Offset, Buffer, (size_t)(EndOffset - Offset));
 | |
| 
 | |
|     *PBytesTransferred = (ULONG)(EndOffset - Offset);
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode)
 | |
| {
 | |
|     /* nothing to do, since we do not cache anything */
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, UINT32 FileAttributes,
 | |
|     UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     if (INVALID_FILE_ATTRIBUTES != FileAttributes)
 | |
|         FileNode->FileInfo.FileAttributes = FileAttributes;
 | |
|     if (0 != CreationTime)
 | |
|         FileNode->FileInfo.CreationTime = CreationTime;
 | |
|     if (0 != LastAccessTime)
 | |
|         FileNode->FileInfo.LastAccessTime = LastAccessTime;
 | |
|     if (0 != LastWriteTime)
 | |
|         FileNode->FileInfo.LastWriteTime = LastWriteTime;
 | |
| 
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS SetAllocationSize(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, UINT64 AllocationSize,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
|     PVOID FileData;
 | |
| 
 | |
|     if (FileNode->FileInfo.AllocationSize != AllocationSize)
 | |
|     {
 | |
|         if (AllocationSize > Memfs->MaxFileSize)
 | |
|             return STATUS_DISK_FULL;
 | |
| 
 | |
|         FileData = realloc(FileNode->FileData, (size_t)AllocationSize);
 | |
|         if (0 == FileData)
 | |
|             return STATUS_INSUFFICIENT_RESOURCES;
 | |
| 
 | |
|         FileNode->FileData = FileData;
 | |
| 
 | |
|         FileNode->FileInfo.AllocationSize = AllocationSize;
 | |
|         if (FileNode->FileInfo.FileSize > AllocationSize)
 | |
|             FileNode->FileInfo.FileSize = AllocationSize;
 | |
|     }
 | |
| 
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, UINT64 FileSize,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     if (FileNode->FileInfo.FileSize != FileSize)
 | |
|     {
 | |
|         if (FileNode->FileInfo.AllocationSize < FileSize)
 | |
|         {
 | |
|             UINT64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT;
 | |
|             UINT64 AllocationSize = (FileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit;
 | |
| 
 | |
|             NTSTATUS Result = SetAllocationSize(FileSystem, Request, FileNode, AllocationSize, FileInfo);
 | |
|             if (!NT_SUCCESS(Result))
 | |
|                 return Result;
 | |
|         }
 | |
| 
 | |
|         if (FileNode->FileInfo.FileSize < FileSize)
 | |
|             memset((PUINT8)FileNode->FileData + FileNode->FileInfo.FileSize, 0,
 | |
|                 (size_t)(FileSize - FileNode->FileInfo.FileSize));
 | |
|         FileNode->FileInfo.FileSize = FileSize;
 | |
|     }
 | |
| 
 | |
|     *FileInfo = FileNode->FileInfo;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, PWSTR FileName)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     assert(0 == FileName || 0 == wcscmp(FileNode->FileName, FileName));
 | |
| 
 | |
|     if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
 | |
|         return STATUS_DIRECTORY_NOT_EMPTY;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| typedef struct _MEMFS_RENAME_CONTEXT
 | |
| {
 | |
|     MEMFS_FILE_NODE **FileNodes;
 | |
|     ULONG Count;
 | |
| } MEMFS_RENAME_CONTEXT;
 | |
| 
 | |
| static BOOLEAN RenameEnumFn(MEMFS_FILE_NODE *FileNode, PVOID Context0)
 | |
| {
 | |
|     MEMFS_RENAME_CONTEXT *Context = (MEMFS_RENAME_CONTEXT *)Context0;
 | |
| 
 | |
|     Context->FileNodes[Context->Count++] = FileNode;
 | |
|     FileNode->RefCount++;
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0,
 | |
|     PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
|     MEMFS_FILE_NODE *NewFileNode, *DescendantFileNode;
 | |
|     MEMFS_RENAME_CONTEXT Context = { 0 };
 | |
|     ULONG Index, FileNameLen, NewFileNameLen;
 | |
|     BOOLEAN Inserted;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     assert(0 == FileName || 0 == wcscmp(FileNode->FileName, FileName));
 | |
| 
 | |
|     NewFileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, NewFileName);
 | |
|     if (0 != NewFileNode)
 | |
|     {
 | |
|         if (!ReplaceIfExists)
 | |
|         {
 | |
|             Result = STATUS_OBJECT_NAME_COLLISION;
 | |
|             goto exit;
 | |
|         }
 | |
| 
 | |
|         if (NewFileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 | |
|         {
 | |
|             Result = STATUS_ACCESS_DENIED;
 | |
|             goto exit;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     Context.FileNodes = (MEMFS_FILE_NODE **)malloc(Memfs->MaxFileNodes * sizeof Context.FileNodes[0]);
 | |
|     if (0 == Context.FileNodes)
 | |
|     {
 | |
|         Result = STATUS_INSUFFICIENT_RESOURCES;
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     MemfsFileNodeMapEnumerateDescendants(Memfs->FileNodeMap, FileNode, RenameEnumFn, &Context);
 | |
| 
 | |
|     FileNameLen = (ULONG)wcslen(FileNode->FileName);
 | |
|     NewFileNameLen = (ULONG)wcslen(NewFileName);
 | |
|     for (Index = 0; Context.Count > Index; Index++)
 | |
|     {
 | |
|         DescendantFileNode = Context.FileNodes[Index];
 | |
|         assert(MemfsFileNameHasPrefix(DescendantFileNode->FileName, FileNode->FileName));
 | |
|         if (MAX_PATH <= wcslen(DescendantFileNode->FileName) - FileNameLen + NewFileNameLen)
 | |
|         {
 | |
|             Result = STATUS_OBJECT_NAME_INVALID;
 | |
|             goto exit;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (0 != NewFileNode)
 | |
|     {
 | |
|         NewFileNode->RefCount++;
 | |
|         MemfsFileNodeMapRemove(Memfs->FileNodeMap, NewFileNode);
 | |
|         if (0 == --NewFileNode->RefCount)
 | |
|             MemfsFileNodeDelete(NewFileNode);
 | |
|     }
 | |
| 
 | |
|     for (Index = 0; Context.Count > Index; Index++)
 | |
|     {
 | |
|         DescendantFileNode = Context.FileNodes[Index];
 | |
|         MemfsFileNodeMapRemove(Memfs->FileNodeMap, DescendantFileNode);
 | |
|         memmove(DescendantFileNode->FileName + NewFileNameLen,
 | |
|             DescendantFileNode->FileName + FileNameLen,
 | |
|             (wcslen(DescendantFileNode->FileName) + 1 - FileNameLen) * sizeof(WCHAR));
 | |
|         memcpy(DescendantFileNode->FileName, NewFileName, NewFileNameLen * sizeof(WCHAR));
 | |
|         Result = MemfsFileNodeMapInsert(Memfs->FileNodeMap, DescendantFileNode, &Inserted);
 | |
|         if (!NT_SUCCESS(Result))
 | |
|         {
 | |
|             FspDebugLog(__FUNCTION__ ": cannot insert into FileNodeMap; aborting\n");
 | |
|             abort();
 | |
|         }
 | |
|         assert(Inserted);
 | |
|     }
 | |
| 
 | |
|     Result = STATUS_SUCCESS;
 | |
| 
 | |
| exit:
 | |
|     for (Index = 0; Context.Count > Index; Index++)
 | |
|     {
 | |
|         DescendantFileNode = Context.FileNodes[Index];
 | |
|         DescendantFileNode->RefCount--;
 | |
|     }
 | |
|     free(Context.FileNodes);
 | |
| 
 | |
|     return Result;
 | |
| }
 | |
| 
 | |
| static NTSTATUS GetSecurity(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0,
 | |
|     PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
 | |
| {
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
| 
 | |
|     if (FileNode->FileSecuritySize > *PSecurityDescriptorSize)
 | |
|     {
 | |
|         *PSecurityDescriptorSize = FileNode->FileSecuritySize;
 | |
|         return STATUS_BUFFER_OVERFLOW;
 | |
|     }
 | |
| 
 | |
|     *PSecurityDescriptorSize = FileNode->FileSecuritySize;
 | |
|     if (0 != SecurityDescriptor)
 | |
|         memcpy(SecurityDescriptor, FileNode->FileSecurity, FileNode->FileSecuritySize);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0,
 | |
|     SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor)
 | |
| {
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
|     PSECURITY_DESCRIPTOR NewSecurityDescriptor, FileSecurity;
 | |
|     SIZE_T FileSecuritySize;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     Result = FspSetSecurityDescriptor(FileSystem, Request, FileNode->FileSecurity,
 | |
|         &NewSecurityDescriptor);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|         return Result;
 | |
| 
 | |
|     FileSecuritySize = GetSecurityDescriptorLength(NewSecurityDescriptor);
 | |
|     FileSecurity = (PSECURITY_DESCRIPTOR)malloc(FileSecuritySize);
 | |
|     if (0 == FileSecurity)
 | |
|     {
 | |
|         FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor);
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
|     }
 | |
|     memcpy(FileSecurity, NewSecurityDescriptor, FileSecuritySize);
 | |
|     FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor);
 | |
| 
 | |
|     free(FileNode->FileSecurity);
 | |
|     FileNode->FileSecuritySize = FileSecuritySize;
 | |
|     FileNode->FileSecurity = FileSecurity;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| typedef struct _MEMFS_READ_DIRECTORY_CONTEXT
 | |
| {
 | |
|     PVOID Buffer;
 | |
|     UINT64 Offset;
 | |
|     ULONG Length;
 | |
|     PULONG PBytesTransferred;
 | |
|     BOOLEAN OffsetFound;
 | |
| } MEMFS_READ_DIRECTORY_CONTEXT;
 | |
| 
 | |
| static BOOLEAN AddDirInfo(MEMFS_FILE_NODE *FileNode, PWSTR FileName,
 | |
|     PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
 | |
| {
 | |
|     UINT8 DirInfoBuf[sizeof(FSP_FSCTL_DIR_INFO) + sizeof FileNode->FileName];
 | |
|     FSP_FSCTL_DIR_INFO *DirInfo = (FSP_FSCTL_DIR_INFO *)DirInfoBuf;
 | |
|     WCHAR Root[2] = L"\\";
 | |
|     PWSTR Remain, Suffix;
 | |
| 
 | |
|     if (0 == FileName)
 | |
|     {
 | |
|         FspPathSuffix(FileNode->FileName, &Remain, &Suffix, Root);
 | |
|         FileName = Suffix;
 | |
|         FspPathCombine(FileNode->FileName, Suffix);
 | |
|     }
 | |
| 
 | |
|     memset(DirInfo->Padding, 0, sizeof DirInfo->Padding);
 | |
|     DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(FileName) * sizeof(WCHAR));
 | |
|     DirInfo->FileInfo = FileNode->FileInfo;
 | |
|     DirInfo->NextOffset = FileNode->FileInfo.IndexNumber;
 | |
|     memcpy(DirInfo->FileNameBuf, FileName, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO));
 | |
| 
 | |
|     return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred);
 | |
| }
 | |
| 
 | |
| static BOOLEAN ReadDirectoryEnumFn(MEMFS_FILE_NODE *FileNode, PVOID Context0)
 | |
| {
 | |
|     MEMFS_READ_DIRECTORY_CONTEXT *Context = (MEMFS_READ_DIRECTORY_CONTEXT *)Context0;
 | |
| 
 | |
|     if (0 != Context->Offset && !Context->OffsetFound)
 | |
|     {
 | |
|         Context->OffsetFound = FileNode->FileInfo.IndexNumber == Context->Offset;
 | |
|         return TRUE;
 | |
|     }
 | |
| 
 | |
|     return AddDirInfo(FileNode, 0,
 | |
|         Context->Buffer, Context->Length, Context->PBytesTransferred);
 | |
| }
 | |
| 
 | |
| static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request,
 | |
|     PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length,
 | |
|     PWSTR Pattern,
 | |
|     PULONG PBytesTransferred)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
 | |
|     MEMFS_FILE_NODE *ParentNode;
 | |
|     MEMFS_READ_DIRECTORY_CONTEXT Context;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     ParentNode = MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileNode->FileName, &Result);
 | |
|     if (0 == ParentNode)
 | |
|         return Result;
 | |
| 
 | |
|     Context.Buffer = Buffer;
 | |
|     Context.Offset = Offset;
 | |
|     Context.Length = Length;
 | |
|     Context.PBytesTransferred = PBytesTransferred;
 | |
|     Context.OffsetFound = FALSE;
 | |
| 
 | |
|     if (0 == Offset)
 | |
|         if (!AddDirInfo(FileNode, L".", Buffer, Length, PBytesTransferred))
 | |
|             return STATUS_SUCCESS;
 | |
|     if (0 == Offset || FileNode->FileInfo.IndexNumber == Offset)
 | |
|     {
 | |
|         Context.OffsetFound = FileNode->FileInfo.IndexNumber == Context.Offset;
 | |
| 
 | |
|         if (!AddDirInfo(ParentNode, L"..", Buffer, Length, PBytesTransferred))
 | |
|             return STATUS_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     if (MemfsFileNodeMapEnumerateChildren(Memfs->FileNodeMap, FileNode, ReadDirectoryEnumFn, &Context))
 | |
|         FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
 | |
| {
 | |
|     GetVolumeInfo,
 | |
|     SetVolumeLabel,
 | |
|     GetSecurityByName,
 | |
|     Create,
 | |
|     Open,
 | |
|     Overwrite,
 | |
|     Cleanup,
 | |
|     Close,
 | |
|     Read,
 | |
|     Write,
 | |
|     Flush,
 | |
|     GetFileInfo,
 | |
|     SetBasicInfo,
 | |
|     SetAllocationSize,
 | |
|     SetFileSize,
 | |
|     CanDelete,
 | |
|     Rename,
 | |
|     GetSecurity,
 | |
|     SetSecurity,
 | |
|     ReadDirectory,
 | |
| };
 | |
| 
 | |
| static VOID MemfsEnterOperation(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     EnterCriticalSection(&Memfs->Lock);
 | |
| }
 | |
| 
 | |
| static VOID MemfsLeaveOperation(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
 | |
| {
 | |
|     MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
 | |
|     LeaveCriticalSection(&Memfs->Lock);
 | |
| }
 | |
| 
 | |
| NTSTATUS MemfsCreate(
 | |
|     ULONG Flags,
 | |
|     ULONG FileInfoTimeout,
 | |
|     ULONG MaxFileNodes,
 | |
|     ULONG MaxFileSize,
 | |
|     PWSTR VolumePrefix,
 | |
|     PWSTR RootSddl,
 | |
|     MEMFS **PMemfs)
 | |
| {
 | |
|     NTSTATUS Result;
 | |
|     FSP_FSCTL_VOLUME_PARAMS VolumeParams;
 | |
|     PWSTR DevicePath = (Flags & MemfsNet) ?
 | |
|         L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME;
 | |
|     UINT64 AllocationUnit;
 | |
|     MEMFS *Memfs;
 | |
|     MEMFS_FILE_NODE *RootNode;
 | |
|     PSECURITY_DESCRIPTOR RootSecurity;
 | |
|     ULONG RootSecuritySize;
 | |
|     BOOLEAN Inserted;
 | |
| 
 | |
|     *PMemfs = 0;
 | |
| 
 | |
|     if (0 == 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());
 | |
| 
 | |
|     Memfs = (MEMFS *)malloc(sizeof *Memfs);
 | |
|     if (0 == Memfs)
 | |
|     {
 | |
|         LocalFree(RootSecurity);
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
|     }
 | |
| 
 | |
|     memset(Memfs, 0, sizeof *Memfs);
 | |
|     Memfs->MaxFileNodes = MaxFileNodes;
 | |
|     AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT;
 | |
|     Memfs->MaxFileSize = (ULONG)((MaxFileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit);
 | |
| 
 | |
|     Result = MemfsFileNodeMapCreate(&Memfs->FileNodeMap);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|     {
 | |
|         free(Memfs);
 | |
|         LocalFree(RootSecurity);
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     memset(&VolumeParams, 0, sizeof VolumeParams);
 | |
|     VolumeParams.SectorSize = MEMFS_SECTOR_SIZE;
 | |
|     VolumeParams.SectorsPerAllocationUnit = MEMFS_SECTORS_PER_ALLOCATION_UNIT;
 | |
|     VolumeParams.VolumeCreationTime = MemfsGetSystemTime();
 | |
|     VolumeParams.VolumeSerialNumber = (UINT32)(MemfsGetSystemTime() / (10000 * 1000));
 | |
|     VolumeParams.FileInfoTimeout = FileInfoTimeout;
 | |
|     VolumeParams.CaseSensitiveSearch = 1;
 | |
|     VolumeParams.CasePreservedNames = 1;
 | |
|     VolumeParams.UnicodeOnDisk = 1;
 | |
|     VolumeParams.PersistentAcls = 1;
 | |
|     if (0 != VolumePrefix)
 | |
|         wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
 | |
| 
 | |
|     Result = FspFileSystemCreate(DevicePath, &VolumeParams, &MemfsInterface, &Memfs->FileSystem);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|     {
 | |
|         MemfsFileNodeMapDelete(Memfs->FileNodeMap);
 | |
|         free(Memfs);
 | |
|         LocalFree(RootSecurity);
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     Memfs->FileSystem->UserContext = Memfs;
 | |
|     Memfs->VolumeLabelLength = sizeof L"MEMFS" - sizeof(WCHAR);
 | |
|     memcpy(Memfs->VolumeLabel, L"MEMFS", Memfs->VolumeLabelLength);
 | |
| 
 | |
|     InitializeCriticalSection(&Memfs->Lock);
 | |
| 
 | |
|     FspFileSystemSetOperationGuard(Memfs->FileSystem,
 | |
|         MemfsEnterOperation,
 | |
|         MemfsLeaveOperation);
 | |
| 
 | |
|     /*
 | |
|      * Create root directory.
 | |
|      */
 | |
| 
 | |
|     Result = MemfsFileNodeCreate(L"\\", &RootNode);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|     {
 | |
|         MemfsDelete(Memfs);
 | |
|         LocalFree(RootSecurity);
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     RootNode->FileInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
 | |
| 
 | |
|     RootNode->FileSecurity = malloc(RootSecuritySize);
 | |
|     if (0 == RootNode->FileSecurity)
 | |
|     {
 | |
|         MemfsFileNodeDelete(RootNode);
 | |
|         MemfsDelete(Memfs);
 | |
|         LocalFree(RootSecurity);
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
|     }
 | |
|     RootNode->FileSecuritySize = RootSecuritySize;
 | |
|     memcpy(RootNode->FileSecurity, RootSecurity, RootSecuritySize);
 | |
| 
 | |
|     Result = MemfsFileNodeMapInsert(Memfs->FileNodeMap, RootNode, &Inserted);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|     {
 | |
|         MemfsFileNodeDelete(RootNode);
 | |
|         MemfsDelete(Memfs);
 | |
|         LocalFree(RootSecurity);
 | |
|         return Result;
 | |
|     }
 | |
| 
 | |
|     LocalFree(RootSecurity);
 | |
| 
 | |
|     *PMemfs = Memfs;
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| VOID MemfsDelete(MEMFS *Memfs)
 | |
| {
 | |
|     DeleteCriticalSection(&Memfs->Lock);
 | |
| 
 | |
|     FspFileSystemDelete(Memfs->FileSystem);
 | |
| 
 | |
|     MemfsFileNodeMapDelete(Memfs->FileNodeMap);
 | |
| 
 | |
|     free(Memfs);
 | |
| }
 | |
| 
 | |
| NTSTATUS MemfsStart(MEMFS *Memfs)
 | |
| {
 | |
|     return FspFileSystemStartDispatcher(Memfs->FileSystem, 0);
 | |
| }
 | |
| 
 | |
| VOID MemfsStop(MEMFS *Memfs)
 | |
| {
 | |
|     FspFileSystemStopDispatcher(Memfs->FileSystem);
 | |
| }
 | |
| 
 | |
| FSP_FILE_SYSTEM *MemfsFileSystem(MEMFS *Memfs)
 | |
| {
 | |
|     return Memfs->FileSystem;
 | |
| }
 |