diff --git a/inc/fuse/fuse_common.h b/inc/fuse/fuse_common.h index 0bc13297..53cb75fe 100644 --- a/inc/fuse/fuse_common.h +++ b/inc/fuse/fuse_common.h @@ -41,6 +41,15 @@ extern "C" { #define FUSE_CAP_EXPORT_SUPPORT (1 << 4) #define FUSE_CAP_BIG_WRITES (1 << 5) #define FUSE_CAP_DONT_MASK (1 << 6) +#define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */ +#define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */ +#define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */ +#define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */ +#define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */ + +#define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */ +#define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */ +#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE #define FUSE_IOCTL_COMPAT (1 << 0) #define FUSE_IOCTL_UNRESTRICTED (1 << 1) diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 54bb5cad..8c422960 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -38,8 +38,6 @@ struct fsp_fuse_core_opt_data set_attr_timeout, attr_timeout, rellinks; int set_FileInfoTimeout; - int CaseInsensitiveSearch, - ReadOnlyVolume; FSP_FSCTL_VOLUME_PARAMS VolumeParams; }; @@ -89,8 +87,6 @@ static struct fuse_opt fsp_fuse_core_opts[] = FSP_FUSE_CORE_OPT("VolumeSerialNumber=%lx", VolumeParams.VolumeSerialNumber, 0), FSP_FUSE_CORE_OPT("FileInfoTimeout=", set_FileInfoTimeout, 1), FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0), - FSP_FUSE_CORE_OPT("CaseInsensitiveSearch", CaseInsensitiveSearch, 1), - FSP_FUSE_CORE_OPT("ReadOnlyVolume", ReadOnlyVolume, 1), FUSE_OPT_KEY("--UNC=", 'U'), FUSE_OPT_KEY("--VolumePrefix=", 'U'), FUSE_OPT_KEY("--FileSystemName=", 'F'), @@ -252,9 +248,17 @@ static NTSTATUS fsp_fuse_svcstart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) //FUSE_CAP_ATOMIC_O_TRUNC | /* due to Windows/WinFsp design, no support */ //FUSE_CAP_EXPORT_SUPPORT | /* not needed in Windows/WinFsp */ FUSE_CAP_BIG_WRITES | - FUSE_CAP_DONT_MASK; + FUSE_CAP_DONT_MASK | + FSP_FUSE_CAP_READDIR_PLUS | + FSP_FUSE_CAP_READ_ONLY | + FSP_FUSE_CAP_CASE_INSENSITIVE; if (0 != f->ops.init) + { 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); + f->conn_want = conn.want; + } f->fsinit = TRUE; if (0 != f->ops.statfs) { @@ -417,7 +421,6 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key, " -o VolumeCreationTime=T volume creation time (FILETIME hex format)\n" " -o VolumeSerialNumber=N 32-bit wide\n" " -o FileInfoTimeout=N FileInfo/Security/VolumeInfo timeout (millisec)\n" - " -o CaseInsensitiveSearch file system supports case-insensitive file names\n" " --UNC=U --VolumePrefix=U UNC prefix (\\Server\\Share)\n" " --FileSystemName=FSN Name of user mode file system\n"); opt_data->help = 1; @@ -501,12 +504,12 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, if (!opt_data.set_FileInfoTimeout && opt_data.set_attr_timeout) opt_data.VolumeParams.FileInfoTimeout = opt_data.set_attr_timeout * 1000; - opt_data.VolumeParams.CaseSensitiveSearch = !opt_data.CaseInsensitiveSearch; + opt_data.VolumeParams.CaseSensitiveSearch = TRUE; opt_data.VolumeParams.PersistentAcls = TRUE; opt_data.VolumeParams.ReparsePoints = TRUE; opt_data.VolumeParams.ReparsePointsAccessCheck = FALSE; opt_data.VolumeParams.NamedStreams = FALSE; - opt_data.VolumeParams.ReadOnlyVolume = !!opt_data.ReadOnlyVolume; + opt_data.VolumeParams.ReadOnlyVolume = FALSE; opt_data.VolumeParams.PostCleanupWhenModifiedOnly = TRUE; opt_data.VolumeParams.UmFileContextIsUserContext2 = TRUE; if (L'\0' == opt_data.VolumeParams.FileSystemName[0]) diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 1df75b8c..14113114 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -311,28 +311,34 @@ static BOOLEAN fsp_fuse_intf_CheckSymlinkDirectory(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) + fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, PUid, PGid, PMode, 0, FileInfo) static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem, - const char *PosixPath, struct fuse_file_info *fi, + const char *PosixPath, struct fuse_file_info *fi, const struct fuse_stat *stbufp, PUINT32 PUid, PUINT32 PGid, PUINT32 PMode, PUINT32 PDev, FSP_FSCTL_FILE_INFO *FileInfo) { struct fuse *f = FileSystem->UserContext; UINT64 AllocationUnit; struct fuse_stat stbuf; - int err; - memset(&stbuf, 0, sizeof stbuf); - - if (0 != f->ops.fgetattr && 0 != fi && -1 != fi->fh) - err = f->ops.fgetattr(PosixPath, (void *)&stbuf, fi); - else if (0 != f->ops.getattr) - err = f->ops.getattr(PosixPath, (void *)&stbuf); + if (0 != stbufp) + memcpy(&stbuf, stbufp, sizeof stbuf); else - return STATUS_INVALID_DEVICE_REQUEST; + { + int err; - if (0 != err) - return fsp_fuse_ntstatus_from_errno(f->env, err); + memset(&stbuf, 0, sizeof stbuf); + + if (0 != f->ops.fgetattr && 0 != fi && -1 != fi->fh) + err = f->ops.fgetattr(PosixPath, (void *)&stbuf, fi); + else if (0 != f->ops.getattr) + err = f->ops.getattr(PosixPath, (void *)&stbuf); + else + return STATUS_INVALID_DEVICE_REQUEST; + + if (0 != err) + return fsp_fuse_ntstatus_from_errno(f->env, err); + } if (f->set_umask) stbuf.st_mode = (stbuf.st_mode & 0170000) | (0777 & ~f->umask); @@ -513,7 +519,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem, SIZE_T Size; NTSTATUS Result; - Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, + Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, &Uid, &Gid, &Mode, &Dev, &FileInfo); if (!NT_SUCCESS(Result)) return Result; @@ -1576,6 +1582,17 @@ static int fsp_fuse_intf_AddDirInfo(void *buf, const char *name, memset(DirInfo, 0, sizeof *DirInfo); DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + SizeW * sizeof(WCHAR)); + if (dh->ReaddirPlus && 0 != stbuf) + { + UINT32 Uid, Gid, Mode; + NTSTATUS Result0; + + Result0 = fsp_fuse_intf_GetFileInfoFunnel(dh->FileSystem, 0, 0, stbuf, + &Uid, &Gid, &Mode, 0, &DirInfo->FileInfo); + if (NT_SUCCESS(Result0)) + DirInfo->Padding[0] = 1; /* HACK: remember that the FileInfo is valid */ + } + return !FspFileSystemFillDirectoryBuffer(&filedesc->DirBuffer, DirInfo, &dh->Result); } @@ -1619,44 +1636,53 @@ static NTSTATUS fsp_fuse_intf_FixDirInfo(FSP_FILE_SYSTEM *FileSystem, DirInfo = (FSP_FSCTL_DIR_INFO *)(Buffer + *Index); SizeW = (DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR); - if (1 == SizeW && L'.' == DirInfo->FileNameBuf[0]) + if (DirInfo->Padding[0]) { - PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 1 : PosixName; - SavedPathChar = *PosixPathEnd; - *PosixPathEnd = '\0'; - } - else - if (2 == SizeW && L'.' == DirInfo->FileNameBuf[0] && L'.' == DirInfo->FileNameBuf[1]) - { - PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 2 : PosixName; - while (PosixPath < PosixPathEnd && '/' != *PosixPathEnd) - PosixPathEnd--; - if (PosixPath == PosixPathEnd) - PosixPathEnd++; - SavedPathChar = *PosixPathEnd; - *PosixPathEnd = '\0'; + /* DirInfo has been filled already! */ + + DirInfo->Padding[0] = 0; } else { - PosixPathEnd = 0; - SizeA = WideCharToMultiByte(CP_UTF8, 0, DirInfo->FileNameBuf, SizeW, PosixName, 255, 0, 0); - if (0 == SizeA) + if (1 == SizeW && L'.' == DirInfo->FileNameBuf[0]) { - /* this should never happen because we just converted using MultiByteToWideChar */ - Result = STATUS_OBJECT_NAME_INVALID; - goto exit; + PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 1 : PosixName; + SavedPathChar = *PosixPathEnd; + *PosixPathEnd = '\0'; } - PosixName[SizeA] = '\0'; + else + if (2 == SizeW && L'.' == DirInfo->FileNameBuf[0] && L'.' == DirInfo->FileNameBuf[1]) + { + PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 2 : PosixName; + while (PosixPath < PosixPathEnd && '/' != *PosixPathEnd) + PosixPathEnd--; + if (PosixPath == PosixPathEnd) + PosixPathEnd++; + SavedPathChar = *PosixPathEnd; + *PosixPathEnd = '\0'; + } + else + { + PosixPathEnd = 0; + SizeA = WideCharToMultiByte(CP_UTF8, 0, DirInfo->FileNameBuf, SizeW, PosixName, 255, 0, 0); + if (0 == SizeA) + { + /* this should never happen because we just converted using MultiByteToWideChar */ + Result = STATUS_OBJECT_NAME_INVALID; + goto exit; + } + PosixName[SizeA] = '\0'; + } + + Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0, + &Uid, &Gid, &Mode, &DirInfo->FileInfo); + if (!NT_SUCCESS(Result)) + goto exit; + + if (0 != PosixPathEnd) + *PosixPathEnd = SavedPathChar; } - Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0, - &Uid, &Gid, &Mode, &DirInfo->FileInfo); - if (!NT_SUCCESS(Result)) - goto exit; - - if (0 != PosixPathEnd) - *PosixPathEnd = SavedPathChar; - FspPosixDecodeWindowsPath(DirInfo->FileNameBuf, SizeW); } @@ -1683,6 +1709,8 @@ static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem, { memset(&dh, 0, sizeof dh); dh.filedesc = filedesc; + dh.FileSystem = FileSystem; + dh.ReaddirPlus = 0 != (f->conn_want & FSP_FUSE_CAP_READDIR_PLUS); dh.Result = STATUS_SUCCESS; if (0 != f->ops.readdir) diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index f0c3cf5d..d538552c 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -40,12 +40,13 @@ struct fuse int rellinks; struct fuse_operations ops; void *data; + unsigned conn_want; + BOOLEAN fsinit; UINT32 DebugLog; FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy; FSP_FSCTL_VOLUME_PARAMS VolumeParams; PWSTR MountPoint; FSP_FILE_SYSTEM *FileSystem; - BOOLEAN fsinit; FSP_SERVICE *Service; /* weak */ }; @@ -66,8 +67,12 @@ struct fsp_fuse_file_desc struct fuse_dirhandle { + /* ReadDirectory */ struct fsp_fuse_file_desc *filedesc; + FSP_FILE_SYSTEM *FileSystem; + BOOLEAN ReaddirPlus; NTSTATUS Result; + /* CanDelete */ BOOLEAN DotFiles, HasChild; }; diff --git a/tst/passthrough-fuse/passthrough-fuse.c b/tst/passthrough-fuse/passthrough-fuse.c index d69bb9b3..1ca04726 100644 --- a/tst/passthrough-fuse/passthrough-fuse.c +++ b/tst/passthrough-fuse/passthrough-fuse.c @@ -187,7 +187,11 @@ static int ptfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, fus errno = 0; if (0 == (de = readdir(dirp))) break; +#if defined(_WIN64) || defined(_WIN32) + if (0 != filler(buf, de->d_name, &de->d_stat, 0)) +#else if (0 != filler(buf, de->d_name, 0, 0)) +#endif return -ENOMEM; } @@ -201,6 +205,19 @@ static int ptfs_releasedir(const char *path, struct fuse_file_info *fi) return -1 != closedir(dirp) ? 0 : -errno; } +static void *ptfs_init(struct fuse_conn_info *conn) +{ +#if 0 && defined(FSP_FUSE_CAP_READDIR_PLUS) + conn->want |= (conn->capable & FSP_FUSE_CAP_READDIR_PLUS); +#endif + +#if 0 && defined(FSP_FUSE_CAP_CASE_INSENSITIVE) + conn->want |= (conn->capable & FSP_FUSE_CAP_CASE_INSENSITIVE); +#endif + + return fuse_get_context()->private_data; +} + static int ptfs_create(const char *path, fuse_mode_t mode, struct fuse_file_info *fi) { ptfs_impl_fullpath(path); @@ -254,7 +271,7 @@ static struct fuse_operations ptfs_ops = ptfs_readdir, ptfs_releasedir, 0, //fsyncdir - 0, //init + ptfs_init, 0, //destroy 0, //access ptfs_create, diff --git a/tst/passthrough-fuse/winposix.c b/tst/passthrough-fuse/winposix.c index d8da50bb..c7c7edc2 100644 --- a/tst/passthrough-fuse/winposix.c +++ b/tst/passthrough-fuse/winposix.c @@ -115,7 +115,7 @@ int statvfs(const char *path, struct fuse_statvfs *stbuf) memset(stbuf, 0, sizeof *stbuf); stbuf->f_bsize = SectorsPerCluster * BytesPerSector; - stbuf->f_frsize = BytesPerSector; + stbuf->f_frsize = SectorsPerCluster * BytesPerSector; stbuf->f_blocks = TotalNumberOfClusters; stbuf->f_bfree = NumberOfFreeClusters; stbuf->f_bavail = TotalNumberOfClusters; @@ -388,6 +388,8 @@ void rewinddir(DIR *dirp) struct dirent *readdir(DIR *dirp) { WIN32_FIND_DATAA FindData; + UINT64 CreationTime, LastAccessTime, LastWriteTime; + struct fuse_stat *stbuf = &dirp->de.d_stat; if (INVALID_HANDLE_VALUE == dirp->fh) { @@ -405,6 +407,24 @@ struct dirent *readdir(DIR *dirp) } } + CreationTime = ((PLARGE_INTEGER)(&FindData.ftCreationTime))->QuadPart - 116444736000000000; + LastAccessTime = ((PLARGE_INTEGER)(&FindData.ftLastAccessTime))->QuadPart - 116444736000000000; + LastWriteTime = ((PLARGE_INTEGER)(&FindData.ftLastWriteTime))->QuadPart - 116444736000000000; + + memset(stbuf, 0, sizeof *stbuf); + stbuf->st_mode = 0777 | + ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0); + stbuf->st_nlink = 1; + stbuf->st_size = ((UINT64)FindData.nFileSizeHigh << 32) | ((UINT64)FindData.nFileSizeLow); + stbuf->st_atim.tv_sec = LastAccessTime / 10000000; + stbuf->st_atim.tv_nsec = LastAccessTime % 10000000 * 100; + stbuf->st_mtim.tv_sec = LastWriteTime / 10000000; + stbuf->st_mtim.tv_nsec = LastWriteTime % 10000000 * 100; + stbuf->st_ctim.tv_sec = LastWriteTime / 10000000; + stbuf->st_ctim.tv_nsec = LastWriteTime % 10000000 * 100; + stbuf->st_birthtim.tv_sec = CreationTime / 10000000; + stbuf->st_birthtim.tv_nsec = CreationTime % 10000000 * 100; + strcpy(dirp->de.d_name, FindData.cFileName); return &dirp->de; diff --git a/tst/passthrough-fuse/winposix.h b/tst/passthrough-fuse/winposix.h index 0952f7a6..5bc09660 100644 --- a/tst/passthrough-fuse/winposix.h +++ b/tst/passthrough-fuse/winposix.h @@ -31,6 +31,7 @@ typedef struct _DIR DIR; struct dirent { + struct fuse_stat d_stat; char d_name[255]; };