From 44c86ff9a4ccae2e048b9f10aa35cb2b3e51e563 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 8 Sep 2016 10:43:01 -0700 Subject: [PATCH] dll: fuse: use NFS reparse points for POSIX special files --- inc/winfsp/fsctl.h | 1 - inc/winfsp/winfsp.h | 30 +--- src/dll/fs.c | 2 - src/dll/fsop.c | 149 ++++------------ src/dll/fuse/fuse.c | 5 +- src/dll/fuse/fuse_intf.c | 371 +++++++++++++++++++++++++++++++-------- src/dll/fuse/library.h | 8 + 7 files changed, 343 insertions(+), 223 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index ee3b18fa..1d05e760 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -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 */ diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index d8dfe239..deb44dfd 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -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. diff --git a/src/dll/fs.c b/src/dll/fs.c index 5d127625..74f9c47e 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -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; diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 8a35c9a6..9633c338 100644 --- a/src/dll/fsop.c +++ b/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) && diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index ed7766fe..8426a603 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -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: diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 8d9d6406..e88b6a2a 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -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); diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index 7b0084cb..b44c6e60 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -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