dll: fuse: use NFS reparse points for POSIX special files

This commit is contained in:
Bill Zissimopoulos 2016-09-08 10:43:01 -07:00
parent 28931f4687
commit 44c86ff9a4
7 changed files with 343 additions and 223 deletions

View File

@ -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 */

View File

@ -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.

View File

@ -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;

View File

@ -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) &&

View File

@ -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:

View File

@ -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);

View File

@ -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