mirror of
				https://github.com/winfsp/winfsp.git
				synced 2025-10-31 12:08:41 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			509 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			509 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * @file notifyfs.c
 | |
|  *
 | |
|  * @copyright 2015-2022 Bill Zissimopoulos
 | |
|  */
 | |
| /*
 | |
|  * This file is part of WinFsp.
 | |
|  *
 | |
|  * You can redistribute it and/or modify it under the terms of the GNU
 | |
|  * General Public License version 3 as published by the Free Software
 | |
|  * Foundation.
 | |
|  *
 | |
|  * Licensees holding a valid commercial license may use this software
 | |
|  * in accordance with the commercial license agreement provided in
 | |
|  * conjunction with the software.  The terms and conditions of any such
 | |
|  * commercial license agreement shall govern, supersede, and render
 | |
|  * ineffective any application of the GPLv3 license to this software,
 | |
|  * notwithstanding of any reference thereto in the software or
 | |
|  * associated repository.
 | |
|  */
 | |
| 
 | |
| #include <winfsp/winfsp.h>
 | |
| #include <sddl.h>
 | |
| 
 | |
| #define DEBUGFLAGS                      0
 | |
| //#define DEBUGFLAGS                      -1
 | |
| 
 | |
| #define PROGNAME                        "notifyfs"
 | |
| #define ALLOCATION_UNIT                 4096
 | |
| 
 | |
| #define info(format, ...)               FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__)
 | |
| #define warn(format, ...)               FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__)
 | |
| #define fail(format, ...)               FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__)
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
|     FSP_FILE_SYSTEM *FileSystem;
 | |
|     PTP_TIMER Timer;
 | |
|     UINT32 Ticks;
 | |
| } NOTIFYFS;
 | |
| 
 | |
| static PSECURITY_DESCRIPTOR DefaultSecurity;
 | |
| static ULONG DefaultSecuritySize;
 | |
| 
 | |
| static UINT32 CountFromTicks(UINT32 Ticks)
 | |
| {
 | |
|     /*
 | |
|      * The formula below produces the periodic sequence:
 | |
|      *     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
 | |
|      *     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
 | |
|      *     ...
 | |
|      */
 | |
|     UINT32 div10 = (Ticks % 20) / 10;
 | |
|     UINT32 mod10 = Ticks % 10;
 | |
|     UINT32 mdv10 = 1 - div10;
 | |
|     UINT32 mmd10 = 10 - mod10;
 | |
|     return mdv10 * mod10 + div10 * mmd10;
 | |
| }
 | |
| 
 | |
| static UINT32 FileCount(NOTIFYFS *Notifyfs)
 | |
| {
 | |
|     UINT32 Ticks = InterlockedOr(&Notifyfs->Ticks, 0);
 | |
|     return CountFromTicks(Ticks);
 | |
| }
 | |
| 
 | |
| static UINT32 FileLookup(NOTIFYFS *Notifyfs, PWSTR FileName)
 | |
| {
 | |
|     FileName++;
 | |
|     PWSTR Endp;
 | |
|     UINT32 Count = FileCount(Notifyfs);
 | |
|     UINT32 Index = wcstoul(FileName, &Endp, 10);
 | |
|     if ('\0' != *Endp || (FileName != Endp && (0 == Index || Index > Count)))
 | |
|         return -1;  /* not found */
 | |
|     if (FileName == Endp)
 | |
|         return 0;   /* root */
 | |
|     return Index;   /* regular file named 1, 2, ..., Count */
 | |
| }
 | |
| 
 | |
| static UINT32 FileContents(UINT32 Index, WCHAR P[32])
 | |
| {
 | |
|     WCHAR Buffer[32];
 | |
|     if (0 == P)
 | |
|         P = Buffer;
 | |
|     if (0 == Index)
 | |
|         P[0] = '\0';
 | |
|     else
 | |
|         wsprintfW(P, L"%u\n", (unsigned)Index);
 | |
|     return lstrlenW(P);
 | |
| }
 | |
| 
 | |
