diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 5042a4fa..1b5ca1c2 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -219,6 +219,17 @@ typedef struct } Rename; } Info; } SetInformation; + struct + { + UINT32 FsInformationClass; + union + { + struct + { + FSP_FSCTL_TRANSACT_BUF VolumeLabel; + } Label; + } Info; + } SetVolumeInformation; } Req; FSP_FSCTL_TRANSACT_BUF FileName; /* {Create,Cleanup,SetInformation/{Disposition,Rename}} */ FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; @@ -268,6 +279,10 @@ typedef struct { FSP_FSCTL_VOLUME_INFO VolumeInfo; } QueryVolumeInformation; + struct + { + FSP_FSCTL_VOLUME_INFO VolumeInfo; + } SetVolumeInformation; } Rsp; FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; } FSP_FSCTL_TRANSACT_RSP; diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 0b01c395..9caff29b 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -38,6 +38,10 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE NTSTATUS (*GetVolumeInfo)(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_VOLUME_INFO *VolumeInfo); + NTSTATUS (*SetVolumeLabel)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo); NTSTATUS (*GetSecurity)(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName, PUINT32 PFileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize); @@ -180,6 +184,8 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpSetVolumeInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); static inline NTSTATUS FspAccessCheck(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, diff --git a/src/dll/dispatch.c b/src/dll/dispatch.c index 43fa7769..862a606f 100644 --- a/src/dll/dispatch.c +++ b/src/dll/dispatch.c @@ -47,6 +47,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, FileSystem->Operations[FspFsctlTransactQueryInformationKind] = FspFileSystemOpQueryInformation; FileSystem->Operations[FspFsctlTransactSetInformationKind] = FspFileSystemOpSetInformation; FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation; + FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation; // !!!: ... FileSystem->Interface = Interface; diff --git a/src/dll/volinfo.c b/src/dll/volinfo.c index 3693bd68..12b2ccc8 100644 --- a/src/dll/volinfo.c +++ b/src/dll/volinfo.c @@ -23,3 +23,27 @@ FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSyst memcpy(&Response->Rsp.QueryVolumeInformation.VolumeInfo, &VolumeInfo, sizeof VolumeInfo); return STATUS_SUCCESS; } + +FSP_API NTSTATUS FspFileSystemOpSetVolumeInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + NTSTATUS Result; + FSP_FSCTL_VOLUME_INFO VolumeInfo; + + Result = STATUS_INVALID_DEVICE_REQUEST; + memset(&VolumeInfo, 0, sizeof VolumeInfo); + switch (Request->Req.SetVolumeInformation.FsInformationClass) + { + case 2/*FileFsLabelInformation*/: + if (0 != FileSystem->Interface->SetVolumeLabel) + Result = FileSystem->Interface->SetVolumeLabel(FileSystem, Request, + (PWSTR)Request->Buffer, + &VolumeInfo); + } + + if (!NT_SUCCESS(Result)) + return Result; + + memcpy(&Response->Rsp.SetVolumeInformation.VolumeInfo, &VolumeInfo, sizeof VolumeInfo); + return STATUS_SUCCESS; +} diff --git a/src/sys/volinfo.c b/src/sys/volinfo.c index 64d4002f..1bce4533 100644 --- a/src/sys/volinfo.c +++ b/src/sys/volinfo.c @@ -22,6 +22,9 @@ static NTSTATUS FspFsvolQueryFsVolumeInformation( static NTSTATUS FspFsvolQueryVolumeInformation( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolQueryVolumeInformationComplete; +static NTSTATUS FspFsvolSetFsLabelInformation( + PDEVICE_OBJECT FsvolDeviceObject, PVOID Buffer, ULONG Length, PULONG PRequestExtraSize, + FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_TRANSACT_RSP *Response); static NTSTATUS FspFsvolSetVolumeInformation( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolSetVolumeInformationComplete; @@ -36,6 +39,7 @@ FSP_DRIVER_DISPATCH FspSetVolumeInformation; #pragma alloc_text(PAGE, FspFsvolQueryFsVolumeInformation) #pragma alloc_text(PAGE, FspFsvolQueryVolumeInformation) #pragma alloc_text(PAGE, FspFsvolQueryVolumeInformationComplete) +#pragma alloc_text(PAGE, FspFsvolSetFsLabelInformation) #pragma alloc_text(PAGE, FspFsvolSetVolumeInformation) #pragma alloc_text(PAGE, FspFsvolSetVolumeInformationComplete) #pragma alloc_text(PAGE, FspQueryVolumeInformation) @@ -293,12 +297,83 @@ NTSTATUS FspFsvolQueryVolumeInformationComplete( FsInformationClassSym(IrpSp->Parameters.QueryVolume.FsInformationClass)); } -static NTSTATUS FspFsvolSetVolumeInformation( - PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +static NTSTATUS FspFsvolSetFsLabelInformation( + PDEVICE_OBJECT FsvolDeviceObject, PVOID Buffer, ULONG Length, PULONG PRequestExtraSize, + FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_TRANSACT_RSP *Response) { PAGED_CODE(); - return STATUS_INVALID_DEVICE_REQUEST; + if (0 == Request) + { + if (sizeof(FILE_FS_LABEL_INFORMATION) > Length) + return STATUS_INVALID_PARAMETER; + + PFILE_FS_LABEL_INFORMATION Info = (PFILE_FS_LABEL_INFORMATION)Buffer; + + *PRequestExtraSize = Info->VolumeLabelLength + sizeof(WCHAR); + } + else if (0 == Response) + { + PFILE_FS_LABEL_INFORMATION Info = (PFILE_FS_LABEL_INFORMATION)Buffer; + + Request->Req.SetVolumeInformation.Info.Label.VolumeLabel.Offset = 0; + Request->Req.SetVolumeInformation.Info.Label.VolumeLabel.Size = + (UINT16)(Info->VolumeLabelLength + sizeof(WCHAR)); + RtlCopyMemory(Request->Buffer, Info->VolumeLabel, Info->VolumeLabelLength); + ((PWSTR)Request->Buffer)[Info->VolumeLabelLength / sizeof(WCHAR)] = L'\0'; + } + else + FspFsvolSetVolumeInfo(FsvolDeviceObject, &Response->Rsp.SetVolumeInformation.VolumeInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS FspFsvolSetVolumeInformation( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + NTSTATUS Result; + FS_INFORMATION_CLASS FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG Length = IrpSp->Parameters.SetVolume.Length; + ULONG RequestExtraSize = 0; + + switch (FsInformationClass) + { + case FileFsLabelInformation: + Result = FspFsvolSetFsLabelInformation(FsvolDeviceObject, Buffer, Length, &RequestExtraSize, 0, 0); + break; + default: + Result = STATUS_INVALID_PARAMETER; + break; + } + + if (!NT_SUCCESS(Result)) + return Result; + + FSP_FSCTL_TRANSACT_REQ *Request; + + Result = FspIopCreateRequest(Irp, 0, RequestExtraSize, &Request); + if (!NT_SUCCESS(Result)) + return Result; + + Request->Kind = FspFsctlTransactSetVolumeInformationKind; + Request->Req.SetVolumeInformation.FsInformationClass = FsInformationClass; + + switch (FsInformationClass) + { + case FileFsLabelInformation: + Result = FspFsvolSetFsLabelInformation(FsvolDeviceObject, Buffer, Length, 0, Request, 0); + break; + default: + ASSERT(0); + Result = STATUS_INVALID_PARAMETER; + break; + } + + if (!NT_SUCCESS(Result)) + return Result; + + return FSP_STATUS_IOQ_POST; } NTSTATUS FspFsvolSetVolumeInformationComplete( @@ -306,6 +381,32 @@ NTSTATUS FspFsvolSetVolumeInformationComplete( { FSP_ENTER_IOC(PAGED_CODE()); + if (!NT_SUCCESS(Response->IoStatus.Status)) + { + Irp->IoStatus.Information = 0; + Result = Response->IoStatus.Status; + FSP_RETURN(); + } + + FS_INFORMATION_CLASS FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; + PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject; + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG Length = IrpSp->Parameters.SetFile.Length; + FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); + + switch (FsInformationClass) + { + case FileFsLabelInformation: + Result = FspFsvolSetFsLabelInformation(FsvolDeviceObject, Buffer, Length, 0, Request, Response); + break; + default: + ASSERT(0); + Result = STATUS_INVALID_PARAMETER; + break; + } + + Irp->IoStatus.Information = 0; + FSP_LEAVE_IOC("%s", FsInformationClassSym(IrpSp->Parameters.SetVolume.FsInformationClass)); } diff --git a/tst/winfsp-tests/info-test.c b/tst/winfsp-tests/info-test.c index 94c32267..3a499755 100644 --- a/tst/winfsp-tests/info-test.c +++ b/tst/winfsp-tests/info-test.c @@ -459,6 +459,98 @@ void getvolinfo_test(void) } } +void setvolinfo_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) +{ + if (-1 == Flags) + return;/* avoid accidentally changing the volume label on our NTFS disk */ + if (0 != Prefix) + return;/* cannot do SetVolumeLabel on a network share! */ + + void *memfs = memfs_start_ex(Flags, FileInfoTimeout); + + BOOL Success; + WCHAR FilePath[MAX_PATH]; + WCHAR VolumeLabelBuf[MAX_PATH]; + DWORD VolumeSerialNumber; + DWORD MaxComponentLength; + DWORD FileSystemFlags; + WCHAR FileSystemNameBuf[MAX_PATH]; + + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Success = SetVolumeLabelW(FilePath, L"12345678901234567890123456789012"); + ASSERT(Success); + + Success = GetVolumeInformationW(FilePath, + VolumeLabelBuf, sizeof VolumeLabelBuf, + &VolumeSerialNumber, &MaxComponentLength, &FileSystemFlags, + FileSystemNameBuf, sizeof FileSystemNameBuf); + ASSERT(Success); + if (-1 != Flags) + { + ASSERT(0 == wcscmp(VolumeLabelBuf, L"12345678901234567890123456789012")); + ASSERT(255 == MaxComponentLength); + ASSERT(0 != (FileSystemFlags & + (FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | FILE_PERSISTENT_ACLS))); + ASSERT(0 == wcscmp(FileSystemNameBuf, L"WinFsp")); + } + + Success = SetVolumeLabelW(FilePath, L"TestLabel"); + ASSERT(Success); + + Success = GetVolumeInformationW(FilePath, + VolumeLabelBuf, sizeof VolumeLabelBuf, + &VolumeSerialNumber, &MaxComponentLength, &FileSystemFlags, + FileSystemNameBuf, sizeof FileSystemNameBuf); + ASSERT(Success); + if (-1 != Flags) + { + ASSERT(0 == wcscmp(VolumeLabelBuf, L"TestLabel")); + ASSERT(255 == MaxComponentLength); + ASSERT(0 != (FileSystemFlags & + (FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | FILE_PERSISTENT_ACLS))); + ASSERT(0 == wcscmp(FileSystemNameBuf, L"WinFsp")); + } + + Success = SetVolumeLabelW(FilePath, L"123456789012345678901234567890123"); + ASSERT(Success); + + Success = GetVolumeInformationW(FilePath, + VolumeLabelBuf, sizeof VolumeLabelBuf, + &VolumeSerialNumber, &MaxComponentLength, &FileSystemFlags, + FileSystemNameBuf, sizeof FileSystemNameBuf); + ASSERT(Success); + if (-1 != Flags) + { + ASSERT(0 == wcscmp(VolumeLabelBuf, L"12345678901234567890123456789012")); + ASSERT(255 == MaxComponentLength); + ASSERT(0 != (FileSystemFlags & + (FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | FILE_PERSISTENT_ACLS))); + ASSERT(0 == wcscmp(FileSystemNameBuf, L"WinFsp")); + } + + memfs_stop(memfs); +} + +void setvolinfo_test(void) +{ +#if 0 + if (NtfsTests) + setvolinfo_dotest(-1, L"C:", 0); +#endif + if (WinFspDiskTests) + { + setvolinfo_dotest(MemfsDisk, 0, 0); + setvolinfo_dotest(MemfsDisk, 0, 1000); + } + if (WinFspNetTests) + { + setvolinfo_dotest(MemfsNet, L"\\\\memfs\\share", 0); + setvolinfo_dotest(MemfsNet, L"\\\\memfs\\share", 1000); + } +} + void info_tests(void) { TEST(getfileinfo_test); @@ -466,4 +558,5 @@ void info_tests(void) TEST(delete_test); TEST(rename_test); TEST(getvolinfo_test); + TEST(setvolinfo_test); } diff --git a/tst/winfsp-tests/memfs.cpp b/tst/winfsp-tests/memfs.cpp index 7322b431..3a0c54ee 100644 --- a/tst/winfsp-tests/memfs.cpp +++ b/tst/winfsp-tests/memfs.cpp @@ -51,6 +51,8 @@ typedef struct _MEMFS MEMFS_FILE_NODE_MAP *FileNodeMap; ULONG MaxFileNodes; ULONG MaxFileSize; + UINT16 VolumeLabelLength; + WCHAR VolumeLabel[32]; CRITICAL_SECTION Lock; } MEMFS; @@ -196,17 +198,33 @@ static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo) { MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; - MEMFS_FILE_NODE *RootNode; - - RootNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, L"\\"); - if (0 == RootNode) - return STATUS_DISK_CORRUPT_ERROR; VolumeInfo->TotalSize = Memfs->MaxFileNodes * Memfs->MaxFileSize; VolumeInfo->FreeSize = (Memfs->MaxFileNodes - MemfsFileNodeMapCount(Memfs->FileNodeMap)) * Memfs->MaxFileSize; - VolumeInfo->VolumeLabelLength = sizeof L"MEMFS" - sizeof(WCHAR); - memcpy(VolumeInfo->VolumeLabel, L"MEMFS", VolumeInfo->VolumeLabelLength); + VolumeInfo->VolumeLabelLength = Memfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Memfs->VolumeLabel, Memfs->VolumeLabelLength); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetVolumeLabel(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + + Memfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof(WCHAR)); + if (Memfs->VolumeLabelLength > sizeof Memfs->VolumeLabel) + Memfs->VolumeLabelLength = sizeof Memfs->VolumeLabel; + memcpy(Memfs->VolumeLabel, VolumeLabel, Memfs->VolumeLabelLength); + + VolumeInfo->TotalSize = Memfs->MaxFileNodes * Memfs->MaxFileSize; + VolumeInfo->FreeSize = + (Memfs->MaxFileNodes - MemfsFileNodeMapCount(Memfs->FileNodeMap)) * Memfs->MaxFileSize; + VolumeInfo->VolumeLabelLength = Memfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Memfs->VolumeLabel, Memfs->VolumeLabelLength); return STATUS_SUCCESS; } @@ -538,6 +556,7 @@ NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = { GetVolumeInfo, + SetVolumeLabel, GetSecurity, Create, Open, @@ -616,7 +635,10 @@ NTSTATUS MemfsCreate(ULONG Flags, ULONG FileInfoTimeout, free(Memfs); return Result; } + Memfs->FileSystem->UserContext = Memfs; + Memfs->VolumeLabelLength = sizeof L"MEMFS" - sizeof(WCHAR); + memcpy(Memfs->VolumeLabel, L"MEMFS", Memfs->VolumeLabelLength); InitializeCriticalSection(&Memfs->Lock);