sys,dll: reparse point implementation: DeleteReparsePoint

This commit is contained in:
Bill Zissimopoulos
2016-08-26 07:43:25 -07:00
parent b88b2ec51d
commit 80e07cead6
6 changed files with 297 additions and 73 deletions

View File

@ -954,12 +954,15 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
if (FileSystem->ReparsePointsSymlinkOnly)
{
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
SetReparseData->SymbolicLinkReparseBuffer.PathBuffer +
SetReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
SetReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength);
if (IO_REPARSE_TAG_SYMLINK == *(PULONG)SetReparseData)
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
SetReparseData->SymbolicLinkReparseBuffer.PathBuffer +
SetReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
SetReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength);
else
Result = STATUS_IO_REPARSE_TAG_MISMATCH;
}
else
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
@ -970,58 +973,27 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
}
break;
case FSCTL_DELETE_REPARSE_POINT:
if (0 != FileSystem->Interface->GetReparsePoint &&
0 != FileSystem->Interface->SetReparsePoint)
if (0 != FileSystem->Interface->DeleteReparsePoint)
{
GetReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer;
memset(GetReparseData, 0, sizeof *GetReparseData);
SetReparseData = (PREPARSE_DATA_BUFFER)
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
if (FileSystem->ReparsePointsSymlinkOnly)
{
GetReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK;
Result = STATUS_SUCCESS;
if (IO_REPARSE_TAG_SYMLINK == *(PULONG)SetReparseData)
Result = FileSystem->Interface->DeleteReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
0, 0);
else
Result = STATUS_IO_REPARSE_TAG_MISMATCH;
}
else
{
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer);
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
Result = FileSystem->Interface->DeleteReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer, GetReparseData, &Size);
}
if (NT_SUCCESS(Result))
{
SetReparseData = (PREPARSE_DATA_BUFFER)
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
if (GetReparseData->ReparseTag != SetReparseData->ReparseTag)
Result = STATUS_IO_REPARSE_TAG_MISMATCH;
else if (!IsReparseTagMicrosoft(SetReparseData->ReparseTag) && (
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data1 !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data1 ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data2 !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data2 ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data3 !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data3 ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data4[0] !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data4[0] ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data4[1] !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data4[1] ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data4[2] !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data4[2] ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data4[3] !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data4[3] ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data4[4] !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data4[4] ||
((PREPARSE_GUID_DATA_BUFFER)GetReparseData)->ReparseGuid.Data4[5] !=
((PREPARSE_GUID_DATA_BUFFER)SetReparseData)->ReparseGuid.Data4[5]))
Result = STATUS_REPARSE_ATTRIBUTE_CONFLICT;
if (NT_SUCCESS(Result))
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer, 0, 0);
}
(PWSTR)Request->Buffer,
SetReparseData,
Request->Req.FileSystemControl.Buffer.Size);
}
break;
}
@ -1231,8 +1203,7 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
continue;
else if (!NT_SUCCESS(Result))
{
if ((STATUS_OBJECT_NAME_NOT_FOUND == Result && '\0' != c) ||
STATUS_NOT_A_DIRECTORY == Result)
if (STATUS_OBJECT_NAME_NOT_FOUND != Result || '\0' != c)
Result = STATUS_OBJECT_PATH_NOT_FOUND;
return Result;
}
@ -1301,3 +1272,38 @@ no_symlink_exit:
PIoStatus->Information = ReparseData->ReparseTag;
return STATUS_REPARSE;
}
FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint(
PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize,
PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize)
{
if (sizeof(ULONG) > CurrentReparseDataSize ||
sizeof(ULONG) > ReplaceReparseDataSize)
return STATUS_IO_REPARSE_DATA_INVALID; /* should not happen! */
else if (*(PULONG)CurrentReparseData != *(PULONG)ReplaceReparseData)
return STATUS_IO_REPARSE_TAG_MISMATCH;
else if (!IsReparseTagMicrosoft(*(PULONG)CurrentReparseData) && (
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE > CurrentReparseDataSize ||
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE > ReplaceReparseDataSize ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data1 !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data1 ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data2 !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data2 ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data3 !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data3 ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[0] !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[0] ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[1] !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[1] ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[2] !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[2] ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[3] !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[3] ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[4] !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[4] ||
((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[5] !=
((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[5]))
return STATUS_REPARSE_ATTRIBUTE_CONFLICT;
else
return STATUS_SUCCESS;
}

View File

@ -183,6 +183,86 @@ NTSTATUS fsp_fuse_op_leave(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS;
}
static NTSTATUS fsp_fuse_intf_NewHiddenName(FSP_FILE_SYSTEM *FileSystem,
char *PosixPath, char **PPosixHiddenPath)
{
struct fuse *f = FileSystem->UserContext;
NTSTATUS Result;
char *PosixHiddenPath = 0;
char *p, *lastp;
size_t Size;
RPC_STATUS RpcStatus;
union
{
struct { UINT32 V[4]; } Values;
UUID Uuid;
} UuidBuf;
struct fuse_stat stbuf;
int err, maxtries = 3;
*PPosixHiddenPath = 0;
p = PosixPath;
for (;;)
{
while ('/' == *p)
p++;
lastp = p;
while ('/' != *p)
{
if ('\0' == *p)
goto loopend;
p++;
}
}
loopend:;
Size = lastp - PosixPath + sizeof ".fuse_hidden0123456789abcdef";
PosixHiddenPath = MemAlloc(Size);
if (0 == PosixHiddenPath)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
memcpy(PosixHiddenPath, PosixPath, lastp - PosixPath);
do
{
RpcStatus = UuidCreate(&UuidBuf.Uuid);
if (S_OK != RpcStatus && RPC_S_UUID_LOCAL_ONLY != RpcStatus)
{
Result = STATUS_INTERNAL_ERROR;
goto exit;
}
wsprintfA(PosixHiddenPath + (lastp - PosixPath),
".fuse_hidden%08lx%08lx",
UuidBuf.Values.V[0] ^ UuidBuf.Values.V[2],
UuidBuf.Values.V[1] ^ UuidBuf.Values.V[3]);
memset(&stbuf, 0, sizeof stbuf);
if (0 != f->ops.getattr)
err = f->ops.getattr(PosixPath, (void *)&stbuf);
else
err = -ENOSYS;
} while (0 == err && 0 < --maxtries);
if (0 == err)
{
Result = STATUS_DEVICE_BUSY; // current EBUSY mapping
goto exit;
}
*PPosixHiddenPath = PosixHiddenPath;
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
MemFree(PosixHiddenPath);
return Result;
}
static NTSTATUS fsp_fuse_intf_GetFileInfoEx(FSP_FILE_SYSTEM *FileSystem,
const char *PosixPath, struct fuse_file_info *fi,
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode,
@ -321,7 +401,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(
return STATUS_INVALID_DEVICE_REQUEST;
err = f->ops.readlink(PosixPath, PosixTargetPath, sizeof PosixTargetPath);
if (EINVAL/* same on MSVC and Cygwin */ == err)
if (-EINVAL/* same on MSVC and Cygwin */ == err)
{
Result = STATUS_NOT_A_REPARSE_POINT;
goto exit;
@ -1653,16 +1733,19 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
struct fsp_fuse_file_desc *filedesc =
(PVOID)(UINT_PTR)Request->Req.FileSystemControl.UserContext2;
WCHAR TargetPath[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)];
char *PosixTargetPath = 0;
char *PosixTargetPath = 0, *PosixHiddenPath = 0;
int err;
NTSTATUS Result;
if (0 == f->ops.symlink)
if (0 == f->ops.symlink || 0 == f->ops.rename || 0 == f->ops.unlink)
return STATUS_INVALID_DEVICE_REQUEST;
/* were we asked to delete the reparse point? no can do! */
if (0 == Buffer)
return STATUS_ACCESS_DENIED;
/* FUSE cannot make a directory into a symlink */
if (filedesc->IsDirectory)
{
Result = STATUS_ACCESS_DENIED;
goto exit;
}
/* is this an absolute path? */
if (Size > 4 * sizeof(WCHAR) &&
@ -1691,22 +1774,64 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
if (!NT_SUCCESS(Result))
goto exit;
err = f->ops.symlink(PosixTargetPath, filedesc->PosixPath);
/*
* How we implement this is probably one of the worst aspects of this FUSE implementation.
*
* In Windows a CreateSymbolicLink is the sequence of the following:
* Create
* DeviceIoControl(FSCTL_SET_REPARSE_POINT)
* Cleanup
* Close
*
* The Create call creates the new file and the DeviceIoControl(FSCTL_SET_REPARSE_POINT)
* call is supposed to convert it into a reparse point. However FUSE symlink() will fail
* with -EEXIST in this case.
*
* We must therefore find a solution using rename, which is unreliable and error-prone.
* Note that this will also result in a change of the inode number for the symlink!
*/
Result = fsp_fuse_intf_NewHiddenName(FileSystem, filedesc->PosixPath, &PosixHiddenPath);
if (!NT_SUCCESS(Result))
goto exit;
err = f->ops.symlink(PosixTargetPath, PosixHiddenPath);
if (0 != err)
{
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
goto exit;
}
err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath);
if (0 != err)
{
/* on failure unlink "hidden" symlink */
f->ops.unlink(PosixHiddenPath);
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
goto exit;
}
Result = STATUS_SUCCESS;
exit:
MemFree(PosixHiddenPath);
if (0 != PosixTargetPath)
FspPosixDeletePath(PosixTargetPath);
return Result;
}
static NTSTATUS fsp_fuse_intf_DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, SIZE_T Size)
{
/* we were asked to delete the reparse point? no can do! */
return STATUS_ACCESS_DENIED;
}
FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
{
fsp_fuse_intf_GetVolumeInfo,
@ -1731,4 +1856,5 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
fsp_fuse_intf_ResolveReparsePoints,
fsp_fuse_intf_GetReparsePoint,
fsp_fuse_intf_SetReparsePoint,
fsp_fuse_intf_DeleteReparsePoint,
};