| static VOID FillFileInfo(UINT32 Index, FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     FILETIME SystemTime;
 | |
| 
 | |
|     GetSystemTimeAsFileTime(&SystemTime);
 | |
| 
 | |
|     memset(FileInfo, 0, sizeof FileInfo);
 | |
|     FileInfo->FileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0;
 | |
|     FileInfo->FileSize = FileContents(Index, 0);
 | |
|     FileInfo->AllocationSize = (FileInfo->FileSize + ALLOCATION_UNIT - 1)
 | |
|         / ALLOCATION_UNIT * ALLOCATION_UNIT;
 | |
|     FileInfo->CreationTime =
 | |
|     FileInfo->LastAccessTime =
 | |
|     FileInfo->LastWriteTime =
 | |
|     FileInfo->ChangeTime = *(PUINT64)&SystemTime;
 | |
| }
 | |
| 
 | |
| static BOOLEAN AddDirInfo(PWSTR FileName, UINT32 Index,
 | |
|     PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
 | |
| {
 | |
|     union
 | |
|     {
 | |
|         UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)];
 | |
|         FSP_FSCTL_DIR_INFO D;
 | |
|     } DirInfoBuf;
 | |
|     FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D;
 | |
| 
 | |
|     memset(DirInfo->Padding, 0, sizeof DirInfo->Padding);
 | |
|     if (0 != FileName)
 | |
|         lstrcpyW(DirInfo->FileNameBuf, FileName);
 | |
|     else
 | |
|         wsprintfW(DirInfo->FileNameBuf, L"%u", (unsigned)Index);
 | |
|     DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(DirInfo->FileNameBuf) * sizeof(WCHAR));
 | |
|     FillFileInfo(Index, &DirInfo->FileInfo);
 | |
| 
 | |
|     return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred);
 | |
| }
 | |
| 
 | |
| static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
 | |
|     FSP_FSCTL_VOLUME_INFO *VolumeInfo)
 | |
| {
 | |
|     memset(VolumeInfo, 0, sizeof *VolumeInfo);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PWSTR FileName, PUINT32 PFileAttributes,
 | |
|     PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
 | |
|     UINT32 Index;
 | |
| 
 | |
|     Index = FileLookup(Notifyfs, FileName);
 | |
|     if (-1 == Index)
 | |
|         return STATUS_OBJECT_NAME_NOT_FOUND;
 | |
| 
 | |
|     if (0 != PFileAttributes)
 | |
|         *PFileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0;
 | |
| 
 | |
|     if (0 != PSecurityDescriptorSize)
 | |
|     {
 | |
|         if (DefaultSecuritySize > *PSecurityDescriptorSize)
 | |
|         {
 | |
|             *PSecurityDescriptorSize = DefaultSecuritySize;
 | |
|             return STATUS_BUFFER_OVERFLOW;
 | |
|         }
 | |
| 
 | |
|         *PSecurityDescriptorSize = DefaultSecuritySize;
 | |
|         if (0 != SecurityDescriptor)
 | |
|             memcpy(SecurityDescriptor, DefaultSecurity, DefaultSecuritySize);
 | |
|     }
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
 | |
|     UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
 | |
|     PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     return STATUS_INVALID_DEVICE_REQUEST;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
 | |
|     PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
 | |
|     FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = (PVOID)PFileContext;
 | |
|     UINT32 Index;
 | |
| 
 | |
|     Index = FileLookup(Notifyfs, FileName);
 | |
|     if (-1 == Index)
 | |
|         return STATUS_OBJECT_NAME_NOT_FOUND;
 | |
| 
 | |
|     FullContext->UserContext = Index;
 | |
|     FullContext->UserContext2 = 0;
 | |
| 
 | |
|     FillFileInfo(Index, FileInfo);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     return STATUS_INVALID_DEVICE_REQUEST;
 | |
| }
 | |
| 
 | |
| static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length,
 | |
|     PULONG PBytesTransferred)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
 | |
|     FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext;
 | |
|     UINT32 Index = (UINT32)FullContext->UserContext;
 | |
