diff --git a/inc/fuse/fuse.h b/inc/fuse/fuse.h index 191f3d2b..23067a35 100644 --- a/inc/fuse/fuse.h +++ b/inc/fuse/fuse.h @@ -103,8 +103,10 @@ struct fuse_operations /* _ */ int (*flock)(const char *path, struct fuse_file_info *, int op); /* _ */ int (*fallocate)(const char *path, int mode, fuse_off_t off, fuse_off_t len, struct fuse_file_info *fi); + /* WinFsp */ + /* S */ int (*getpath)(const char *path, char *buf, size_t size, + struct fuse_file_info *fi); /* OSXFUSE */ - /* _ */ int (*reserved00)(); /* _ */ int (*reserved01)(); /* _ */ int (*reserved02)(); /* _ */ int (*statfs_x)(const char *path, struct fuse_statfs *stbuf); diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 684308e3..24ae44d7 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -850,6 +850,29 @@ exit: return Result; } +static VOID fsp_fuse_intf_GetOpenFileInfoPath( + struct fuse *f, + const char *PosixPath, + struct fuse_file_info *fi, + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo) +{ + char PosixNormalizedName[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)]; + PWSTR NormalizedName; + ULONG NormalizedNameSize; + + if (0 == f->ops.getpath(PosixPath, PosixNormalizedName, sizeof PosixNormalizedName, fi) && + NT_SUCCESS(FspPosixMapPosixToWindowsPath(PosixNormalizedName, &NormalizedName))) + { + NormalizedNameSize = lstrlenW(NormalizedName) * sizeof(WCHAR); + if (OpenFileInfo->NormalizedNameSize >= NormalizedNameSize) + { + OpenFileInfo->NormalizedNameSize = (UINT16)NormalizedNameSize; + memcpy(OpenFileInfo->NormalizedName, NormalizedName, NormalizedNameSize); + } + FspPosixDeletePath(NormalizedName); + } +} + static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, @@ -1015,13 +1038,6 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, goto exit; } } - /* - * Ignore fuse_file_info::direct_io, fuse_file_info::keep_cache. - * NOTE: Originally WinFsp dit not support disabling the cache manager - * for an individual file. This is now possible and we should revisit. - * - * Ignore fuse_file_info::nonseekable. - */ Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, contexthdr->PosixPath, FUSE_FILE_INFO(CreateOptions & FILE_DIRECTORY_FILE, &fi), @@ -1029,6 +1045,14 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, if (!NT_SUCCESS(Result)) goto exit; + /* + * Ignore fuse_file_info::direct_io, fuse_file_info::keep_cache. + * NOTE: Originally WinFsp did not support disabling the cache manager + * for an individual file. This is now possible and we should revisit. + * + * Ignore fuse_file_info::nonseekable. + */ + *PFileDesc = filedesc; memcpy(FileInfo, &FileInfoBuf, sizeof FileInfoBuf); @@ -1040,6 +1064,10 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, filedesc->DirBuffer = 0; contexthdr->PosixPath = 0; + if (!f->VolumeParams.CaseSensitiveSearch && 0 != f->ops.getpath) + fsp_fuse_intf_GetOpenFileInfoPath(f, filedesc->PosixPath, -1 != fi.fh ? &fi : 0, + FspFileSystemGetOpenFileInfo(FileInfo)); + Result = STATUS_SUCCESS; exit: @@ -1163,10 +1191,9 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem, goto exit; /* - * Ignore fuse_file_info::direct_io, fuse_file_info::keep_cache - * WinFsp does not currently support disabling the cache manager - * for an individual file although it should not be hard to add - * if required. + * Ignore fuse_file_info::direct_io, fuse_file_info::keep_cache. + * NOTE: Originally WinFsp did not support disabling the cache manager + * for an individual file. This is now possible and we should revisit. * * Ignore fuse_file_info::nonseekable. */ @@ -1182,6 +1209,10 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem, filedesc->DirBuffer = 0; contexthdr->PosixPath = 0; + if (!f->VolumeParams.CaseSensitiveSearch && 0 != f->ops.getpath) + fsp_fuse_intf_GetOpenFileInfoPath(f, filedesc->PosixPath, -1 != fi.fh ? &fi : 0, + FspFileSystemGetOpenFileInfo(FileInfo)); + Result = STATUS_SUCCESS; exit: @@ -2112,6 +2143,10 @@ static NTSTATUS fsp_fuse_intf_GetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, char PosixPath[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)]; int ParentLength, FSlashLength, PosixNameLength; UINT32 Uid, Gid, Mode; + char PosixNormalizedName[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)]; + PWSTR NormalizedName, NormalizedNameSuffix; + ULONG NormalizedNameSize; + BOOLEAN Normalized = FALSE; NTSTATUS Result; if (!filedesc->IsDirectory || filedesc->IsReparsePoint) @@ -2145,13 +2180,33 @@ static NTSTATUS fsp_fuse_intf_GetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, goto exit; } - /* - * FUSE does not do FileName normalization; so just return the FileName as given to us! - */ - //memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); - DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + lstrlenW(FileName) * sizeof(WCHAR)); - memcpy(DirInfo->FileNameBuf, FileName, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + + if (!f->VolumeParams.CaseSensitiveSearch && 0 != f->ops.getpath) + { + if (0 == f->ops.getpath(PosixPath, PosixNormalizedName, sizeof PosixNormalizedName, 0) && + NT_SUCCESS(FspPosixMapPosixToWindowsPath(PosixNormalizedName, &NormalizedName))) + { + NormalizedNameSuffix = NormalizedName; + for (PWSTR P = NormalizedNameSuffix; *P; P++) + if (L'\\' == *P) + NormalizedNameSuffix = P + 1; + NormalizedNameSize = lstrlenW(NormalizedNameSuffix) * sizeof(WCHAR); + if (f->VolumeParams.MaxComponentLength * sizeof(WCHAR) >= NormalizedNameSize) + { + DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + NormalizedNameSize); + memcpy(DirInfo->FileNameBuf, NormalizedNameSuffix, NormalizedNameSize); + Normalized = TRUE; + } + FspPosixDeletePath(NormalizedName); + } + } + + if (!Normalized) + { + DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + lstrlenW(FileName) * sizeof(WCHAR)); + memcpy(DirInfo->FileNameBuf, FileName, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + } Result = STATUS_SUCCESS; diff --git a/src/dll/fuse/fuse_loop.c b/src/dll/fuse/fuse_loop.c index be2f8ff4..ce83758d 100644 --- a/src/dll/fuse/fuse_loop.c +++ b/src/dll/fuse/fuse_loop.c @@ -95,9 +95,9 @@ static NTSTATUS fsp_fuse_loop_start(struct fuse *f) context->private_data = f->data = f->ops.init(&conn); f->VolumeParams.ReadOnlyVolume = 0 != (conn.want & FSP_FUSE_CAP_READ_ONLY); f->VolumeParams.CaseSensitiveSearch = 0 == (conn.want & FSP_FUSE_CAP_CASE_INSENSITIVE); - if (!f->VolumeParams.CaseSensitiveSearch) + if (!f->VolumeParams.CaseSensitiveSearch && 0 == f->ops.getpath) /* - * Disable GetDirInfoByName when file system is case-insensitive. + * Disable GetDirInfoByName when file system is case-insensitive and getpath == 0. * The reason is that Windows always sends us queries with uppercase * file names in GetDirInfoByName and we have no way in FUSE to normalize * those file names when embedding them in FSP_FSCTL_DIR_INFO. diff --git a/tst/passthrough-fuse/passthrough-fuse.c b/tst/passthrough-fuse/passthrough-fuse.c index 01337ab5..42fe8980 100644 --- a/tst/passthrough-fuse/passthrough-fuse.c +++ b/tst/passthrough-fuse/passthrough-fuse.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -59,6 +60,7 @@ typedef struct { const char *rootdir; + size_t rootlen; } PTFS; static int ptfs_getattr(const char *path, struct fuse_stat *stbuf) @@ -293,6 +295,43 @@ static int ptfs_utimens(const char *path, const struct fuse_timespec tv[2]) } #endif +#if defined(_WIN64) || defined(_WIN32) +static int ptfs_getpath(const char *path, char *buf, size_t size, + struct fuse_file_info *fi) +{ + if (0 == fi) + { + ptfs_impl_fullpath(path); + + if (-1 == getpath(path, buf, size)) + return errno; + } + else + { + int fd = fi_fd(fi); + + if (-1 == fgetpath(fd, buf, size)) + return errno; + } + + PTFS *ptfs = fuse_get_context()->private_data; + size = strlen(buf); + if (size > ptfs->rootlen) + { + size -= ptfs->rootlen; + memmove(buf, buf + ptfs->rootlen, size); + buf[size] = '\0'; + } + else + { + buf[0] = '/'; + buf[1] = '\0'; + } + + return 0; +} +#endif + #if defined(_WIN64) || defined(_WIN32) static int ptfs_setcrtime(const char *path, const struct fuse_timespec *tv) { @@ -344,6 +383,9 @@ static struct fuse_operations ptfs_ops = #if defined(PTFS_UTIMENS) .utimens = ptfs_utimens, #endif +#if defined(_WIN64) || defined(_WIN32) + .getpath = ptfs_getpath, +#endif #if defined(_WIN64) || defined(_WIN32) .setcrtime = ptfs_setcrtime, #endif @@ -413,5 +455,17 @@ int main(int argc, char *argv[]) if (0 == ptfs.rootdir) usage(); +#if defined(_WIN64) || defined(_WIN32) + { + char buf[PATH_MAX]; + if (-1 != getpath(ptfs.rootdir, buf, sizeof buf)) + { + ptfs.rootlen = strlen(buf); + if (1 == ptfs.rootlen) + ptfs.rootlen = 0; + } + } +#endif + return fuse_main(argc, argv, &ptfs_ops, &ptfs); } diff --git a/tst/passthrough-fuse/winposix.c b/tst/passthrough-fuse/winposix.c index 256e1997..8c7f866f 100644 --- a/tst/passthrough-fuse/winposix.c +++ b/tst/passthrough-fuse/winposix.c @@ -212,6 +212,28 @@ int open(const char *path, int oflag, ...) return (int)(intptr_t)h; } +int fgetpath(int fd, char *buf, size_t size) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + union + { + FILE_NAME_INFO V; + UINT8 B[FIELD_OFFSET(FILE_NAME_INFO, FileName) + FSP_FSCTL_TRANSACT_PATH_SIZEMAX]; + } NameInfo; + + if (!GetFileInformationByHandleEx(h, FileNormalizedNameInfo, &NameInfo, sizeof NameInfo)) + return error(); + + FspPosixEncodeWindowsPath(NameInfo.V.FileName, NameInfo.V.FileNameLength / sizeof(WCHAR)); + size = WideCharToMultiByte(CP_UTF8, 0, + NameInfo.V.FileName, NameInfo.V.FileNameLength / sizeof(WCHAR), buf, (int)(size - 1), 0, 0); + if (0 == size) + return error(); + buf[size] = '\0'; + + return 0; +} + int fstat(int fd, struct fuse_stat *stbuf) { HANDLE h = (HANDLE)(intptr_t)fd; @@ -303,6 +325,22 @@ int close(int fd) return 0; } +int getpath(const char *path, char *buf, size_t size) +{ + HANDLE h = CreateFileA(path, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + int res = fgetpath((int)(intptr_t)h, buf, size); + + CloseHandle(h); + + return res; +} + int lstat(const char *path, struct fuse_stat *stbuf) { HANDLE h = CreateFileA(path, diff --git a/tst/passthrough-fuse/winposix.h b/tst/passthrough-fuse/winposix.h index 9d8fc5d3..122f56c2 100644 --- a/tst/passthrough-fuse/winposix.h +++ b/tst/passthrough-fuse/winposix.h @@ -46,6 +46,7 @@ char *realpath(const char *path, char *resolved); int statvfs(const char *path, struct fuse_statvfs *stbuf); int open(const char *path, int oflag, ...); +int fgetpath(int fd, char *buf, size_t size); int fstat(int fd, struct fuse_stat *stbuf); int ftruncate(int fd, fuse_off_t size); int pread(int fd, void *buf, size_t nbyte, fuse_off_t offset); @@ -53,6 +54,7 @@ int pwrite(int fd, const void *buf, size_t nbyte, fuse_off_t offset); int fsync(int fd); int close(int fd); +int getpath(const char *path, char *buf, size_t size); int lstat(const char *path, struct fuse_stat *stbuf); int chmod(const char *path, fuse_mode_t mode); int lchown(const char *path, fuse_uid_t uid, fuse_gid_t gid);