Skip to content

Commit

Permalink
Improve FreeBSD support:
Browse files Browse the repository at this point in the history
- include posix_openpt() usage patch
- add workaround for readdir() issue: #211
- fix few warnings
  • Loading branch information
rozhuk-im committed May 9, 2020
1 parent a7e1038 commit 952a9c3
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 21 deletions.
75 changes: 54 additions & 21 deletions sshfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <netdb.h>
#include <signal.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -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
};

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions sshfs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 952a9c3

Please sign in to comment.