|     UINT64 EndOffset;
 | |
|     WCHAR ContentBuf[32];
 | |
|     UINT32 ContentLen;
 | |
| 
 | |
|     ContentLen = FileContents(Index, ContentBuf);
 | |
| 
 | |
|     if (Offset >= ContentLen)
 | |
|         return STATUS_END_OF_FILE;
 | |
| 
 | |
|     EndOffset = Offset + Length;
 | |
|     if (EndOffset > ContentLen)
 | |
|         EndOffset = ContentLen;
 | |
| 
 | |
|     memcpy(Buffer, (PUINT8)ContentBuf + Offset, (size_t)(EndOffset - Offset));
 | |
| 
 | |
|     *PBytesTransferred = (ULONG)(EndOffset - Offset);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PVOID FileContext,
 | |
|     FSP_FSCTL_FILE_INFO *FileInfo)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
 | |
|     FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext;
 | |
|     UINT32 Index = (UINT32)FullContext->UserContext;
 | |
| 
 | |
|     FillFileInfo(Index, FileInfo);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
 | |
|     PVOID FileContext, PWSTR Pattern, PWSTR Marker,
 | |
|     PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
 | |
|     UINT32 Count = FileCount(Notifyfs);
 | |
|     UINT32 Index;
 | |
| 
 | |
|     Index = 0 == Marker ? 1 : wcstoul(Marker, 0, 10) + 1;
 | |
|     for (; Count >= Index; Index++)
 | |
|         if (!AddDirInfo(0, Index, Buffer, Length, PBytesTransferred))
 | |
|             break;
 | |
|     FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static FSP_FILE_SYSTEM_INTERFACE NotifyfsInterface =
 | |
| {
 | |
|     .GetVolumeInfo = GetVolumeInfo,
 | |
|     .GetSecurityByName = GetSecurityByName,
 | |
|     .Create = Create,
 | |
|     .Open = Open,
 | |
|     .Overwrite = Overwrite,
 | |
|     .Read = Read,
 | |
|     .GetFileInfo = GetFileInfo,
 | |
|     .ReadDirectory = ReadDirectory,
 | |
| };
 | |
| 
 | |
| static VOID CALLBACK NotifyfsTick(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = Context;
 | |
|     UINT32 Ticks = InterlockedIncrement(&Notifyfs->Ticks);
 | |
|     UINT32 OldCount = CountFromTicks(Ticks - 1);
 | |
|     UINT32 NewCount = CountFromTicks(Ticks);
 | |
|     union
 | |
|     {
 | |
|         UINT8 B[FIELD_OFFSET(FSP_FSCTL_NOTIFY_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)];
 | |
|         FSP_FSCTL_NOTIFY_INFO V;
 | |
|     } NotifyInfoBuf;
 | |
|     FSP_FSCTL_NOTIFY_INFO *NotifyInfo = &NotifyInfoBuf.V;
 | |
| 
 | |
|     memset(NotifyInfo, 0, sizeof NotifyInfo);
 | |
|     if (OldCount < NewCount)
 | |
|     {
 | |
|         wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)NewCount);
 | |
|         NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) +
 | |
|             wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR));
 | |
|         NotifyInfo->Action = FILE_ACTION_ADDED;
 | |
|         NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
 | |
|         FspDebugLog("CREATE \\%u\n", (unsigned)NewCount);
 | |
|     }
 | |
|     else if (OldCount > NewCount)
 | |
|     {
 | |
|         wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)OldCount);
 | |
|         NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) +
 | |
|             wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR));
 | |
|         NotifyInfo->Action = FILE_ACTION_REMOVED;
 | |
|         NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
 | |
|         FspDebugLog("REMOVE \\%u\n", (unsigned)OldCount);
 | |
|     }
 | |
| 
 | |
|     if (OldCount != NewCount)
 | |
