sys, dll: implement POSIX semantics for Rename

This commit is contained in:
Bill Zissimopoulos
2021-10-21 15:39:25 +01:00
parent 76bfa395a8
commit bb3e92df6c
8 changed files with 249 additions and 20 deletions

View File

@ -383,7 +383,10 @@ NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
Request->Req.SetInformation.Info.Rename.NewFileName.Size;
CreateRequest->Kind = FspFsctlTransactCreateKind;
CreateRequest->Req.Create.CreateOptions =
FILE_DELETE_ON_CLOSE | /* force read-only check! */
(65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
0 != (0x40/*IGNORE_READONLY_ATTRIBUTE*/ & Request->Req.SetInformation.Info.RenameEx.Flags) ?
0 :
FILE_DELETE_ON_CLOSE) | /* force read-only check! */
FILE_OPEN_REPARSE_POINT; /* allow rename over reparse point */
CreateRequest->Req.Create.AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
CreateRequest->Req.Create.UserMode = TRUE;

View File

@ -141,7 +141,8 @@ NTSTATUS fsp_fuse_op_enter(FSP_FILE_SYSTEM *FileSystem,
AccessToken = Request->Req.Create.AccessToken;
}
else if (FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass)
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass))
{
FileName = (PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset);
AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;

View File

@ -1577,7 +1577,8 @@ NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
PUNICODE_STRING StreamFileName);
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
PUNICODE_STRING FileName, BOOLEAN CheckingOldName);
PUNICODE_STRING FileName, BOOLEAN CheckingOldName,
BOOLEAN PosixRename);
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);

View File

