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(