dll: fuse: multiple improvements and fixes

- Symlinks: Now supports conventing a directory into a symlink reparse
point.

- Symlinks: The determination of whether a symlink is a file or
directory is now possible for file systems that do not support slashdot
(/.) queries.

- EA: Now allows the removal of non-existant EA without error (this is
allowed on Windows).
This commit is contained in:
Bill Zissimopoulos 2021-11-13 23:27:34 +00:00
parent ec3386c2b3
commit 0b94e8bc6a
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
8 changed files with 215 additions and 65 deletions

View File

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

View File

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

View File

@ -161,6 +161,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
FileSystem->UmFileContextIsUserContext2 = !!VolumeParams->UmFileContextIsUserContext2;
FileSystem->UmFileContextIsFullContext = !!VolumeParams->UmFileContextIsFullContext;
FileSystem->UmNoReparsePointsDirCheck = VolumeParams->UmNoReparsePointsDirCheck;
*PFileSystem = FileSystem;

View File

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

View File

@ -21,6 +21,13 @@
#include <dll/fuse/library.h>
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,

View File

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

View File

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

View File

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