From 952a9c3fda977bce61303ae3311a3adca3d338bf Mon Sep 17 00:00:00 2001 From: Rozhuk Ivan Date: Sat, 9 May 2020 13:29:00 +0300 Subject: [PATCH] Improve FreeBSD support: - include posix_openpt() usage patch - add workaround for readdir() issue: #211 - fix few warnings --- sshfs.c | 75 +++++++++++++++++++++++++++++++++++++++---------------- sshfs.rst | 7 ++++++ 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/sshfs.c b/sshfs.c index b102dbd1..c6378e75 100644 --- a/sshfs.c +++ b/sshfs.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,7 @@ struct sshfs { int unrel_append; int fstat_workaround; int createmode_workaround; + int readdir_workaround; int transform_symlinks; int follow_symlinks; int no_check_root; @@ -542,6 +544,7 @@ static struct fuse_opt workaround_opts[] = { SSHFS_OPT("none", truncate_workaround, 0), SSHFS_OPT("none", buflimit_workaround, 0), SSHFS_OPT("none", fstat_workaround, 0), + SSHFS_OPT("none", readdir_workaround, 0), SSHFS_OPT("rename", rename_workaround, 1), SSHFS_OPT("norename", rename_workaround, 0), SSHFS_OPT("renamexdev", renamexdev_workaround, 1), @@ -554,6 +557,8 @@ static struct fuse_opt workaround_opts[] = { SSHFS_OPT("nofstat", fstat_workaround, 0), SSHFS_OPT("createmode", createmode_workaround, 1), SSHFS_OPT("nocreatemode", createmode_workaround, 0), + SSHFS_OPT("readdir", readdir_workaround, 1), + SSHFS_OPT("noreaddir", readdir_workaround, 0), FUSE_OPT_END }; @@ -1106,7 +1111,11 @@ static int pty_master(char **name) { int mfd; +#ifdef __FreeBSD__ + mfd = posix_openpt(O_RDWR | O_NOCTTY); +#else mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); +#endif if (mfd == -1) { perror("failed to open pty"); return -1; @@ -1886,12 +1895,20 @@ static void *sshfs_init(struct fuse_conn_info *conn, if (conn->capable & FUSE_CAP_ASYNC_READ) sshfs.sync_read = 1; - // These workarounds require the "path" argument. - cfg->nullpath_ok = !(sshfs.truncate_workaround || sshfs.fstat_workaround); - - // When using multiple connections, release() needs to know the path - if (sshfs.max_conns > 1) + /* These workarounds require the "path" argument: + * - truncate_workaround + * - fstat_workaround + * - readdir_workaround + * Also it required when using multiple connections: release() + * needs to know the path. + */ + if (sshfs.truncate_workaround || + sshfs.fstat_workaround || + sshfs.readdir_workaround || + sshfs.max_conns > 1) cfg->nullpath_ok = 0; + else + cfg->nullpath_ok = 1; // Lookup of . and .. is supported conn->capable |= FUSE_CAP_EXPORT_SUPPORT; @@ -2198,6 +2215,7 @@ static int sshfs_req_pending(struct request *req) static int sftp_readdir_async(struct conn *conn, struct buffer *handle, void *buf, off_t offset, fuse_fill_dir_t filler) { + (void) offset; int err = 0; int outstanding = 0; int max = READDIR_START; @@ -2276,6 +2294,7 @@ static int sftp_readdir_async(struct conn *conn, struct buffer *handle, static int sftp_readdir_sync(struct conn *conn, struct buffer *handle, void *buf, off_t offset, fuse_fill_dir_t filler) { + (void) offset; int err; assert(offset == 0); do { @@ -2321,14 +2340,39 @@ static int sshfs_opendir(const char *path, struct fuse_file_info *fi) return err; } +static int sshfs_releasedir(const char *path, struct fuse_file_info *fi) +{ + (void) path; + int err; + struct dir_handle *handle; + + handle = (struct dir_handle*) fi->fh; + err = sftp_request(handle->conn, SSH_FXP_CLOSE, &handle->buf, 0, NULL); + pthread_mutex_lock(&sshfs.lock); + handle->conn->dir_count--; + pthread_mutex_unlock(&sshfs.lock); + buf_free(&handle->buf); + g_free(handle); + return err; +} + static int sshfs_readdir(const char *path, void *dbuf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { - (void) path; (void) flags; + (void) flags; int err; struct dir_handle *handle; + if (sshfs.readdir_workaround) { + if (path == NULL) + return -EIO; + err = sshfs_opendir(path, fi); + if (err) + return err; + offset = 0; + } + handle = (struct dir_handle*) fi->fh; if (sshfs.sync_readdir) @@ -2338,22 +2382,9 @@ static int sshfs_readdir(const char *path, void *dbuf, fuse_fill_dir_t filler, err = sftp_readdir_async(handle->conn, &handle->buf, dbuf, offset, filler); - return err; -} - -static int sshfs_releasedir(const char *path, struct fuse_file_info *fi) -{ - (void) path; - int err; - struct dir_handle *handle; + if (sshfs.readdir_workaround) + sshfs_releasedir(path, fi); - handle = (struct dir_handle*) fi->fh; - err = sftp_request(handle->conn, SSH_FXP_CLOSE, &handle->buf, 0, NULL); - pthread_mutex_lock(&sshfs.lock); - handle->conn->dir_count--; - pthread_mutex_unlock(&sshfs.lock); - buf_free(&handle->buf); - g_free(handle); return err; } @@ -3616,6 +3647,7 @@ static void usage(const char *progname) " [no]buflimit fix buffer fillup bug in server (default: off)\n" " [no]fstat always use stat() instead of fstat() (default: off)\n" " [no]createmode always pass mode 0 to create (default: off)\n" +" [no]readdir always open/read/close dir on readdir (default: on)\n" " -o idmap=TYPE user/group ID mapping (default: " IDMAP_DEFAULT ")\n" " none no translation of the ID space\n" " user only translate UID/GID of connecting user\n" @@ -4173,6 +4205,7 @@ int main(int argc, char *argv[]) sshfs.truncate_workaround = 0; sshfs.buflimit_workaround = 0; sshfs.createmode_workaround = 0; + sshfs.readdir_workaround = 1; sshfs.ssh_ver = 2; sshfs.progname = argv[0]; sshfs.max_conns = 1; diff --git a/sshfs.rst b/sshfs.rst index 0fe84f29..bd231f40 100644 --- a/sshfs.rst +++ b/sshfs.rst @@ -120,6 +120,13 @@ Options :buflimit: Work around OpenSSH "buffer fillup" bug. :createmode: Work around broken servers that produce an error when passing a non-zero mode to create, by always passing a mode of 0. + :readdir: Work around file manager used that keeps dir open while + user add/remove files/dirs, that produce an error - all dirs + become empty for a while or until remount. + This happen because handle cached after opendir() but readdir() + does not use offset. + Workaround converts readdir() into "getdir()": opendir() and + releasedir() not exported to fuse; offset set to 0. -o idmap=TYPE How to map remote UID/GIDs to local values. Possible values are: