diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index acf50b52..88231ea8 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -62,6 +62,26 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE FSP_FSCTL_TRANSACT_REQ *Request, PVOID FileNode, FSP_FSCTL_FILE_INFO *FileInfo); + NTSTATUS (*SetBasicInfo)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, + FSP_FSCTL_FILE_INFO *FileInfo); + NTSTATUS (*SetAllocationSize)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, UINT64 AllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo); + NTSTATUS (*SetFileSize)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, UINT64 FileSize, BOOLEAN AdvanceOnly, + FSP_FSCTL_FILE_INFO *FileInfo); + NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode); + NTSTATUS (*Rename)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, + PWSTR ExistingFileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists); } FSP_FILE_SYSTEM_INTERFACE; typedef struct _FSP_FILE_SYSTEM { @@ -167,6 +187,8 @@ FSP_API NTSTATUS FspFileSystemOpClose(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request); FSP_API NTSTATUS FspFileSystemOpQueryInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request); +FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request); FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request); FSP_API NTSTATUS FspFileSystemSendCreateResponse(FSP_FILE_SYSTEM *FileSystem, @@ -181,6 +203,8 @@ FSP_API NTSTATUS FspFileSystemSendCloseResponse(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request); FSP_API NTSTATUS FspFileSystemSendQueryInformationResponse(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_FILE_INFO *FileInfo); +FSP_API NTSTATUS FspFileSystemSendSetInformationResponse(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_FILE_INFO *FileInfo); FSP_API NTSTATUS FspFileSystemSendQueryVolumeInformationResponse(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_VOLUME_INFO *VolumeInfo); diff --git a/src/dll/fileinfo.c b/src/dll/fileinfo.c index f7e8f7c0..850abf12 100644 --- a/src/dll/fileinfo.c +++ b/src/dll/fileinfo.c @@ -17,13 +17,88 @@ FSP_API NTSTATUS FspFileSystemOpQueryInformation(FSP_FILE_SYSTEM *FileSystem, memset(&FileInfo, 0, sizeof FileInfo); Result = FileSystem->Interface->GetFileInfo(FileSystem, Request, - (PVOID)Request->Req.Close.UserContext, &FileInfo); + (PVOID)Request->Req.QueryInformation.UserContext, &FileInfo); if (!NT_SUCCESS(Result)) return FspFileSystemSendResponseWithStatus(FileSystem, Request, Result); return FspFileSystemSendQueryInformationResponse(FileSystem, Request, &FileInfo); } +FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request) +{ + NTSTATUS Result; + FSP_FSCTL_FILE_INFO FileInfo; + + Result = STATUS_INVALID_DEVICE_REQUEST; + memset(&FileInfo, 0, sizeof FileInfo); + switch (Request->Req.SetInformation.FileInformationClass) + { + case 4/*FileBasicInformation*/: + if (0 != FileSystem->Interface->SetBasicInfo) + Result = FileSystem->Interface->SetBasicInfo(FileSystem, Request, + (PVOID)Request->Req.SetInformation.UserContext, + Request->Req.SetInformation.Info.Basic.FileAttributes, + Request->Req.SetInformation.Info.Basic.CreationTime, + Request->Req.SetInformation.Info.Basic.LastAccessTime, + Request->Req.SetInformation.Info.Basic.LastWriteTime, + &FileInfo); + break; + case 19/*FileAllocationInformation*/: + if (0 != FileSystem->Interface->SetAllocationSize) + Result = FileSystem->Interface->SetAllocationSize(FileSystem, Request, + (PVOID)Request->Req.SetInformation.UserContext, + Request->Req.SetInformation.Info.Allocation.AllocationSize, + &FileInfo); + else + if (0 != FileSystem->Interface->GetFileInfo && + 0 != FileSystem->Interface->SetFileSize) + { + Result = FileSystem->Interface->GetFileInfo(FileSystem, Request, + (PVOID)Request->Req.SetInformation.UserContext, &FileInfo); + if (NT_SUCCESS(Result) && + Request->Req.SetInformation.Info.Allocation.AllocationSize < FileInfo.FileSize) + { + Result = FileSystem->Interface->SetFileSize(FileSystem, Request, + (PVOID)Request->Req.SetInformation.UserContext, + Request->Req.SetInformation.Info.Allocation.AllocationSize, + FALSE, + &FileInfo); + } + } + break; + case 20/*FileEndOfFileInformation*/: + if (0 != FileSystem->Interface->SetFileSize) + Result = FileSystem->Interface->SetFileSize(FileSystem, Request, + (PVOID)Request->Req.SetInformation.UserContext, + Request->Req.SetInformation.Info.EndOfFile.FileSize, + Request->Req.SetInformation.Info.EndOfFile.AdvanceOnly, + &FileInfo); + break; + case 13/*FileDispositionInformation*/: + if (0 != FileSystem->Interface->CanDelete) + if (Request->Req.SetInformation.Info.Disposition.Delete) + Result = FileSystem->Interface->CanDelete(FileSystem, Request, + (PVOID)Request->Req.Close.UserContext); + else + Result = STATUS_SUCCESS; + break; + case 10/*FileRenameInformation*/: + if (0 != FileSystem->Interface->Rename) + Result = FileSystem->Interface->Rename(FileSystem, Request, + (PVOID)Request->Req.SetInformation.UserContext, + (PWSTR)Request->Buffer, + (PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset), + Request->Req.SetInformation.Info.Rename.ReplaceIfExists); + break; + } + + if (!NT_SUCCESS(Result)) + return FspFileSystemSendResponseWithStatus(FileSystem, Request, Result); + + return FspFileSystemSendSetInformationResponse(FileSystem, Request, &FileInfo); +} + FSP_API NTSTATUS FspFileSystemSendQueryInformationResponse(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_FILE_INFO *FileInfo) { @@ -38,3 +113,18 @@ FSP_API NTSTATUS FspFileSystemSendQueryInformationResponse(FSP_FILE_SYSTEM *File Response.Rsp.QueryInformation.FileInfo = *FileInfo; return FspFileSystemSendResponse(FileSystem, &Response); } + +FSP_API NTSTATUS FspFileSystemSendSetInformationResponse(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, const FSP_FSCTL_FILE_INFO *FileInfo) +{ + FSP_FSCTL_TRANSACT_RSP Response; + + memset(&Response, 0, sizeof Response); + Response.Size = sizeof Response; + Response.Kind = FspFsctlTransactSetInformationKind; + Response.Hint = Request->Hint; + Response.IoStatus.Status = STATUS_SUCCESS; + Response.IoStatus.Information = 0; + Response.Rsp.SetInformation.FileInfo = *FileInfo; + return FspFileSystemSendResponse(FileSystem, &Response); +} diff --git a/src/sys/create.c b/src/sys/create.c index 5a966cdd..dfda7923 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -116,7 +116,8 @@ static NTSTATUS FspFsvolCreate( USHORT FileAttributes = IrpSp->Parameters.Create.FileAttributes; PSECURITY_DESCRIPTOR SecurityDescriptor = AccessState->SecurityDescriptor; ULONG SecurityDescriptorSize = 0; - LARGE_INTEGER AllocationSize = Irp->Overlay.AllocationSize; + UINT64 AllocationSize = Irp->Overlay.AllocationSize.QuadPart; + UINT64 AllocationUnit; ACCESS_MASK DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; USHORT ShareAccess = IrpSp->Parameters.Create.ShareAccess; PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer; @@ -156,6 +157,11 @@ static NTSTATUS FspFsvolCreate( SecurityDescriptorSize = RtlLengthSecurityDescriptor(SecurityDescriptor); } + /* align allocation size */ + AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize * + FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit; + AllocationSize = (AllocationSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + /* according to fastfat, filenames that begin with two backslashes are ok */ if (sizeof(WCHAR) * 2 <= FileName.Length && L'\\' == FileName.Buffer[1] && L'\\' == FileName.Buffer[0]) @@ -323,7 +329,7 @@ static NTSTATUS FspFsvolCreate( Request->Req.Create.SecurityDescriptor.Offset = 0 == SecurityDescriptorSize ? 0 : FSP_FSCTL_DEFAULT_ALIGN_UP(Request->FileName.Size); Request->Req.Create.SecurityDescriptor.Size = (UINT16)SecurityDescriptorSize; - Request->Req.Create.AllocationSize = AllocationSize.QuadPart; + Request->Req.Create.AllocationSize = AllocationSize; Request->Req.Create.AccessToken = 0; Request->Req.Create.DesiredAccess = DesiredAccess; Request->Req.Create.ShareAccess = ShareAccess; diff --git a/src/sys/file.c b/src/sys/file.c index 2348dac6..9c75e7e6 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -401,10 +401,18 @@ VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject, { PAGED_CODE(); - UINT64 FileInfoTimeout = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject)-> - VolumeParams.FileInfoTimeout * 10000ULL; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + UINT64 FileInfoTimeout = FsvolDeviceExtension->VolumeParams.FileInfoTimeout * 10000ULL; + UINT64 AllocationSize = FileInfo->AllocationSize > FileInfo->FileSize ? + FileInfo->AllocationSize : FileInfo->FileSize; + UINT64 AllocationUnit; - FileNode->Header.AllocationSize.QuadPart = FileInfo->AllocationSize; + AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize * + FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit; + AllocationSize = (AllocationSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + + FileNode->Header.AllocationSize.QuadPart = AllocationSize; FileNode->Header.FileSize.QuadPart = FileInfo->FileSize; FileNode->FileAttributes = FileInfo->FileAttributes; diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 49c13cc5..bf5f7058 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -531,9 +531,17 @@ static NTSTATUS FspFsvolSetAllocationInformation(PFILE_OBJECT FileObject, return STATUS_SUCCESS; PFILE_ALLOCATION_INFORMATION Info = (PFILE_ALLOCATION_INFORMATION)Buffer; + + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FileObject->DeviceObject); + UINT64 AllocationSize = Info->AllocationSize.QuadPart; + UINT64 AllocationUnit; BOOLEAN Success; - Request->Req.SetInformation.Info.Allocation.AllocationSize = Info->AllocationSize.QuadPart; + AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize * + FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit; + AllocationSize = (AllocationSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + Request->Req.SetInformation.Info.Allocation.AllocationSize = AllocationSize; Success = MmCanFileBeTruncated(FileObject->SectionObjectPointer, &Info->AllocationSize); if (!Success) @@ -599,11 +607,14 @@ static NTSTATUS FspFsvolSetDispositionInformation(PFILE_OBJECT FileObject, PFILE_DISPOSITION_INFORMATION Info = (PFILE_DISPOSITION_INFORMATION)Buffer; BOOLEAN Success; - /* make sure no process is mapping the file as an image */ - Success = MmFlushImageSection(FileObject->SectionObjectPointer, - MmFlushForDelete); - if (!Success) - return STATUS_CANNOT_DELETE; + if (Info->DeleteFile) + { + /* make sure no process is mapping the file as an image */ + Success = MmFlushImageSection(FileObject->SectionObjectPointer, + MmFlushForDelete); + if (!Success) + return STATUS_CANNOT_DELETE; + } Request->Req.SetInformation.Info.Disposition.Delete = Info->DeleteFile; @@ -802,20 +813,21 @@ NTSTATUS FspFsvolSetInformationComplete( ULONG Length = IrpSp->Parameters.SetFile.Length; FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); - FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo); - switch (FileInformationClass) { case FileAllocationInformation: + FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo); Result = FspFsvolSetAllocationInformation(FileObject, Buffer, Length, Request, Response); break; case FileBasicInformation: + FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo); Result = FspFsvolSetBasicInformation(FileObject, Buffer, Length, Request, Response); break; case FileDispositionInformation: Result = FspFsvolSetDispositionInformation(FileObject, Buffer, Length, Request, Response); break; case FileEndOfFileInformation: + FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo); Result = FspFsvolSetEndOfFileInformation(FileObject, Buffer, Length, IrpSp->Parameters.SetFile.AdvanceOnly, Request, Response); break; diff --git a/tst/winfsp-tests/memfs.cpp b/tst/winfsp-tests/memfs.cpp index 5cc74c2e..4745a834 100644 --- a/tst/winfsp-tests/memfs.cpp +++ b/tst/winfsp-tests/memfs.cpp @@ -295,7 +295,7 @@ static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, memcpy(FileNode->FileSecurity, SecurityDescriptor, FileNode->FileSecuritySize); } - FileNode->FileInfo.AllocationSize = FSP_FSCTL_ALIGN_UP((ULONG)AllocationSize, MEMFS_SECTOR_SIZE); + FileNode->FileInfo.AllocationSize = AllocationSize; if (0 != FileNode->FileInfo.AllocationSize) { FileNode->FileData = malloc(FileNode->FileInfo.AllocationSize); @@ -407,6 +407,96 @@ static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, return STATUS_SUCCESS; } +NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode0, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + if (INVALID_FILE_ATTRIBUTES != FileAttributes) + FileNode->FileInfo.FileAttributes = FileAttributes; + if (0 != CreationTime) + FileNode->FileInfo.CreationTime = CreationTime; + if (0 != LastAccessTime) + FileNode->FileInfo.CreationTime = LastAccessTime; + if (0 != LastWriteTime) + FileNode->FileInfo.CreationTime = LastWriteTime; + + *FileInfo = FileNode->FileInfo; + + return STATUS_SUCCESS; +} + +NTSTATUS SetAllocationSize(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode0, UINT64 AllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + PVOID FileData; + + FileData = realloc(FileNode->FileData, AllocationSize); + if (0 == FileData) + return STATUS_INSUFFICIENT_RESOURCES; + + FileNode->FileData = FileData; + + FileNode->FileInfo.AllocationSize = AllocationSize; + if (FileNode->FileInfo.FileSize > AllocationSize) + FileNode->FileInfo.FileSize = AllocationSize; + + *FileInfo = FileNode->FileInfo; + + return STATUS_SUCCESS; +} + +NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode0, UINT64 FileSize, BOOLEAN AdvanceOnly, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + if (FileNode->FileInfo.AllocationSize < FileSize) + { + UINT64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + UINT64 AllocationSize = (FileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + + NTSTATUS Result = SetAllocationSize(FileSystem, Request, FileNode, AllocationSize, FileInfo); + if (!NT_SUCCESS(Result)) + return Result; + } + + FileNode->FileInfo.FileSize = FileSize; + + *FileInfo = FileNode->FileInfo; + + return STATUS_SUCCESS; +} + +NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode0) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) + return STATUS_DIRECTORY_NOT_EMPTY; + + return STATUS_SUCCESS; +} + +NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, + PWSTR ExistingFileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = { GetVolumeInfo, @@ -417,6 +507,11 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = Cleanup, Close, GetFileInfo, + SetBasicInfo, + SetAllocationSize, + SetFileSize, + CanDelete, + Rename, }; static VOID MemfsEnterOperation(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request) @@ -439,6 +534,7 @@ NTSTATUS MemfsCreate(ULONG Flags, ULONG FileInfoTimeout, FSP_FSCTL_VOLUME_PARAMS VolumeParams; PWSTR DevicePath = (Flags & MemfsNet) ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME; + UINT64 AllocationUnit; MEMFS *Memfs; MEMFS_FILE_NODE *RootNode; BOOLEAN Inserted; @@ -451,7 +547,8 @@ NTSTATUS MemfsCreate(ULONG Flags, ULONG FileInfoTimeout, memset(Memfs, 0, sizeof *Memfs); Memfs->MaxFileNodes = MaxFileNodes; - Memfs->MaxFileSize = FSP_FSCTL_ALIGN_UP(MaxFileSize, MEMFS_SECTOR_SIZE); + AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + Memfs->MaxFileSize = (ULONG)((MaxFileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit); Result = MemfsFileNodeMapCreate(&Memfs->FileNodeMap); if (!NT_SUCCESS(Result))