mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 08:23:05 -05:00
dll: fuse: use NFS reparse points for POSIX special files
This commit is contained in:
parent
28931f4687
commit
44c86ff9a4
@ -128,7 +128,6 @@ typedef struct
|
||||
UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */
|
||||
UINT32 ReparsePoints:1; /* file system supports reparse points */
|
||||
UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */
|
||||
UINT32 ReparsePointsSymlinkOnly:1; /* file system supports only symbolic link reparse points */
|
||||
UINT32 NamedStreams:1; /* file system supports named streams (!!!: unimplemented) */
|
||||
UINT32 HardLinks:1; /* unimplemented; set to 0 */
|
||||
UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */
|
||||
|
@ -654,16 +654,12 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* Resolve reparse points.
|
||||
*
|
||||
* Reparse points are a general mechanism for attaching special behavior to files.
|
||||
* A file or directory can contain a reparse point. A reparse point is data that has
|
||||
* special meaning to the file system, Windows or user applications. For example, NTFS
|
||||
* and Windows use reparse points to implement symbolic links. As another example,
|
||||
* a particular file system may use reparse points to emulate UNIX FIFO's.
|
||||
*
|
||||
* Reparse points are a general mechanism for attaching special behavior to files. WinFsp
|
||||
* supports any kind of reparse point. The symbolic link reparse point however is so
|
||||
* important for many file systems (especially POSIX ones) that WinFsp implements special
|
||||
* support for it.
|
||||
*
|
||||
* This function is expected to resolve as many reparse points as possible. If a reparse
|
||||
* point is encountered that is not understood by the file system further reparse point
|
||||
* resolution should stop; the reparse point data should be returned to the FSD with status
|
||||
@ -702,14 +698,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* Get 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 fill the buffer with a full reparse point. If the value of
|
||||
* ReparsePointsSymlinkOnly is TRUE the file system supports symbolic links only
|
||||
* as reparse points and this function is expected to fill the buffer with the symbolic
|
||||
* link path.
|
||||
*
|
||||
* @param FileSystem
|
||||
* The file system on which this request is posted.
|
||||
* @param Request
|
||||
@ -736,14 +724,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* Set 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 set 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 this function is expected to set the symbolic link path contained
|
||||
* in the buffer.
|
||||
*
|
||||
* @param FileSystem
|
||||
* The file system on which this request is posted.
|
||||
* @param Request
|
||||
@ -769,13 +749,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* 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
|
||||
@ -827,7 +800,6 @@ typedef struct _FSP_FILE_SYSTEM
|
||||
UINT32 DebugLog;
|
||||
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy;
|
||||
SRWLOCK OpGuardLock;
|
||||
UINT32 ReparsePointsSymlinkOnly:1;
|
||||
} FSP_FILE_SYSTEM;
|
||||
/**
|
||||
* Create a file system object.
|
||||
|
@ -106,8 +106,6 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
|
||||
FileSystem->EnterOperation = FspFileSystemOpEnter;
|
||||
FileSystem->LeaveOperation = FspFileSystemOpLeave;
|
||||
|
||||
FileSystem->ReparsePointsSymlinkOnly = VolumeParams->ReparsePointsSymlinkOnly;
|
||||
|
||||
*PFileSystem = FileSystem;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
149
src/dll/fsop.c
149
src/dll/fsop.c
@ -887,8 +887,8 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
PREPARSE_DATA_BUFFER GetReparseData, SetReparseData;
|
||||
SIZE_T Offset, Size;
|
||||
PREPARSE_DATA_BUFFER ReparseData;
|
||||
SIZE_T Size;
|
||||
|
||||
Result = STATUS_INVALID_DEVICE_REQUEST;
|
||||
switch (Request->Req.FileSystemControl.FsControlCode)
|
||||
@ -896,104 +896,41 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
|
||||
case FSCTL_GET_REPARSE_POINT:
|
||||
if (0 != FileSystem->Interface->GetReparsePoint)
|
||||
{
|
||||
GetReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer;
|
||||
memset(GetReparseData, 0, sizeof *GetReparseData);
|
||||
ReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer;
|
||||
memset(ReparseData, 0, sizeof *ReparseData);
|
||||
|
||||
if (FileSystem->ReparsePointsSymlinkOnly)
|
||||
{
|
||||
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer);
|
||||
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer,
|
||||
GetReparseData->SymbolicLinkReparseBuffer.PathBuffer,
|
||||
&Size);
|
||||
if (NT_SUCCESS(Result))
|
||||
{
|
||||
Offset = 0;
|
||||
if (Size > 4 * sizeof(WCHAR) &&
|
||||
'\\' == GetReparseData->SymbolicLinkReparseBuffer.PathBuffer[0] &&
|
||||
'?' == GetReparseData->SymbolicLinkReparseBuffer.PathBuffer[1] &&
|
||||
'?' == GetReparseData->SymbolicLinkReparseBuffer.PathBuffer[2] &&
|
||||
'\\' == GetReparseData->SymbolicLinkReparseBuffer.PathBuffer[3])
|
||||
Offset = 4 * sizeof(WCHAR);
|
||||
|
||||
GetReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK;
|
||||
GetReparseData->ReparseDataLength = (USHORT)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) +
|
||||
Size);
|
||||
GetReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
|
||||
GetReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT)Size;
|
||||
GetReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = (USHORT)Offset;
|
||||
GetReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)(Size - Offset);
|
||||
GetReparseData->SymbolicLinkReparseBuffer.Flags = 0 == Offset ?
|
||||
0 : SYMLINK_FLAG_RELATIVE;
|
||||
|
||||
Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) +
|
||||
Size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer);
|
||||
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer, GetReparseData, &Size);
|
||||
if (NT_SUCCESS(Result))
|
||||
Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size;
|
||||
}
|
||||
Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer);
|
||||
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer, ReparseData, &Size);
|
||||
if (NT_SUCCESS(Result))
|
||||
Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size;
|
||||
}
|
||||
break;
|
||||
case FSCTL_SET_REPARSE_POINT:
|
||||
if (0 != FileSystem->Interface->SetReparsePoint)
|
||||
{
|
||||
SetReparseData = (PREPARSE_DATA_BUFFER)
|
||||
ReparseData = (PREPARSE_DATA_BUFFER)
|
||||
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
|
||||
|
||||
if (FileSystem->ReparsePointsSymlinkOnly)
|
||||
{
|
||||
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,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer,
|
||||
SetReparseData,
|
||||
Request->Req.FileSystemControl.Buffer.Size);
|
||||
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer,
|
||||
ReparseData,
|
||||
Request->Req.FileSystemControl.Buffer.Size);
|
||||
}
|
||||
break;
|
||||
case FSCTL_DELETE_REPARSE_POINT:
|
||||
if (0 != FileSystem->Interface->DeleteReparsePoint)
|
||||
{
|
||||
SetReparseData = (PREPARSE_DATA_BUFFER)
|
||||
ReparseData = (PREPARSE_DATA_BUFFER)
|
||||
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
|
||||
|
||||
if (FileSystem->ReparsePointsSymlinkOnly)
|
||||
{
|
||||
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
|
||||
Result = FileSystem->Interface->DeleteReparsePoint(FileSystem, Request,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer,
|
||||
SetReparseData,
|
||||
Request->Req.FileSystemControl.Buffer.Size);
|
||||
Result = FileSystem->Interface->DeleteReparsePoint(FileSystem, Request,
|
||||
(PVOID)Request->Req.FileSystemControl.UserContext,
|
||||
(PWSTR)Request->Buffer,
|
||||
ReparseData,
|
||||
Request->Req.FileSystemControl.Buffer.Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1184,18 +1121,9 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
|
||||
|
||||
c = *p;
|
||||
*p = L'\0';
|
||||
if (FileSystem->ReparsePointsSymlinkOnly)
|
||||
{
|
||||
Size = FSP_FSCTL_TRANSACT_PATH_SIZEMAX;
|
||||
Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != c,
|
||||
ReparseData->SymbolicLinkReparseBuffer.PathBuffer, &Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Size = sizeof ReparseDataBuf;
|
||||
Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != c,
|
||||
ReparseData, &Size);
|
||||
}
|
||||
Size = sizeof ReparseDataBuf;
|
||||
Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != c,
|
||||
ReparseData, &Size);
|
||||
*p = c;
|
||||
|
||||
if (STATUS_NOT_A_REPARSE_POINT == Result)
|
||||
@ -1213,25 +1141,20 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (0 == --MaxTries)
|
||||
return STATUS_REPARSE_POINT_NOT_RESOLVED;
|
||||
|
||||
if (FileSystem->ReparsePointsSymlinkOnly)
|
||||
TargetLink = ReparseData->SymbolicLinkReparseBuffer.PathBuffer;
|
||||
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
|
||||
{
|
||||
TargetLink = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset;
|
||||
Size = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
|
||||
{
|
||||
TargetLink = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset;
|
||||
Size = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* not a symlink; return the full reparse point! */
|
||||
if (Size > *PSize)
|
||||
return STATUS_OBJECT_NAME_INVALID;
|
||||
memcpy(Buffer, ReparseData, Size);
|
||||
/* not a symlink; return the full reparse point! */
|
||||
if (Size > *PSize)
|
||||
return STATUS_OBJECT_NAME_INVALID;
|
||||
memcpy(Buffer, ReparseData, Size);
|
||||
|
||||
goto no_symlink_exit;
|
||||
}
|
||||
goto no_symlink_exit;
|
||||
}
|
||||
|
||||
if (Size > 4 * sizeof(WCHAR) &&
|
||||
|
@ -502,9 +502,8 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
opt_data.VolumeParams.FileInfoTimeout = opt_data.set_attr_timeout * 1000;
|
||||
opt_data.VolumeParams.CaseSensitiveSearch = !opt_data.CaseInsensitiveSearch;
|
||||
opt_data.VolumeParams.PersistentAcls = TRUE;
|
||||
opt_data.VolumeParams.ReparsePoints = FALSE; /* see FSP_FUSE_HAS_SYMLINKS use below */
|
||||
opt_data.VolumeParams.ReparsePoints = TRUE;
|
||||
opt_data.VolumeParams.ReparsePointsAccessCheck = FALSE;
|
||||
opt_data.VolumeParams.ReparsePointsSymlinkOnly = TRUE;
|
||||
opt_data.VolumeParams.NamedStreams = !!opt_data.NamedStreams;
|
||||
opt_data.VolumeParams.ReadOnlyVolume = !!opt_data.ReadOnlyVolume;
|
||||
|
||||
@ -557,8 +556,6 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->VolumeParams.ReparsePoints = FSP_FUSE_HAS_SYMLINKS(f);
|
||||
|
||||
return f;
|
||||
|
||||
fail:
|
||||
|
@ -263,9 +263,11 @@ exit:
|
||||
return Result;
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetFileInfoEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
#define fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, fi, PUid, PGid, PMode, FileInfo)\
|
||||
fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, PUid, PGid, PMode, 0, FileInfo)
|
||||
static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem,
|
||||
const char *PosixPath, struct fuse_file_info *fi,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode, PUINT32 PDev,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
@ -295,16 +297,25 @@ static NTSTATUS fsp_fuse_intf_GetFileInfoEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
*PUid = stbuf.st_uid;
|
||||
*PGid = stbuf.st_gid;
|
||||
*PMode = stbuf.st_mode;
|
||||
if (0 != PDev)
|
||||
*PDev = stbuf.st_rdev;
|
||||
|
||||
AllocationUnit = (UINT64)f->VolumeParams.SectorSize *
|
||||
(UINT64)f->VolumeParams.SectorsPerAllocationUnit;
|
||||
switch (stbuf.st_mode & 0170000)
|
||||
{
|
||||
case 0040000:
|
||||
case 0040000: /* S_IFDIR */
|
||||
FileInfo->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
||||
FileInfo->ReparseTag = 0;
|
||||
break;
|
||||
case 0120000:
|
||||
case 0010000: /* S_IFIFO */
|
||||
case 0020000: /* S_IFCHR */
|
||||
case 0060000: /* S_IFBLK */
|
||||
case 0140000: /* S_IFSOCK */
|
||||
FileInfo->FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
FileInfo->ReparseTag = IO_REPARSE_TAG_NFS;
|
||||
break;
|
||||
case 0120000: /* S_IFLNK */
|
||||
if (FSP_FUSE_HAS_SYMLINKS(f))
|
||||
{
|
||||
FileInfo->FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
@ -386,9 +397,8 @@ exit:
|
||||
return Result;
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointEx(
|
||||
FSP_FILE_SYSTEM *FileSystem,
|
||||
char *PosixPath, PVOID Buffer, PSIZE_T PSize)
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointSymlink(FSP_FILE_SYSTEM *FileSystem,
|
||||
const char *PosixPath, PVOID Buffer, PSIZE_T PSize)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
char PosixTargetPath[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)];
|
||||
@ -397,9 +407,6 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(
|
||||
int err;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (0 == f->ops.readlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
err = f->ops.readlink(PosixPath, PosixTargetPath, sizeof PosixTargetPath);
|
||||
if (-EINVAL/* same on MSVC and Cygwin */ == err)
|
||||
{
|
||||
@ -426,7 +433,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(
|
||||
* Transform absolute path to relative.
|
||||
*/
|
||||
|
||||
char *p, *t, *pstem, *tstem;
|
||||
const char *p, *t, *pstem, *tstem;
|
||||
unsigned index, count;
|
||||
|
||||
p = pstem = PosixPath;
|
||||
@ -527,6 +534,124 @@ exit:
|
||||
return Result;
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
const char *PosixPath, struct fuse_file_info *fi,
|
||||
PVOID Buffer, PSIZE_T PSize)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
UINT32 Uid, Gid, Mode, Dev;
|
||||
FSP_FSCTL_FILE_INFO FileInfo;
|
||||
PREPARSE_DATA_BUFFER ReparseData;
|
||||
USHORT ReparseDataLength;
|
||||
SIZE_T Size;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi,
|
||||
&Uid, &Gid, &Mode, &Dev, &FileInfo);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return Result;
|
||||
|
||||
if (0 == (FILE_ATTRIBUTE_REPARSE_POINT & FileInfo.FileAttributes))
|
||||
return STATUS_NOT_A_REPARSE_POINT;
|
||||
|
||||
if (0 == Buffer)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
switch (Mode & 0170000)
|
||||
{
|
||||
case 0010000: /* S_IFIFO */
|
||||
ReparseDataLength = (USHORT)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
|
||||
sizeof(UINT64));
|
||||
break;
|
||||
|
||||
case 0020000: /* S_IFCHR */
|
||||
ReparseDataLength = (USHORT)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
|
||||
sizeof(UINT64) + sizeof(UINT32) + sizeof(UINT32));
|
||||
break;
|
||||
|
||||
case 0060000: /* S_IFBLK */
|
||||
ReparseDataLength = (USHORT)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
|
||||
sizeof(UINT64) + sizeof(UINT32) + sizeof(UINT32));
|
||||
break;
|
||||
|
||||
case 0140000: /* S_IFSOCK */
|
||||
ReparseDataLength = (USHORT)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
|
||||
sizeof(UINT64));
|
||||
break;
|
||||
|
||||
case 0120000: /* S_IFLNK */
|
||||
ReparseDataLength = (USHORT)(
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* cannot happen! */
|
||||
return STATUS_NOT_A_REPARSE_POINT;
|
||||
}
|
||||
|
||||
if (FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) + ReparseDataLength > *PSize)
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
ReparseData = (PREPARSE_DATA_BUFFER)Buffer;
|
||||
ReparseData->ReparseTag = FileInfo.ReparseTag;
|
||||
ReparseData->ReparseDataLength = ReparseDataLength;
|
||||
|
||||
switch (Mode & 0170000)
|
||||
{
|
||||
case 0010000: /* S_IFIFO */
|
||||
*(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer = NFS_SPECFILE_FIFO;
|
||||
break;
|
||||
|
||||
case 0020000: /* S_IFCHR */
|
||||
*(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer = NFS_SPECFILE_CHR;
|
||||
((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[0] = (Dev >> 16) & 0xffff;
|
||||
((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[1] = Dev & 0xffff;
|
||||
break;
|
||||
|
||||
case 0060000: /* S_IFBLK */
|
||||
*(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer = NFS_SPECFILE_BLK;
|
||||
((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[0] = (Dev >> 16) & 0xffff;
|
||||
((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[1] = Dev & 0xffff;
|
||||
break;
|
||||
|
||||
case 0140000: /* S_IFSOCK */
|
||||
*(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer = NFS_SPECFILE_SOCK;
|
||||
break;
|
||||
|
||||
case 0120000: /* S_IFLNK */
|
||||
Size = *PSize -
|
||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer);
|
||||
Result = fsp_fuse_intf_GetReparsePointSymlink(FileSystem, PosixPath,
|
||||
ReparseData->SymbolicLinkReparseBuffer.PathBuffer, &Size);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return Result;
|
||||
|
||||
ReparseData->ReparseDataLength += (USHORT)Size;
|
||||
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
|
||||
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT)Size;
|
||||
ReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = 0;
|
||||
ReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)Size;
|
||||
ReparseData->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* cannot happen! */
|
||||
return STATUS_NOT_A_REPARSE_POINT;
|
||||
}
|
||||
|
||||
*PSize = FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) + ReparseData->ReparseDataLength;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
|
||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize);
|
||||
@ -1684,27 +1809,13 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
char *PosixPath = 0;
|
||||
struct fuse_stat stbuf;
|
||||
int err;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspPosixMapWindowsToPosixPath(FileName, &PosixPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
if (0 == Buffer)
|
||||
{
|
||||
memset(&stbuf, 0, sizeof stbuf);
|
||||
err = f->ops.getattr(PosixPath, (void *)&stbuf);
|
||||
if (0 != err)
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
else if (0120000 != (stbuf.st_mode & 0170000))
|
||||
Result = STATUS_NOT_A_REPARSE_POINT;
|
||||
else
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, Buffer, PSize);
|
||||
Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, 0, Buffer, PSize);
|
||||
|
||||
exit:
|
||||
if (0 != PosixPath)
|
||||
@ -1720,8 +1831,13 @@ static NTSTATUS fsp_fuse_intf_GetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
{
|
||||
struct fsp_fuse_file_desc *filedesc =
|
||||
(PVOID)(UINT_PTR)Request->Req.FileSystemControl.UserContext2;
|
||||
struct fuse_file_info fi;
|
||||
|
||||
return fsp_fuse_intf_GetReparsePointEx(FileSystem, filedesc->PosixPath, Buffer, PSize);
|
||||
memset(&fi, 0, sizeof fi);
|
||||
fi.flags = filedesc->OpenFlags;
|
||||
fi.fh = filedesc->FileHandle;
|
||||
|
||||
return fsp_fuse_intf_GetReparsePointEx(FileSystem, filedesc->PosixPath, &fi, Buffer, PSize);
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
@ -1732,48 +1848,18 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
struct fsp_fuse_file_desc *filedesc =
|
||||
(PVOID)(UINT_PTR)Request->Req.FileSystemControl.UserContext2;
|
||||
struct fuse_file_info fi;
|
||||
UINT32 Uid, Gid, Mode, Dev;
|
||||
FSP_FSCTL_FILE_INFO FileInfo;
|
||||
PREPARSE_DATA_BUFFER ReparseData;
|
||||
PWSTR ReparseTargetPath;
|
||||
SIZE_T ReparseTargetPathLength;
|
||||
WCHAR TargetPath[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)];
|
||||
char *PosixTargetPath = 0, *PosixHiddenPath = 0;
|
||||
BOOLEAN IsSymlink;
|
||||
int err;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (0 == f->ops.symlink || 0 == f->ops.rename || 0 == f->ops.unlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
/* 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) &&
|
||||
'\\' == ((PWSTR)Buffer)[0] &&
|
||||
'?' == ((PWSTR)Buffer)[1] &&
|
||||
'?' == ((PWSTR)Buffer)[2] &&
|
||||
'\\' == ((PWSTR)Buffer)[3])
|
||||
{
|
||||
/* we do not support absolute paths that point outside this file system */
|
||||
if (!Request->Req.FileSystemControl.TargetOnFileSystem)
|
||||
{
|
||||
Result = STATUS_ACCESS_DENIED;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (Size > sizeof TargetPath - sizeof(WCHAR))
|
||||
{
|
||||
Result = STATUS_IO_REPARSE_DATA_INVALID;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(TargetPath, Buffer, Size);
|
||||
TargetPath[Size / sizeof(WCHAR)] = L'\0';
|
||||
|
||||
Result = FspPosixMapWindowsToPosixPath(TargetPath, &PosixTargetPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
/*
|
||||
* How we implement this is probably one of the worst aspects of this FUSE implementation.
|
||||
*
|
||||
@ -1784,22 +1870,159 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
* 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.
|
||||
* call is supposed to convert it into a reparse point. However FUSE mknod/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!
|
||||
* Note that this will also result in a change of the inode number for the reparse point!
|
||||
*/
|
||||
|
||||
Result = fsp_fuse_intf_NewHiddenName(FileSystem, filedesc->PosixPath, &PosixHiddenPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
if (0 == f->ops.rename || 0 == f->ops.unlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
err = f->ops.symlink(PosixTargetPath, PosixHiddenPath);
|
||||
if (0 != err)
|
||||
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag || (
|
||||
IO_REPARSE_TAG_NFS == ReparseData->ReparseTag &&
|
||||
NFS_SPECFILE_LNK == *(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer))
|
||||
{
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
goto exit;
|
||||
if (0 == f->ops.symlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
IsSymlink = TRUE;
|
||||
}
|
||||
else if (IO_REPARSE_TAG_NFS == ReparseData->ReparseTag)
|
||||
{
|
||||
if (0 == f->ops.mknod)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
IsSymlink = FALSE;
|
||||
}
|
||||
else
|
||||
return STATUS_IO_REPARSE_TAG_MISMATCH;
|
||||
|
||||
/* FUSE cannot make a directory into a reparse point */
|
||||
if (filedesc->IsDirectory)
|
||||
return STATUS_ACCESS_DENIED;
|
||||
|
||||
memset(&fi, 0, sizeof fi);
|
||||
fi.flags = filedesc->OpenFlags;
|
||||
fi.fh = filedesc->FileHandle;
|
||||
|
||||
Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, filedesc->PosixPath, &fi,
|
||||
&Uid, &Gid, &Mode, &FileInfo);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return Result;
|
||||
|
||||
ReparseData = (PREPARSE_DATA_BUFFER)Buffer;
|
||||
|
||||
if (IsSymlink)
|
||||
{
|
||||
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
|
||||
{
|
||||
ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
|
||||
ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
|
||||
/* is this an absolute path? */
|
||||
if (ReparseTargetPathLength > 4 * sizeof(WCHAR) &&
|
||||
'\\' == ReparseTargetPath[0] &&
|
||||
'?' == ReparseTargetPath[1] &&
|
||||
'?' == ReparseTargetPath[2] &&
|
||||
'\\' == ReparseTargetPath[3])
|
||||
{
|
||||
/* we do not support absolute paths that point outside this file system */
|
||||
if (!Request->Req.FileSystemControl.TargetOnFileSystem)
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* the first path component is assumed to be the device name; skip it! */
|
||||
ReparseTargetPath += 4;
|
||||
ReparseTargetPathLength -= 4 * sizeof(WCHAR);
|
||||
while (0 < ReparseTargetPathLength && '\\' != ReparseTargetPath)
|
||||
ReparseTargetPathLength--, ReparseTargetPath++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReparseTargetPath = (PVOID)(ReparseData->GenericReparseBuffer.DataBuffer + sizeof(UINT64));
|
||||
ReparseTargetPathLength = ReparseData->ReparseDataLength - sizeof(UINT64);
|
||||
}
|
||||
|
||||
memcpy(TargetPath, ReparseTargetPath, ReparseTargetPathLength);
|
||||
TargetPath[ReparseTargetPathLength / sizeof(WCHAR)] = L'\0';
|
||||
|
||||
/*
|
||||
* From this point forward we must jump to the EXIT label on failure.
|
||||
*/
|
||||
|
||||
Result = FspPosixMapWindowsToPosixPath(TargetPath, &PosixTargetPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (*(PULONG)ReparseData->GenericReparseBuffer.DataBuffer)
|
||||
{
|
||||
case NFS_SPECFILE_FIFO:
|
||||
Mode = (Mode & ~0170000) | 0010000;
|
||||
Dev = 0;
|
||||
break;
|
||||
case NFS_SPECFILE_SOCK:
|
||||
Mode = (Mode & ~0170000) | 0140000;
|
||||
Dev = 0;
|
||||
break;
|
||||
case NFS_SPECFILE_CHR:
|
||||
Mode = (Mode & ~0170000) | 0020000;
|
||||
Dev =
|
||||
(((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[0] << 16) |
|
||||
(((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[1]);
|
||||
break;
|
||||
case NFS_SPECFILE_BLK:
|
||||
Mode = (Mode & ~0170000) | 0060000;
|
||||
Dev =
|
||||
(((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[0] << 16) |
|
||||
(((PUINT32)ReparseData->GenericReparseBuffer.DataBuffer)[1]);
|
||||
break;
|
||||
default:
|
||||
return STATUS_IO_REPARSE_DATA_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* From this point forward we must jump to the EXIT label on failure.
|
||||
*/
|
||||
|
||||
Result = fsp_fuse_intf_NewHiddenName(FileSystem, filedesc->PosixPath, &PosixHiddenPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
err = f->ops.mknod(PosixHiddenPath, Mode, Dev);
|
||||
if (0 != err)
|
||||
{
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != f->ops.chown)
|
||||
{
|
||||
err = f->ops.chown(PosixHiddenPath, Uid, Gid);
|
||||
if (0 != err)
|
||||
{
|
||||
/* on failure unlink "hidden" symlink */
|
||||
f->ops.unlink(PosixHiddenPath);
|
||||
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath);
|
||||
|
@ -92,4 +92,12 @@ NTSTATUS fsp_fuse_op_leave(FSP_FILE_SYSTEM *FileSystem,
|
||||
|
||||
extern FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf;
|
||||
|
||||
/* NFS reparse points */
|
||||
|
||||
#define NFS_SPECFILE_FIFO 0x000000004F464946
|
||||
#define NFS_SPECFILE_CHR 0x0000000000524843
|
||||
#define NFS_SPECFILE_BLK 0x00000000004b4c42
|
||||
#define NFS_SPECFILE_LNK 0x00000000014b4e4c
|
||||
#define NFS_SPECFILE_SOCK 0x000000004B434F53
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user