From c897ddd864e5fcc38f0b13638bc4198323a885a0 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 21 Dec 2016 16:09:24 -0800 Subject: [PATCH] sys,dll: Cleanup/TruncateOnClose overhaul --- inc/winfsp/fsctl.h | 7 ++- inc/winfsp/winfsp.h | 24 +++++--- src/dll/fsop.c | 7 ++- src/dll/fuse/fuse.c | 2 +- src/dll/fuse/fuse_intf.c | 4 +- src/sys/cleanup.c | 20 +++++- src/sys/create.c | 9 +++ src/sys/driver.h | 4 +- src/sys/file.c | 32 ++++++---- src/sys/fileinfo.c | 9 +++ src/sys/write.c | 8 ++- tst/memfs/memfs.cpp | 108 +++++++++++++++++++++++---------- tst/winfsp-tests/create-test.c | 38 +++++++++--- 13 files changed, 203 insertions(+), 69 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index e8d022a1..57c17f75 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -146,7 +146,7 @@ typedef struct UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */ UINT32 ReadOnlyVolume:1; /* kernel-mode flags */ - UINT32 PostCleanupOnDeleteOnly:1; /* post Cleanup when deleting a file only */ + UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */ UINT32 KmReservedFlags:5; /* user-mode flags */ UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */ @@ -248,6 +248,11 @@ typedef struct UINT64 UserContext; UINT64 UserContext2; UINT32 Delete:1; /* file must be deleted */ + UINT32 SetAllocationSize:1; + UINT32 SetArchiveBit:1; + UINT32 SetLastAccessTime:1; + UINT32 SetLastWriteTime:1; + UINT32 SetChangeTime:1; } Cleanup; struct { diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index a17c42f8..a14407d7 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -122,6 +122,15 @@ typedef enum FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE = 0, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE, } FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY; +enum +{ + FspCleanupDelete = 0x01, + FspCleanupSetAllocationSize = 0x02, + FspCleanupSetArchiveBit = 0x10, + FspCleanupSetLastAccessTime = 0x20, + FspCleanupSetLastWriteTime = 0x40, + FspCleanupSetChangeTime = 0x80, +}; /** * @class FSP_FILE_SYSTEM * File system interface. @@ -332,8 +341,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * deleted during Cleanup. * * As an optimization a file system may specify the FSP_FSCTL_VOLUME_PARAMS :: - * PostCleanupOnDeleteOnly flag. In this case the FSD will only post Cleanup requests when a - * file is being deleted. + * PostCleanupWhenModifiedOnly flag. In this case the FSD will only post Cleanup requests when + * the file was modified/deleted. * * @param FileSystem * The file system on which this request is posted. @@ -341,16 +350,17 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * The file context of the file or directory to cleanup. * @param FileName * The name of the file or directory to cleanup. Sent only when a Delete is requested. - * @param Delete - * Determines whether to delete the file. Note that there is no way to report failure of - * this operation. Also note that when this parameter is TRUE this is the last outstanding - * cleanup for this particular file node. + * @param Flags + * These flags determine whether the file was modified and whether to delete the file. + * Note that there is no way to report failure of this operation. Also note that when + * this parameter has the FspCleanupDelete bit set, this is the last outstanding cleanup + * for this particular file node. * @see * Close * CanDelete */ VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem, - PVOID FileContext, PWSTR FileName, BOOLEAN Delete); + PVOID FileContext, PWSTR FileName, ULONG Flags); /** * Close a file. * diff --git a/src/dll/fsop.c b/src/dll/fsop.c index b0eef140..7807f428 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -875,7 +875,12 @@ FSP_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem, FileSystem->Interface->Cleanup(FileSystem, (PVOID)ValOfFileContext(Request->Req.Cleanup), 0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0, - 0 != Request->Req.Cleanup.Delete); + (0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) | + (0 != Request->Req.Cleanup.SetAllocationSize ? FspCleanupSetAllocationSize : 0) | + (0 != Request->Req.Cleanup.SetArchiveBit ? FspCleanupSetArchiveBit : 0) | + (0 != Request->Req.Cleanup.SetLastAccessTime ? FspCleanupSetLastAccessTime : 0) | + (0 != Request->Req.Cleanup.SetLastWriteTime ? FspCleanupSetLastWriteTime : 0) | + (0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0)); return STATUS_SUCCESS; } diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 3b08e35f..73562e02 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -523,7 +523,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, opt_data.VolumeParams.ReparsePointsAccessCheck = FALSE; opt_data.VolumeParams.NamedStreams = FALSE; opt_data.VolumeParams.ReadOnlyVolume = !!opt_data.ReadOnlyVolume; - opt_data.VolumeParams.PostCleanupOnDeleteOnly = TRUE; + opt_data.VolumeParams.PostCleanupWhenModifiedOnly = TRUE; opt_data.VolumeParams.UmFileContextIsUserContext2 = TRUE; if (L'\0' == opt_data.VolumeParams.FileSystemName[0]) memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR)); diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index deb0f41d..f1aed4f0 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -1050,7 +1050,7 @@ static NTSTATUS fsp_fuse_intf_Overwrite(FSP_FILE_SYSTEM *FileSystem, } static VOID fsp_fuse_intf_Cleanup(FSP_FILE_SYSTEM *FileSystem, - PVOID FileNode, PWSTR FileName, BOOLEAN Delete) + PVOID FileNode, PWSTR FileName, ULONG Flags) { struct fuse *f = FileSystem->UserContext; struct fsp_fuse_file_desc *filedesc = FileNode; @@ -1072,7 +1072,7 @@ static VOID fsp_fuse_intf_Cleanup(FSP_FILE_SYSTEM *FileSystem, * FUSE option and can safely remove the file at this time. */ - if (Delete) + if (Flags & FspCleanupDelete) if (filedesc->IsDirectory && !filedesc->IsReparsePoint) { if (0 != f->ops.rmdir) diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index 7b8f1d24..5d31fea5 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -77,13 +77,17 @@ static NTSTATUS FspFsvolCleanup( FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FILE_DESC *FileDesc = FileObject->FsContext2; FSP_FSCTL_TRANSACT_REQ *Request; - BOOLEAN DeletePending; + ULONG CleanupFlags; + BOOLEAN DeletePending, SetAllocationSize, FileModified; ASSERT(FileNode == FileDesc->FileNode); FspFileNodeAcquireExclusive(FileNode, Main); - FspFileNodeCleanup(FileNode, FileObject, &DeletePending); + FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags); + DeletePending = CleanupFlags & 1; + SetAllocationSize = !!(CleanupFlags & 2); + FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED); /* if this is a directory inform the FSRTL Notify mechanism */ if (FileNode->IsDirectory) @@ -106,13 +110,23 @@ static NTSTATUS FspFsvolCleanup( Request->Req.Cleanup.UserContext = FileNode->UserContext; Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2; Request->Req.Cleanup.Delete = DeletePending; + Request->Req.Cleanup.SetAllocationSize = SetAllocationSize; + Request->Req.Cleanup.SetArchiveBit = FileModified; + Request->Req.Cleanup.SetLastAccessTime = !FileDesc->DidSetLastAccessTime; + Request->Req.Cleanup.SetLastWriteTime = FileModified && !FileDesc->DidSetLastWriteTime; + Request->Req.Cleanup.SetChangeTime = FileModified; FspFileNodeAcquireExclusive(FileNode, Pgio); FspFileNodeSetOwner(FileNode, Full, Request); FspIopRequestContext(Request, RequestIrp) = Irp; - if (DeletePending || !FsvolDeviceExtension->VolumeParams.PostCleanupOnDeleteOnly) + if (Request->Req.Cleanup.Delete || + Request->Req.Cleanup.SetAllocationSize || + Request->Req.Cleanup.SetArchiveBit || + Request->Req.Cleanup.SetLastWriteTime || + Request->Req.Cleanup.SetChangeTime || + !FsvolDeviceExtension->VolumeParams.PostCleanupWhenModifiedOnly) /* * Note that it is still possible for this request to not be delivered, * if the volume device Ioq is stopped. But such failures are benign diff --git a/src/sys/create.c b/src/sys/create.c index 998d4f38..5492e498 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -1025,6 +1025,10 @@ NTSTATUS FspFsvolCreateComplete( if (0 == FileNode->MainFileNode) FspFileNodeOverwriteStreams(FileNode); FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.Overwrite.FileInfo); + + if (0 != Response->Rsp.Overwrite.FileInfo.AllocationSize) + FileNode->TruncateOnClose = TRUE; + FspFileNodeNotifyChange(FileNode, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); @@ -1115,9 +1119,14 @@ static NTSTATUS FspFsvolCreateTryOpen(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Re } if (FILE_CREATED == Response->IoStatus.Information) + { + if (0 != Response->Rsp.Create.Opened.FileInfo.AllocationSize) + FileNode->TruncateOnClose = TRUE; + FspFileNodeNotifyChange(FileNode, FileNode->IsDirectory ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); + } FspFileNodeRelease(FileNode, Main); diff --git a/src/sys/driver.h b/src/sys/driver.h index 8bf50b32..0c24b8e8 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -1200,6 +1200,7 @@ typedef struct BOOLEAN CaseSensitive; BOOLEAN HasTraversePrivilege; BOOLEAN DeleteOnClose; + BOOLEAN DidSetLastAccessTime, DidSetLastWriteTime; BOOLEAN DirectoryHasSuchFile; UNICODE_STRING DirectoryPattern; UINT64 DirectoryOffset; @@ -1259,8 +1260,7 @@ VOID FspFileNodeReleaseForeign(FSP_FILE_NODE *FileNode) NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason); -VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - PBOOLEAN PDeletePending); +VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, /* non-0 to remove share access */ diff --git a/src/sys/file.c b/src/sys/file.c index 689f7dbb..ee704d3a 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -34,8 +34,7 @@ VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner) NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason); -VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - PBOOLEAN PDeletePending); +VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, /* non-0 to remove share access */ @@ -635,8 +634,7 @@ exit: return Result; } -VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - PBOOLEAN PDeletePending) +VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags) { /* * Determine whether a FileNode should be deleted. Note that when FileNode->DeletePending @@ -650,7 +648,7 @@ VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; FSP_FILE_DESC *FileDesc = FileObject->FsContext2; - BOOLEAN DeletePending, SingleHandle; + BOOLEAN DeletePending, SetAllocationSize, SingleHandle; FspFsvolDeviceLockContextTable(FsvolDeviceObject); @@ -659,12 +657,13 @@ VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, DeletePending = 0 != FileNode->DeletePending; MemoryBarrier(); + SetAllocationSize = !DeletePending && FileNode->TruncateOnClose; + SingleHandle = 1 == FileNode->HandleCount; FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); - if (0 != PDeletePending) - *PDeletePending = SingleHandle && DeletePending; + *PCleanupFlags = SingleHandle ? DeletePending | (SetAllocationSize << 1) : 0; } VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject) @@ -684,7 +683,7 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject PAGED_CODE(); PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; - LARGE_INTEGER TruncateSize = { 0 }, *PTruncateSize = 0; + LARGE_INTEGER TruncateSize, *PTruncateSize = 0; BOOLEAN DeletePending; BOOLEAN DeletedFromContextTable = FALSE; @@ -715,23 +714,34 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject if (DeletePending) { - PTruncateSize = &TruncateSize; - FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName, &DeletedFromContextTable); ASSERT(DeletedFromContextTable); FileNode->OpenCount = 0; + FileNode->Header.FileSize.QuadPart = 0; } - else if (FileNode->TruncateOnClose && FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED)) + + if (DeletePending || FileNode->TruncateOnClose) { + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = + FspFsvolDeviceExtension(FsvolDeviceObject); + UINT64 AllocationUnit = + FsvolDeviceExtension->VolumeParams.SectorSize * + FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit; + /* * Even when the FileInfo is expired, this is the best guess for a file size * without asking the user-mode file system. */ TruncateSize = FileNode->Header.FileSize; PTruncateSize = &TruncateSize; + + FileNode->Header.AllocationSize.QuadPart = (TruncateSize.QuadPart + AllocationUnit - 1) + / AllocationUnit * AllocationUnit; } + + FileNode->TruncateOnClose = FALSE; } FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 31100394..3482c62d 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -956,8 +956,11 @@ static NTSTATUS FspFsvolSetBasicInformation(PFILE_OBJECT FileObject, else { FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; ULONG NotifyFilter = 0; + ASSERT(FileNode == FileDesc->FileNode); + if (!FileNode->IsDirectory) { /* properly set temporary bit for lazy writer */ @@ -975,9 +978,15 @@ static NTSTATUS FspFsvolSetBasicInformation(PFILE_OBJECT FileObject, if (0 != Request->Req.SetInformation.Info.Basic.CreationTime) NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; if (0 != Request->Req.SetInformation.Info.Basic.LastAccessTime) + { + FileDesc->DidSetLastAccessTime = TRUE; NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + } if (0 != Request->Req.SetInformation.Info.Basic.LastWriteTime) + { + FileDesc->DidSetLastWriteTime = TRUE; NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + } FspFileNodeNotifyChange(FileNode, NotifyFilter, FILE_ACTION_MODIFIED); } diff --git a/src/sys/write.c b/src/sys/write.c index 00314c6e..854ce62a 100644 --- a/src/sys/write.c +++ b/src/sys/write.c @@ -195,6 +195,7 @@ static NTSTATUS FspFsvolWriteCached( { ASSERT(CanWait); + /* send EndOfFileInformation IRP; this will also set TruncateOnClose, etc. */ EndOfFileInformation.EndOfFile.QuadPart = WriteEndOffset; Result = FspSendSetInformationIrp(FsvolDeviceObject/* bypass filters */, FileObject, FileEndOfFileInformation, &EndOfFileInformation, sizeof EndOfFileInformation); @@ -471,11 +472,14 @@ NTSTATUS FspFsvolWriteComplete( /* update file info */ FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.Write.FileInfo); - if (OriginalFileSize != Response->Rsp.Write.FileInfo.FileSize) + if (!PagingIo && OriginalFileSize != Response->Rsp.Write.FileInfo.FileSize) + { + FileNode->TruncateOnClose = TRUE; FspFileNodeNotifyChange(FileNode, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); + } /* update the current file offset if synchronous I/O (and not paging I/O) */ - if (SynchronousIo && !PagingIo) + if (!PagingIo && SynchronousIo) FileObject->CurrentByteOffset.QuadPart = WriteToEndOfFile ? Response->Rsp.Write.FileInfo.FileSize : WriteOffset.QuadPart + Response->IoStatus.Information; diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index 91effce8..f5dca035 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -627,9 +627,8 @@ static NTSTATUS GetReparsePointByName( PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize); #endif -static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, - PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize, - FSP_FSCTL_FILE_INFO *FileInfo); +static NTSTATUS SetFileSizeInternal(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize); static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo) @@ -888,21 +887,6 @@ static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, return Result; } - /* - * NTFS and FastFat do this at Cleanup time, but we are going to cheat. - * - * To properly implement this we should maintain some state of whether - * we modified the file or not. Alternatively we could have the driver - * report to us at Cleanup time whether the file was modified. [The - * FSD does maintain the FO_FILE_MODIFIED bit, but does not send it - * to us.] - * - * TBD. - */ - if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && - (GrantedAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))) - FileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; - MemfsFileNodeReference(FileNode); *PFileNode = FileNode; MemfsFileNodeGetFileInfo(FileNode, FileInfo); @@ -944,7 +928,7 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, MemfsFileNodeMapEnumerateFree(&Context); #endif - Result = SetFileSize(FileSystem, FileNode, AllocationSize, TRUE, FileInfo); + Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); if (!NT_SUCCESS(Result)) return Result; @@ -954,8 +938,9 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, FileNode->FileInfo.FileAttributes |= FileAttributes | FILE_ATTRIBUTE_ARCHIVE; FileNode->FileInfo.FileSize = 0; + FileNode->FileInfo.LastAccessTime = FileNode->FileInfo.LastWriteTime = - FileNode->FileInfo.LastAccessTime = MemfsGetSystemTime(); + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); MemfsFileNodeGetFileInfo(FileNode, FileInfo); @@ -963,14 +948,41 @@ static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, } static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem, - PVOID FileNode0, PWSTR FileName, BOOLEAN Delete) + PVOID FileNode0, PWSTR FileName, ULONG Flags) { MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; - assert(Delete); /* the new FSP_FSCTL_VOLUME_PARAMS::PostCleanupOnDeleteOnly ensures this */ + assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */ - if (Delete && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) + if (Flags & FspCleanupSetArchiveBit) + { + if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + FileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + } + + if (Flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime | FspCleanupSetChangeTime)) + { + UINT64 SystemTime = MemfsGetSystemTime(); + + if (Flags & FspCleanupSetLastAccessTime) + FileNode->FileInfo.LastAccessTime = SystemTime; + if (Flags & FspCleanupSetLastWriteTime) + FileNode->FileInfo.LastWriteTime = SystemTime; + if (Flags & FspCleanupSetChangeTime) + FileNode->FileInfo.ChangeTime = SystemTime; + } + + if (Flags & FspCleanupSetAllocationSize) + { + UINT64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + UINT64 AllocationSize = (FileNode->FileInfo.FileSize + AllocationUnit - 1) / + AllocationUnit * AllocationUnit; + + SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); + } + + if ((Flags & FspCleanupDelete) && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) { #if defined(MEMFS_NAMED_STREAMS) MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE }; @@ -1062,7 +1074,7 @@ static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, EndOffset = Offset + Length; if (EndOffset > FileNode->FileInfo.FileSize) { - Result = SetFileSize(FileSystem, FileNode, EndOffset, FALSE, FileInfo); + Result = SetFileSizeInternal(FileSystem, FileNode, EndOffset, FALSE); if (!NT_SUCCESS(Result)) return Result; } @@ -1077,9 +1089,19 @@ static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, } NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem, - PVOID FileNode) + PVOID FileNode0) { - /* nothing to do, since we do not cache anything */ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + /* nothing to flush, since we do not cache anything */ + + if (0 != FileNode) + { + FileNode->FileInfo.LastAccessTime = + FileNode->FileInfo.LastWriteTime = + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + } + return STATUS_SUCCESS; } @@ -1120,9 +1142,8 @@ static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, return STATUS_SUCCESS; } -static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, - PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize, - FSP_FSCTL_FILE_INFO *FileInfo) +static NTSTATUS SetFileSizeInternal(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize) { MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; @@ -1154,8 +1175,7 @@ static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, UINT64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; UINT64 AllocationSize = (NewSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; - NTSTATUS Result = SetFileSize(FileSystem, FileNode, AllocationSize, TRUE, - FileInfo); + NTSTATUS Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); if (!NT_SUCCESS(Result)) return Result; } @@ -1167,6 +1187,24 @@ static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, } } + return STATUS_SUCCESS; +} + +static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + + Result = SetFileSizeInternal(FileSystem, FileNode0, NewSize, SetAllocationSize); + if (!NT_SUCCESS(Result)) + return Result; + + FileNode->FileInfo.LastAccessTime = + FileNode->FileInfo.LastWriteTime = + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + MemfsFileNodeGetFileInfo(FileNode, FileInfo); return STATUS_SUCCESS; @@ -1319,6 +1357,8 @@ static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem, FileNode->FileSecuritySize = FileSecuritySize; FileNode->FileSecurity = FileSecurity; + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + return STATUS_SUCCESS; } @@ -1523,6 +1563,8 @@ static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, FileNode->ReparseData = ReparseData; memcpy(FileNode->ReparseData, Buffer, Size); + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + return STATUS_SUCCESS; } @@ -1556,6 +1598,8 @@ static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, FileNode->ReparseDataSize = 0; FileNode->ReparseData = 0; + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + return STATUS_SUCCESS; } #endif @@ -1737,7 +1781,7 @@ NTSTATUS MemfsCreateFunnel( #if defined(MEMFS_NAMED_STREAMS) VolumeParams.NamedStreams = 1; #endif - VolumeParams.PostCleanupOnDeleteOnly = 1; + VolumeParams.PostCleanupWhenModifiedOnly = 1; if (0 != VolumePrefix) wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), diff --git a/tst/winfsp-tests/create-test.c b/tst/winfsp-tests/create-test.c index 3ee3d25d..da403ad8 100644 --- a/tst/winfsp-tests/create-test.c +++ b/tst/winfsp-tests/create-test.c @@ -288,9 +288,9 @@ void create_related_test(void) create_related_dotest(MemfsNet, L"\\\\memfs\\share"); } -void create_allocation_dotest(ULONG Flags, PWSTR Prefix) +void create_allocation_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) { - void *memfs = memfs_start(Flags); + void *memfs = memfs_start_ex(Flags, FileInfoTimeout); HANDLE DirHandle, FileHandle, FileHandle2; NTSTATUS Result; @@ -346,10 +346,27 @@ void create_allocation_dotest(ULONG Flags, PWSTR Prefix) ASSERT(Success); ASSERT(65536 == StandardInfo.AllocationSize.QuadPart); + CloseHandle(FileHandle2); + + AllocationSize.QuadPart = 0; + UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR); + UnicodePath.MaximumLength = sizeof UnicodePathBuf; + UnicodePath.Buffer = UnicodePathBuf; + InitializeObjectAttributes(&Obja, &UnicodePath, 0, DirHandle, 0); + Result = NtCreateFile(&FileHandle2, + FILE_READ_ATTRIBUTES, &Obja, &Iosb, + &AllocationSize, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, 0, 0, 0); + ASSERT(STATUS_SUCCESS == Result); + + Success = GetFileInformationByHandleEx(FileHandle2, FileStandardInfo, &StandardInfo, sizeof StandardInfo); + ASSERT(Success); + ASSERT(65536 == StandardInfo.AllocationSize.QuadPart); + CloseHandle(FileHandle2); CloseHandle(FileHandle); -#if 0 AllocationSize.QuadPart = 0; UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR); UnicodePath.MaximumLength = sizeof UnicodePathBuf; @@ -366,7 +383,6 @@ void create_allocation_dotest(ULONG Flags, PWSTR Prefix) ASSERT(0 == StandardInfo.AllocationSize.QuadPart); CloseHandle(FileHandle); -#endif StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2", Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); @@ -388,12 +404,20 @@ void create_allocation_test(void) { WCHAR DirBuf[MAX_PATH]; GetTestDirectory(DirBuf); - create_allocation_dotest(-1, DirBuf); + create_allocation_dotest(-1, DirBuf, 0); } if (WinFspDiskTests) - create_allocation_dotest(MemfsDisk, 0); + { + create_allocation_dotest(MemfsDisk, 0, 0); + create_allocation_dotest(MemfsDisk, 0, 1000); + create_allocation_dotest(MemfsDisk, 0, INFINITE); + } if (WinFspNetTests) - create_allocation_dotest(MemfsNet, L"\\\\memfs\\share"); + { + create_allocation_dotest(MemfsNet, L"\\\\memfs\\share", 0); + create_allocation_dotest(MemfsNet, L"\\\\memfs\\share", 1000); + create_allocation_dotest(MemfsNet, L"\\\\memfs\\share", INFINITE); + } } void create_sd_dotest(ULONG Flags, PWSTR Prefix)