|     {
 | |
|         if (STATUS_SUCCESS == FspFileSystemNotifyBegin(Notifyfs->FileSystem, 500))
 | |
|         {
 | |
|             FspFileSystemNotify(Notifyfs->FileSystem, NotifyInfo, NotifyInfo->Size);
 | |
|             FspFileSystemNotifyEnd(Notifyfs->FileSystem);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static VOID NotifyfsDelete(NOTIFYFS *Notifyfs);
 | |
| 
 | |
| static NTSTATUS NotifyfsCreate(PWSTR VolumePrefix, PWSTR MountPoint, NOTIFYFS **PNotifyfs)
 | |
| {
 | |
|     FSP_FSCTL_VOLUME_PARAMS VolumeParams;
 | |
|     NOTIFYFS *Notifyfs = 0;
 | |
|     INT64 TimerDue;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     *PNotifyfs = 0;
 | |
| 
 | |
|     if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
 | |
|         L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)", SDDL_REVISION_1,
 | |
|         &DefaultSecurity, &DefaultSecuritySize))
 | |
|     {
 | |
|         Result = FspNtStatusFromWin32(GetLastError());
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     Notifyfs = malloc(sizeof *Notifyfs);
 | |
|     if (0 == Notifyfs)
 | |
|     {
 | |
|         Result = STATUS_INSUFFICIENT_RESOURCES;
 | |
|         goto exit;
 | |
|     }
 | |
|     memset(Notifyfs, 0, sizeof *Notifyfs);
 | |
| 
 | |
|     memset(&VolumeParams, 0, sizeof VolumeParams);
 | |
|     VolumeParams.SectorSize = ALLOCATION_UNIT;
 | |
|     VolumeParams.SectorsPerAllocationUnit = 1;
 | |
|     VolumeParams.VolumeCreationTime = 0;
 | |
|     VolumeParams.VolumeSerialNumber = 0;
 | |
|     VolumeParams.FileInfoTimeout = 1000;
 | |
|     VolumeParams.CaseSensitiveSearch = 0;
 | |
|     VolumeParams.CasePreservedNames = 1;
 | |
|     VolumeParams.UnicodeOnDisk = 1;
 | |
|     VolumeParams.PersistentAcls = 0;
 | |
|     VolumeParams.PostCleanupWhenModifiedOnly = 1;
 | |
|     VolumeParams.UmFileContextIsFullContext = 1;
 | |
|     if (0 != VolumePrefix)
 | |
|         wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
 | |
|     wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),
 | |
|         L"" PROGNAME);
 | |
| 
 | |
|     Result = FspFileSystemCreate(
 | |
|         VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME,
 | |
|         &VolumeParams,
 | |
|         &NotifyfsInterface,
 | |
|         &Notifyfs->FileSystem);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|         goto exit;
 | |
|     Notifyfs->FileSystem->UserContext = Notifyfs;
 | |
| 
 | |
|     FspFileSystemSetDebugLog(Notifyfs->FileSystem, DEBUGFLAGS);
 | |
| 
 | |
|     Result = FspFileSystemSetMountPoint(Notifyfs->FileSystem, MountPoint);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|         goto exit;
 | |
| 
 | |
|     Notifyfs->Timer = CreateThreadpoolTimer(NotifyfsTick, Notifyfs, 0);
 | |
|     if (0 == Notifyfs->Timer)
 | |
|     {
 | |
|         Result = FspNtStatusFromWin32(GetLastError());
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     TimerDue = -1000;
 | |
|     SetThreadpoolTimer(Notifyfs->Timer, (PVOID)&TimerDue, 1000, 0);
 | |
| 
 | |
|     Result = STATUS_SUCCESS;
 | |
| 
 | |
| exit:
 | |
|     if (NT_SUCCESS(Result))
 | |
|         *PNotifyfs = Notifyfs;
 | |
|     else if (0 != Notifyfs)
 | |
|         NotifyfsDelete(Notifyfs);
 | |
| 
 | |
|     return Result;
 | |
| }
 | |
| 
 | |
| static VOID NotifyfsDelete(NOTIFYFS *Notifyfs)
 | |
