diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 57c17f75..5e6451d3 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -300,6 +300,7 @@ typedef struct UINT64 CreationTime; UINT64 LastAccessTime; UINT64 LastWriteTime; + UINT64 ChangeTime; } Basic; struct { diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index a14407d7..ea4d9f0a 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -473,6 +473,9 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * @param LastWriteTime * Last write time to apply to the file or directory. If the value 0 is sent, the last * write time should not be changed. + * @param ChangeTime + * Change time to apply to the file or directory. If the value 0 is sent, the change time + * should not be changed. * @param FileInfo [out] * Pointer to a structure that will receive the file information on successful return * from this call. This information includes file attributes, file times, etc. @@ -481,7 +484,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE */ NTSTATUS (*SetBasicInfo)(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext, UINT32 FileAttributes, - UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, FSP_FSCTL_FILE_INFO *FileInfo); /** * Set file/allocation size. diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 7807f428..c93e6cfb 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -999,6 +999,7 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, Request->Req.SetInformation.Info.Basic.CreationTime, Request->Req.SetInformation.Info.Basic.LastAccessTime, Request->Req.SetInformation.Info.Basic.LastWriteTime, + Request->Req.SetInformation.Info.Basic.ChangeTime, &FileInfo); break; case 19/*FileAllocationInformation*/: diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index f1aed4f0..6a6c8083 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -1272,7 +1272,7 @@ static NTSTATUS fsp_fuse_intf_GetFileInfo(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS fsp_fuse_intf_SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID FileNode, UINT32 FileAttributes, - UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, FSP_FSCTL_FILE_INFO *FileInfo) { struct fuse *f = FileSystem->UserContext; diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index 5d31fea5..adb4e6d0 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -111,10 +111,11 @@ static NTSTATUS FspFsvolCleanup( Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2; Request->Req.Cleanup.Delete = DeletePending; Request->Req.Cleanup.SetAllocationSize = SetAllocationSize; - Request->Req.Cleanup.SetArchiveBit = FileModified; + Request->Req.Cleanup.SetArchiveBit = FileModified && !FileDesc->DidSetFileAttributes; Request->Req.Cleanup.SetLastAccessTime = !FileDesc->DidSetLastAccessTime; Request->Req.Cleanup.SetLastWriteTime = FileModified && !FileDesc->DidSetLastWriteTime; - Request->Req.Cleanup.SetChangeTime = FileModified; + Request->Req.Cleanup.SetChangeTime = (FileModified || FileDesc->DidSetBasicInfo) && + !FileDesc->DidSetChangeTime; FspFileNodeAcquireExclusive(FileNode, Pgio); @@ -136,9 +137,9 @@ static NTSTATUS FspFsvolCleanup( return FSP_STATUS_IOQ_POST_BEST_EFFORT; else { - /* if the file is being resized invalidate the volume info */ - if (FileNode->TruncateOnClose) - FspFsvolDeviceInvalidateVolumeInfo(IrpSp->DeviceObject); + if (FileDesc->DidSetBasicInfo) + /* invalidate the parent dir info */ + FspFileNodeInvalidateParentDirInfo(FileNode); return STATUS_SUCCESS; /* FspFsvolCleanupRequestFini will take care of the rest! */ } @@ -155,12 +156,22 @@ NTSTATUS FspFsvolCleanupComplete( /* if the file is being deleted do a change notification */ if (Request->Req.Cleanup.Delete) + { FspFileNodeNotifyChange(FileNode, FileNode->IsDirectory ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); - /* if the file is being resized invalidate the volume info */ - else if (FileNode->TruncateOnClose) - FspFsvolDeviceInvalidateVolumeInfo(IrpSp->DeviceObject); + + /* FspFileNodeNotifyChange also invalidates parent dir and volume info */ + } + else + { + /* invalidate the parent dir info */ + FspFileNodeInvalidateParentDirInfo(FileNode); + + /* if the file is being resized invalidate the volume info */ + if (FileNode->TruncateOnClose) + FspFsvolDeviceInvalidateVolumeInfo(IrpSp->DeviceObject); + } FSP_LEAVE_IOC("FileObject=%p", IrpSp->FileObject); } diff --git a/src/sys/driver.h b/src/sys/driver.h index 8b129479..605e101f 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -1197,11 +1197,12 @@ typedef struct { FSP_FILE_NODE *FileNode; UINT64 UserContext2; - BOOLEAN CaseSensitive; - BOOLEAN HasTraversePrivilege; - BOOLEAN DeleteOnClose; - BOOLEAN DidSetLastAccessTime, DidSetLastWriteTime; - BOOLEAN DirectoryHasSuchFile; + UINT32 + CaseSensitive:1, HasTraversePrivilege:1, DeleteOnClose:1, + DidSetBasicInfo:1, + DidSetFileAttributes:1, + DidSetCreationTime:1, DidSetLastAccessTime:1, DidSetLastWriteTime:1, DidSetChangeTime:1, + DirectoryHasSuchFile:1; UNICODE_STRING DirectoryPattern; UINT64 DirectoryOffset; UINT64 DirInfo; @@ -1313,6 +1314,7 @@ ULONG FspFileNodeDirInfoChangeNumber(FSP_FILE_NODE *FileNode) { return FileNode->DirInfoChangeNumber; } +VOID FspFileNodeInvalidateParentDirInfo(FSP_FILE_NODE *FileNode); BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, diff --git a/src/sys/file.c b/src/sys/file.c index d35ec6a5..1f290c22 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -67,6 +67,9 @@ VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG DirInfoChangeNumber); static VOID FspFileNodeInvalidateDirInfo(FSP_FILE_NODE *FileNode); +static VOID FspFileNodeInvalidateDirInfoByName(PDEVICE_OBJECT FsvolDeviceObject, + PUNICODE_STRING FileName); +VOID FspFileNodeInvalidateParentDirInfo(FSP_FILE_NODE *FileNode); BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, @@ -128,6 +131,8 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp); // !#pragma alloc_text(PAGE, FspFileNodeSetDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeInvalidateDirInfo) +#pragma alloc_text(PAGE, FspFileNodeInvalidateDirInfoByName) +#pragma alloc_text(PAGE, FspFileNodeInvalidateParentDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeReferenceStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo) @@ -1649,6 +1654,40 @@ static VOID FspFileNodeInvalidateDirInfo(FSP_FILE_NODE *FileNode) FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, DirInfo); } +static VOID FspFileNodeInvalidateDirInfoByName(PDEVICE_OBJECT FsvolDeviceObject, + PUNICODE_STRING FileName) +{ + PAGED_CODE(); + + FSP_FILE_NODE *FileNode; + + FspFsvolDeviceLockContextTable(FsvolDeviceObject); + FileNode = FspFsvolDeviceLookupContextByName(FsvolDeviceObject, FileName); + if (0 != FileNode) + FspFileNodeReference(FileNode); + FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); + + if (0 != FileNode) + { + FspFileNodeInvalidateDirInfo(FileNode); + FspFileNodeDereference(FileNode); + } +} + +VOID FspFileNodeInvalidateParentDirInfo(FSP_FILE_NODE *FileNode) +{ + PAGED_CODE(); + + if (sizeof(WCHAR) == FileNode->FileName.Length && L'\\' == FileNode->FileName.Buffer[0]) + return; /* root does not have a parent */ + + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + UNICODE_STRING Parent, Suffix; + + FspFileNameSuffix(&FileNode->FileName, &Parent, &Suffix); + FspFileNodeInvalidateDirInfoByName(FsvolDeviceObject, &Parent); +} + BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize) { // !PAGED_CODE(); @@ -1738,7 +1777,6 @@ VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); UNICODE_STRING Parent, Suffix; - FSP_FILE_NODE *ParentNode; FspFileNameSuffix(&FileNode->FileName, &Parent, &Suffix); @@ -1746,22 +1784,10 @@ VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, { case FILE_ACTION_ADDED: case FILE_ACTION_REMOVED: - //case FILE_ACTION_MODIFIED: case FILE_ACTION_RENAMED_OLD_NAME: case FILE_ACTION_RENAMED_NEW_NAME: FspFsvolDeviceInvalidateVolumeInfo(FsvolDeviceObject); - - FspFsvolDeviceLockContextTable(FsvolDeviceObject); - ParentNode = FspFsvolDeviceLookupContextByName(FsvolDeviceObject, &Parent); - if (0 != ParentNode) - FspFileNodeReference(ParentNode); - FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); - - if (0 != ParentNode) - { - FspFileNodeInvalidateDirInfo(ParentNode); - FspFileNodeDereference(ParentNode); - } + FspFileNodeInvalidateDirInfoByName(FsvolDeviceObject, &Parent); break; } diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 6ee7bf0e..21bfd6fb 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -951,6 +951,7 @@ static NTSTATUS FspFsvolSetBasicInformation(PFILE_OBJECT FileObject, Request->Req.SetInformation.Info.Basic.CreationTime = Info->CreationTime.QuadPart; Request->Req.SetInformation.Info.Basic.LastAccessTime = Info->LastAccessTime.QuadPart; Request->Req.SetInformation.Info.Basic.LastWriteTime = Info->LastWriteTime.QuadPart; + Request->Req.SetInformation.Info.Basic.ChangeTime = Info->ChangeTime.QuadPart; } else { @@ -973,9 +974,15 @@ static NTSTATUS FspFsvolSetBasicInformation(PFILE_OBJECT FileObject, FspFileNodeSetFileInfo(FileNode, FileObject, &Response->Rsp.SetInformation.FileInfo, FALSE); if ((UINT32)-1 != Request->Req.SetInformation.Info.Basic.FileAttributes) + { + FileDesc->DidSetFileAttributes = TRUE; NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; + } if (0 != Request->Req.SetInformation.Info.Basic.CreationTime) + { + FileDesc->DidSetCreationTime = TRUE; NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; + } if (0 != Request->Req.SetInformation.Info.Basic.LastAccessTime) { FileDesc->DidSetLastAccessTime = TRUE; @@ -986,6 +993,10 @@ static NTSTATUS FspFsvolSetBasicInformation(PFILE_OBJECT FileObject, FileDesc->DidSetLastWriteTime = TRUE; NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; } + if (0 != Request->Req.SetInformation.Info.Basic.ChangeTime) + FileDesc->DidSetChangeTime = TRUE; + + FileDesc->DidSetBasicInfo = TRUE; FspFileNodeNotifyChange(FileNode, NotifyFilter, FILE_ACTION_MODIFIED); } diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index f5dca035..3ad8e012 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -1118,7 +1118,7 @@ static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID FileNode0, UINT32 FileAttributes, - UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, FSP_FSCTL_FILE_INFO *FileInfo) { MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; @@ -1136,6 +1136,8 @@ static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, FileNode->FileInfo.LastAccessTime = LastAccessTime; if (0 != LastWriteTime) FileNode->FileInfo.LastWriteTime = LastWriteTime; + if (0 != ChangeTime) + FileNode->FileInfo.ChangeTime = ChangeTime; MemfsFileNodeGetFileInfo(FileNode, FileInfo);