kvm-guest-drivers-windows
2854 строки · 82.3 Кб
1/*
2* Copyright (C) 2019-2020 Red Hat, Inc.
3*
4* Written By: Gal Hammer <ghammer@redhat.com>
5*
6* Redistribution and use in source and binary forms, with or without
7* modification, are permitted provided that the following conditions
8* are met :
9* 1. Redistributions of source code must retain the above copyright
10* notice, this list of conditions and the following disclaimer.
11* 2. Redistributions in binary form must reproduce the above copyright
12* notice, this list of conditions and the following disclaimer in the
13* documentation and / or other materials provided with the distribution.
14* 3. Neither the names of the copyright holders nor the names of their contributors
15* may be used to endorse or promote products derived from this software
16* without specific prior written permission.
17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27* SUCH DAMAGE.
28*/
29
30#pragma warning(push)31// '<anonymous-tag>' : structure was padded due to alignment specifier
32#pragma warning(disable: 4324)33// nonstandard extension used : nameless struct / union
34#pragma warning(disable: 4201)35// potentially uninitialized local variable 'Size' used
36#pragma warning(disable: 4701)37#include "winfsp/winfsp.h"38#pragma warning(pop)39
40#include <aclapi.h>41#include <fcntl.h>42#include <initguid.h>43#include <lm.h>44#include <setupapi.h>45#include <stdio.h>46#include <sys/stat.h>47#include <wtsapi32.h>48#include <cfgmgr32.h>49
50#include <map>51#include <string>52
53#include "virtiofs.h"54#include "fusereq.h"55#include "utils.h"56
57#define FS_SERVICE_NAME TEXT("VirtIO-FS")58#define FS_SERVICE_REGKEY TEXT("Software\\") FS_SERVICE_NAME59#define ALLOCATION_UNIT 409660#define PAGE_SZ_4K 409661#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 3262
63#define INVALID_FILE_HANDLE ((uint64_t)(-1))64
65// Some of the constants defined in Windows doesn't match the values that are
66// used in Linux. Don't try to understand, just redefine them to match.
67#undef O_DIRECTORY68#undef O_EXCL69#undef S_IFMT70#undef S_IFDIR71
72#define O_DIRECTORY 020000073#define O_EXCL 020074#define S_IFMT 017000075#define S_IFDIR 04000076#define S_IFLNK 012000077
78#define DEFAULT_OVERFLOWUID 6553479#define DEFAULT_OVERFLOWGID 6553480
81#define DBG(format, ...) \82FspDebugLog("*** %s: " format "\n", __FUNCTION__, __VA_ARGS__)83
84#define SafeHeapFree(p) if (p != NULL) { HeapFree(GetProcessHeap(), 0, p); }85
86#define ReadAndExecute(x) ((x) | (((x) & 0444) >> 2))87#define GroupAsOwner(x) (((x) & ~0070) | (((x) & 0700) >> 3))88
89static uint32_t OverflowUid;90static uint32_t OverflowGid;91
92typedef struct93{
94PVOID DirBuffer;95BOOLEAN IsDirectory;96
97uint64_t NodeId;98uint64_t FileHandle;99
100} VIRTFS_FILE_CONTEXT, *PVIRTFS_FILE_CONTEXT;101
102struct VIRTFS103{
104FSP_FILE_SYSTEM *FileSystem{ NULL };105
106HANDLE Device{ NULL };107
108ULONG DebugFlags{ 0 };109
110// Used to handle device arrive notification.111DeviceInterfaceNotification DevInterfaceNotification{};112// Used to handle device remove notification.113DeviceHandleNotification DevHandleNotification{};114
115bool CaseInsensitive{ false };116std::wstring FileSystemName{};117std::wstring MountPoint{ L"*" };118std::wstring Tag{};119
120UINT32 MaxPages{ 0 };121// A write request buffer size must not exceed this value.122UINT32 MaxWrite{ 0 };123
124// Uid/Gid used to describe files' owner on the guest side.125// Equals to well-known SID 'Everyone' by default.126UINT32 LocalUid{ 0x10100 };127UINT32 LocalGid{ 0x10100 };128
129// Uid/Gid used to describe files' owner on the host side.130UINT32 OwnerUid{ 0 };131UINT32 OwnerGid{ 0 };132
133// Set owner UID/GID to shared directory owner UID/GID, otherwise use134// commandline/registry parameters.135bool AutoOwnerIds{ true };136
137// Maps NodeId to its Nlookup counter.138std::map<UINT64, UINT64> LookupMap{};139
140VIRTFS(ULONG DebugFlags, bool CaseInsensitive,141const std::wstring& FileSystemName, const std::wstring& MountPoint,142const std::wstring& Tag, bool AutoOwnerIds,143uint32_t OwnerUid, uint32_t OwnerGid) :144DebugFlags{ DebugFlags }, CaseInsensitive{ CaseInsensitive },145FileSystemName { FileSystemName },146MountPoint{ MountPoint }, Tag{ Tag }, AutoOwnerIds{ AutoOwnerIds }147{148if (!AutoOwnerIds)149{150this->OwnerUid = OwnerUid;151this->OwnerGid = OwnerGid;152}153}154
155NTSTATUS Start();156VOID Stop();157
158DWORD FindDeviceInterface();159VOID CloseDeviceInterface();160
161DWORD DevInterfaceArrival();162VOID DevQueryRemove();163
164VOID LookupMapNewOrIncNode(UINT64 NodeId);165UINT64 LookupMapPopNode(UINT64 NodeId);166
167NTSTATUS ReadDirAndIgnoreCaseSearch(const VIRTFS_FILE_CONTEXT* ParentContext,168const char *filename, std::string& result);169template<class Request, class... Args>170requires std::invocable<Request, VIRTFS *, uint64_t, const char *, Args...>171NTSTATUS NameAwareRequest(uint64_t parent, const char *name, Request req, Args... args);172
173NTSTATUS RenameWithFallbackRequest(uint64_t oldparent, const char *oldname,174uint64_t newparent, const char *newname, uint32_t flags);175
176NTSTATUS SubmitInitRequest();177NTSTATUS SubmitOpenRequest(UINT32 GrantedAccess,178VIRTFS_FILE_CONTEXT *FileContext);179NTSTATUS SubmitReadDirRequest(const VIRTFS_FILE_CONTEXT *FileContext, uint64_t Offset,180bool Plus, FUSE_READ_OUT *read_out, uint32_t read_out_size);181NTSTATUS SubmitReleaseRequest(const VIRTFS_FILE_CONTEXT *FileContext);182NTSTATUS SubmitLookupRequest(uint64_t parent, const char *filename,183FUSE_LOOKUP_OUT *lookup_out);184NTSTATUS SubmitDeleteRequest(uint64_t parent, const char *filename,185const VIRTFS_FILE_CONTEXT *FileContext);186NTSTATUS SubmitRenameRequest(uint64_t oldparent, uint64_t newparent,187const char *oldname, int oldname_size, const char *newname, int newname_size);188NTSTATUS SubmitRename2Request(uint64_t oldparent, uint64_t newparent,189const char *oldname, int oldname_size, const char *newname, int newname_size,190uint32_t flags);191NTSTATUS SubmitDestroyRequest();192};193
194static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,195UINT32 FileAttributes, UINT64 CreationTime, UINT64 LastAccessTime,196UINT64 LastWriteTime, UINT64 ChangeTime, FSP_FSCTL_FILE_INFO *FileInfo);197
198static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,199UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO *FileInfo);200
201static VOID FixReparsePointAttributes(VIRTFS *VirtFs, uint64_t nodeid,202UINT32 *PFileAttributes);203
204static VOID GetVolumeName(HANDLE Device, PWSTR VolumeName, DWORD VolumeNameSize);205
206static NTSTATUS VirtFsLookupFileName(VIRTFS *VirtFs, PWSTR FileName,207FUSE_LOOKUP_OUT *LookupOut);208
209static DWORD WINAPI DeviceNotificationCallback(HCMNOTIFICATION Notify,210PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData,211DWORD EventDataSize);212
213static int64_t GetUniqueIdentifier()214{
215static int64_t uniq = 1;216
217return InterlockedIncrement64(&uniq);218}
219
220static void FUSE_HEADER_INIT(struct fuse_in_header *hdr, uint32_t opcode,221uint64_t nodeid, uint32_t datalen)222{
223hdr->len = sizeof(*hdr) + datalen;224hdr->opcode = opcode;225hdr->unique = GetUniqueIdentifier();226hdr->nodeid = nodeid;227hdr->uid = 0;228hdr->gid = 0;229hdr->pid = GetCurrentProcessId();230}
231
232VOID VIRTFS::Stop()233{
234if (FileSystem == NULL)235{236return;237}238
239FspFileSystemStopDispatcher(FileSystem);240FspFileSystemDelete(FileSystem);241FileSystem = NULL;242
243LookupMap.clear();244
245SubmitDestroyRequest();246}
247
248DWORD VIRTFS::FindDeviceInterface()249{
250if (Tag.empty())251{252return ::FindDeviceInterface(&GUID_DEVINTERFACE_VIRT_FS, &Device, 0);253}254
255auto tag_cmp_fn = [this](HANDLE Device) {256WCHAR VolumeName[MAX_FILE_SYSTEM_NAME + 1];257GetVolumeName(Device, VolumeName, sizeof(VolumeName));258return Tag == VolumeName;259};260
261return ::FindDeviceInterface(&GUID_DEVINTERFACE_VIRT_FS, &Device, tag_cmp_fn);262}
263
264DWORD VIRTFS::DevInterfaceArrival()265{
266DWORD Error;267NTSTATUS Status;268
269// Wait for unregister work to end, if any.270DevHandleNotification.WaitForUnregWork();271
272Error = FindDeviceInterface();273if (Error != ERROR_SUCCESS)274{275return Error;276}277
278Error = DevHandleNotification.Register(DeviceNotificationCallback, this, Device);279if (Error != ERROR_SUCCESS)280{281goto out_close_handle;282}283
284Status = Start();285if (!NT_SUCCESS(Status))286{287Error = FspWin32FromNtStatus(Status);288goto out_unreg_dh_notify;289}290
291return ERROR_SUCCESS;292
293out_unreg_dh_notify:294DevHandleNotification.AsyncUnregister();295out_close_handle:296CloseHandle(Device);297
298return Error;299}
300
301VOID VIRTFS::CloseDeviceInterface()302{
303if (Device != INVALID_HANDLE_VALUE)304{305CloseHandle(Device);306Device = INVALID_HANDLE_VALUE;307}308}
309
310VOID VIRTFS::DevQueryRemove()311{
312Stop();313DevHandleNotification.AsyncUnregister();314CloseDeviceInterface();315}
316
317DWORD WINAPI DeviceNotificationCallback(HCMNOTIFICATION Notify,318PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData,319DWORD EventDataSize)320{
321auto VirtFs = static_cast<VIRTFS *>(Context);322
323UNREFERENCED_PARAMETER(Notify);324UNREFERENCED_PARAMETER(EventData);325UNREFERENCED_PARAMETER(EventDataSize);326
327switch (Action)328{329case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL:330case CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED:331VirtFs->DevInterfaceArrival();332break;333case CM_NOTIFY_ACTION_DEVICEQUERYREMOVE:334case CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE:335VirtFs->DevQueryRemove();336break;337default:338break;339}340
341return ERROR_SUCCESS;342}
343
344static UINT32 PosixUnixModeToAttributes(VIRTFS *VirtFs, uint64_t nodeid,345uint32_t mode)346{
347UINT32 Attributes;348
349switch (mode & S_IFMT)350{351case S_IFDIR:352Attributes = FILE_ATTRIBUTE_DIRECTORY;353break;354
355case S_IFLNK:356Attributes = FILE_ATTRIBUTE_REPARSE_POINT;357break;358
359default:360Attributes = FILE_ATTRIBUTE_ARCHIVE;361break;362}363
364if (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)365{366FixReparsePointAttributes(VirtFs, nodeid, &Attributes);367}368
369if (!!(mode & 0222) == FALSE)370{371Attributes |= FILE_ATTRIBUTE_READONLY;372}373
374return Attributes;375}
376
377static uint32_t AccessToUnixFlags(UINT32 GrantedAccess)378{
379uint32_t flags;380
381switch (GrantedAccess & (FILE_READ_DATA | FILE_WRITE_DATA))382{383case FILE_WRITE_DATA:384flags = O_WRONLY;385break;386case FILE_READ_DATA | FILE_WRITE_DATA:387flags = O_RDWR;388break;389case FILE_READ_DATA:390__fallthrough;391default:392flags = O_RDONLY;393break;394}395
396if ((GrantedAccess & FILE_APPEND_DATA) && (flags == 0))397{398flags = O_RDWR;399}400
401return flags;402}
403
404static VOID FileTimeToUnixTime(UINT64 FileTime, uint64_t *time,405uint32_t *nsec)406{
407__int3264 UnixTime[2];408FspPosixFileTimeToUnixTime(FileTime, UnixTime);409*time = UnixTime[0];410*nsec = (uint32_t)UnixTime[1];411}
412
413static VOID UnixTimeToFileTime(uint64_t time, uint32_t nsec,414PUINT64 PFileTime)415{
416*PFileTime = time * 10000000 + nsec / 100 +417116444736000000000LL;418}
419
420static VOID SetFileInfo(VIRTFS *VirtFs, struct fuse_entry_out *entry,421FSP_FSCTL_FILE_INFO *FileInfo)422{
423struct fuse_attr *attr = &entry->attr;424
425FileInfo->FileAttributes = PosixUnixModeToAttributes(VirtFs,426entry->nodeid, attr->mode);427
428if (FileInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)429{430FileInfo->ReparseTag = IO_REPARSE_TAG_SYMLINK;431FileInfo->FileSize = 0;432FileInfo->AllocationSize = 0;433}434else435{436FileInfo->ReparseTag = 0;437FileInfo->FileSize = attr->size;438FileInfo->AllocationSize = attr->blocks * 512;439}440UnixTimeToFileTime(attr->ctime, attr->ctimensec, &FileInfo->CreationTime);441UnixTimeToFileTime(attr->atime, attr->atimensec,442&FileInfo->LastAccessTime);443UnixTimeToFileTime(attr->mtime, attr->mtimensec,444&FileInfo->LastWriteTime);445FileInfo->ChangeTime = FileInfo->LastWriteTime;446FileInfo->IndexNumber = 0;447FileInfo->HardLinks = 0;448FileInfo->EaSize = 0;449
450DBG("ino=%I64u size=%I64u blocks=%I64u atime=%I64u mtime=%I64u ctime=%I64u "451"atimensec=%u mtimensec=%u ctimensec=%u mode=%x nlink=%u uid=%u "452"gid=%u rdev=%u blksize=%u", attr->ino, attr->size, attr->blocks,453attr->atime, attr->mtime, attr->ctime, attr->atimensec,454attr->mtimensec, attr->ctimensec, attr->mode, attr->nlink,455attr->uid, attr->gid, attr->rdev, attr->blksize);456}
457
458static NTSTATUS VirtFsFuseRequest(HANDLE Device, LPVOID InBuffer,459DWORD InBufferSize, LPVOID OutBuffer, DWORD OutBufferSize)460{
461NTSTATUS Status = STATUS_SUCCESS;462DWORD BytesReturned = 0;463BOOL Result;464struct fuse_in_header *in_hdr = (struct fuse_in_header *)InBuffer;465struct fuse_out_header *out_hdr = (struct fuse_out_header *)OutBuffer;466
467DBG(">>req: %d unique: %I64u len: %u", in_hdr->opcode, in_hdr->unique,468in_hdr->len);469
470Result = DeviceIoControl(Device, IOCTL_VIRTFS_FUSE_REQUEST,471InBuffer, InBufferSize, OutBuffer, OutBufferSize,472&BytesReturned, NULL);473
474if (Result == FALSE)475{476return FspNtStatusFromWin32(GetLastError());477}478
479DBG("<<len: %u error: %d unique: %I64u", out_hdr->len, out_hdr->error,480out_hdr->unique);481
482if (BytesReturned != out_hdr->len)483{484DBG("BytesReturned != hdr->len");485}486
487if ((BytesReturned != sizeof(struct fuse_out_header)) &&488(BytesReturned < OutBufferSize))489{490DBG("Bytes Returned: %d Expected: %d", BytesReturned, OutBufferSize);491// XXX return STATUS_UNSUCCESSFUL;492}493
494if (out_hdr->error < 0)495{496switch (out_hdr->error)497{498case -EPERM:499case -EACCES:500Status = STATUS_ACCESS_DENIED;501break;502case -ENOENT:503Status = STATUS_OBJECT_NAME_NOT_FOUND;504break;505case -EIO:506Status = STATUS_IO_DEVICE_ERROR;507break;508case -EBADF:509Status = STATUS_OBJECT_NAME_INVALID;510break;511case -ENOMEM:512Status = STATUS_INSUFFICIENT_RESOURCES;513break;514case -EEXIST:515Status = STATUS_OBJECT_NAME_COLLISION;516break;517case -EINVAL:518Status = STATUS_INVALID_PARAMETER;519break;520case -ENAMETOOLONG:521Status = STATUS_NAME_TOO_LONG;522break;523case -ENOSYS:524Status = STATUS_NOT_IMPLEMENTED;525break;526case -EOPNOTSUPP:527Status = STATUS_NOT_SUPPORTED;528break;529default:530Status = STATUS_UNSUCCESSFUL;531break;532}533}534
535return Status;536}
537
538static NTSTATUS VirtFsCreateFile(VIRTFS *VirtFs,539VIRTFS_FILE_CONTEXT *FileContext, UINT32 GrantedAccess, CHAR *FileName,540UINT64 Parent, UINT32 Mode, UINT64 AllocationSize,541FSP_FSCTL_FILE_INFO *FileInfo)542{
543NTSTATUS Status;544FUSE_CREATE_IN create_in;545FUSE_CREATE_OUT create_out;546
547FUSE_HEADER_INIT(&create_in.hdr, FUSE_CREATE, Parent,548sizeof(struct fuse_create_in) + lstrlenA(FileName) + 1);549
550create_in.hdr.uid = VirtFs->OwnerUid;551create_in.hdr.gid = VirtFs->OwnerGid;552
553lstrcpyA(create_in.name, FileName);554create_in.create.mode = Mode;555create_in.create.umask = 0;556create_in.create.flags = AccessToUnixFlags(GrantedAccess) | O_EXCL;557
558DBG("create_in.create.flags: 0x%08x", create_in.create.flags);559DBG("create_in.create.mode: 0x%08x", create_in.create.mode);560
561Status = VirtFsFuseRequest(VirtFs->Device, &create_in, create_in.hdr.len,562&create_out, sizeof(create_out));563
564if (NT_SUCCESS(Status))565{566FileContext->NodeId = create_out.entry.nodeid;567FileContext->FileHandle = create_out.open.fh;568
569// Newly created file has nlookup = 1570if (!VirtFs->LookupMap.emplace(FileContext->NodeId, 1).second)571{572return STATUS_UNSUCCESSFUL;573}574
575if (AllocationSize > 0)576{577SetFileSize(VirtFs->FileSystem, FileContext,578AllocationSize, TRUE, FileInfo);579}580else581{582SetFileInfo(VirtFs, &create_out.entry, FileInfo);583}584}585
586return Status;587}
588
589static NTSTATUS VirtFsCreateDir(VIRTFS *VirtFs,590VIRTFS_FILE_CONTEXT *FileContext, CHAR *FileName, UINT64 Parent,591UINT32 Mode, FSP_FSCTL_FILE_INFO *FileInfo)592{
593NTSTATUS Status;594FUSE_MKDIR_IN mkdir_in;595FUSE_MKDIR_OUT mkdir_out;596
597FUSE_HEADER_INIT(&mkdir_in.hdr, FUSE_MKDIR, Parent,598sizeof(struct fuse_mkdir_in) + lstrlenA(FileName) + 1);599
600mkdir_in.hdr.uid = VirtFs->OwnerUid;601mkdir_in.hdr.gid = VirtFs->OwnerGid;602
603lstrcpyA(mkdir_in.name, FileName);604mkdir_in.mkdir.mode = Mode | 0111; /* ---x--x--x */605mkdir_in.mkdir.umask = 0;606
607Status = VirtFsFuseRequest(VirtFs->Device, &mkdir_in, mkdir_in.hdr.len,608&mkdir_out, sizeof(mkdir_out));609
610if (NT_SUCCESS(Status))611{612FileContext->NodeId = mkdir_out.entry.nodeid;613
614// Newly created directory has nlookup = 1615if (!VirtFs->LookupMap.emplace(FileContext->NodeId, 1).second)616{617return STATUS_UNSUCCESSFUL;618}619
620SetFileInfo(VirtFs, &mkdir_out.entry, FileInfo);621}622
623return Status;624}
625
626VOID VIRTFS::LookupMapNewOrIncNode(UINT64 NodeId)627{
628auto EmplaceResult = LookupMap.emplace(NodeId, 1);629
630if (!EmplaceResult.second)631{632EmplaceResult.first->second += 1;633}634}
635
636UINT64 VIRTFS::LookupMapPopNode(UINT64 NodeId)637{
638auto Item = LookupMap.extract(NodeId);639
640return Item.empty() ? 0 : Item.mapped();641}
642
643static VOID SubmitForgetRequest(HANDLE Device, UINT64 NodeId, UINT64 Nlookup)644{
645FUSE_FORGET_IN forget_in;646FUSE_FORGET_OUT forget_out;647
648DBG("NodeId: %lu Nlookup: %lu", NodeId, Nlookup);649
650FUSE_HEADER_INIT(&forget_in.hdr, FUSE_FORGET, NodeId,651sizeof(forget_in.forget));652
653forget_in.forget.nlookup = Nlookup;654
655VirtFsFuseRequest(Device, &forget_in, forget_in.hdr.len,656&forget_out, sizeof(forget_out));657}
658
659NTSTATUS VIRTFS::SubmitDeleteRequest(uint64_t parent, const char *filename,660const VIRTFS_FILE_CONTEXT *FileContext)661{
662FUSE_UNLINK_IN unlink_in;663FUSE_UNLINK_OUT unlink_out;664
665FUSE_HEADER_INIT(&unlink_in.hdr,666FileContext->IsDirectory ? FUSE_RMDIR : FUSE_UNLINK, parent,667lstrlenA(filename) + 1);668
669lstrcpyA(unlink_in.name, filename);670
671NTSTATUS Status = VirtFsFuseRequest(Device, &unlink_in, unlink_in.hdr.len,672&unlink_out, sizeof(unlink_out));673
674if (NT_SUCCESS(Status))675{676UINT64 Nlookup = LookupMapPopNode(FileContext->NodeId);677
678SubmitForgetRequest(Device, FileContext->NodeId, Nlookup);679}680
681return Status;682}
683
684NTSTATUS VIRTFS::SubmitLookupRequest(uint64_t parent, const char *filename,685FUSE_LOOKUP_OUT *lookup_out)686{
687DBG("filename = '%s' parent = %I64u", filename, parent);688
689NTSTATUS Status;690FUSE_LOOKUP_IN lookup_in;691
692FUSE_HEADER_INIT(&lookup_in.hdr, FUSE_LOOKUP, parent,693lstrlenA(filename) + 1);694
695lstrcpyA(lookup_in.name, filename);696
697Status = VirtFsFuseRequest(Device, &lookup_in, lookup_in.hdr.len,698lookup_out, sizeof(*lookup_out));699
700if (NT_SUCCESS(Status))701{702struct fuse_attr *attr = &lookup_out->entry.attr;703
704LookupMapNewOrIncNode(lookup_out->entry.nodeid);705
706DBG("nodeid=%I64u ino=%I64u size=%I64u blocks=%I64u atime=%I64u mtime=%I64u "707"ctime=%I64u atimensec=%u mtimensec=%u ctimensec=%u mode=%x "708"nlink=%u uid=%u gid=%u rdev=%u blksize=%u",709lookup_out->entry.nodeid, attr->ino, attr->size, attr->blocks,710attr->atime, attr->mtime, attr->ctime, attr->atimensec,711attr->mtimensec, attr->ctimensec, attr->mode, attr->nlink,712attr->uid, attr->gid, attr->rdev, attr->blksize);713}714
715return Status;716}
717
718static NTSTATUS SubmitReadLinkRequest(HANDLE Device, UINT64 NodeId,719PWSTR SubstituteName, PUSHORT SubstituteNameLength)720{
721FUSE_READLINK_IN readlink_in;722FUSE_READLINK_OUT readlink_out;723NTSTATUS Status;724
725FUSE_HEADER_INIT(&readlink_in.hdr, FUSE_READLINK, NodeId,726sizeof(readlink_out.name));727
728Status = VirtFsFuseRequest(Device, &readlink_in, readlink_in.hdr.len,729&readlink_out, sizeof(readlink_out));730
731if (NT_SUCCESS(Status))732{733int namelen = readlink_out.hdr.len - sizeof(readlink_out.hdr);734
735*SubstituteNameLength = (USHORT)MultiByteToWideChar(CP_UTF8, 0,736readlink_out.name, namelen, SubstituteName, MAX_PATH - 1);737
738SubstituteName[*SubstituteNameLength] = L'\0';739
740if (*SubstituteNameLength == 0)741{742Status = FspNtStatusFromWin32(GetLastError());743}744}745
746return Status;747}
748
749NTSTATUS VIRTFS::SubmitRenameRequest(uint64_t oldparent,750uint64_t newparent, const char *oldname, int oldname_size, const char *newname,751int newname_size)752{
753FUSE_RENAME_IN *rename_in;754FUSE_RENAME_OUT rename_out;755NTSTATUS Status;756
757rename_in = (FUSE_RENAME_IN *)HeapAlloc(GetProcessHeap(), 0,758sizeof(*rename_in) + oldname_size + newname_size);759
760if (rename_in == NULL)761{762return STATUS_INSUFFICIENT_RESOURCES;763}764
765FUSE_HEADER_INIT(&rename_in->hdr, FUSE_RENAME, oldparent,766sizeof(rename_in->rename) + oldname_size + newname_size);767
768rename_in->rename.newdir = newparent;769CopyMemory(rename_in->names, oldname, oldname_size);770CopyMemory(rename_in->names + oldname_size, newname, newname_size);771
772Status = VirtFsFuseRequest(Device, rename_in, rename_in->hdr.len,773&rename_out, sizeof(rename_out));774
775SafeHeapFree(rename_in);776
777return Status;778}
779
780NTSTATUS VIRTFS::SubmitRename2Request(uint64_t oldparent,781uint64_t newparent, const char *oldname, int oldname_size, const char *newname,782int newname_size, uint32_t flags)783{
784FUSE_RENAME2_IN *rename2_in;785FUSE_RENAME_OUT rename_out;786NTSTATUS Status;787
788rename2_in = (FUSE_RENAME2_IN *)HeapAlloc(GetProcessHeap(), 0,789sizeof(*rename2_in) + oldname_size + newname_size);790
791if (rename2_in == NULL)792{793return STATUS_INSUFFICIENT_RESOURCES;794}795
796FUSE_HEADER_INIT(&rename2_in->hdr, FUSE_RENAME2, oldparent,797sizeof(rename2_in->rename) + oldname_size + newname_size);798
799rename2_in->rename.newdir = newparent;800rename2_in->rename.flags = flags;801CopyMemory(rename2_in->names, oldname, oldname_size);802CopyMemory(rename2_in->names + oldname_size, newname, newname_size);803
804Status = VirtFsFuseRequest(Device, rename2_in, rename2_in->hdr.len,805&rename_out, sizeof(rename_out));806
807SafeHeapFree(rename2_in);808
809return Status;810}
811
812NTSTATUS VIRTFS::RenameWithFallbackRequest(uint64_t oldparent, const char *oldname,813uint64_t newparent, const char *newname, uint32_t flags)814{
815int oldname_size = lstrlenA(oldname) + 1;816int newname_size = lstrlenA(newname) + 1;817
818DBG("old: %s (%d) new: %s (%d) flags: %d",819oldname, oldname_size, newname, newname_size, flags);820
821NTSTATUS Status = SubmitRename2Request(oldparent, newparent,822oldname, oldname_size, newname, newname_size, flags);823
824// Rename2 fails on NFS shared folder with EINVAL error. So retry to825// rename the file without the flags.826if (Status == STATUS_INVALID_PARAMETER)827{828Status = SubmitRenameRequest(oldparent, newparent,829oldname, oldname_size, newname, newname_size);830}831
832return Status;833}
834
835template<class Request, class... Args>836requires std::invocable<Request, VIRTFS *, uint64_t, const char *, Args...>837NTSTATUS VIRTFS::NameAwareRequest(uint64_t parent, const char *name, Request req, Args... args)838{
839// First attempt840NTSTATUS Status = std::invoke(req, this, parent, name, args...);841if (NT_SUCCESS(Status) || !CaseInsensitive)842{843return Status;844}845
846VIRTFS_FILE_CONTEXT ParentContext = { .IsDirectory = TRUE, .NodeId = parent, };847
848Status = SubmitOpenRequest(0, &ParentContext);849if (!NT_SUCCESS(Status))850{851return Status;852}853SCOPE_EXIT(ParentContext, { SubmitReleaseRequest(&ParentContext); }, this);854
855std::string result_name{};856Status = ReadDirAndIgnoreCaseSearch(&ParentContext, name, result_name);857if (!NT_SUCCESS(Status))858{859return Status;860}861
862// Second attempt863Status = std::invoke(req, this, parent, result_name.c_str(), args...);864
865return Status;866}
867
868static NTSTATUS PathWalkthough(VIRTFS *VirtFs, CHAR *FullPath,869CHAR **FileName, UINT64 *Parent)870{
871WCHAR SubstituteName[MAX_PATH];872USHORT SubstituteNameLength = 0;873NTSTATUS Status = STATUS_SUCCESS;874FUSE_LOOKUP_OUT LookupOut;875CHAR *Separator;876
877*Parent = FUSE_ROOT_ID;878*FileName = FullPath;879
880while ((Separator = strchr(*FileName, '/')) != NULL)881{882*Separator = '\0';883
884Status = VirtFs->NameAwareRequest(*Parent, *FileName,885&VIRTFS::SubmitLookupRequest, &LookupOut);886if (!NT_SUCCESS(Status))887{888break;889}890
891if ((LookupOut.entry.attr.mode & S_IFLNK) == S_IFLNK)892{893Status = SubmitReadLinkRequest(VirtFs->Device,894LookupOut.entry.nodeid, SubstituteName, &SubstituteNameLength);895
896if (!NT_SUCCESS(Status))897{898break;899}900
901Status = VirtFsLookupFileName(VirtFs, SubstituteName, &LookupOut);902if (!NT_SUCCESS(Status))903{904break;905}906}907
908*Parent = LookupOut.entry.nodeid;909*FileName = Separator + 1;910}911
912return Status;913}
914
915static NTSTATUS VirtFsLookupFileName(VIRTFS *VirtFs, PWSTR FileName,916FUSE_LOOKUP_OUT *LookupOut)917{
918NTSTATUS Status;919char *filename, *fullpath;920uint64_t parent;921
922if (lstrcmp(FileName, TEXT("\\")) == 0)923{924FileName = (PWSTR)L".";925}926else if (FileName[0] == TEXT('\\'))927{928// Skip backslash if exist.929FileName += 1;930}931
932Status = FspPosixMapWindowsToPosixPath(FileName, &fullpath);933if (!NT_SUCCESS(Status))934{935return Status;936}937SCOPE_EXIT(fullpath, { FspPosixDeletePath(fullpath); });938
939Status = PathWalkthough(VirtFs, fullpath, &filename, &parent);940if (NT_SUCCESS(Status))941{942Status = VirtFs->NameAwareRequest(parent, filename,943&VIRTFS::SubmitLookupRequest, LookupOut);944}945
946return Status;947}
948
949static VOID FixReparsePointAttributes(VIRTFS *VirtFs, uint64_t nodeid,950UINT32 *PFileAttributes)951{
952WCHAR SubstituteName[MAX_PATH];953USHORT SubstituteNameLength = 0;954UINT32 FileAttributes;955NTSTATUS Status;956FUSE_LOOKUP_OUT lookup_out;957
958Status = SubmitReadLinkRequest(VirtFs->Device, nodeid, SubstituteName,959&SubstituteNameLength);960
961if (NT_SUCCESS(Status))962{963Status = VirtFsLookupFileName(VirtFs, SubstituteName, &lookup_out);964
965if (NT_SUCCESS(Status))966{967struct fuse_attr *attr = &lookup_out.entry.attr;968
969FileAttributes = PosixUnixModeToAttributes(VirtFs,970lookup_out.entry.nodeid, attr->mode);971
972if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)973{974*PFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;975}976}977}978}
979
980static NTSTATUS GetFileInfoInternal(VIRTFS *VirtFs,981PVIRTFS_FILE_CONTEXT FileContext, FSP_FSCTL_FILE_INFO *FileInfo,982PSECURITY_DESCRIPTOR *SecurityDescriptor)983{
984NTSTATUS Status;985FUSE_GETATTR_IN getattr_in;986FUSE_GETATTR_OUT getattr_out;987
988if ((FileInfo != NULL) && (SecurityDescriptor != NULL))989{990return STATUS_INVALID_PARAMETER;991}992
993FUSE_HEADER_INIT(&getattr_in.hdr, FUSE_GETATTR, FileContext->NodeId,994sizeof(getattr_in.getattr));995
996if (FileContext->FileHandle != INVALID_FILE_HANDLE)997{998getattr_in.getattr.fh = FileContext->FileHandle;999getattr_in.getattr.getattr_flags |= FUSE_GETATTR_FH;1000}1001
1002getattr_in.getattr.getattr_flags = 0;1003
1004Status = VirtFsFuseRequest(VirtFs->Device, &getattr_in,1005sizeof(getattr_in), &getattr_out, sizeof(getattr_out));1006
1007if (NT_SUCCESS(Status))1008{1009struct fuse_attr *attr = &getattr_out.attr.attr;1010
1011if (FileInfo != NULL)1012{1013struct fuse_entry_out entry;1014
1015ZeroMemory(&entry, sizeof(entry));1016entry.nodeid = FileContext->NodeId;1017entry.attr = *attr;1018
1019SetFileInfo(VirtFs, &entry, FileInfo);1020}1021
1022if (SecurityDescriptor != NULL)1023{1024Status = FspPosixMapPermissionsToSecurityDescriptor(1025VirtFs->LocalUid, VirtFs->LocalGid,1026GroupAsOwner(ReadAndExecute(attr->mode)), SecurityDescriptor);1027}1028}1029
1030return Status;1031}
1032
1033static NTSTATUS IsEmptyDirectory(FSP_FILE_SYSTEM *FileSystem,1034PVOID FileContext0)1035{
1036VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1037VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1038BYTE ReadOutBuf[0x1000];1039struct fuse_dirent *DirEntry;1040NTSTATUS Status = STATUS_SUCCESS;1041UINT32 Entries;1042UINT32 Remains;1043FUSE_READ_IN read_in;1044FUSE_READ_OUT *read_out = (FUSE_READ_OUT *)ReadOutBuf;1045
1046FUSE_HEADER_INIT(&read_in.hdr, FUSE_READDIR, FileContext->NodeId,1047sizeof(read_in.read));1048
1049read_in.read.fh = FileContext->FileHandle;1050read_in.read.offset = 0;1051read_in.read.size = sizeof(ReadOutBuf) - sizeof(struct fuse_out_header);1052read_in.read.read_flags = 0;1053read_in.read.lock_owner = 0;1054read_in.read.flags = 0;1055
1056Status = VirtFsFuseRequest(VirtFs->Device, &read_in, sizeof(read_in),1057read_out, sizeof(struct fuse_out_header) + read_in.read.size);1058
1059if (NT_SUCCESS(Status))1060{1061Entries = 0;1062Remains = read_out->hdr.len - sizeof(struct fuse_out_header);1063DirEntry = (struct fuse_dirent *)read_out->buf;1064
1065while (Remains > sizeof(struct fuse_dirent))1066{1067if (++Entries > 2)1068{1069Status = STATUS_DIRECTORY_NOT_EMPTY;1070break;1071}1072
1073Remains -= FUSE_DIRENT_SIZE(DirEntry);1074DirEntry = (struct fuse_dirent *)((PBYTE)DirEntry +1075FUSE_DIRENT_SIZE(DirEntry));1076}1077}1078
1079return Status;1080}
1081
1082static VOID GetVolumeName(HANDLE Device, PWSTR VolumeName,1083DWORD VolumeNameSize)1084{
1085DWORD BytesReturned;1086BOOL Result;1087
1088Result = DeviceIoControl(Device, IOCTL_VIRTFS_GET_VOLUME_NAME, NULL, 0,1089VolumeName, VolumeNameSize, &BytesReturned, NULL);1090
1091if (Result == FALSE)1092{1093lstrcpy(VolumeName, L"Default");1094}1095}
1096
1097static NTSTATUS GetReparsePointByName(FSP_FILE_SYSTEM *FileSystem,1098PVOID Context, PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer,1099PSIZE_T PSize)1100{
1101VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1102PREPARSE_DATA_BUFFER ReparseData = (PREPARSE_DATA_BUFFER)Buffer;1103FUSE_LOOKUP_OUT lookup_out;1104WCHAR SubstituteName[MAX_PATH];1105USHORT SubstituteNameLength = 0;1106NTSTATUS Status;1107
1108UNREFERENCED_PARAMETER(Context);1109UNREFERENCED_PARAMETER(IsDirectory);1110UNREFERENCED_PARAMETER(PSize);1111
1112Status = VirtFsLookupFileName(VirtFs, FileName, &lookup_out);1113if (!NT_SUCCESS(Status))1114{1115return Status;1116}1117
1118if ((lookup_out.entry.attr.mode & S_IFLNK) != S_IFLNK)1119{1120return STATUS_NOT_A_REPARSE_POINT;1121}1122
1123Status = SubmitReadLinkRequest(VirtFs->Device, lookup_out.entry.nodeid,1124SubstituteName, &SubstituteNameLength);1125
1126if (NT_SUCCESS(Status))1127{1128ReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK;1129ReparseData->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;1130ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;1131ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength =1132SubstituteNameLength * sizeof(WCHAR);1133CopyMemory(ReparseData->SymbolicLinkReparseBuffer.PathBuffer,1134SubstituteName, SubstituteNameLength * sizeof(WCHAR));1135}1136
1137return Status;1138}
1139
1140static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,1141FSP_FSCTL_VOLUME_INFO *VolumeInfo)1142{
1143VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1144NTSTATUS Status;1145FUSE_STATFS_IN statfs_in;1146FUSE_STATFS_OUT statfs_out;1147
1148FUSE_HEADER_INIT(&statfs_in.hdr, FUSE_STATFS, FUSE_ROOT_ID, 0);1149
1150Status = VirtFsFuseRequest(VirtFs->Device, &statfs_in, sizeof(statfs_in),1151&statfs_out, sizeof(statfs_out));1152
1153if (NT_SUCCESS(Status))1154{1155struct fuse_kstatfs *kstatfs = &statfs_out.statfs.st;1156
1157VolumeInfo->TotalSize = kstatfs->bsize * kstatfs->blocks;1158VolumeInfo->FreeSize = kstatfs->bsize * kstatfs->bavail;1159
1160GetVolumeName(VirtFs->Device, VolumeInfo->VolumeLabel,1161sizeof(VolumeInfo->VolumeLabel));1162
1163VolumeInfo->VolumeLabelLength =1164(UINT16)(wcslen(VolumeInfo->VolumeLabel) * sizeof(WCHAR));1165}1166
1167DBG("VolumeLabel: %S", VolumeInfo->VolumeLabel);1168
1169return Status;1170}
1171
1172static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName,1173PUINT32 PFileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor,1174SIZE_T *PSecurityDescriptorSize)1175{
1176VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1177PSECURITY_DESCRIPTOR Security = NULL;1178DWORD SecuritySize;1179NTSTATUS Status;1180FUSE_LOOKUP_OUT lookup_out;1181
1182DBG("\"%S\"", FileName);1183
1184Status = VirtFsLookupFileName(VirtFs, FileName, &lookup_out);1185if (NT_SUCCESS(Status))1186{1187struct fuse_attr *attr = &lookup_out.entry.attr;1188
1189if (VirtFs->AutoOwnerIds && (lstrcmp(FileName, TEXT("\\")) == 0))1190{1191// If the shared directory UID or GID turns out to be 'nobody', it1192// means the host daemon is inside the user namespace. So, the1193// previous identity is 0 or another valid value. So, let's try to1194// preserve it.1195VirtFs->OwnerUid = (attr->uid != OverflowUid) ?1196attr->uid : VirtFs->OwnerUid;1197VirtFs->OwnerGid = (attr->gid != OverflowGid) ?1198attr->gid : VirtFs->OwnerGid;1199}1200
1201if (PFileAttributes != NULL)1202{1203*PFileAttributes = PosixUnixModeToAttributes(VirtFs,1204lookup_out.entry.nodeid, attr->mode);1205}1206
1207Status = FspPosixMapPermissionsToSecurityDescriptor(VirtFs->LocalUid,1208VirtFs->LocalGid, GroupAsOwner(ReadAndExecute(attr->mode)), &Security);1209
1210if (NT_SUCCESS(Status))1211{1212SecuritySize = GetSecurityDescriptorLength(Security);1213
1214if ((PSecurityDescriptorSize != NULL) &&1215(*PSecurityDescriptorSize < SecuritySize))1216{1217Status = STATUS_BUFFER_OVERFLOW;1218}1219else1220{1221if (SecurityDescriptor != NULL)1222{1223memcpy(SecurityDescriptor, Security, SecuritySize);1224}1225}1226SafeHeapFree(Security);1227}1228else1229{1230SecuritySize = 0;1231}1232
1233if (PSecurityDescriptorSize != NULL)1234{1235*PSecurityDescriptorSize = SecuritySize;1236}1237}1238
1239return Status;1240}
1241
1242NTSTATUS VIRTFS::SubmitOpenRequest(UINT32 GrantedAccess,1243VIRTFS_FILE_CONTEXT *FileContext)1244{
1245NTSTATUS Status;1246FUSE_OPEN_IN open_in;1247FUSE_OPEN_OUT open_out;1248
1249FUSE_HEADER_INIT(&open_in.hdr,1250FileContext->IsDirectory ? FUSE_OPENDIR : FUSE_OPEN,1251FileContext->NodeId, sizeof(open_in.open));1252
1253open_in.open.flags = FileContext->IsDirectory ?1254(O_RDONLY | O_DIRECTORY) : AccessToUnixFlags(GrantedAccess);1255
1256Status = VirtFsFuseRequest(Device, &open_in, sizeof(open_in),1257&open_out, sizeof(open_out));1258
1259if (!NT_SUCCESS(Status))1260{1261return Status;1262}1263
1264FileContext->FileHandle = open_out.open.fh;1265
1266return STATUS_SUCCESS;1267}
1268
1269static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName,1270UINT32 CreateOptions, UINT32 GrantedAccess, UINT32 FileAttributes,1271PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,1272PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)1273{
1274VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1275VIRTFS_FILE_CONTEXT *FileContext;1276NTSTATUS Status;1277UINT32 Mode = 0664 /* -rw-rw-r-- */;1278char *filename, *fullpath;1279uint64_t parent;1280
1281UNREFERENCED_PARAMETER(SecurityDescriptor);1282
1283DBG("\"%S\" CreateOptions: 0x%08x GrantedAccess: 0x%08x "1284"FileAttributes: 0x%08x AllocationSize: %I64u", FileName,1285CreateOptions, GrantedAccess, FileAttributes, AllocationSize);1286
1287Status = FspPosixMapWindowsToPosixPath(FileName + 1, &fullpath);1288if (!NT_SUCCESS(Status))1289{1290return Status;1291}1292SCOPE_EXIT(fullpath, { FspPosixDeletePath(fullpath); });1293
1294Status = PathWalkthough(VirtFs, fullpath, &filename, &parent);1295if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND))1296{1297return Status;1298}1299
1300FileContext = (VIRTFS_FILE_CONTEXT *)HeapAlloc(GetProcessHeap(),1301HEAP_ZERO_MEMORY, sizeof(*FileContext));1302
1303if (FileContext == NULL)1304{1305return STATUS_INSUFFICIENT_RESOURCES;1306}1307
1308FileContext->FileHandle = INVALID_FILE_HANDLE;1309
1310if (!!(FileAttributes & FILE_ATTRIBUTE_READONLY) == TRUE)1311{1312Mode &= ~0222;1313}1314
1315if (CreateOptions & FILE_DIRECTORY_FILE)1316{1317FileContext->IsDirectory = TRUE;1318
1319Status = VirtFsCreateDir(VirtFs, FileContext, filename, parent, Mode,1320FileInfo);1321
1322if (NT_SUCCESS(Status))1323{1324Status = VirtFs->SubmitOpenRequest(GrantedAccess, FileContext);1325}1326}1327else1328{1329Status = VirtFsCreateFile(VirtFs, FileContext, GrantedAccess,1330filename, parent, Mode, AllocationSize, FileInfo);1331}1332
1333if (!NT_SUCCESS(Status))1334{1335SafeHeapFree(FileContext);1336return Status;1337}1338
1339*PFileContext = FileContext;1340
1341return Status;1342}
1343
1344static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName,1345UINT32 CreateOptions, UINT32 GrantedAccess, PVOID *PFileContext,1346FSP_FSCTL_FILE_INFO *FileInfo)1347{
1348VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1349VIRTFS_FILE_CONTEXT *FileContext;1350NTSTATUS Status;1351FUSE_LOOKUP_OUT lookup_out;1352
1353DBG("\"%S\" CreateOptions: 0x%08x GrantedAccess: 0x%08x", FileName,1354CreateOptions, GrantedAccess);1355
1356FileContext = (VIRTFS_FILE_CONTEXT *)HeapAlloc(GetProcessHeap(),1357HEAP_ZERO_MEMORY, sizeof(*FileContext));1358
1359if (FileContext == NULL)1360{1361return STATUS_INSUFFICIENT_RESOURCES;1362}1363
1364Status = VirtFsLookupFileName(VirtFs, FileName, &lookup_out);1365if (!NT_SUCCESS(Status))1366{1367SafeHeapFree(FileContext);1368return Status;1369}1370
1371FileContext->NodeId = lookup_out.entry.nodeid;1372
1373if ((lookup_out.entry.attr.mode & S_IFLNK) != S_IFLNK)1374{1375FileContext->IsDirectory = !!(lookup_out.entry.attr.mode & S_IFDIR);1376Status = VirtFs->SubmitOpenRequest(GrantedAccess, FileContext);1377if (!NT_SUCCESS(Status))1378{1379SafeHeapFree(FileContext);1380return Status;1381}1382}1383
1384SetFileInfo(VirtFs, &lookup_out.entry, FileInfo);1385*PFileContext = FileContext;1386
1387return Status;1388}
1389
1390static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,1391PVOID FileContext0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes,1392UINT64 AllocationSize, FSP_FSCTL_FILE_INFO *FileInfo)1393{
1394VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1395VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1396NTSTATUS Status;1397
1398DBG("FileAttributes: 0x%08x ReplaceFileAttributes: %d "1399"AllocationSize: %I64u", FileAttributes, ReplaceFileAttributes,1400AllocationSize);1401
1402if ((FileAttributes != 0) && (FileAttributes != INVALID_FILE_ATTRIBUTES))1403{1404if (ReplaceFileAttributes == FALSE)1405{1406Status = GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1407if (!NT_SUCCESS(Status))1408{1409return Status;1410}1411
1412FileAttributes |= FileInfo->FileAttributes;1413}1414
1415if ((FileAttributes != FileInfo->FileAttributes))1416{1417Status = SetBasicInfo(FileSystem, FileContext0, FileAttributes,14180LL, 0LL, 0LL, 0LL, FileInfo);1419
1420if (!NT_SUCCESS(Status))1421{1422return Status;1423}1424}1425}1426
1427return SetFileSize(FileSystem, FileContext0, AllocationSize, FALSE,1428FileInfo);1429}
1430
1431NTSTATUS VIRTFS::SubmitReleaseRequest(const VIRTFS_FILE_CONTEXT *FileContext)1432{
1433FUSE_RELEASE_IN release_in;1434FUSE_RELEASE_OUT release_out;1435
1436FUSE_HEADER_INIT(&release_in.hdr,1437FileContext->IsDirectory ? FUSE_RELEASEDIR : FUSE_RELEASE,1438FileContext->NodeId, sizeof(release_in.release));1439
1440release_in.release.fh = FileContext->FileHandle;1441release_in.release.flags = 0;1442release_in.release.lock_owner = 0;1443release_in.release.release_flags = 0;1444
1445return VirtFsFuseRequest(Device, &release_in, sizeof(release_in),1446&release_out, sizeof(release_out));1447}
1448
1449static VOID Close(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0)1450{
1451VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1452VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1453
1454DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1455
1456(VOID)VirtFs->SubmitReleaseRequest(FileContext);1457
1458FspFileSystemDeleteDirectoryBuffer(&FileContext->DirBuffer);1459
1460SafeHeapFree(FileContext);1461}
1462
1463static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1464PVOID Buffer, UINT64 Offset, ULONG Length, PULONG PBytesTransferred)1465{
1466VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1467VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1468FUSE_READ_OUT *read_out;1469NTSTATUS Status = STATUS_SUCCESS;1470// Host page size is unknown, but it can't be less than 4KiB1471UINT32 BufSize = min(VirtFs->MaxPages * PAGE_SZ_4K, Length);1472PUCHAR Buf = (PUCHAR)Buffer;1473
1474DBG("Offset: %I64u Length: %u", Offset, Length);1475DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1476
1477*PBytesTransferred = 0;1478
1479if (Buffer == NULL)1480{1481return STATUS_INVALID_PARAMETER;1482}1483
1484read_out = (FUSE_READ_OUT *)HeapAlloc(GetProcessHeap(), 0,1485sizeof(*read_out) + BufSize);1486if (read_out == NULL)1487{1488return STATUS_INSUFFICIENT_RESOURCES;1489}1490
1491while (Length)1492{1493UINT32 Size = min(Length, BufSize);1494FUSE_READ_IN read_in;1495UINT32 OutSize;1496
1497read_in.read.fh = FileContext->FileHandle;1498read_in.read.offset = Offset;1499read_in.read.size = Size;1500read_in.read.read_flags = 0;1501read_in.read.lock_owner = 0;1502read_in.read.flags = 0;1503
1504FUSE_HEADER_INIT(&read_in.hdr, FUSE_READ, FileContext->NodeId,1505sizeof(read_in.read));1506
1507Status = VirtFsFuseRequest(VirtFs->Device, &read_in, sizeof(read_in),1508read_out, sizeof(*read_out) + Size);1509if (!NT_SUCCESS(Status))1510{1511SafeHeapFree(read_out);1512return Status;1513}1514
1515OutSize = read_out->hdr.len - sizeof(struct fuse_out_header);1516CopyMemory(Buf, read_out->buf, OutSize);1517*PBytesTransferred += OutSize;1518
1519// A successful read with no bytes read means file offset is at or past1520// the end of file.1521if (OutSize == 0)1522{1523Status = STATUS_END_OF_FILE;1524}1525
1526if (OutSize < Size)1527{1528break;1529}1530
1531Buf += OutSize;1532Offset += OutSize;1533Length -= OutSize;1534}1535
1536DBG("BytesTransferred: %d", *PBytesTransferred);1537
1538SafeHeapFree(read_out);1539
1540return Status;1541}
1542
1543static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1544PVOID Buffer, UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile,1545BOOLEAN ConstrainedIo, PULONG PBytesTransferred,1546FSP_FSCTL_FILE_INFO *FileInfo)1547{
1548VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1549VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1550ULONG WriteSize;1551NTSTATUS Status;1552FUSE_WRITE_IN *write_in;1553FUSE_WRITE_OUT write_out;1554
1555DBG("Buffer: %p Offset: %I64u Length: %u WriteToEndOfFile: %d "1556"ConstrainedIo: %d", Buffer, Offset, Length, WriteToEndOfFile,1557ConstrainedIo);1558DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1559
1560// Both these cases requires knowing the actual file size.1561if ((WriteToEndOfFile == TRUE) || (ConstrainedIo == TRUE))1562{1563Status = GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1564if (!NT_SUCCESS(Status))1565{1566return Status;1567}1568}1569
1570if (WriteToEndOfFile == TRUE)1571{1572Offset = FileInfo->FileSize;1573}1574
1575if (ConstrainedIo == TRUE)1576{1577if (Offset >= FileInfo->FileSize)1578{1579return STATUS_SUCCESS;1580}1581
1582if ((Offset + Length) > FileInfo->FileSize)1583{1584Length = (ULONG)(FileInfo->FileSize - Offset);1585}1586}1587
1588WriteSize = min(Length, VirtFs->MaxWrite);1589
1590write_in = (FUSE_WRITE_IN *)HeapAlloc(GetProcessHeap(), 0,1591sizeof(*write_in) + WriteSize);1592if (write_in == NULL)1593{1594return STATUS_INSUFFICIENT_RESOURCES;1595}1596
1597do1598{1599FUSE_HEADER_INIT(&write_in->hdr, FUSE_WRITE, FileContext->NodeId,1600sizeof(struct fuse_write_in) + WriteSize);1601
1602write_in->write.fh = FileContext->FileHandle;1603write_in->write.offset = Offset + *PBytesTransferred;1604write_in->write.size = WriteSize;1605write_in->write.write_flags = 0;1606write_in->write.lock_owner = 0;1607write_in->write.flags = 0;1608
1609CopyMemory(write_in->buf, (BYTE*)Buffer + *PBytesTransferred,1610WriteSize);1611
1612Status = VirtFsFuseRequest(VirtFs->Device, write_in,1613write_in->hdr.len, &write_out, sizeof(write_out));1614
1615if (!NT_SUCCESS(Status))1616{1617break;1618}1619
1620*PBytesTransferred += write_out.write.size;1621Length -= write_out.write.size;1622WriteSize = min(Length, VirtFs->MaxWrite);1623}1624while (Length > 0);1625
1626SafeHeapFree(write_in);1627
1628if (!NT_SUCCESS(Status))1629{1630return Status;1631}1632
1633return GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1634}
1635
1636static NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1637FSP_FSCTL_FILE_INFO *FileInfo)1638{
1639VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1640VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1641NTSTATUS Status;1642FUSE_FLUSH_IN flush_in;1643FUSE_FLUSH_OUT flush_out;1644
1645DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1646
1647FUSE_HEADER_INIT(&flush_in.hdr, FUSE_FLUSH, FileContext->NodeId,1648sizeof(flush_in.flush));1649
1650flush_in.flush.fh = FileContext->FileHandle;1651flush_in.flush.unused = 0;1652flush_in.flush.padding = 0;1653flush_in.flush.lock_owner = 0;1654
1655Status = VirtFsFuseRequest(VirtFs->Device, &flush_in, sizeof(flush_in),1656&flush_out, sizeof(flush_out));1657
1658if (!NT_SUCCESS(Status))1659{1660return Status;1661}1662
1663return GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1664}
1665
1666static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1667FSP_FSCTL_FILE_INFO *FileInfo)1668{
1669VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1670VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1671
1672DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1673
1674return GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1675}
1676
1677static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1678UINT32 FileAttributes, UINT64 CreationTime, UINT64 LastAccessTime,1679UINT64 LastWriteTime, UINT64 ChangeTime, FSP_FSCTL_FILE_INFO *FileInfo)1680{
1681VIRTFS* VirtFs = (VIRTFS*)FileSystem->UserContext;1682VIRTFS_FILE_CONTEXT* FileContext = (VIRTFS_FILE_CONTEXT*)FileContext0;1683NTSTATUS Status;1684FUSE_SETATTR_IN setattr_in;1685FUSE_SETATTR_OUT setattr_out;1686
1687DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1688
1689FUSE_HEADER_INIT(&setattr_in.hdr, FUSE_SETATTR, FileContext->NodeId,1690sizeof(setattr_in.setattr));1691
1692ZeroMemory(&setattr_in.setattr, sizeof(setattr_in.setattr));1693
1694if ((FileContext->IsDirectory == FALSE) &&1695(FileContext->FileHandle != INVALID_FILE_HANDLE))1696{1697setattr_in.setattr.valid |= FATTR_FH;1698setattr_in.setattr.fh = FileContext->FileHandle;1699}1700
1701if (FileAttributes != INVALID_FILE_ATTRIBUTES)1702{1703setattr_in.setattr.valid |= FATTR_MODE;1704setattr_in.setattr.mode = 0664 /* -rw-rw-r-- */;1705
1706if (!!(FileAttributes & FILE_ATTRIBUTE_READONLY) == TRUE)1707{1708setattr_in.setattr.mode &= ~0222;1709}1710
1711if (!!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == TRUE)1712{1713setattr_in.setattr.mode |= 040111;1714}1715}1716
1717if (LastAccessTime != 0)1718{1719setattr_in.setattr.valid |= FATTR_ATIME;1720FileTimeToUnixTime(LastAccessTime, &setattr_in.setattr.atime,1721&setattr_in.setattr.atimensec);1722}1723if ((LastWriteTime != 0) || (ChangeTime != 0))1724{1725if (LastWriteTime == 0)1726{1727LastWriteTime = ChangeTime;1728}1729setattr_in.setattr.valid |= FATTR_MTIME;1730FileTimeToUnixTime(LastWriteTime, &setattr_in.setattr.mtime,1731&setattr_in.setattr.mtimensec);1732}1733if (CreationTime != 0)1734{1735setattr_in.setattr.valid |= FATTR_CTIME;1736FileTimeToUnixTime(CreationTime, &setattr_in.setattr.ctime,1737&setattr_in.setattr.ctimensec);1738}1739
1740Status = VirtFsFuseRequest(VirtFs->Device, &setattr_in,1741sizeof(setattr_in), &setattr_out, sizeof(setattr_out));1742
1743if (!NT_SUCCESS(Status))1744{1745return Status;1746}1747
1748return GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1749}
1750
1751static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1752PWSTR FileName, ULONG Flags)1753{
1754VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1755VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1756UINT64 LastAccessTime, LastWriteTime;1757FILETIME CurrentTime;1758NTSTATUS Status;1759char *filename, *fullpath;1760uint64_t parent;1761
1762DBG("\"%S\" Flags: 0x%02x", FileName, Flags);1763
1764if (FileName == NULL)1765{1766return;1767}1768
1769Status = FspPosixMapWindowsToPosixPath(FileName + 1, &fullpath);1770if (!NT_SUCCESS(Status))1771{1772return;1773}1774SCOPE_EXIT(fullpath, { FspPosixDeletePath(fullpath); });1775
1776Status = PathWalkthough(VirtFs, fullpath, &filename, &parent);1777if (!NT_SUCCESS(Status))1778{1779return;1780}1781
1782if (Flags & FspCleanupDelete)1783{1784VirtFs->NameAwareRequest(parent, filename,1785&VIRTFS::SubmitDeleteRequest, FileContext);1786}1787else1788{1789GetSystemTimeAsFileTime(&CurrentTime);1790LastAccessTime = LastWriteTime = 0;1791
1792if (Flags & FspCleanupSetAllocationSize)1793{1794
1795}1796if (Flags & FspCleanupSetArchiveBit)1797{1798
1799}1800if (Flags & FspCleanupSetLastAccessTime)1801{1802LastAccessTime = ((PLARGE_INTEGER)&CurrentTime)->QuadPart;1803}1804if ((Flags & FspCleanupSetLastWriteTime) ||1805(Flags & FspCleanupSetChangeTime))1806{1807LastWriteTime = ((PLARGE_INTEGER)&CurrentTime)->QuadPart;1808}1809
1810(VOID)SetBasicInfo(FileSystem, FileContext0, 0, 0, LastAccessTime,1811LastWriteTime, 0, NULL);1812}1813}
1814
1815static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1816UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO *FileInfo)1817{
1818VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1819VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1820NTSTATUS Status;1821
1822DBG("NewSize: %I64u SetAllocationSize: %d", NewSize, SetAllocationSize);1823DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle,1824FileContext->NodeId);1825
1826if (SetAllocationSize == TRUE)1827{1828if (NewSize > 0)1829{1830FUSE_FALLOCATE_IN falloc_in;1831FUSE_FALLOCATE_OUT falloc_out;1832
1833FUSE_HEADER_INIT(&falloc_in.hdr, FUSE_FALLOCATE,1834FileContext->NodeId, sizeof(struct fuse_fallocate_in));1835
1836falloc_in.hdr.uid = VirtFs->OwnerUid;1837falloc_in.hdr.gid = VirtFs->OwnerGid;1838
1839falloc_in.falloc.fh = FileContext->FileHandle;1840falloc_in.falloc.offset = 0;1841falloc_in.falloc.length = NewSize;1842falloc_in.falloc.mode = 0x01; /* FALLOC_FL_KEEP_SIZE */1843falloc_in.falloc.padding = 0;1844
1845Status = VirtFsFuseRequest(VirtFs->Device, &falloc_in,1846falloc_in.hdr.len, &falloc_out, sizeof(falloc_out));1847}1848else1849{1850// fallocate on host fails when len is less than or equal to 0.1851// So ignore the request and report success. This fix a failure1852// to create a new file through Windows Explorer.1853Status = STATUS_SUCCESS;1854}1855}1856else1857{1858FUSE_SETATTR_IN setattr_in;1859FUSE_SETATTR_OUT setattr_out;1860
1861FUSE_HEADER_INIT(&setattr_in.hdr, FUSE_SETATTR, FileContext->NodeId,1862sizeof(setattr_in.setattr));1863
1864ZeroMemory(&setattr_in.setattr, sizeof(setattr_in.setattr));1865setattr_in.setattr.valid = FATTR_SIZE;1866setattr_in.setattr.size = NewSize;1867
1868Status = VirtFsFuseRequest(VirtFs->Device, &setattr_in,1869sizeof(setattr_in), &setattr_out, sizeof(setattr_out));1870}1871
1872if (!NT_SUCCESS(Status))1873{1874return Status;1875}1876
1877return GetFileInfoInternal(VirtFs, FileContext, FileInfo, NULL);1878}
1879
1880static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1881PWSTR FileName)1882{
1883VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1884NTSTATUS Status = STATUS_SUCCESS;1885
1886DBG("\"%S\"", FileName);1887
1888if (FileContext->IsDirectory == TRUE)1889{1890Status = IsEmptyDirectory(FileSystem, FileContext0);1891}1892
1893return Status;1894}
1895
1896static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1897PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists)1898{
1899VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1900VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1901NTSTATUS Status;1902char *oldname, *newname, *oldfullpath, *newfullpath;1903uint64_t oldparent, newparent;1904uint32_t flags;1905
1906DBG("\"%S\" -> \"%S\" ReplaceIfExist: %d", FileName, NewFileName,1907ReplaceIfExists);1908DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1909
1910Status = FspPosixMapWindowsToPosixPath(FileName + 1, &oldfullpath);1911if (!NT_SUCCESS(Status))1912{1913return Status;1914}1915SCOPE_EXIT(oldfullpath, { FspPosixDeletePath(oldfullpath); });1916
1917Status = FspPosixMapWindowsToPosixPath(NewFileName + 1, &newfullpath);1918if (!NT_SUCCESS(Status))1919{1920return Status;1921}1922SCOPE_EXIT(newfullpath, { FspPosixDeletePath(newfullpath); });1923
1924Status = PathWalkthough(VirtFs, oldfullpath, &oldname, &oldparent);1925if (!NT_SUCCESS(Status))1926{1927return Status;1928}1929
1930Status = PathWalkthough(VirtFs, newfullpath, &newname, &newparent);1931if (!NT_SUCCESS(Status))1932{1933return Status;1934}1935
1936if (VirtFs->CaseInsensitive && oldparent == newparent &&1937FileNameIgnoreCaseCompare(oldname, newname))1938{1939return STATUS_SUCCESS;1940}1941
1942// It is not allowed to rename to an existing directory even when1943// ReplaceIfExists is set.1944flags = ((FileContext->IsDirectory == FALSE) &&1945(ReplaceIfExists == TRUE)) ? 0 : (1 << 0) /* RENAME_NOREPLACE */;1946
1947Status = VirtFs->NameAwareRequest(oldparent, oldname,1948&VIRTFS::RenameWithFallbackRequest, newparent, newname, flags);1949
1950// Fix to expected error when renaming a directory to existing directory.1951if ((FileContext->IsDirectory == TRUE) && (ReplaceIfExists == TRUE) &&1952(Status == STATUS_OBJECT_NAME_COLLISION))1953{1954Status = STATUS_ACCESS_DENIED;1955}1956
1957return Status;1958}
1959
1960static NTSTATUS GetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1961PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)1962{
1963VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;1964VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;1965PSECURITY_DESCRIPTOR Security;1966DWORD SecurityLength;1967NTSTATUS Status;1968
1969DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);1970
1971Status = GetFileInfoInternal(VirtFs, FileContext, NULL, &Security);1972if (!NT_SUCCESS(Status))1973{1974*PSecurityDescriptorSize = 0;1975return Status;1976}1977
1978SecurityLength = GetSecurityDescriptorLength(Security);1979if (*PSecurityDescriptorSize < SecurityLength)1980{1981*PSecurityDescriptorSize = SecurityLength;1982SafeHeapFree(Security);1983return STATUS_BUFFER_TOO_SMALL;1984}1985
1986*PSecurityDescriptorSize = SecurityLength;1987if (SecurityDescriptor != NULL)1988{1989CopyMemory(SecurityDescriptor, Security, SecurityLength);1990}1991
1992SafeHeapFree(Security);1993
1994return STATUS_SUCCESS;1995}
1996
1997static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,1998SECURITY_INFORMATION SecurityInformation,1999PSECURITY_DESCRIPTOR ModificationDescriptor)2000{
2001VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;2002VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;2003PSECURITY_DESCRIPTOR FileSecurity, NewSecurityDescriptor;2004UINT32 Uid, Gid, Mode, NewMode;2005NTSTATUS Status;2006
2007DBG("fh: %I64u nodeid: %I64u", FileContext->FileHandle, FileContext->NodeId);2008
2009Status = GetFileInfoInternal(VirtFs, FileContext, NULL, &FileSecurity);2010if (!NT_SUCCESS(Status))2011{2012SafeHeapFree(FileSecurity);2013return Status;2014}2015
2016Status = FspPosixMapSecurityDescriptorToPermissions(FileSecurity, &Uid,2017&Gid, &Mode);2018
2019if (!NT_SUCCESS(Status))2020{2021SafeHeapFree(FileSecurity);2022return Status;2023}2024
2025Status = FspSetSecurityDescriptor(FileSecurity, SecurityInformation,2026ModificationDescriptor, &NewSecurityDescriptor);2027
2028if (!NT_SUCCESS(Status))2029{2030SafeHeapFree(FileSecurity);2031return Status;2032}2033
2034SafeHeapFree(FileSecurity);2035
2036Status = FspPosixMapSecurityDescriptorToPermissions(NewSecurityDescriptor,2037&Uid, &Gid, &NewMode);2038
2039if (!NT_SUCCESS(Status))2040{2041FspDeleteSecurityDescriptor(NewSecurityDescriptor,2042(NTSTATUS(*)())FspSetSecurityDescriptor);2043return Status;2044}2045
2046FspDeleteSecurityDescriptor(NewSecurityDescriptor,2047(NTSTATUS(*)())FspSetSecurityDescriptor);2048
2049if (Mode != NewMode)2050{2051FUSE_SETATTR_IN setattr_in;2052FUSE_SETATTR_OUT setattr_out;2053
2054FUSE_HEADER_INIT(&setattr_in.hdr, FUSE_SETATTR, FileContext->NodeId,2055sizeof(setattr_in.setattr));2056
2057ZeroMemory(&setattr_in.setattr, sizeof(setattr_in.setattr));2058setattr_in.setattr.valid = FATTR_MODE;2059setattr_in.setattr.mode = NewMode;2060
2061Status = VirtFsFuseRequest(VirtFs->Device, &setattr_in,2062sizeof(setattr_in), &setattr_out, sizeof(setattr_out));2063}2064
2065return Status;2066}
2067
2068NTSTATUS VIRTFS::SubmitReadDirRequest(const VIRTFS_FILE_CONTEXT *FileContext,2069uint64_t Offset, bool Plus, FUSE_READ_OUT *read_out, uint32_t read_out_size)2070{
2071FUSE_READ_IN read_in;2072
2073FUSE_HEADER_INIT(&read_in.hdr, Plus ? FUSE_READDIRPLUS : FUSE_READDIR,2074FileContext->NodeId, sizeof(read_in.read));2075
2076read_in.read.fh = FileContext->FileHandle;2077read_in.read.offset = Offset;2078read_in.read.size = read_out_size - sizeof(struct fuse_out_header);2079read_in.read.read_flags = 0;2080read_in.read.lock_owner = 0;2081read_in.read.flags = 0;2082
2083return VirtFsFuseRequest(Device, &read_in, sizeof(read_in),2084read_out, read_out_size);2085}
2086
2087NTSTATUS VIRTFS::ReadDirAndIgnoreCaseSearch(const VIRTFS_FILE_CONTEXT *ParentContext,2088const char *filename, std::string &result)2089{
2090NTSTATUS Status;2091struct fuse_dirent *dirent;2092UINT64 Offset = 0;2093UINT32 Remains;2094uint32_t buf_size = PAGE_SZ_4K - sizeof(struct fuse_out_header);2095WCHAR FileName[MAX_PATH];2096
2097DBG("filename = '%s'", filename);2098
2099FUSE_READ_OUT *read_out = (FUSE_READ_OUT *)HeapAlloc(GetProcessHeap(), 0,2100sizeof(struct fuse_out_header) + buf_size);2101if (read_out == NULL)2102{2103return STATUS_INSUFFICIENT_RESOURCES;2104}2105SCOPE_EXIT(read_out, { SafeHeapFree(read_out); });2106
2107if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, FileName, MAX_PATH) == 0)2108{2109return FspNtStatusFromWin32(GetLastError());2110}2111
2112for (;;)2113{2114Status = SubmitReadDirRequest(ParentContext, Offset, FALSE,2115read_out, buf_size + sizeof(struct fuse_out_header));2116if (!NT_SUCCESS(Status))2117{2118return Status;2119}2120
2121Remains = read_out->hdr.len - sizeof(struct fuse_out_header);2122if (Remains == 0)2123{2124break;2125}2126
2127dirent = (struct fuse_dirent *)read_out->buf;2128
2129while (Remains > sizeof(struct fuse_dirent))2130{2131if (FileNameIgnoreCaseCompare(FileName, dirent->name, dirent->namelen))2132{2133result.assign(dirent->name, dirent->namelen);2134DBG("match: name = '%s' (%u) type = %u",2135result.c_str(), dirent->namelen, dirent->type);2136
2137return STATUS_SUCCESS;2138}2139
2140Offset = dirent->off;2141Remains -= FUSE_DIRENT_SIZE(dirent);2142dirent = (struct fuse_dirent *)((PBYTE)dirent + FUSE_DIRENT_SIZE(dirent));2143}2144}2145
2146return STATUS_OBJECT_NAME_NOT_FOUND;2147}
2148
2149static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0,2150PWSTR Pattern, PWSTR Marker, PVOID Buffer, ULONG BufferLength,2151PULONG PBytesTransferred)2152{
2153VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;2154VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;2155BYTE DirInfoBuf[sizeof(FSP_FSCTL_DIR_INFO) + MAX_PATH * sizeof(WCHAR)];2156FSP_FSCTL_DIR_INFO *DirInfo = (FSP_FSCTL_DIR_INFO *)DirInfoBuf;2157struct fuse_direntplus *DirEntryPlus;2158NTSTATUS Status = STATUS_SUCCESS;2159UINT64 Offset = 0;2160UINT32 Remains;2161BOOLEAN Result;2162int FileNameLength;2163FUSE_READ_OUT *read_out;2164
2165DBG("Pattern: %S Marker: %S BufferLength: %u",2166Pattern ? Pattern : TEXT("(null)"), Marker ? Marker : TEXT("(null)"),2167BufferLength);2168
2169Result = FspFileSystemAcquireDirectoryBuffer(&FileContext->DirBuffer,2170Marker == NULL, &Status);2171
2172if (Result == TRUE)2173{2174read_out = (FUSE_READ_OUT *)HeapAlloc(GetProcessHeap(), 0,2175sizeof(struct fuse_out_header) + (ULONG64)BufferLength * 2);2176
2177if (read_out != NULL)2178{2179for (;;)2180{2181VirtFs->SubmitReadDirRequest(FileContext, Offset, TRUE, read_out,2182sizeof(struct fuse_out_header) + (ULONG64)BufferLength * 2);2183
2184if (!NT_SUCCESS(Status))2185{2186break;2187}2188
2189Remains = read_out->hdr.len - sizeof(struct fuse_out_header);2190if (Remains == 0)2191{2192// A successful request with no data means no more2193// entries.2194break;2195}2196
2197DirEntryPlus = (struct fuse_direntplus *)read_out->buf;2198
2199while (Remains > sizeof(struct fuse_direntplus))2200{2201DBG("ino=%I64u off=%I64u namelen=%u type=%u name=%s",2202DirEntryPlus->dirent.ino, DirEntryPlus->dirent.off,2203DirEntryPlus->dirent.namelen,2204DirEntryPlus->dirent.type, DirEntryPlus->dirent.name);2205
2206ZeroMemory(DirInfoBuf, sizeof(DirInfoBuf));2207
2208// Not using FspPosixMapPosixToWindowsPath so we can do2209// the conversion in-place.2210FileNameLength = MultiByteToWideChar(CP_UTF8, 0,2211DirEntryPlus->dirent.name,2212DirEntryPlus->dirent.namelen, DirInfo->FileNameBuf,2213MAX_PATH);2214
2215DBG("\"%S\" (%d)", DirInfo->FileNameBuf, FileNameLength);2216
2217if (FileNameLength > 0)2218{2219DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) +2220FileNameLength * sizeof(WCHAR));2221
2222SetFileInfo(VirtFs, &DirEntryPlus->entry_out,2223&DirInfo->FileInfo);2224
2225Result = FspFileSystemFillDirectoryBuffer(2226&FileContext->DirBuffer, DirInfo, &Status);2227
2228if (Result == FALSE)2229{2230break;2231}2232}2233
2234if (wcscmp(DirInfo->FileNameBuf, L".") &&2235wcscmp(DirInfo->FileNameBuf, L".."))2236{2237VirtFs->LookupMapNewOrIncNode(DirEntryPlus->entry_out.nodeid);2238}2239
2240Offset = DirEntryPlus->dirent.off;2241Remains -= FUSE_DIRENTPLUS_SIZE(DirEntryPlus);2242DirEntryPlus = (struct fuse_direntplus *)(2243(PBYTE)DirEntryPlus +2244FUSE_DIRENTPLUS_SIZE(DirEntryPlus));2245}2246}2247
2248SafeHeapFree(read_out);2249}2250else2251{2252Status = STATUS_INSUFFICIENT_RESOURCES;2253}2254
2255FspFileSystemReleaseDirectoryBuffer(&FileContext->DirBuffer);2256}2257
2258if (NT_SUCCESS(Status))2259{2260FspFileSystemReadDirectoryBuffer(&FileContext->DirBuffer, Marker,2261Buffer, BufferLength, PBytesTransferred);2262}2263
2264return Status;2265}
2266
2267static NTSTATUS ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,2268PWSTR FileName, UINT32 ReparsePointIndex,2269BOOLEAN ResolveLastPathComponent, PIO_STATUS_BLOCK PIoStatus,2270PVOID Buffer, PSIZE_T PSize)2271{
2272return FspFileSystemResolveReparsePoints(FileSystem,2273GetReparsePointByName, NULL, FileName, ReparsePointIndex,2274ResolveLastPathComponent, PIoStatus, Buffer, PSize);2275}
2276
2277static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,2278PVOID FileContext, PWSTR FileName, PVOID Buffer, SIZE_T Size)2279{
2280VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;2281PREPARSE_DATA_BUFFER ReparseData = (PREPARSE_DATA_BUFFER)Buffer;2282FUSE_SYMLINK_IN *symlink_in;2283FUSE_SYMLINK_OUT symlink_out;2284WCHAR TargetName[MAX_PATH];2285USHORT TargetLength;2286NTSTATUS Status;2287char *filename, *linkname, *targetname;2288int linkname_len, targetname_len;2289uint64_t parent;2290
2291UNREFERENCED_PARAMETER(Size);2292
2293DBG("\"%S\"", FileName);2294
2295if (!(ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE))2296{2297return STATUS_INVALID_PARAMETER;2298}2299
2300Cleanup(FileSystem, FileContext, FileName, FspCleanupDelete);2301
2302CopyMemory(TargetName,2303ReparseData->SymbolicLinkReparseBuffer.PathBuffer +2304(ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset /2305sizeof(WCHAR)),2306ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength);2307
2308TargetLength =2309ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength /2310sizeof(WCHAR);2311
2312TargetName[TargetLength] = TEXT('\0');2313
2314Status = FspPosixMapWindowsToPosixPath(TargetName, &targetname);2315if (!NT_SUCCESS(Status))2316{2317return Status;2318}2319SCOPE_EXIT(targetname, { FspPosixDeletePath(targetname); });2320
2321Status = FspPosixMapWindowsToPosixPath(FileName + 1, &linkname);2322if (!NT_SUCCESS(Status))2323{2324return Status;2325}2326SCOPE_EXIT(linkname, { FspPosixDeletePath(linkname); });2327
2328Status = PathWalkthough(VirtFs, linkname, &filename, &parent);2329if (!NT_SUCCESS(Status))2330{2331return Status;2332}2333
2334linkname_len = lstrlenA(filename) + 1;2335targetname_len = lstrlenA(targetname) + 1;2336
2337symlink_in = (FUSE_SYMLINK_IN *)HeapAlloc(GetProcessHeap(),23380, sizeof(*symlink_in) + linkname_len + targetname_len);2339
2340if (symlink_in == NULL)2341{2342return STATUS_INSUFFICIENT_RESOURCES;2343}2344
2345FUSE_HEADER_INIT(&symlink_in->hdr, FUSE_SYMLINK, parent,2346linkname_len + targetname_len);2347
2348CopyMemory(symlink_in->names, filename, linkname_len);2349CopyMemory(symlink_in->names + linkname_len, targetname, targetname_len);2350
2351Status = VirtFsFuseRequest(VirtFs->Device, symlink_in,2352symlink_in->hdr.len, &symlink_out, sizeof(symlink_out));2353
2354SafeHeapFree(symlink_in);2355
2356return Status;2357}
2358
2359static NTSTATUS GetDirInfoByName(FSP_FILE_SYSTEM *FileSystem,2360PVOID FileContext0, PWSTR FileName, FSP_FSCTL_DIR_INFO *DirInfo)2361{
2362VIRTFS *VirtFs = (VIRTFS *)FileSystem->UserContext;2363VIRTFS_FILE_CONTEXT *FileContext = (VIRTFS_FILE_CONTEXT *)FileContext0;2364FUSE_LOOKUP_OUT lookup_out;2365NTSTATUS Status;2366char *filename;2367
2368DBG("\"%S\"", FileName);2369
2370Status = FspPosixMapWindowsToPosixPath(FileName, &filename);2371if (!NT_SUCCESS(Status))2372{2373return Status;2374}2375SCOPE_EXIT(filename, { FspPosixDeletePath(filename); });2376
2377Status = VirtFs->NameAwareRequest(FileContext->NodeId, filename,2378&VIRTFS::SubmitLookupRequest, &lookup_out);2379
2380if (NT_SUCCESS(Status))2381{2382DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) +2383wcslen(FileName) * sizeof(WCHAR));2384
2385SetFileInfo(VirtFs, &lookup_out.entry, &DirInfo->FileInfo);2386
2387CopyMemory(DirInfo->FileNameBuf, FileName,2388DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO));2389}2390
2391return Status;2392}
2393
2394static FSP_FILE_SYSTEM_INTERFACE VirtFsInterface =2395{
2396.GetVolumeInfo = GetVolumeInfo,2397.GetSecurityByName = GetSecurityByName,2398.Create = Create,2399.Open = Open,2400.Overwrite = Overwrite,2401.Cleanup = Cleanup,2402.Close = Close,2403.Read = Read,2404.Write = Write,2405.Flush = Flush,2406.GetFileInfo = GetFileInfo,2407.SetBasicInfo = SetBasicInfo,2408.SetFileSize = SetFileSize,2409.CanDelete = CanDelete,2410.Rename = Rename,2411.GetSecurity = GetSecurity,2412.SetSecurity = SetSecurity,2413.ReadDirectory = ReadDirectory,2414.ResolveReparsePoints = ResolveReparsePoints,2415.SetReparsePoint = SetReparsePoint,2416.GetDirInfoByName = GetDirInfoByName2417};2418
2419static ULONG wcstol_deflt(wchar_t *w, ULONG deflt)2420{
2421wchar_t *endp;2422ULONG ul = wcstol(w, &endp, 0);2423return L'\0' != w[0] && L'\0' == *endp ? ul : deflt;2424}
2425
2426NTSTATUS VIRTFS::SubmitInitRequest()2427{
2428NTSTATUS Status;2429FUSE_INIT_IN init_in;2430FUSE_INIT_OUT init_out;2431
2432FUSE_HEADER_INIT(&init_in.hdr, FUSE_INIT, FUSE_ROOT_ID,2433sizeof(init_in.init));2434
2435init_in.init.major = FUSE_KERNEL_VERSION;2436init_in.init.minor = FUSE_KERNEL_MINOR_VERSION;2437init_in.init.max_readahead = 0;2438init_in.init.flags = FUSE_DO_READDIRPLUS | FUSE_MAX_PAGES;2439
2440Status = VirtFsFuseRequest(Device, &init_in, sizeof(init_in),2441&init_out, sizeof(init_out));2442if (!NT_SUCCESS(Status))2443{2444return Status;2445}2446
2447MaxWrite = init_out.init.max_write;2448MaxPages = init_out.init.max_pages ?2449init_out.init.max_pages : FUSE_DEFAULT_MAX_PAGES_PER_REQ;2450
2451DBG("Init: MaxWrite %u bytes, MaxPages %u", MaxWrite, MaxPages);2452return STATUS_SUCCESS;2453}
2454
2455NTSTATUS VIRTFS::SubmitDestroyRequest()2456{
2457NTSTATUS Status;2458FUSE_DESTROY_IN destroy_in;2459FUSE_DESTROY_OUT destroy_out;2460
2461FUSE_HEADER_INIT(&destroy_in.hdr, FUSE_DESTROY, FUSE_ROOT_ID, 0);2462
2463Status = VirtFsFuseRequest(Device, &destroy_in, sizeof(destroy_in),2464&destroy_out, sizeof(destroy_out));2465if (!NT_SUCCESS(Status))2466{2467return Status;2468}2469
2470return STATUS_SUCCESS;2471}
2472
2473NTSTATUS VIRTFS::Start()2474{
2475NTSTATUS Status;2476FILETIME FileTime;2477FSP_FSCTL_VOLUME_PARAMS VolumeParams;2478
2479Status = SubmitInitRequest();2480if (!NT_SUCCESS(Status))2481{2482return Status;2483}2484
2485GetSystemTimeAsFileTime(&FileTime);2486
2487ZeroMemory(&VolumeParams, sizeof(VolumeParams));2488VolumeParams.Version = sizeof(FSP_FSCTL_VOLUME_PARAMS);2489VolumeParams.SectorSize = ALLOCATION_UNIT;2490VolumeParams.SectorsPerAllocationUnit = 1;2491VolumeParams.VolumeCreationTime = ((PLARGE_INTEGER)&FileTime)->QuadPart;2492// VolumeParams.VolumeSerialNumber = 0;
2493VolumeParams.FileInfoTimeout = 1000;2494VolumeParams.CaseSensitiveSearch = !CaseInsensitive;2495VolumeParams.CasePreservedNames = 1;2496VolumeParams.UnicodeOnDisk = 1;2497VolumeParams.PersistentAcls = 1;2498VolumeParams.ReparsePoints = 1;2499VolumeParams.ReparsePointsAccessCheck = 0;2500VolumeParams.PostCleanupWhenModifiedOnly = 1;2501// VolumeParams.PassQueryDirectoryPattern = 1;
2502VolumeParams.PassQueryDirectoryFileName = 1;2503VolumeParams.FlushAndPurgeOnCleanup = 1;2504VolumeParams.UmFileContextIsUserContext2 = 1;2505// VolumeParams.DirectoryMarkerAsNextOffset = 1;
2506wcscpy_s(VolumeParams.FileSystemName,2507sizeof(VolumeParams.FileSystemName) / sizeof(WCHAR),2508FileSystemName.empty() ? FS_SERVICE_NAME : FileSystemName.c_str());2509
2510Status = FspFileSystemCreate((PWSTR)TEXT(FSP_FSCTL_DISK_DEVICE_NAME),2511&VolumeParams, &VirtFsInterface, &FileSystem);2512if (!NT_SUCCESS(Status))2513{2514return Status;2515}2516FileSystem->UserContext = this;2517
2518FspFileSystemSetDebugLog(FileSystem, DebugFlags);2519
2520Status = FspFileSystemSetMountPoint(FileSystem,2521(MountPoint == L"*") ? NULL : (PWSTR)MountPoint.c_str());2522if (!NT_SUCCESS(Status))2523{2524goto out_del_fs;2525}2526
2527Status = FspFileSystemStartDispatcher(FileSystem, 0);2528if (!NT_SUCCESS(Status))2529{2530FspServiceLog(EVENTLOG_ERROR_TYPE,2531(PWSTR)L"Failed to mount virtio-fs file system.");2532goto out_del_fs;2533}2534
2535return STATUS_SUCCESS;2536
2537out_del_fs:2538FspFileSystemDelete(FileSystem);2539
2540return Status;2541}
2542
2543static NTSTATUS ParseArgs(ULONG argc, PWSTR *argv,2544ULONG& DebugFlags, std::wstring& DebugLogFile, bool& CaseInsensitive,2545std::wstring& FileSystemName, std::wstring& MountPoint, std::wstring& Tag, std::wstring& Owner)2546{
2547#define argtos(v) if (arge > ++argp && *argp) v.assign(*argp); else goto usage2548#define argtol(v) if (arge > ++argp) v = wcstol_deflt(*argp, v); \2549else goto usage2550
2551wchar_t **argp, **arge;2552
2553for (argp = argv + 1, arge = argv + argc; arge > argp; argp++)2554{2555if (L'-' != argp[0][0])2556{2557break;2558}2559
2560switch (argp[0][1])2561{2562case L'?':2563goto usage;2564case L'd':2565argtol(DebugFlags);2566break;2567case L'D':2568argtos(DebugLogFile);2569break;2570case L'i':2571CaseInsensitive = true;2572break;2573case L'F':2574argtos(FileSystemName);2575break;2576case L'm':2577argtos(MountPoint);2578break;2579case L't':2580argtos(Tag);2581break;2582case L'o':2583argtos(Owner);2584if (!CheckIds(Owner))2585{2586goto usage;2587}2588break;2589default:2590goto usage;2591}2592}2593
2594if (arge > argp)2595{2596goto usage;2597}2598
2599return STATUS_SUCCESS;2600
2601#undef argtos2602#undef argtol2603
2604usage:2605static wchar_t usage[] = L""2606"Usage: %s OPTIONS\n"2607"\n"2608"options:\n"2609" -d DebugFlags [-1: enable all debug logs]\n"2610" -D DebugLogFile [file path; use - for stderr]\n"2611" -i [case insensitive file system]\n"2612" -F FileSystemName [file system name for OS]\n"2613" -m MountPoint [X:|* (required if no UNC prefix)]\n"2614" -t Tag [mount tag; max 36 symbols]\n"2615" -o UID:GID [host owner UID:GID]\n";2616
2617FspServiceLog(EVENTLOG_ERROR_TYPE, usage, FS_SERVICE_NAME);2618
2619return STATUS_UNSUCCESSFUL;2620}
2621
2622static VOID ParseRegistry(ULONG& DebugFlags, std::wstring& DebugLogFile,2623bool &CaseInsensitive, std::wstring& FileSystemName, std::wstring& MountPoint, std::wstring& Owner)2624{
2625RegistryGetVal(FS_SERVICE_REGKEY, L"DebugFlags", DebugFlags);2626RegistryGetVal(FS_SERVICE_REGKEY, L"DebugLogFile", DebugLogFile);2627RegistryGetVal(FS_SERVICE_REGKEY, L"CaseInsensitive", CaseInsensitive);2628RegistryGetVal(FS_SERVICE_REGKEY, L"FileSystemName", FileSystemName);2629RegistryGetVal(FS_SERVICE_REGKEY, L"MountPoint", MountPoint);2630RegistryGetVal(FS_SERVICE_REGKEY, L"Owner", Owner);2631}
2632
2633static VOID ParseRegistryCommon()2634{
2635OverflowUid = DEFAULT_OVERFLOWUID;2636OverflowGid = DEFAULT_OVERFLOWGID;2637
2638RegistryGetVal(FS_SERVICE_REGKEY, L"OverflowUid", OverflowUid);2639RegistryGetVal(FS_SERVICE_REGKEY, L"OverflowGid", OverflowGid);2640}
2641
2642static NTSTATUS DebugLogSet(const std::wstring& DebugLogFile)2643{
2644HANDLE DebugLogHandle = INVALID_HANDLE_VALUE;2645
2646if (DebugLogFile == L"-")2647{2648DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE);2649}2650else2651{2652DebugLogHandle = CreateFileW(DebugLogFile.c_str(), FILE_APPEND_DATA,2653FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS,2654FILE_ATTRIBUTE_NORMAL, 0);2655}2656
2657if (DebugLogHandle == INVALID_HANDLE_VALUE)2658{2659FspServiceLog(EVENTLOG_ERROR_TYPE,2660(PWSTR)L"Can not open debug log file.");2661return STATUS_UNSUCCESSFUL;2662}2663
2664FspDebugLogSetHandle(DebugLogHandle);2665
2666return STATUS_SUCCESS;2667}
2668
2669static NTSTATUS SvcStart(FSP_SERVICE* Service, ULONG argc, PWSTR* argv)2670{
2671std::wstring DebugLogFile{};2672ULONG DebugFlags{ 0 };2673bool CaseInsensitive{ false };2674std::wstring MountPoint{ L"*" };2675std::wstring FileSystemName{};2676std::wstring Tag{};2677std::wstring Owner{};2678uint32_t OwnerUid, OwnerGid;2679bool AutoOwnerIds;2680VIRTFS *VirtFs;2681NTSTATUS Status{ STATUS_SUCCESS };2682DWORD Error;2683
2684if (argc > 1)2685{2686Status = ParseArgs(argc, argv, DebugFlags, DebugLogFile,2687CaseInsensitive, FileSystemName, MountPoint, Tag, Owner);2688}2689else2690{2691ParseRegistry(DebugFlags, DebugLogFile, CaseInsensitive, FileSystemName, MountPoint,2692Owner);2693}2694
2695ParseRegistryCommon();2696
2697AutoOwnerIds = !ParseIds(Owner, OwnerUid, OwnerGid);2698
2699if (!NT_SUCCESS(Status))2700{2701return Status;2702}2703
2704if (!DebugLogFile.empty())2705{2706Status = DebugLogSet(DebugLogFile);2707if (!NT_SUCCESS(Status))2708{2709return Status;2710}2711}2712
2713try2714{2715VirtFs = new VIRTFS(DebugFlags, CaseInsensitive, FileSystemName, MountPoint, Tag,2716AutoOwnerIds, OwnerUid, OwnerGid);2717}2718catch (std::bad_alloc)2719{2720return STATUS_INSUFFICIENT_RESOURCES;2721}2722
2723Service->UserContext = VirtFs;2724
2725if (!VirtFs->DevHandleNotification.CreateUnregWork())2726{2727Status = STATUS_UNSUCCESSFUL;2728goto out_free_virtfs;2729}2730
2731Error = VirtFs->DevInterfaceNotification.Register(DeviceNotificationCallback,2732VirtFs, GUID_DEVINTERFACE_VIRT_FS);2733if (Error != ERROR_SUCCESS)2734{2735Status = FspNtStatusFromWin32(Error);2736goto out_unreg_di_notify;2737}2738
2739Error = VirtFs->FindDeviceInterface();2740if (Error != ERROR_SUCCESS)2741{2742// Wait for device to be found by arrival notification callback.2743FspServiceLog(EVENTLOG_INFORMATION_TYPE,2744(PWSTR)L"The %s service will start and wait for the device.",2745FS_SERVICE_NAME);2746return STATUS_SUCCESS;2747}2748
2749Error = VirtFs->DevHandleNotification.Register(DeviceNotificationCallback,2750VirtFs, VirtFs->Device);2751if (Error != ERROR_SUCCESS)2752{2753Status = FspNtStatusFromWin32(Error);2754goto out_close_handle;2755}2756
2757Status = VirtFs->Start();2758if (!NT_SUCCESS(Status))2759{2760goto out_unreg_dh_notify;2761}2762
2763return STATUS_SUCCESS;2764
2765out_unreg_dh_notify:2766VirtFs->DevHandleNotification.Unregister();2767out_close_handle:2768CloseHandle(VirtFs->Device);2769out_unreg_di_notify:2770VirtFs->DevInterfaceNotification.Unregister();2771out_free_virtfs:2772delete VirtFs;2773
2774return Status;2775}
2776
2777static NTSTATUS SvcStop(FSP_SERVICE *Service)2778{
2779VIRTFS *VirtFs = (VIRTFS *)Service->UserContext;2780
2781VirtFs->Stop();2782VirtFs->DevHandleNotification.Unregister();2783VirtFs->CloseDeviceInterface();2784VirtFs->DevInterfaceNotification.Unregister();2785delete VirtFs;2786
2787return STATUS_SUCCESS;2788}
2789
2790static NTSTATUS SvcControl(FSP_SERVICE *Service, ULONG Control,2791ULONG EventType, PVOID EventData)2792{
2793UNREFERENCED_PARAMETER(Service);2794UNREFERENCED_PARAMETER(EventType);2795UNREFERENCED_PARAMETER(EventData);2796
2797switch (Control)2798{2799case SERVICE_CONTROL_DEVICEEVENT:2800break;2801
2802default:2803break;2804}2805
2806return STATUS_SUCCESS;2807}
2808
2809int wmain(int argc, wchar_t **argv)2810{
2811FSP_SERVICE *Service;2812NTSTATUS Result;2813ULONG ExitCode;2814
2815UNREFERENCED_PARAMETER(argc);2816UNREFERENCED_PARAMETER(argv);2817
2818Result = FspLoad(0);2819
2820if (!NT_SUCCESS(Result))2821{2822fwprintf(stderr,2823L"The service %s failed to load WinFsp DLL (Status=%lx).",2824FS_SERVICE_NAME, Result);2825
2826return ERROR_DELAY_LOAD_FAILED;2827}2828
2829Result = FspServiceCreate((PWSTR)FS_SERVICE_NAME, SvcStart, SvcStop,2830SvcControl, &Service);2831
2832if (!NT_SUCCESS(Result))2833{2834FspServiceLog(EVENTLOG_ERROR_TYPE,2835(PWSTR)L"The service %s cannot be created (Status=%lx).",2836FS_SERVICE_NAME, Result);2837return FspWin32FromNtStatus(Result);2838}2839FspServiceAllowConsoleMode(Service);2840FspServiceAcceptControl(Service, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);2841Result = FspServiceLoop(Service);2842ExitCode = FspServiceGetExitCode(Service);2843FspServiceDelete(Service);2844
2845if (!NT_SUCCESS(Result))2846{2847FspServiceLog(EVENTLOG_ERROR_TYPE,2848(PWSTR)L"The service %s has failed to run (Status=%lx).",2849FS_SERVICE_NAME, Result);2850return FspWin32FromNtStatus(Result);2851}2852
2853return ExitCode;2854}
2855