| {
 | |
|     if (0 != Notifyfs->Timer)
 | |
|     {
 | |
|         SetThreadpoolTimer(Notifyfs->Timer, 0, 0, 0);
 | |
|         WaitForThreadpoolTimerCallbacks(Notifyfs->Timer, TRUE);
 | |
|         CloseThreadpoolTimer(Notifyfs->Timer);
 | |
|     }
 | |
| 
 | |
|     if (0 != Notifyfs->FileSystem)
 | |
|         FspFileSystemDelete(Notifyfs->FileSystem);
 | |
| 
 | |
|     free(Notifyfs);
 | |
| }
 | |
| 
 | |
| static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
 | |
| {
 | |
| #define argtos(v)                       if (arge > ++argp) v = *argp; else goto usage
 | |
| 
 | |
|     wchar_t **argp, **arge;
 | |
|     PWSTR VolumePrefix = 0;
 | |
|     PWSTR MountPoint = 0;
 | |
|     NOTIFYFS *Notifyfs = 0;
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     for (argp = argv + 1, arge = argv + argc; arge > argp; argp++)
 | |
|     {
 | |
|         if (L'-' != argp[0][0])
 | |
|             break;
 | |
|         switch (argp[0][1])
 | |
|         {
 | |
|         case L'?':
 | |
|             goto usage;
 | |
|         case L'm':
 | |
|             argtos(MountPoint);
 | |
|             break;
 | |
|         case L'u':
 | |
|             argtos(VolumePrefix);
 | |
|             break;
 | |
|         default:
 | |
|             goto usage;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (arge > argp)
 | |
|         goto usage;
 | |
| 
 | |
|     if (0 == MountPoint)
 | |
|         goto usage;
 | |
| 
 | |
|     FspDebugLogSetHandle(GetStdHandle(STD_ERROR_HANDLE));
 | |
| 
 | |
|     Result = NotifyfsCreate(VolumePrefix, MountPoint, &Notifyfs);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|     {
 | |
|         fail(L"cannot create file system");
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     Result = FspFileSystemStartDispatcher(Notifyfs->FileSystem, 0);
 | |
|     if (!NT_SUCCESS(Result))
 | |
|     {
 | |
|         fail(L"cannot start file system");
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     MountPoint = FspFileSystemMountPoint(Notifyfs->FileSystem);
 | |
| 
 | |
|     info(L"%s%s%s -m %s",
 | |
|         L"" PROGNAME,
 | |
|         0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"",
 | |
|             0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"",
 | |
|         MountPoint);
 | |
| 
 | |
|     Service->UserContext = Notifyfs;
 | |
|     Result = STATUS_SUCCESS;
 | |
| 
 | |
| exit:
 | |
|     if (!NT_SUCCESS(Result) && 0 != Notifyfs)
 | |
|         NotifyfsDelete(Notifyfs);
 | |
| 
 | |
|     return Result;
 | |
| 
 | |
| usage:;
 | |
|     static wchar_t usage[] = L""
 | |
|         "usage: %s OPTIONS\n"
 | |
|         "\n"
 | |
|         "options:\n"
 | |
|         "    -u \\Server\\Share    [UNC prefix (single backslash)]\n"
 | |
|         "    -m MountPoint       [X:|*|directory]\n";
 | |
| 
 | |
|     fail(usage, L"" PROGNAME);
 | |
| 
 | |
|     return STATUS_UNSUCCESSFUL;
 | |
| 
 | |
| #undef argtos
 | |
| }
 | |
| 
 | |
| static NTSTATUS SvcStop(FSP_SERVICE *Service)
 | |
| {
 | |
|     NOTIFYFS *Notifyfs = Service->UserContext;
 | |
| 
 | |
|     FspFileSystemStopDispatcher(Notifyfs->FileSystem);
 | |
|     NotifyfsDelete(Notifyfs);
 | |
| 
 | |
|     return STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| int wmain(int argc, wchar_t **argv)
 | |
| {
 | |
|     if (!NT_SUCCESS(FspLoad(0)))
 | |
|         return ERROR_DELAY_LOAD_FAILED;
 | |
| 
 | |
|     return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0);
 | |
| }
 |