diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj index f0067639..d7e697da 100644 --- a/build/VStudio/winfsp_dll.vcxproj +++ b/build/VStudio/winfsp_dll.vcxproj @@ -182,7 +182,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor $(OutDir)$(TargetFileName).map false ..\..\src\dll\library.def - %(AdditionalDependencies);credui.lib;version.lib + %(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib @@ -207,7 +207,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor $(OutDir)$(TargetFileName).map true ..\..\src\dll\library.def - %(AdditionalDependencies);credui.lib;version.lib + %(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib @@ -235,7 +235,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor $(OutDir)$(TargetFileName).map false ..\..\src\dll\library.def - %(AdditionalDependencies);credui.lib;version.lib + %(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib @@ -263,7 +263,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor $(OutDir)$(TargetFileName).map true ..\..\src\dll\library.def - %(AdditionalDependencies);credui.lib;version.lib + %(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 0ca965d9..d8dfe239 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -766,12 +766,41 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE FSP_FSCTL_TRANSACT_REQ *Request, PVOID FileNode, PWSTR FileName, PVOID Buffer, SIZE_T Size); + /** + * Delete reparse point. + * + * The behavior of this function depends on the value of FSP_FSCTL_VOLUME_PARAMS :: + * ReparsePointsSymlinkOnly. If the value of ReparsePointsSymlinkOnly + * is FALSE the file system supports full reparse points and this function is expected + * to delete the reparse point contained in the buffer. If the value of + * ReparsePointsSymlinkOnly is TRUE the file system supports symbolic links only + * as reparse points and the Buffer and Size arguments will be NULL. + * + * @param FileSystem + * The file system on which this request is posted. + * @param Request + * The request posted by the kernel mode FSD. + * @param FileNode + * The file node of the reparse point. + * @param FileName + * The file name of the reparse point. + * @param Buffer + * Pointer to a buffer that contains the data for this operation. + * @param Size + * Size of data to write. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*DeleteReparsePoint)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode, + PWSTR FileName, PVOID Buffer, SIZE_T Size); /* * This ensures that this interface will always contain 64 function pointers. * Please update when changing the interface as it is important for future compatibility. */ - NTSTATUS (*Reserved[42])(); + NTSTATUS (*Reserved[41])(); } FSP_FILE_SYSTEM_INTERFACE; #if defined(WINFSP_DLL_INTERNAL) /* @@ -1126,6 +1155,29 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN OpenReparsePoint, PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize); +/** + * Test whether reparse data can be replaced. + * + * This is a helper for implementing the SetReparsePoint/DeleteReparsePoint operation + * in file systems that support reparse points. + * + * @param CurrentReparseData + * Pointer to the current reparse data. + * @param CurrentReparseDataSize + * Pointer to the current reparse data size. + * @param ReplaceReparseData + * Pointer to the replacement reparse data. + * @param ReplaceReparseDataSize + * Pointer to the replacement reparse data size. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetReparsePoint + * DeleteReparsePoint + */ +FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint( + PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize, + PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize); /* * Security diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 4805cb3f..8a35c9a6 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -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; +} diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index bf9a41ab..8d9d6406 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -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, }; diff --git a/src/sys/create.c b/src/sys/create.c index cb46ab9a..1f9f1a23 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -339,8 +339,6 @@ static NTSTATUS FspFsvolCreateNoLock( FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT); if (CreateOptions & FILE_DIRECTORY_FILE) SetFlag(FileAttributes, FILE_ATTRIBUTE_DIRECTORY); - if (CreateOptions & FILE_OPEN_REPARSE_POINT) - SetFlag(FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT); /* * The new request is associated with our IRP. Go ahead and associate our FileNode/FileDesc diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index f6d3173b..e83a7961 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -1007,25 +1007,66 @@ static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID FileNode0, PWSTR FileName, PVOID Buffer, SIZE_T Size) { + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; PVOID ReparseData; + NTSTATUS Result; - if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) - return STATUS_NOT_A_REPARSE_POINT; + if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) + return STATUS_DIRECTORY_NOT_EMPTY; + + if (0 != FileNode->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + FileNode->ReparseData, FileNode->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } ReparseData = realloc(FileNode->ReparseData, Size); - if (0 == ReparseData) + if (0 == ReparseData && 0 != Size) return STATUS_INSUFFICIENT_RESOURCES; - /* the first field in a reparse buffer is the reparse tag */ - FileNode->FileInfo.ReparseTag = 0 != Buffer ? *(PULONG)Buffer : 0; - + FileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + FileNode->FileInfo.ReparseTag = *(PULONG)Buffer; + /* the first field in a reparse buffer is the reparse tag */ FileNode->ReparseDataSize = Size; + FileNode->ReparseData = ReparseData; memcpy(FileNode->ReparseData, Buffer, Size); return STATUS_SUCCESS; } +static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PVOID FileNode0, + PWSTR FileName, PVOID Buffer, SIZE_T Size) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + + if (0 != FileNode->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + FileNode->ReparseData, FileNode->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + else + return STATUS_NOT_A_REPARSE_POINT; + + free(FileNode->ReparseData); + + FileNode->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; + FileNode->FileInfo.ReparseTag = 0; + FileNode->ReparseDataSize = 0; + FileNode->ReparseData = 0; + + return STATUS_SUCCESS; +} + static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = { GetVolumeInfo, @@ -1050,6 +1091,7 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = ResolveReparsePoints, GetReparsePoint, SetReparsePoint, + DeleteReparsePoint, }; NTSTATUS MemfsCreate(