From a6857bd42d5e225428ff0310a11277d272eb16e0 Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Thu, 6 Sep 2018 10:45:31 -0400 Subject: [PATCH 1/7] nfs: Add nfs protocol version detection Add preliminary support for NFS version 4 by implementing RPC based version detection. NFS version 4 is tried first, if it is successful, the pseudo root is mounted. If it fails, then version 3 is tried using the standard flow to find the export passed by the user Signed-off-by: Max Ehrlich --- daemon/gvfsbackendnfs.c | 176 ++++++++++++++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 36 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index b7888cca..2b75ee37 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -57,9 +57,12 @@ #include "gvfsutils.h" #include +#include #include +#include #include + struct _GVfsBackendNfs { GVfsBackend parent_instance; @@ -79,6 +82,15 @@ typedef struct int events; /* IO events we're interested in */ } NfsSource; +struct sync_cb_data { + int is_finished; + int status; + uint64_t offset; + void *return_data; + int return_int; + const char *call; +}; + G_DEFINE_TYPE (GVfsBackendNfs, g_vfs_backend_nfs, G_VFS_TYPE_BACKEND) static void @@ -165,6 +177,82 @@ nfs_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) return G_SOURCE_CONTINUE; } +static void +nfs_version_detect_cb (struct rpc_context *mount_context, + int status, + void *data, + void *private_data) +{ + struct sync_cb_data *cb_data = private_data; + + cb_data->is_finished = 1; + cb_data->status = status; + cb_data->return_data = NULL; +} + +static void +wait_for_reply (struct rpc_context *rpc, struct sync_cb_data *cb_data) +{ + struct pollfd pfd; + int revents; + int ret; + + while (!cb_data->is_finished) + { + pfd.fd = rpc_get_fd (rpc); + pfd.events = rpc_which_events (rpc); + pfd.revents = 0; + + ret = poll (&pfd, 1, 100); + if (ret < 0) + { + revents = -1; + } + else + { + revents = pfd.revents; + } + + if (rpc_service (rpc, revents) < 0) + { + cb_data->status = -EIO; + break; + } + + if (rpc_get_fd (rpc) == -1) + { + break; + } + } +} + +static int +nfs_version_detect (const char *host) +{ + struct rpc_context *ctx; + struct sync_cb_data cb_data; + int version; + + memset (&cb_data, 0, sizeof (cb_data)); + + ctx = rpc_init_context (); + rpc_connect_port_async (ctx, host, 2049, NFS4_PROGRAM, NFS_V4, nfs_version_detect_cb, &cb_data); + + wait_for_reply (ctx, &cb_data); + rpc_destroy_context (ctx); + + if (cb_data.status == RPC_STATUS_SUCCESS) + { + version = NFS_V4; + } + else + { + version = NFS_V3; + } + + return version; +} + static void do_mount (GVfsBackend *backend, GVfsJobMount *job, @@ -179,7 +267,7 @@ do_mount (GVfsBackend *backend, struct exportnode *export_list, *ptr; const char *host, *debug; char *basename, *display_name, *export = NULL; - int err, debug_val; + int err, debug_val, nfs_ver; size_t pathlen = strlen (mount_spec->mount_prefix); size_t exportlen = SIZE_MAX; static GSourceFuncs nfs_source_callbacks = { @@ -197,57 +285,73 @@ do_mount (GVfsBackend *backend, _("No hostname specified")); return; } - export_list = mount_getexports (host); - /* Find the shortest matching mount. E.g. if the given mount_prefix is - * /some/long/path and there exist two mounts, /some and /some/long, match - * against /some. */ - for (ptr = export_list; ptr; ptr = ptr->ex_next) + nfs_ver = nfs_version_detect (host); + + if (nfs_ver == NFS_V4) { - /* First check that the NFS mount point is a prefix of the mount_prefix. */ - if (g_str_has_prefix (mount_spec->mount_prefix, ptr->ex_dir)) - { - size_t this_exportlen = strlen (ptr->ex_dir); + export = strdup ("/"); + } + else + { + export_list = mount_getexports (host); - /* Check if the mount_prefix is longer than the NFS mount point. - * E.g. mount_prefix: /mnt/file, mount point: /mnt */ - if (pathlen > this_exportlen) + /* Find the shortest matching mount. E.g. if the given mount_prefix is + * /some/long/path and there exist two mounts, /some and /some/long, match + * against /some. */ + for (ptr = export_list; ptr; ptr = ptr->ex_next) + { + /* First check that the NFS mount point is a prefix of the mount_prefix. */ + if (g_str_has_prefix (mount_spec->mount_prefix, ptr->ex_dir)) { - /* Check if the mount_prefix has a slash at the correct point. - * E.g. if the mount point is /mnt, then it's a match if the - * mount_prefix is /mnt/a but not a match if the mount_prefix is - * /mnta. Choose it if it is the shortest found so far. */ - char *s = mount_spec->mount_prefix + this_exportlen; - if (*s == '/' && this_exportlen < exportlen) + size_t this_exportlen = strlen (ptr->ex_dir); + + /* Check if the mount_prefix is longer than the NFS mount point. + * E.g. mount_prefix: /mnt/file, mount point: /mnt */ + if (pathlen > this_exportlen) + { + /* Check if the mount_prefix has a slash at the correct point. + * E.g. if the mount point is /mnt, then it's a match if the + * mount_prefix is /mnt/a but not a match if the mount_prefix is + * /mnta. Choose it if it is the shortest found so far. */ + char *s = mount_spec->mount_prefix + this_exportlen; + if (*s == '/' && this_exportlen < exportlen) + { + export = ptr->ex_dir; + exportlen = this_exportlen; + } + } + /* The mount_prefix and NFS mount point are identical. Choose it if + * it is the shortest found so far. */ + else if (this_exportlen < exportlen) { export = ptr->ex_dir; exportlen = this_exportlen; } } - /* The mount_prefix and NFS mount point are identical. Choose it if - * it is the shortest found so far. */ - else if (this_exportlen < exportlen) - { - export = ptr->ex_dir; - exportlen = this_exportlen; - } } - } - if (!export) - { + if (!export) + { + mount_free_export_list (export_list); + g_vfs_job_failed_literal (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Mount point does not exist")); + return; + } + + + export = strdup (export); mount_free_export_list (export_list); - g_vfs_job_failed_literal (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - _("Mount point does not exist")); - return; } - export = strdup (export); - mount_free_export_list (export_list); - op_backend->ctx = nfs_init_context (); + if (nfs_ver == NFS_V4) + { + nfs_set_version (op_backend->ctx, NFS_V4); + } + debug = g_getenv ("GVFS_NFS_DEBUG"); if (debug) debug_val = atoi (debug); -- GitLab From 9fc83a184294b5845801ce3f09fe34800801e580 Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Thu, 6 Sep 2018 12:13:52 -0400 Subject: [PATCH 2/7] nfs: Change to stat64 There was still one instance of stat (32) being used in file reads, this needs to be changed to stat64 because libnfs does not support 32bit stats to v4 servers --- daemon/gvfsbackendnfs.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index 2b75ee37..e867cbe5 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -447,9 +447,9 @@ open_for_read_fstat_cb (int err, if (err == 0) { GVfsJobOpenForRead *op_job = G_VFS_JOB_OPEN_FOR_READ (job); - struct stat *st = data; + struct nfs_stat_64 *st = data; - if (S_ISDIR (st->st_mode)) + if (S_ISDIR (st->nfs_mode)) { struct nfsfh *fh = op_job->backend_handle; @@ -476,7 +476,7 @@ open_for_read_cb (int err, g_vfs_job_open_for_read_set_handle (op_job, data); g_vfs_job_open_for_read_set_can_seek (op_job, TRUE); - nfs_fstat_async (ctx, data, open_for_read_fstat_cb, private_data); + nfs_fstat64_async (ctx, data, open_for_read_fstat_cb, private_data); } else { @@ -524,6 +524,8 @@ try_read (GVfsBackend *backend, GVfsBackendNfs *op_backend = G_VFS_BACKEND_NFS (backend); struct nfsfh *fh = _handle; + printf("try_read\n"); + nfs_read_async (op_backend->ctx, fh, bytes_requested, read_cb, job); return TRUE; } -- GitLab From 54fe77ccda956b0a73a7f7b7fe2d26c336672bd6 Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Thu, 6 Sep 2018 14:48:00 -0400 Subject: [PATCH 3/7] nfs: Stat mount prefix before mounting NFSV4 always mounts the pseudo-root, so we need to stat the requested mount prefix before finishing the mount to make sure it exists. Signed-off-by: Max Ehrlich --- daemon/gvfsbackendnfs.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index e867cbe5..1729048b 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -377,6 +377,20 @@ do_mount (GVfsBackend *backend, return; } + if (nfs_ver == NFS_V4) + { + struct nfs_stat_64 st; + err = nfs_stat64 (op_backend->ctx, mount_spec->mount_prefix, &st); + + if (err) + { + g_vfs_job_failed_literal (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Mount point does not exist")); + return; + } + } + source = g_source_new (&nfs_source_callbacks, sizeof (NfsSource)); nfs_source = (NfsSource *) source; nfs_source->ctx = op_backend->ctx; @@ -524,8 +538,6 @@ try_read (GVfsBackend *backend, GVfsBackendNfs *op_backend = G_VFS_BACKEND_NFS (backend); struct nfsfh *fh = _handle; - printf("try_read\n"); - nfs_read_async (op_backend->ctx, fh, bytes_requested, read_cb, job); return TRUE; } -- GitLab From 51b314203ababd98f928ec4035e26a775500ffeb Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Thu, 27 Sep 2018 12:50:55 -0400 Subject: [PATCH 4/7] Fix whitespace problems --- daemon/gvfsbackendnfs.c | 61 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index 1729048b..4cce37f0 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -62,7 +62,6 @@ #include #include - struct _GVfsBackendNfs { GVfsBackend parent_instance; @@ -83,12 +82,12 @@ typedef struct } NfsSource; struct sync_cb_data { - int is_finished; - int status; - uint64_t offset; - void *return_data; - int return_int; - const char *call; + int is_finished; + int status; + uint64_t offset; + void *return_data; + int return_int; + const char *call; }; G_DEFINE_TYPE (GVfsBackendNfs, g_vfs_backend_nfs, G_VFS_TYPE_BACKEND) @@ -185,45 +184,45 @@ nfs_version_detect_cb (struct rpc_context *mount_context, { struct sync_cb_data *cb_data = private_data; - cb_data->is_finished = 1; - cb_data->status = status; - cb_data->return_data = NULL; + cb_data->is_finished = 1; + cb_data->status = status; + cb_data->return_data = NULL; } static void wait_for_reply (struct rpc_context *rpc, struct sync_cb_data *cb_data) { - struct pollfd pfd; - int revents; - int ret; + struct pollfd pfd; + int revents; + int ret; - while (!cb_data->is_finished) + while (!cb_data->is_finished) { - pfd.fd = rpc_get_fd (rpc); - pfd.events = rpc_which_events (rpc); - pfd.revents = 0; + pfd.fd = rpc_get_fd (rpc); + pfd.events = rpc_which_events (rpc); + pfd.revents = 0; - ret = poll (&pfd, 1, 100); - if (ret < 0) + ret = poll (&pfd, 1, 100); + if (ret < 0) { - revents = -1; - } + revents = -1; + } else { - revents = pfd.revents; - } + revents = pfd.revents; + } - if (rpc_service (rpc, revents) < 0) + if (rpc_service (rpc, revents) < 0) { - cb_data->status = -EIO; - break; - } + cb_data->status = -EIO; + break; + } - if (rpc_get_fd (rpc) == -1) + if (rpc_get_fd (rpc) == -1) { - break; - } - } + break; + } + } } static int -- GitLab From 6792a9daed6188b43e21606ca5608b10670131cf Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Thu, 27 Sep 2018 12:53:14 -0400 Subject: [PATCH 5/7] Simplify setting nfs version explicitly --- daemon/gvfsbackendnfs.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index 4cce37f0..e76af6f6 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -345,11 +345,7 @@ do_mount (GVfsBackend *backend, } op_backend->ctx = nfs_init_context (); - - if (nfs_ver == NFS_V4) - { - nfs_set_version (op_backend->ctx, NFS_V4); - } + nfs_set_version (op_backend->ctx, nfs_ver); debug = g_getenv ("GVFS_NFS_DEBUG"); if (debug) -- GitLab From f7470e1b0093126e2b7b002dd4f57669647d3b63 Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Thu, 27 Sep 2018 13:12:21 -0400 Subject: [PATCH 6/7] More whitespace fixes --- daemon/gvfsbackendnfs.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index e76af6f6..aec494d4 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -192,36 +192,36 @@ nfs_version_detect_cb (struct rpc_context *mount_context, static void wait_for_reply (struct rpc_context *rpc, struct sync_cb_data *cb_data) { - struct pollfd pfd; - int revents; - int ret; + struct pollfd pfd; + int revents; + int ret; - while (!cb_data->is_finished) + while (!cb_data->is_finished) { - pfd.fd = rpc_get_fd (rpc); - pfd.events = rpc_which_events (rpc); - pfd.revents = 0; + pfd.fd = rpc_get_fd (rpc); + pfd.events = rpc_which_events (rpc); + pfd.revents = 0; - ret = poll (&pfd, 1, 100); - if (ret < 0) + ret = poll (&pfd, 1, 100); + if (ret < 0) { - revents = -1; - } + revents = -1; + } else { - revents = pfd.revents; - } + revents = pfd.revents; + } - if (rpc_service (rpc, revents) < 0) + if (rpc_service (rpc, revents) < 0) { - cb_data->status = -EIO; - break; - } + cb_data->status = -EIO; + break; + } - if (rpc_get_fd (rpc) == -1) + if (rpc_get_fd (rpc) == -1) { - break; - } + break; + } } } -- GitLab From 43fa9ecdf9255d3ec0877837e69ac7ec40eacaf4 Mon Sep 17 00:00:00 2001 From: Max Ehrlich Date: Tue, 2 Oct 2018 09:45:16 -0400 Subject: [PATCH 7/7] Fixing code style --- daemon/gvfsbackendnfs.c | 51 +++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/daemon/gvfsbackendnfs.c b/daemon/gvfsbackendnfs.c index aec494d4..57022691 100644 --- a/daemon/gvfsbackendnfs.c +++ b/daemon/gvfsbackendnfs.c @@ -81,13 +81,14 @@ typedef struct int events; /* IO events we're interested in */ } NfsSource; -struct sync_cb_data { - int is_finished; - int status; - uint64_t offset; - void *return_data; - int return_int; - const char *call; +struct sync_cb_data +{ + int is_finished; + int status; + uint64_t offset; + void *return_data; + int return_int; + const char *call; }; G_DEFINE_TYPE (GVfsBackendNfs, g_vfs_backend_nfs, G_VFS_TYPE_BACKEND) @@ -184,9 +185,9 @@ nfs_version_detect_cb (struct rpc_context *mount_context, { struct sync_cb_data *cb_data = private_data; - cb_data->is_finished = 1; - cb_data->status = status; - cb_data->return_data = NULL; + cb_data->is_finished = 1; + cb_data->status = status; + cb_data->return_data = NULL; } static void @@ -199,7 +200,7 @@ wait_for_reply (struct rpc_context *rpc, struct sync_cb_data *cb_data) while (!cb_data->is_finished) { pfd.fd = rpc_get_fd (rpc); - pfd.events = rpc_which_events (rpc); + pfd.events = rpc_which_events (rpc); pfd.revents = 0; ret = poll (&pfd, 1, 100); @@ -212,17 +213,17 @@ wait_for_reply (struct rpc_context *rpc, struct sync_cb_data *cb_data) revents = pfd.revents; } - if (rpc_service (rpc, revents) < 0) + if (rpc_service (rpc, revents) < 0) { cb_data->status = -EIO; break; } - if (rpc_get_fd (rpc) == -1) + if (rpc_get_fd (rpc) == -1) { break; } - } + } } static int @@ -335,7 +336,7 @@ do_mount (GVfsBackend *backend, mount_free_export_list (export_list); g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - _("Mount point does not exist")); + _ ("Mount point does not exist")); return; } @@ -373,18 +374,18 @@ do_mount (GVfsBackend *backend, } if (nfs_ver == NFS_V4) - { - struct nfs_stat_64 st; - err = nfs_stat64 (op_backend->ctx, mount_spec->mount_prefix, &st); - - if (err) { - g_vfs_job_failed_literal (G_VFS_JOB (job), - G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - _("Mount point does not exist")); - return; + struct nfs_stat_64 st; + err = nfs_stat64 (op_backend->ctx, mount_spec->mount_prefix, &st); + + if (err) + { + g_vfs_job_failed_literal (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _ ("Mount point does not exist")); + return; + } } - } source = g_source_new (&nfs_source_callbacks, sizeof (NfsSource)); nfs_source = (NfsSource *) source; -- GitLab