diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 6d2c5bb3..0eec0da7 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -197,7 +197,8 @@ enum /* user-mode flags */\ UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */\ UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */\ - UINT32 UmReservedFlags:6;\ + UINT32 UmNoReparsePointsDirCheck:1; /* user mode: no dir option check for reparse points */\ + UINT32 UmReservedFlags:5;\ /* additional kernel-mode flags */\ UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\ diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 9c3aa0c3..78a430d9 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1143,7 +1143,13 @@ typedef struct _FSP_FILE_SYSTEM FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy; SRWLOCK OpGuardLock; BOOLEAN UmFileContextIsUserContext2, UmFileContextIsFullContext; + UINT16 UmNoReparsePointsDirCheck:1; + UINT16 UmReservedFlags:15; } FSP_FILE_SYSTEM; +FSP_FSCTL_STATIC_ASSERT( + (4 == sizeof(PVOID) && 660 == sizeof(FSP_FILE_SYSTEM)) || + (8 == sizeof(PVOID) && 792 == sizeof(FSP_FILE_SYSTEM)), + "sizeof(FSP_FILE_SYSTEM) must be exactly 660 in 32-bit and 792 in 64-bit."); typedef struct _FSP_FILE_SYSTEM_OPERATION_CONTEXT { FSP_FSCTL_TRANSACT_REQ *Request; diff --git a/src/dll/fs.c b/src/dll/fs.c index 8371bd4d..677eb926 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -161,6 +161,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, FileSystem->UmFileContextIsUserContext2 = !!VolumeParams->UmFileContextIsUserContext2; FileSystem->UmFileContextIsFullContext = !!VolumeParams->UmFileContextIsFullContext; + FileSystem->UmNoReparsePointsDirCheck = VolumeParams->UmNoReparsePointsDirCheck; *PFileSystem = FileSystem; diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 05ad1da8..71bb9a21 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -642,6 +642,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, opt_data.VolumeParams.RejectIrpPriorToTransact0 = TRUE; #endif opt_data.VolumeParams.UmFileContextIsUserContext2 = TRUE; + opt_data.VolumeParams.UmNoReparsePointsDirCheck = TRUE; if (L'\0' == opt_data.VolumeParams.FileSystemName[0]) memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR)); diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 2bcee172..0b5bb104 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -21,6 +21,13 @@ #include +static NTSTATUS fsp_fuse_intf_GetReparsePointByName( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize); +static NTSTATUS fsp_fuse_intf_SetEaEntry( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PFILE_FULL_EA_INFORMATION SingleEa); + static inline VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) @@ -304,33 +311,111 @@ static BOOLEAN fsp_fuse_intf_CheckSymlinkDirectory(FSP_FILE_SYSTEM *FileSystem, const char *PosixPath) { struct fuse *f = FileSystem->UserContext; - char *PosixDotPath = 0; - size_t Length; - struct fuse_stat_ex stbuf; - int err; - BOOLEAN Result = FALSE; - Length = lstrlenA(PosixPath); - PosixDotPath = MemAlloc(Length + 3); - if (0 != PosixDotPath) + if (FSP_FUSE_HAS_SLASHDOT(f)) { - memcpy(PosixDotPath, PosixPath, Length); - PosixDotPath[Length + 0] = '/'; - PosixDotPath[Length + 1] = '.'; - PosixDotPath[Length + 2] = '\0'; + char *PosixDotPath = 0; + size_t Length; + struct fuse_stat_ex stbuf; + int err; + BOOLEAN Result = FALSE; + + Length = lstrlenA(PosixPath); + PosixDotPath = MemAlloc(Length + 3); + if (0 != PosixDotPath) + { + memcpy(PosixDotPath, PosixPath, Length); + PosixDotPath[Length + 0] = '/'; + PosixDotPath[Length + 1] = '.'; + PosixDotPath[Length + 2] = '\0'; + + memset(&stbuf, 0, sizeof stbuf); + if (0 != f->ops.getattr) + err = f->ops.getattr(PosixDotPath, (void *)&stbuf); + else + err = -ENOSYS_(f->env); + + MemFree(PosixDotPath); + + Result = 0 == err && 0040000 == (stbuf.st_mode & 0170000); + } + + return Result; + } + else + { + PWSTR WindowsPath = 0, P; + char *PosixResolvedPath = 0; + UINT32 ReparsePointIndex; + UINT32 ResolvedFileAttributes = -1; + IO_STATUS_BLOCK IoStatus; + union + { + REPARSE_DATA_BUFFER V; + UINT8 B[FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + + FSP_FSCTL_TRANSACT_PATH_SIZEMAX + sizeof(WCHAR)/* add space for term-0 */]; + } ReparseDataBuf; + SIZE_T ReparseDataSize; + struct fuse_stat_ex stbuf; + int err; + NTSTATUS Result; + + Result = FspPosixMapPosixToWindowsPath(PosixPath, &WindowsPath); + if (!NT_SUCCESS(Result)) + goto exit; + + ReparsePointIndex = 0; + for (P = WindowsPath; '\0' != *P; P++) + if (L'\\' == *P) + ReparsePointIndex = (UINT32)(P + 1 - WindowsPath); + + ReparseDataSize = sizeof ReparseDataBuf - sizeof(WCHAR)/* leave space for term-0 */; + Result = FspFileSystemResolveReparsePoints(FileSystem, + fsp_fuse_intf_GetReparsePointByName, &ResolvedFileAttributes, + WindowsPath, ReparsePointIndex, TRUE, + &IoStatus, &ReparseDataBuf, + &ReparseDataSize); + if (!NT_SUCCESS(Result)) + goto exit; + + if (IO_REPARSE_TAG_SYMLINK != ReparseDataBuf.V.ReparseTag) + { + Result = STATUS_UNSUCCESSFUL; + goto exit; + } + + if (-1 != ResolvedFileAttributes) + { + Result = (FILE_ATTRIBUTE_DIRECTORY & ResolvedFileAttributes) ? + STATUS_SUCCESS : STATUS_UNSUCCESSFUL; + goto exit; + } + + P = (PWSTR)(ReparseDataBuf.V.SymbolicLinkReparseBuffer.PathBuffer + + ReparseDataBuf.V.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)); + P[ReparseDataBuf.V.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)] = L'\0'; + + Result = FspPosixMapWindowsToPosixPath(P, &PosixResolvedPath); + if (!NT_SUCCESS(Result)) + goto exit; memset(&stbuf, 0, sizeof stbuf); if (0 != f->ops.getattr) - err = f->ops.getattr(PosixDotPath, (void *)&stbuf); + err = f->ops.getattr(PosixResolvedPath, (void *)&stbuf); else err = -ENOSYS_(f->env); - MemFree(PosixDotPath); + Result = 0 == err && 0040000 == (stbuf.st_mode & 0170000) ? + STATUS_SUCCESS : STATUS_UNSUCCESSFUL; - Result = 0 == err && 0040000 == (stbuf.st_mode & 0170000); + exit: + if (0 != PosixResolvedPath) + FspPosixDeletePath(PosixResolvedPath); + if (0 != WindowsPath) + FspPosixDeletePath(WindowsPath); + + return NT_SUCCESS(Result); } - - return Result; } static inline uint32_t fsp_fuse_intf_MapFileAttributesToFlags(UINT32 FileAttributes) @@ -367,10 +452,11 @@ static inline UINT32 fsp_fuse_intf_MapFlagsToFileAttributes(uint32_t flags) #define FUSE_FILE_INFO(IsDirectory, fi) ((IsDirectory) ? 0 : (fi)) #define fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, fi, PUid, PGid, PMode, FileInfo)\ - fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, PUid, PGid, PMode, 0, FileInfo) + fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, PUid, PGid, PMode, 0, TRUE, FileInfo) static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem, const char *PosixPath, struct fuse_file_info *fi, const void *stbufp, PUINT32 PUid, PUINT32 PGid, PUINT32 PMode, PUINT32 PDev, + BOOLEAN CheckSymlinkDirectory, FSP_FSCTL_FILE_INFO *FileInfo) { struct fuse *f = FileSystem->UserContext; @@ -429,7 +515,7 @@ static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem, { FileInfo->FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT; FileInfo->ReparseTag = IO_REPARSE_TAG_SYMLINK; - if (fsp_fuse_intf_CheckSymlinkDirectory(FileSystem, PosixPath)) + if (CheckSymlinkDirectory && fsp_fuse_intf_CheckSymlinkDirectory(FileSystem, PosixPath)) FileInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; break; } @@ -572,7 +658,7 @@ exit: static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem, const char *PosixPath, struct fuse_file_info *fi, - PVOID Buffer, PSIZE_T PSize) + PVOID Buffer, PSIZE_T PSize, PUINT32 PResolvedFileAttributes) { struct fuse *f = FileSystem->UserContext; UINT32 Uid, Gid, Mode, Dev; @@ -583,12 +669,16 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem, NTSTATUS Result; Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, - &Uid, &Gid, &Mode, &Dev, &FileInfo); + &Uid, &Gid, &Mode, &Dev, FALSE, &FileInfo); if (!NT_SUCCESS(Result)) return Result; if (0 == (FILE_ATTRIBUTE_REPARSE_POINT & FileInfo.FileAttributes)) + { + if (0 != PResolvedFileAttributes) + *PResolvedFileAttributes = FileInfo.FileAttributes; return STATUS_NOT_A_REPARSE_POINT; + } if (0 == Buffer) return STATUS_SUCCESS; @@ -688,13 +778,6 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem, return STATUS_SUCCESS; } -static NTSTATUS fsp_fuse_intf_GetReparsePointByName( - FSP_FILE_SYSTEM *FileSystem, PVOID Context, - PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize); -static NTSTATUS fsp_fuse_intf_SetEaEntry( - FSP_FILE_SYSTEM *FileSystem, PVOID Context, - PFILE_FULL_EA_INFORMATION SingleEa); - static NTSTATUS fsp_fuse_intf_GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo) { @@ -1031,7 +1114,12 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem, break; } - if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + { + fi.fh = -1; + Result = STATUS_SUCCESS; + } + else if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (0 != f->ops.opendir) { @@ -1044,11 +1132,6 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem, Result = STATUS_SUCCESS; } } - else if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - { - fi.fh = -1; - Result = STATUS_SUCCESS; - } else { /* @@ -1192,15 +1275,15 @@ static VOID fsp_fuse_intf_Close(FSP_FILE_SYSTEM *FileSystem, fi.flags = filedesc->OpenFlags; fi.fh = filedesc->FileHandle; - if (filedesc->IsDirectory) + if (filedesc->IsReparsePoint) + { + /* reparse points are not opened, nothing to do! */ + } + else if (filedesc->IsDirectory) { if (0 != f->ops.releasedir) f->ops.releasedir(filedesc->PosixPath, &fi); } - else if (filedesc->IsReparsePoint) - { - /* reparse points are not opened, nothing to do! */ - } else { if (0 != f->ops.flush) @@ -1332,7 +1415,9 @@ static NTSTATUS fsp_fuse_intf_Flush(FSP_FILE_SYSTEM *FileSystem, fi.fh = filedesc->FileHandle; Result = STATUS_SUCCESS; /* just say success, if fs does not support fsync */ - if (filedesc->IsDirectory) + if (filedesc->IsReparsePoint) + Result = STATUS_ACCESS_DENIED; + else if (filedesc->IsDirectory) { if (0 != f->ops.fsyncdir) { @@ -1340,8 +1425,6 @@ static NTSTATUS fsp_fuse_intf_Flush(FSP_FILE_SYSTEM *FileSystem, Result = fsp_fuse_ntstatus_from_errno(f->env, err); } } - else if (filedesc->IsReparsePoint) - Result = STATUS_ACCESS_DENIED; else { if (0 != f->ops.fsync) @@ -1854,7 +1937,7 @@ int fsp_fuse_intf_AddDirInfo(void *buf, const char *name, NTSTATUS Result0; Result0 = fsp_fuse_intf_GetFileInfoFunnel(dh->FileSystem, name, 0, stbuf, - &Uid, &Gid, &Mode, 0, &DirInfo->FileInfo); + &Uid, &Gid, &Mode, 0, TRUE, &DirInfo->FileInfo); if (NT_SUCCESS(Result0)) DirInfo->Padding[0] = 1; /* HACK: remember that the FileInfo is valid */ } @@ -1976,6 +2059,9 @@ static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem, int err; NTSTATUS Result; + if (!filedesc->IsDirectory || filedesc->IsReparsePoint) + return STATUS_ACCESS_DENIED; + if (FspFileSystemAcquireDirectoryBuffer(&filedesc->DirBuffer, 0 == Marker, &Result)) { memset(&dh, 0, sizeof dh); @@ -2032,6 +2118,9 @@ static NTSTATUS fsp_fuse_intf_GetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, UINT32 Uid, Gid, Mode; NTSTATUS Result; + if (!filedesc->IsDirectory || filedesc->IsReparsePoint) + return STATUS_ACCESS_DENIED; + Result = FspPosixMapWindowsToPosixPath(FileName, &PosixName); if (!NT_SUCCESS(Result)) { @@ -2098,7 +2187,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointByName( if (!NT_SUCCESS(Result)) goto exit; - Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, 0, Buffer, PSize); + Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, 0, Buffer, PSize, Context); exit: if (0 != PosixPath) @@ -2120,7 +2209,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePoint(FSP_FILE_SYSTEM *FileSystem, return fsp_fuse_intf_GetReparsePointEx(FileSystem, filedesc->PosixPath, FUSE_FILE_INFO(filedesc->IsDirectory, &fi), - Buffer, PSize); + Buffer, PSize, 0); } static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, @@ -2159,23 +2248,25 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, * Note that this will also result in a change of the inode number for the reparse point! */ - if (0 == f->ops.rename || 0 == f->ops.unlink) - return STATUS_INVALID_DEVICE_REQUEST; - ReparseData = (PREPARSE_DATA_BUFFER)Buffer; if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag || ( IO_REPARSE_TAG_NFS == ReparseData->ReparseTag && NFS_SPECFILE_LNK == *(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer)) { - if (0 == f->ops.symlink) + if (filedesc->IsDirectory && 0 == f->ops.rmdir) + return STATUS_INVALID_DEVICE_REQUEST; + if (0 == f->ops.symlink || 0 == f->ops.rename || 0 == f->ops.unlink) return STATUS_INVALID_DEVICE_REQUEST; IsSymlink = TRUE; } else if (IO_REPARSE_TAG_NFS == ReparseData->ReparseTag) { - if (0 == f->ops.mknod) + /* FUSE cannot make a directory into an NFS reparse point */ + if (filedesc->IsDirectory) + return STATUS_ACCESS_DENIED; + if (0 == f->ops.mknod || 0 == f->ops.rename || 0 == f->ops.unlink) return STATUS_INVALID_DEVICE_REQUEST; IsSymlink = FALSE; @@ -2183,10 +2274,6 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, 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; @@ -2297,17 +2384,33 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, } } - err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath); + if (filedesc->IsDirectory) + { + err = f->ops.rmdir(filedesc->PosixPath); + if (0 == err) + err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath); + } + else + 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; } + if (filedesc->IsDirectory) + { + if (0 != f->ops.releasedir) + f->ops.releasedir(filedesc->PosixPath, &fi); + } + else + { + if (0 != f->ops.release) + f->ops.release(filedesc->PosixPath, &fi); + } filedesc->IsReparsePoint = TRUE; + filedesc->FileHandle = -1; Result = STATUS_SUCCESS; @@ -2433,13 +2536,17 @@ static NTSTATUS fsp_fuse_intf_SetEaEntry( int err; if (0 != SingleEa->EaValueLength) - err = f->ops.setxattr(PosixPath, - SingleEa->EaName, SingleEa->EaName + SingleEa->EaNameLength + 1, SingleEa->EaValueLength, 0); + { + err = f->ops.setxattr(PosixPath, SingleEa->EaName, + SingleEa->EaName + SingleEa->EaNameLength + 1, SingleEa->EaValueLength, 0); + return fsp_fuse_ntstatus_from_errno(f->env, err); + } else - err = f->ops.removexattr(PosixPath, - SingleEa->EaName); - - return fsp_fuse_ntstatus_from_errno(f->env, err); + { + err = f->ops.removexattr(PosixPath, SingleEa->EaName); + /* ignore errors */ + return STATUS_SUCCESS; + } } static NTSTATUS fsp_fuse_intf_SetEa(FSP_FILE_SYSTEM *FileSystem, diff --git a/src/dll/fuse/fuse_loop.c b/src/dll/fuse/fuse_loop.c index 5e234a57..fc1ce0b5 100644 --- a/src/dll/fuse/fuse_loop.c +++ b/src/dll/fuse/fuse_loop.c @@ -160,6 +160,29 @@ static NTSTATUS fsp_fuse_loop_start(struct fuse *f) /* this should always fail with ENOSYS or EINVAL */ err = f->ops.readlink("/", buf, sizeof buf); f->has_symlinks = -ENOSYS_(f->env) != err; + + if (f->has_symlinks) + { + /* + * Determine if the file system supports "/." queries. + * + * Symlinks on Windows are differentiated as "file" symlinks or "directory" symlinks. + * When we need to make the distinction we can follow one of two techniques: + * + * - Slashdot technique: We issue a getattr(path + "/.") and check the stat result. + * In general this is not a getattr() query that FUSE file systems are expected + * to handle. For this reason we issue a getattr("/.") below to determine + * if the file system handles this kind of query against the root directory. + * + * - Resolve technique: If the file system cannot handle slashdot queries, we resolve + * the path using readlink on each path component, then issue getattr on the resolved + * path and check the stat result. + */ + struct fuse_stat_ex stbuf; + memset(&stbuf, 0, sizeof stbuf); + err = f->ops.getattr("/.", (void *)&stbuf); + f->has_slashdot = 0 == err && 0040000 == (stbuf.st_mode & 0170000); + } } if (0 != f->ops.listxattr && 0 != f->ops.getxattr && 0 != f->ops.setxattr && 0 != f->ops.removexattr) diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index aec9a642..3ae96ba7 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -34,6 +34,7 @@ ((struct fuse_context *)((PUINT8)(h) + sizeof(struct fsp_fuse_context_header))) #define FSP_FUSE_HAS_SYMLINKS(f) ((f)->has_symlinks) +#define FSP_FUSE_HAS_SLASHDOT(f) ((f)->has_slashdot) #define ENOSYS_(env) ('C' == (env)->environment ? 88 : 40) @@ -61,7 +62,7 @@ struct fuse void *data; unsigned conn_want; BOOLEAN fsinit; - BOOLEAN has_symlinks; + BOOLEAN has_symlinks, has_slashdot; UINT32 DebugLog; FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy; FSP_FSCTL_VOLUME_PARAMS VolumeParams; diff --git a/src/dll/security.c b/src/dll/security.c index b5543a52..e0027205 100644 --- a/src/dll/security.c +++ b/src/dll/security.c @@ -325,6 +325,13 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, goto exit; } + /* + * We allow some file systems (notably FUSE) to open reparse points + * regardless of the FILE_DIRECTORY_FILE / FILE_NON_DIRECTORY_FILE options. + */ + if (0 != (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && FileSystem->UmNoReparsePointsDirCheck) + goto skip_reparse_dir_check; + if ((Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) && 0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { @@ -337,6 +344,9 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, Result = STATUS_FILE_IS_A_DIRECTORY; goto exit; } + + skip_reparse_dir_check: + ; } if (Request->Req.Create.UserMode)