mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 17:32:57 -05:00
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:
@ -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,
|
||||
|
Reference in New Issue
Block a user