@ -58,7 +58,8 @@ NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
PUNICODE_STRING StreamFileName);
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
PUNICODE_STRING FileName, BOOLEAN CheckingOldName);
PUNICODE_STRING FileName, BOOLEAN CheckingOldName,
BOOLEAN PosixRename);
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
@ -1355,7 +1356,8 @@ NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
PUNICODE_STRING FileName, BOOLEAN CheckingOldName)
PUNICODE_STRING FileName, BOOLEAN CheckingOldName,
BOOLEAN PosixRename)
{
PAGED_CODE();
@ -1389,7 +1391,7 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
* "rename" resource exclusively, which disallows new Opens.
*/
if (!CheckingOldName)
if (!PosixRename && !CheckingOldName)
{
/* replaced file cannot be a directory or mapped as an image */
for (
@ -1568,11 +1570,32 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
if (DescendantFileNode != FileNode && 0 < DescendantFileNode->HandleCount)
{
/*
* If we are doing a POSIX rename, then it is ok if we have open handles,
* provided that we do not have sharing violations.
*
* Check our share access:
*
* - If all openers are allowing FILE_SHARE_DELETE.
* - And all named streams openers are allowing FILE_SHARE_DELETE.
*
* Then we are good to go.
*
* (WinFsp cannot rename streams and there is no need to check MainFileDenyDeleteCount).
*
* NOTE: These are derived rules. AFAIK there is no documentation on how NTFS
* does this in the case of POSIX rename.
*/
if (PosixRename &&
DescendantFileNode->ShareAccess.OpenCount == DescendantFileNode->ShareAccess.SharedDelete &&
0 == DescendantFileNode->StreamDenyDeleteCount)
continue;
/* release the FileNode and rename lock in case of failure! */
FspFileNodeReleaseF(FileNode, AcquireFlags);
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
Result = STATUS_ACCESS_DENIED;
Result = PosixRename ? STATUS_SHARING_VIOLATION : STATUS_ACCESS_DENIED;
break;
}
}
@ -1661,14 +1684,14 @@ VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName)
if (!Inserted)
{
/*
* Handle files that have been Cleanup'ed but not Close'd.
* Handle files that have been replaced after a Rename.
* For example, this can happen when the user has mapped and closed a file
* or immediately after breaking a Batch oplock.
* or immediately after breaking a Batch oplock or
* when doing a POSIX rename.
*/
ASSERT(FspFileNodeIsValid(InsertedFileNode));
ASSERT(DescendantFileNode != InsertedFileNode);
ASSERT(0 == InsertedFileNode->HandleCount);
ASSERT(0 != InsertedFileNode->OpenCount);
InsertedFileNode->OpenCount = 0;

View File

@ -1639,7 +1639,9 @@ static NTSTATUS FspFsvolSetRenameInformation(
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PFILE_OBJECT FileObject = IrpSp->FileObject;
PFILE_OBJECT TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
BOOLEAN ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
UINT32 RenameFlags = !!ReplaceIfExists;
PFILE_RENAME_INFORMATION Info = (PFILE_RENAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
ULONG Length = IrpSp->Parameters.SetFile.Length;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
@ -1654,11 +1656,21 @@ static NTSTATUS FspFsvolSetRenameInformation(
PSECURITY_SUBJECT_CONTEXT SecuritySubjectContext = 0;
ASSERT(FileNode == FileDesc->FileNode);
ASSERT(
FileRenameInformation == FileInformationClass ||
FileRenameInformationEx == FileInformationClass);
if (sizeof(FILE_RENAME_INFORMATION) > Length)
return STATUS_INVALID_PARAMETER;
if (sizeof(WCHAR) > Info->FileNameLength)
return STATUS_INVALID_PARAMETER;
if (FileRenameInformationEx == FileInformationClass)
{
if (!FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.SupportsPosixUnlinkRename)
return STATUS_INVALID_PARAMETER;
RenameFlags |= Info->Flags &
(FILE_RENAME_POSIX_SEMANTICS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE);
}
if (FileNode->IsRootDirectory)
/* cannot rename root directory */
return STATUS_INVALID_PARAMETER;
@ -1736,13 +1748,14 @@ retry:
Request->Kind = FspFsctlTransactSetInformationKind;
Request->Req.SetInformation.UserContext = FileNode->UserContext;
Request->Req.SetInformation.UserContext2 = FileDesc->UserContext2;
Request->Req.SetInformation.FileInformationClass = FileRenameInformation;
Request->Req.SetInformation.FileInformationClass = FileInformationClass;
Request->Req.SetInformation.Info.Rename.NewFileName.Offset = Request->FileName.Size;
Request->Req.SetInformation.Info.Rename.NewFileName.Size = NewFileName.Length + sizeof(WCHAR);
Request->Req.SetInformation.Info.RenameEx.Flags = RenameFlags;
}
/*
* Special rules for renaming open files:
* Special rules for renaming open files without POSIX semantics:
* - A file cannot be renamed if it has any open handles,
* unless it is only open because of a batch opportunistic lock (oplock)
* and the batch oplock can be broken immediately.
@ -1754,13 +1767,15 @@ retry:
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
FileNode, FspFileNodeAcquireFull,
&FileNode->FileName, TRUE);
&FileNode->FileName, TRUE,
0 != (FILE_RENAME_POSIX_SEMANTICS & RenameFlags));
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
goto retry;
if (!NT_SUCCESS(Result))
{
Result = STATUS_ACCESS_DENIED;
if (STATUS_SHARING_VIOLATION != Result)
Result = STATUS_ACCESS_DENIED;
goto exit;
}
@ -1768,13 +1783,15 @@ retry:
{
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
FileNode, FspFileNodeAcquireFull,
&NewFileName, FALSE);
&NewFileName, FALSE,
0 != (FILE_RENAME_POSIX_SEMANTICS & RenameFlags));
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
goto retry;
if (!NT_SUCCESS(Result))
{
Result = STATUS_ACCESS_DENIED;
if (STATUS_SHARING_VIOLATION != Result)
Result = STATUS_ACCESS_DENIED;
goto exit;
}
}
@ -1881,7 +1898,7 @@ static NTSTATUS FspFsvolSetInformation(
case FileDispositionInformationEx:
return FspFsvolSetDispositionInformation(FsvolDeviceObject, Irp, IrpSp);
case FileRenameInformation:
//case FileRenameInformationEx:
case FileRenameInformationEx:
return FspFsvolSetRenameInformation(FsvolDeviceObject, Irp, IrpSp);
}
@ -2008,8 +2025,10 @@ NTSTATUS FspFsvolSetInformationPrepare(
PAGED_CODE();
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
if (FileRenameInformation != IrpSp->Parameters.SetFile.FileInformationClass ||
if ((FileRenameInformation != FileInformationClass &&
FileRenameInformationEx != FileInformationClass) ||
0 == FspIopRequestContext(Request, RequestSubjectContextOrAccessToken))
return STATUS_SUCCESS;
@ -2087,7 +2106,7 @@ NTSTATUS FspFsvolSetInformationComplete(
case FileDispositionInformationEx:
FSP_RETURN(Result = FspFsvolSetDispositionInformationSuccess(Irp, Response));
case FileRenameInformation:
//case FileRenameInformationEx:
case FileRenameInformationEx:
FSP_RETURN(Result = FspFsvolSetRenameInformationSuccess(Irp, Response));
}