- From e7b5a3ac981727e4585288bd66d1a9b2dea045dc Mon Sep 17 00:00:00 2001
 - From: Roland Mainz <roland.mainz@nrubsig.org>
 - Date: Fri, 6 Dec 2024 10:58:48 +0100
 - Subject: [PATCH] mount.nfs4: Add support for nfs://-URLs
 - Add support for RFC 2224-style nfs://-URLs as alternative to the
 - traditional hostname:/path+-o port=<tcp-port> notation,
 - providing standardised, extensible, single-string, crossplatform,
 - portable, Character-Encoding independent (e.g. mount point with
 - Japanese, Chinese, French etc. characters) and ASCII-compatible
 - descriptions of NFSv4 server resources (exports).
 - Reviewed-by: Martin Wege <martin.l.wege@gmail.com>
 - Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
 - ---
 - utils/mount/Makefile.am | 3 +-
 - utils/mount/nfs4mount.c | 69 +++++++-
 - utils/mount/nfsmount.c | 93 ++++++++--
 - utils/mount/parse_dev.c | 67 ++++++--
 - utils/mount/stropts.c | 96 ++++++++++-
 - utils/mount/urlparser1.c | 358 +++++++++++++++++++++++++++++++++++++++
 - utils/mount/urlparser1.h | 60 +++++++
 - utils/mount/utils.c | 155 +++++++++++++++++
 - utils/mount/utils.h | 23 +++
 - 9 files changed, 887 insertions(+), 37 deletions(-)
 - create mode 100644 utils/mount/urlparser1.c
 - create mode 100644 utils/mount/urlparser1.h
 - diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
 - index 83a8ee1c..0e4cab3e 100644
 - --- a/utils/mount/Makefile.am
 - +++ b/utils/mount/Makefile.am
 - @@ -13,7 +13,8 @@ sbin_PROGRAMS = mount.nfs
 - EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS)
 - mount_common = error.c network.c token.c \
 - parse_opt.c parse_dev.c \
 - - nfsmount.c nfs4mount.c stropts.c\
 - + nfsmount.c nfs4mount.c \
 - + urlparser1.c urlparser1.h stropts.c \
 - mount_constants.h error.h network.h token.h \
 - parse_opt.h parse_dev.h \
 - nfs4_mount.h stropts.h version.h \
 - diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
 - index 0fe142a7..8e4fbf30 100644
 - --- a/utils/mount/nfs4mount.c
 - +++ b/utils/mount/nfs4mount.c
 - @@ -50,8 +50,10 @@
 - #include "mount_constants.h"
 - #include "nfs4_mount.h"
 - #include "nfs_mount.h"
 - +#include "urlparser1.h"
 - #include "error.h"
 - #include "network.h"
 - +#include "utils.h"
 - #if defined(VAR_LOCK_DIR)
 - #define DEFAULT_DIR VAR_LOCK_DIR
 - @@ -182,7 +184,7 @@ int nfs4mount(const char *spec, const char *node, int flags,
 - int num_flavour = 0;
 - int ip_addr_in_opts = 0;
 - - char *hostname, *dirname, *old_opts;
 - + char *hostname, *dirname, *mb_dirname = NULL, *old_opts;
 - char new_opts[1024];
 - char *opt, *opteq;
 - char *s;
 - @@ -192,15 +194,66 @@ int nfs4mount(const char *spec, const char *node, int flags,
 - int retry;
 - int retval = EX_FAIL;
 - time_t timeout, t;
 - + int nfs_port = NFS_PORT;
 - + parsed_nfs_url pnu;
 - +
 - + (void)memset(&pnu, 0, sizeof(parsed_nfs_url));
 - if (strlen(spec) >= sizeof(hostdir)) {
 - nfs_error(_("%s: excessively long host:dir argument\n"),
 - progname);
 - goto fail;
 - }
 - - strcpy(hostdir, spec);
 - - if (parse_devname(hostdir, &hostname, &dirname))
 - - goto fail;
 - +
 - + /*
 - + * Support nfs://-URLS per RFC 2224 ("NFS URL
 - + * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
 - + * including custom port (nfs://hostname@port/path/...)
 - + * and URL parameter (e.g. nfs://.../?param1=val1¶m2=val2
 - + * support
 - + */
 - + if (is_spec_nfs_url(spec)) {
 - + if (!mount_parse_nfs_url(spec, &pnu)) {
 - + goto fail;
 - + }
 - +
 - + /*
 - + * |pnu.uctx->path| is in UTF-8, but we need the data
 - + * in the current local locale's encoding, as mount(2)
 - + * does not have something like a |MS_UTF8_SPEC| flag
 - + * to indicate that the input path is in UTF-8,
 - + * independently of the current locale
 - + */
 - + hostname = pnu.uctx->hostport.hostname;
 - + dirname = mb_dirname = utf8str2mbstr(pnu.uctx->path);
 - +
 - + (void)snprintf(hostdir, sizeof(hostdir), "%s:/%s",
 - + hostname, dirname);
 - + spec = hostdir;
 - +
 - + if (pnu.uctx->hostport.port != -1) {
 - + nfs_port = pnu.uctx->hostport.port;
 - + }
 - +
 - + /*
 - + * Values added here based on URL parameters
 - + * should be added the front of the list of options,
 - + * so users can override the nfs://-URL given default.
 - + *
 - + * FIXME: We do not do that here for |MS_RDONLY|!
 - + */
 - + if (pnu.mount_params.read_only != TRIS_BOOL_NOT_SET) {
 - + if (pnu.mount_params.read_only)
 - + flags |= MS_RDONLY;
 - + else
 - + flags &= ~MS_RDONLY;
 - + }
 - + } else {
 - + (void)strcpy(hostdir, spec);
 - +
 - + if (parse_devname(hostdir, &hostname, &dirname))
 - + goto fail;
 - + }
 - if (fill_ipv4_sockaddr(hostname, &server_addr))
 - goto fail;
 - @@ -247,7 +300,7 @@ int nfs4mount(const char *spec, const char *node, int flags,
 - /*
 - * NFSv4 specifies that the default port should be 2049
 - */
 - - server_addr.sin_port = htons(NFS_PORT);
 - + server_addr.sin_port = htons(nfs_port);
 - /* parse options */
 - @@ -474,8 +527,14 @@ int nfs4mount(const char *spec, const char *node, int flags,
 - }
 - }
 - + mount_free_parse_nfs_url(&pnu);
 - + free(mb_dirname);
 - +
 - return EX_SUCCESS;
 - fail:
 - + mount_free_parse_nfs_url(&pnu);
 - + free(mb_dirname);
 - +
 - return retval;
 - }
 - diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
 - index a1c92fe8..e61d718a 100644
 - --- a/utils/mount/nfsmount.c
 - +++ b/utils/mount/nfsmount.c
 - @@ -63,11 +63,13 @@
 - #include "xcommon.h"
 - #include "mount.h"
 - #include "nfs_mount.h"
 - +#include "urlparser1.h"
 - #include "mount_constants.h"
 - #include "nls.h"
 - #include "error.h"
 - #include "network.h"
 - #include "version.h"
 - +#include "utils.h"
 - #ifdef HAVE_RPCSVC_NFS_PROT_H
 - #include <rpcsvc/nfs_prot.h>
 - @@ -493,7 +495,7 @@ nfsmount(const char *spec, const char *node, int flags,
 - char **extra_opts, int fake, int running_bg)
 - {
 - char hostdir[1024];
 - - char *hostname, *dirname, *old_opts, *mounthost = NULL;
 - + char *hostname, *dirname, *mb_dirname = NULL, *old_opts, *mounthost = NULL;
 - char new_opts[1024], cbuf[1024];
 - static struct nfs_mount_data data;
 - int val;
 - @@ -521,29 +523,79 @@ nfsmount(const char *spec, const char *node, int flags,
 - time_t t;
 - time_t prevt;
 - time_t timeout;
 - + int nfsurl_port = -1;
 - + parsed_nfs_url pnu;
 - +
 - + (void)memset(&pnu, 0, sizeof(parsed_nfs_url));
 - if (strlen(spec) >= sizeof(hostdir)) {
 - nfs_error(_("%s: excessively long host:dir argument"),
 - progname);
 - goto fail;
 - }
 - - strcpy(hostdir, spec);
 - - if ((s = strchr(hostdir, ':'))) {
 - - hostname = hostdir;
 - - dirname = s + 1;
 - - *s = '\0';
 - - /* Ignore all but first hostname in replicated mounts
 - - until they can be fully supported. (mack@sgi.com) */
 - - if ((s = strchr(hostdir, ','))) {
 - +
 - + /*
 - + * Support nfs://-URLS per RFC 2224 ("NFS URL
 - + * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
 - + * including custom port (nfs://hostname@port/path/...)
 - + * and URL parameter (e.g. nfs://.../?param1=val1¶m2=val2
 - + * support
 - + */
 - + if (is_spec_nfs_url(spec)) {
 - + if (!mount_parse_nfs_url(spec, &pnu)) {
 - + goto fail;
 - + }
 - +
 - + /*
 - + * |pnu.uctx->path| is in UTF-8, but we need the data
 - + * in the current local locale's encoding, as mount(2)
 - + * does not have something like a |MS_UTF8_SPEC| flag
 - + * to indicate that the input path is in UTF-8,
 - + * independently of the current locale
 - + */
 - + hostname = pnu.uctx->hostport.hostname;
 - + dirname = mb_dirname = utf8str2mbstr(pnu.uctx->path);
 - +
 - + (void)snprintf(hostdir, sizeof(hostdir), "%s:/%s",
 - + hostname, dirname);
 - + spec = hostdir;
 - +
 - + if (pnu.uctx->hostport.port != -1) {
 - + nfsurl_port = pnu.uctx->hostport.port;
 - + }
 - +
 - + /*
 - + * Values added here based on URL parameters
 - + * should be added the front of the list of options,
 - + * so users can override the nfs://-URL given default.
 - + *
 - + * FIXME: We do not do that here for |MS_RDONLY|!
 - + */
 - + if (pnu.mount_params.read_only != TRIS_BOOL_NOT_SET) {
 - + if (pnu.mount_params.read_only)
 - + flags |= MS_RDONLY;
 - + else
 - + flags &= ~MS_RDONLY;
 - + }
 - + } else {
 - + (void)strcpy(hostdir, spec);
 - + if ((s = strchr(hostdir, ':'))) {
 - + hostname = hostdir;
 - + dirname = s + 1;
 - *s = '\0';
 - - nfs_error(_("%s: warning: "
 - - "multiple hostnames not supported"),
 - + /* Ignore all but first hostname in replicated mounts
 - + until they can be fully supported. (mack@sgi.com) */
 - + if ((s = strchr(hostdir, ','))) {
 - + *s = '\0';
 - + nfs_error(_("%s: warning: "
 - + "multiple hostnames not supported"),
 - progname);
 - - }
 - - } else {
 - - nfs_error(_("%s: directory to mount not in host:dir format"),
 - + }
 - + } else {
 - + nfs_error(_("%s: directory to mount not in host:dir format"),
 - progname);
 - - goto fail;
 - + goto fail;
 - + }
 - }
 - if (!nfs_gethostbyname(hostname, nfs_saddr))
 - @@ -579,6 +631,14 @@ nfsmount(const char *spec, const char *node, int flags,
 - memset(nfs_pmap, 0, sizeof(*nfs_pmap));
 - nfs_pmap->pm_prog = NFS_PROGRAM;
 - + if (nfsurl_port != -1) {
 - + /*
 - + * Set custom TCP port defined by a nfs://-URL here,
 - + * so $ mount -o port ... # can be used to override
 - + */
 - + nfs_pmap->pm_port = nfsurl_port;
 - + }
 - +
 - /* parse options */
 - new_opts[0] = 0;
 - if (!parse_options(old_opts, &data, &bg, &retry, &mnt_server, &nfs_server,
 - @@ -863,10 +923,13 @@ noauth_flavors:
 - }
 - }
 - + mount_free_parse_nfs_url(&pnu);
 - +
 - return EX_SUCCESS;
 - /* abort */
 - fail:
 - + mount_free_parse_nfs_url(&pnu);
 - if (fsock != -1)
 - close(fsock);
 - return retval;
 - diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c
 - index 2ade5d5d..d9f8cf59 100644
 - --- a/utils/mount/parse_dev.c
 - +++ b/utils/mount/parse_dev.c
 - @@ -27,6 +27,8 @@
 - #include "xcommon.h"
 - #include "nls.h"
 - #include "parse_dev.h"
 - +#include "urlparser1.h"
 - +#include "utils.h"
 - #ifndef NFS_MAXHOSTNAME
 - #define NFS_MAXHOSTNAME (255)
 - @@ -179,17 +181,62 @@ static int nfs_parse_square_bracket(const char *dev,
 - }
 - /*
 - - * RFC 2224 says an NFS client must grok "public file handles" to
 - - * support NFS URLs. Linux doesn't do that yet. Print a somewhat
 - - * helpful error message in this case instead of pressing forward
 - - * with the mount request and failing with a cryptic error message
 - - * later.
 - + * Support nfs://-URLS per RFC 2224 ("NFS URL
 - + * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
 - + * including port support (nfs://hostname@port/path/...)
 - */
 - -static int nfs_parse_nfs_url(__attribute__((unused)) const char *dev,
 - - __attribute__((unused)) char **hostname,
 - - __attribute__((unused)) char **pathname)
 - +static int nfs_parse_nfs_url(const char *dev,
 - + char **out_hostname,
 - + char **out_pathname)
 - {
 - - nfs_error(_("%s: NFS URLs are not supported"), progname);
 - + parsed_nfs_url pnu;
 - +
 - + if (out_hostname)
 - + *out_hostname = NULL;
 - + if (out_pathname)
 - + *out_pathname = NULL;
 - +
 - + /*
 - + * Support nfs://-URLS per RFC 2224 ("NFS URL
 - + * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
 - + * including custom port (nfs://hostname@port/path/...)
 - + * and URL parameter (e.g. nfs://.../?param1=val1¶m2=val2
 - + * support
 - + */
 - + if (!mount_parse_nfs_url(dev, &pnu)) {
 - + goto fail;
 - + }
 - +
 - + if (pnu.uctx->hostport.port != -1) {
 - + /* NOP here, unless we switch from hostname to hostport */
 - + }
 - +
 - + if (out_hostname)
 - + *out_hostname = strdup(pnu.uctx->hostport.hostname);
 - + if (out_pathname)
 - + *out_pathname = utf8str2mbstr(pnu.uctx->path);
 - +
 - + if (((out_hostname)?(*out_hostname == NULL):0) ||
 - + ((out_pathname)?(*out_pathname == NULL):0)) {
 - + nfs_error(_("%s: out of memory"),
 - + progname);
 - + goto fail;
 - + }
 - +
 - + mount_free_parse_nfs_url(&pnu);
 - +
 - + return 1;
 - +
 - +fail:
 - + mount_free_parse_nfs_url(&pnu);
 - + if (out_hostname) {
 - + free(*out_hostname);
 - + *out_hostname = NULL;
 - + }
 - + if (out_pathname) {
 - + free(*out_pathname);
 - + *out_pathname = NULL;
 - + }
 - return 0;
 - }
 - @@ -223,7 +270,7 @@ int nfs_parse_devname(const char *devname,
 - return nfs_pdn_nomem_err();
 - if (*dev == '[')
 - result = nfs_parse_square_bracket(dev, hostname, pathname);
 - - else if (strncmp(dev, "nfs://", 6) == 0)
 - + else if (is_spec_nfs_url(dev))
 - result = nfs_parse_nfs_url(dev, hostname, pathname);
 - else
 - result = nfs_parse_simple_hostname(dev, hostname, pathname);
 - diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
 - index 23f0a8c0..ad92ab78 100644
 - --- a/utils/mount/stropts.c
 - +++ b/utils/mount/stropts.c
 - @@ -42,6 +42,7 @@
 - #include "nls.h"
 - #include "nfsrpc.h"
 - #include "mount_constants.h"
 - +#include "urlparser1.h"
 - #include "stropts.h"
 - #include "error.h"
 - #include "network.h"
 - @@ -50,6 +51,7 @@
 - #include "parse_dev.h"
 - #include "conffile.h"
 - #include "misc.h"
 - +#include "utils.h"
 - #ifndef NFS_PROGRAM
 - #define NFS_PROGRAM (100003)
 - @@ -643,24 +645,106 @@ static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
 - {
 - char *options = NULL;
 - int result;
 - + int nfs_port = 2049;
 - if (mi->fake)
 - return 1;
 - - if (po_join(opts, &options) == PO_FAILED) {
 - - errno = EIO;
 - - return 0;
 - - }
 - + /*
 - + * Support nfs://-URLS per RFC 2224 ("NFS URL
 - + * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
 - + * including custom port (nfs://hostname@port/path/...)
 - + * and URL parameter (e.g. nfs://.../?param1=val1¶m2=val2
 - + * support
 - + */
 - + if (is_spec_nfs_url(mi->spec)) {
 - + parsed_nfs_url pnu;
 - + char *mb_path;
 - + char mount_source[1024];
 - +
 - + if (!mount_parse_nfs_url(mi->spec, &pnu)) {
 - + result = 1;
 - + errno = EINVAL;
 - + goto done;
 - + }
 - +
 - + /*
 - + * |pnu.uctx->path| is in UTF-8, but we need the data
 - + * in the current local locale's encoding, as mount(2)
 - + * does not have something like a |MS_UTF8_SPEC| flag
 - + * to indicate that the input path is in UTF-8,
 - + * independently of the current locale
 - + */
 - + mb_path = utf8str2mbstr(pnu.uctx->path);
 - + if (!mb_path) {
 - + nfs_error(_("%s: Could not convert path to local encoding."),
 - + progname);
 - + mount_free_parse_nfs_url(&pnu);
 - + result = 1;
 - + errno = EINVAL;
 - + goto done;
 - + }
 - +
 - + (void)snprintf(mount_source, sizeof(mount_source),
 - + "%s:/%s",
 - + pnu.uctx->hostport.hostname,
 - + mb_path);
 - + free(mb_path);
 - +
 - + if (pnu.uctx->hostport.port != -1) {
 - + nfs_port = pnu.uctx->hostport.port;
 - + }
 - - result = mount(mi->spec, mi->node, mi->type,
 - + /*
 - + * Insert "port=" option with the value from the nfs://
 - + * URL at the beginning of the list of options, so
 - + * users can override it with $ mount.nfs4 -o port= #,
 - + * e.g.
 - + * $ mount.nfs4 -o port=1234 nfs://10.49.202.230:400//bigdisk /mnt4 #
 - + * should use port 1234, and not port 400 as specified
 - + * in the URL.
 - + */
 - + char portoptbuf[5+32+1];
 - + (void)snprintf(portoptbuf, sizeof(portoptbuf), "port=%d", nfs_port);
 - + (void)po_insert(opts, portoptbuf);
 - +
 - + if (pnu.mount_params.read_only != TRIS_BOOL_NOT_SET) {
 - + if (pnu.mount_params.read_only)
 - + mi->flags |= MS_RDONLY;
 - + else
 - + mi->flags &= ~MS_RDONLY;
 - + }
 - +
 - + mount_free_parse_nfs_url(&pnu);
 - +
 - + if (po_join(opts, &options) == PO_FAILED) {
 - + errno = EIO;
 - + result = 1;
 - + goto done;
 - + }
 - +
 - + result = mount(mount_source, mi->node, mi->type,
 - + mi->flags & ~(MS_USER|MS_USERS), options);
 - + free(options);
 - + } else {
 - + if (po_join(opts, &options) == PO_FAILED) {
 - + errno = EIO;
 - + result = 1;
 - + goto done;
 - + }
 - +
 - + result = mount(mi->spec, mi->node, mi->type,
 - mi->flags & ~(MS_USER|MS_USERS), options);
 - - free(options);
 - + free(options);
 - + }
 - if (verbose && result) {
 - int save = errno;
 - nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
 - errno = save;
 - }
 - +
 - +done:
 - return !result;
 - }
 - diff --git a/utils/mount/urlparser1.c b/utils/mount/urlparser1.c
 - new file mode 100644
 - index 00000000..d4c6f339
 - --- /dev/null
 - +++ b/utils/mount/urlparser1.c
 - @@ -0,0 +1,358 @@
 - +/*
 - + * MIT License
 - + *
 - + * Copyright (c) 2024 Roland Mainz <roland.mainz@nrubsig.org>
 - + *
 - + * Permission is hereby granted, free of charge, to any person obtaining a copy
 - + * of this software and associated documentation files (the "Software"), to deal
 - + * in the Software without restriction, including without limitation the rights
 - + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 - + * copies of the Software, and to permit persons to whom the Software is
 - + * furnished to do so, subject to the following conditions:
 - + *
 - + * The above copyright notice and this permission notice shall be included in all
 - + * copies or substantial portions of the Software.
 - + *
 - + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 - + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 - + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 - + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 - + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 - + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 - + * SOFTWARE.
 - + */
 - +
 - +/* urlparser1.c - simple URL parser */
 - +
 - +#include <stdlib.h>
 - +#include <stdbool.h>
 - +#include <string.h>
 - +#include <ctype.h>
 - +#include <stdio.h>
 - +
 - +#ifdef DBG_USE_WIDECHAR
 - +#include <wchar.h>
 - +#include <locale.h>
 - +#include <io.h>
 - +#include <fcntl.h>
 - +#endif /* DBG_USE_WIDECHAR */
 - +
 - +#include "urlparser1.h"
 - +
 - +typedef struct _url_parser_context_private {
 - + url_parser_context c;
 - +
 - + /* Private data */
 - + char *parameter_string_buff;
 - +} url_parser_context_private;
 - +
 - +#define MAX_URL_PARAMETERS 256
 - +
 - +/*
 - + * Original extended regular expression:
 - + *
 - + * "^"
 - + * "(.+?)" // scheme
 - + * "://" // '://'
 - + * "(" // login
 - + * "(?:"
 - + * "(.+?)" // user (optional)
 - + * "(?::(.+))?" // password (optional)
 - + * "@"
 - + * ")?"
 - + * "(" // hostport
 - + * "(.+?)" // host
 - + * "(?::([[:digit:]]+))?" // port (optional)
 - + * ")"
 - + * ")"
 - + * "(?:/(.*?))?" // path (optional)
 - + * "(?:\?(.*?))?" // URL parameters (optional)
 - + * "$"
 - + */
 - +
 - +#define DBGNULLSTR(s) (((s)!=NULL)?(s):"<NULL>")
 - +#if 0 || defined(TEST_URLPARSER)
 - +#define D(x) x
 - +#else
 - +#define D(x)
 - +#endif
 - +
 - +#ifdef DBG_USE_WIDECHAR
 - +/*
 - + * Use wide-char APIs on WIN32, otherwise we cannot output
 - + * Japanese/Chinese/etc correctly
 - + */
 - +#define DBG_PUTS(str, fp) fputws(L"" str, (fp))
 - +#define DBG_PUTC(c, fp) fputwc(btowc(c), (fp))
 - +#define DBG_PRINTF(fp, fmt, ...) fwprintf((fp), L"" fmt, __VA_ARGS__)
 - +#else
 - +#define DBG_PUTS(str, fp) fputs((str), (fp))
 - +#define DBG_PUTC(c, fp) fputc((c), (fp))
 - +#define DBG_PRINTF(fp, fmt, ...) fprintf((fp), fmt, __VA_ARGS__)
 - +#endif /* DBG_USE_WIDECHAR */
 - +
 - +static
 - +void urldecodestr(char *outbuff, const char *buffer, size_t len)
 - +{
 - + size_t i, j;
 - +
 - + for (i = j = 0 ; i < len ; ) {
 - + switch (buffer[i]) {
 - + case '%':
 - + if ((i + 2) < len) {
 - + if (isxdigit((int)buffer[i+1]) && isxdigit((int)buffer[i+2])) {
 - + const char hexstr[3] = {
 - + buffer[i+1],
 - + buffer[i+2],
 - + '\0'
 - + };
 - + outbuff[j++] = (unsigned char)strtol(hexstr, NULL, 16);
 - + i += 3;
 - + } else {
 - + /* invalid hex digit */
 - + outbuff[j++] = buffer[i];
 - + i++;
 - + }
 - + } else {
 - + /* incomplete hex digit */
 - + outbuff[j++] = buffer[i];
 - + i++;
 - + }
 - + break;
 - + case '+':
 - + outbuff[j++] = ' ';
 - + i++;
 - + break;
 - + default:
 - + outbuff[j++] = buffer[i++];
 - + break;
 - + }
 - + }
 - +
 - + outbuff[j] = '\0';
 - +}
 - +
 - +url_parser_context *url_parser_create_context(const char *in_url, unsigned int flags)
 - +{
 - + url_parser_context_private *uctx;
 - + char *s;
 - + size_t in_url_len;
 - + size_t context_len;
 - +
 - + /* |flags| is for future extensions */
 - + (void)flags;
 - +
 - + if (!in_url)
 - + return NULL;
 - +
 - + in_url_len = strlen(in_url);
 - +
 - + context_len = sizeof(url_parser_context_private) +
 - + ((in_url_len+1)*6) +
 - + (sizeof(url_parser_name_value)*MAX_URL_PARAMETERS)+sizeof(void*);
 - + uctx = malloc(context_len);
 - + if (!uctx)
 - + return NULL;
 - +
 - + s = (void *)(uctx+1);
 - + uctx->c.in_url = s; s+= in_url_len+1;
 - + (void)strcpy(uctx->c.in_url, in_url);
 - + uctx->c.scheme = s; s+= in_url_len+1;
 - + uctx->c.login.username = s; s+= in_url_len+1;
 - + uctx->c.hostport.hostname = s; s+= in_url_len+1;
 - + uctx->c.path = s; s+= in_url_len+1;
 - + uctx->c.hostport.port = -1;
 - + uctx->c.num_parameters = -1;
 - + uctx->c.parameters = (void *)s; s+= (sizeof(url_parser_name_value)*MAX_URL_PARAMETERS)+sizeof(void*);
 - + uctx->parameter_string_buff = s; s+= in_url_len+1;
 - +
 - + return &uctx->c;
 - +}
 - +
 - +int url_parser_parse(url_parser_context *ctx)
 - +{
 - + url_parser_context_private *uctx = (url_parser_context_private *)ctx;
 - +
 - + D((void)DBG_PRINTF(stderr, "## parser in_url='%s'\n", uctx->c.in_url));
 - +
 - + char *s;
 - + const char *urlstr = uctx->c.in_url;
 - + size_t slen;
 - +
 - + s = strstr(urlstr, "://");
 - + if (!s) {
 - + D((void)DBG_PUTS("url_parser: Not an URL\n", stderr));
 - + return -1;
 - + }
 - +
 - + slen = s-urlstr;
 - + (void)memcpy(uctx->c.scheme, urlstr, slen);
 - + uctx->c.scheme[slen] = '\0';
 - + urlstr += slen + 3;
 - +
 - + D((void)DBG_PRINTF(stdout, "scheme='%s', rest='%s'\n", uctx->c.scheme, urlstr));
 - +
 - + s = strstr(urlstr, "@");
 - + if (s) {
 - + /* URL has user/password */
 - + slen = s-urlstr;
 - + urldecodestr(uctx->c.login.username, urlstr, slen);
 - + urlstr += slen + 1;
 - +
 - + s = strstr(uctx->c.login.username, ":");
 - + if (s) {
 - + /* found passwd */
 - + uctx->c.login.passwd = s+1;
 - + *s = '\0';
 - + }
 - + else {
 - + uctx->c.login.passwd = NULL;
 - + }
 - +
 - + /* catch password-only URLs */
 - + if (uctx->c.login.username[0] == '\0')
 - + uctx->c.login.username = NULL;
 - + }
 - + else {
 - + uctx->c.login.username = NULL;
 - + uctx->c.login.passwd = NULL;
 - + }
 - +
 - + D((void)DBG_PRINTF(stdout, "login='%s', passwd='%s', rest='%s'\n",
 - + DBGNULLSTR(uctx->c.login.username),
 - + DBGNULLSTR(uctx->c.login.passwd),
 - + DBGNULLSTR(urlstr)));
 - +
 - + char *raw_parameters;
 - +
 - + uctx->c.num_parameters = 0;
 - + raw_parameters = strstr(urlstr, "?");
 - + /* Do we have a non-empty parameter string ? */
 - + if (raw_parameters && (raw_parameters[1] != '\0')) {
 - + *raw_parameters++ = '\0';
 - + D((void)DBG_PRINTF(stdout, "raw parameters = '%s'\n", raw_parameters));
 - +
 - + char *ps = raw_parameters;
 - + char *pv; /* parameter value */
 - + char *na; /* next '&' */
 - + char *pb = uctx->parameter_string_buff;
 - + char *pname;
 - + char *pvalue;
 - + ssize_t pi;
 - +
 - + for (pi = 0; pi < MAX_URL_PARAMETERS ; pi++) {
 - + pname = ps;
 - +
 - + /*
 - + * Handle parameters without value,
 - + * e.g. "path?name1&name2=value2"
 - + */
 - + na = strstr(ps, "&");
 - + pv = strstr(ps, "=");
 - + if (pv && (na?(na > pv):true)) {
 - + *pv++ = '\0';
 - + pvalue = pv;
 - + ps = pv;
 - + }
 - + else {
 - + pvalue = NULL;
 - + }
 - +
 - + if (na) {
 - + *na++ = '\0';
 - + }
 - +
 - + /* URLDecode parameter name */
 - + urldecodestr(pb, pname, strlen(pname));
 - + uctx->c.parameters[pi].name = pb;
 - + pb += strlen(uctx->c.parameters[pi].name)+1;
 - +
 - + /* URLDecode parameter value */
 - + if (pvalue) {
 - + urldecodestr(pb, pvalue, strlen(pvalue));
 - + uctx->c.parameters[pi].value = pb;
 - + pb += strlen(uctx->c.parameters[pi].value)+1;
 - + }
 - + else {
 - + uctx->c.parameters[pi].value = NULL;
 - + }
 - +
 - + /* Next '&' ? */
 - + if (!na)
 - + break;
 - +
 - + ps = na;
 - + }
 - +
 - + uctx->c.num_parameters = pi+1;
 - + }
 - +
 - + s = strstr(urlstr, "/");
 - + if (s) {
 - + /* URL has hostport */
 - + slen = s-urlstr;
 - + urldecodestr(uctx->c.hostport.hostname, urlstr, slen);
 - + urlstr += slen + 1;
 - +
 - + /*
 - + * check for addresses within '[' and ']', like
 - + * IPv6 addresses
 - + */
 - + s = uctx->c.hostport.hostname;
 - + if (s[0] == '[')
 - + s = strstr(s, "]");
 - +
 - + if (s == NULL) {
 - + D((void)DBG_PUTS("url_parser: Unmatched '[' in hostname\n", stderr));
 - + return -1;
 - + }
 - +
 - + s = strstr(s, ":");
 - + if (s) {
 - + /* found port number */
 - + uctx->c.hostport.port = atoi(s+1);
 - + *s = '\0';
 - + }
 - + }
 - + else {
 - + (void)strcpy(uctx->c.hostport.hostname, urlstr);
 - + uctx->c.path = NULL;
 - + urlstr = NULL;
 - + }
 - +
 - + D(
 - + (void)DBG_PRINTF(stdout,
 - + "hostport='%s', port=%d, rest='%s', num_parameters=%d\n",
 - + DBGNULLSTR(uctx->c.hostport.hostname),
 - + uctx->c.hostport.port,
 - + DBGNULLSTR(urlstr),
 - + (int)uctx->c.num_parameters);
 - + );
 - +
 - +
 - + D(
 - + ssize_t dpi;
 - + for (dpi = 0 ; dpi < uctx->c.num_parameters ; dpi++) {
 - + (void)DBG_PRINTF(stdout,
 - + "param[%d]: name='%s'/value='%s'\n",
 - + (int)dpi,
 - + uctx->c.parameters[dpi].name,
 - + DBGNULLSTR(uctx->c.parameters[dpi].value));
 - + }
 - + );
 - +
 - + if (!urlstr) {
 - + goto done;
 - + }
 - +
 - + urldecodestr(uctx->c.path, urlstr, strlen(urlstr));
 - + D((void)DBG_PRINTF(stdout, "path='%s'\n", uctx->c.path));
 - +
 - +done:
 - + return 0;
 - +}
 - +
 - +void url_parser_free_context(url_parser_context *c)
 - +{
 - + free(c);
 - +}
 - diff --git a/utils/mount/urlparser1.h b/utils/mount/urlparser1.h
 - new file mode 100644
 - index 00000000..515eea9d
 - --- /dev/null
 - +++ b/utils/mount/urlparser1.h
 - @@ -0,0 +1,60 @@
 - +/*
 - + * MIT License
 - + *
 - + * Copyright (c) 2024 Roland Mainz <roland.mainz@nrubsig.org>
 - + *
 - + * Permission is hereby granted, free of charge, to any person obtaining a copy
 - + * of this software and associated documentation files (the "Software"), to deal
 - + * in the Software without restriction, including without limitation the rights
 - + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 - + * copies of the Software, and to permit persons to whom the Software is
 - + * furnished to do so, subject to the following conditions:
 - + *
 - + * The above copyright notice and this permission notice shall be included in all
 - + * copies or substantial portions of the Software.
 - + *
 - + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 - + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 - + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 - + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 - + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 - + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 - + * SOFTWARE.
 - + */
 - +
 - +/* urlparser1.h - header for simple URL parser */
 - +
 - +#ifndef __URLPARSER1_H__
 - +#define __URLPARSER1_H__ 1
 - +
 - +#include <stdlib.h>
 - +
 - +typedef struct _url_parser_name_value {
 - + char *name;
 - + char *value;
 - +} url_parser_name_value;
 - +
 - +typedef struct _url_parser_context {
 - + char *in_url;
 - +
 - + char *scheme;
 - + struct {
 - + char *username;
 - + char *passwd;
 - + } login;
 - + struct {
 - + char *hostname;
 - + signed int port;
 - + } hostport;
 - + char *path;
 - +
 - + ssize_t num_parameters;
 - + url_parser_name_value *parameters;
 - +} url_parser_context;
 - +
 - +/* Prototypes */
 - +url_parser_context *url_parser_create_context(const char *in_url, unsigned int flags);
 - +int url_parser_parse(url_parser_context *uctx);
 - +void url_parser_free_context(url_parser_context *c);
 - +
 - +#endif /* !__URLPARSER1_H__ */
 - diff --git a/utils/mount/utils.c b/utils/mount/utils.c
 - index b7562a47..2d4cfa5a 100644
 - --- a/utils/mount/utils.c
 - +++ b/utils/mount/utils.c
 - @@ -28,6 +28,7 @@
 - #include <unistd.h>
 - #include <sys/types.h>
 - #include <sys/stat.h>
 - +#include <iconv.h>
 - #include "sockaddr.h"
 - #include "nfs_mount.h"
 - @@ -173,3 +174,157 @@ int nfs_umount23(const char *devname, char *string)
 - free(dirname);
 - return result;
 - }
 - +
 - +/* Convert UTF-8 string to multibyte string in the current locale */
 - +char *utf8str2mbstr(const char *utf8_str)
 - +{
 - + iconv_t cd;
 - +
 - + cd = iconv_open("UTF-8", "");
 - + if (cd == (iconv_t)-1) {
 - + perror("utf8str2mbstr: iconv_open failed");
 - + return NULL;
 - + }
 - +
 - + size_t inbytesleft = strlen(utf8_str);
 - + char *inbuf = (char *)utf8_str;
 - + size_t outbytesleft = inbytesleft*4+1;
 - + char *outbuf = malloc(outbytesleft);
 - + char *outbuf_orig = outbuf;
 - +
 - + if (!outbuf) {
 - + perror("utf8str2mbstr: out of memory");
 - + (void)iconv_close(cd);
 - + return NULL;
 - + }
 - +
 - + int ret = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
 - + if (ret == -1) {
 - + perror("utf8str2mbstr: iconv() failed");
 - + free(outbuf_orig);
 - + (void)iconv_close(cd);
 - + return NULL;
 - + }
 - +
 - + *outbuf = '\0';
 - +
 - + (void)iconv_close(cd);
 - + return outbuf_orig;
 - +}
 - +
 - +/* fixme: We should use |bool|! */
 - +int is_spec_nfs_url(const char *spec)
 - +{
 - + return (!strncmp(spec, "nfs://", 6));
 - +}
 - +
 - +int mount_parse_nfs_url(const char *spec, parsed_nfs_url *pnu)
 - +{
 - + int result = 1;
 - + url_parser_context *uctx = NULL;
 - +
 - + (void)memset(pnu, 0, sizeof(parsed_nfs_url));
 - + pnu->mount_params.read_only = TRIS_BOOL_NOT_SET;
 - +
 - + uctx = url_parser_create_context(spec, 0);
 - + if (!uctx) {
 - + nfs_error(_("%s: out of memory"),
 - + progname);
 - + result = 1;
 - + goto done;
 - + }
 - +
 - + if (url_parser_parse(uctx) < 0) {
 - + nfs_error(_("%s: Error parsing nfs://-URL."),
 - + progname);
 - + result = 1;
 - + goto done;
 - + }
 - + if (uctx->login.username || uctx->login.passwd) {
 - + nfs_error(_("%s: Username/Password are not defined for nfs://-URL."),
 - + progname);
 - + result = 1;
 - + goto done;
 - + }
 - + if (!uctx->path) {
 - + nfs_error(_("%s: Path missing in nfs://-URL."),
 - + progname);
 - + result = 1;
 - + goto done;
 - + }
 - + if (uctx->path[0] != '/') {
 - + nfs_error(_("%s: Relative nfs://-URLs are not supported."),
 - + progname);
 - + result = 1;
 - + goto done;
 - + }
 - +
 - + if (uctx->num_parameters > 0) {
 - + int pi;
 - + const char *pname;
 - + const char *pvalue;
 - +
 - + /*
 - + * Values added here based on URL parameters
 - + * should be added the front of the list of options,
 - + * so users can override the nfs://-URL given default.
 - + */
 - + for (pi = 0; pi < uctx->num_parameters ; pi++) {
 - + pname = uctx->parameters[pi].name;
 - + pvalue = uctx->parameters[pi].value;
 - +
 - + if (!strcmp(pname, "rw")) {
 - + if ((pvalue == NULL) || (!strcmp(pvalue, "1"))) {
 - + pnu->mount_params.read_only = TRIS_BOOL_FALSE;
 - + }
 - + else if (!strcmp(pvalue, "0")) {
 - + pnu->mount_params.read_only = TRIS_BOOL_TRUE;
 - + }
 - + else {
 - + nfs_error(_("%s: Unsupported nfs://-URL "
 - + "parameter '%s' value '%s'."),
 - + progname, pname, pvalue);
 - + result = 1;
 - + goto done;
 - + }
 - + }
 - + else if (!strcmp(pname, "ro")) {
 - + if ((pvalue == NULL) || (!strcmp(pvalue, "1"))) {
 - + pnu->mount_params.read_only = TRIS_BOOL_TRUE;
 - + }
 - + else if (!strcmp(pvalue, "0")) {
 - + pnu->mount_params.read_only = TRIS_BOOL_FALSE;
 - + }
 - + else {
 - + nfs_error(_("%s: Unsupported nfs://-URL "
 - + "parameter '%s' value '%s'."),
 - + progname, pname, pvalue);
 - + result = 1;
 - + goto done;
 - + }
 - + }
 - + else {
 - + nfs_error(_("%s: Unsupported nfs://-URL "
 - + "parameter '%s'."),
 - + progname, pname);
 - + result = 1;
 - + goto done;
 - + }
 - + }
 - + }
 - +
 - + result = 0;
 - +done:
 - + if (result != 0) {
 - + url_parser_free_context(uctx);
 - + return 0;
 - + }
 - +
 - + pnu->uctx = uctx;
 - + return 1;
 - +}
 - +
 - +void mount_free_parse_nfs_url(parsed_nfs_url *pnu)
 - +{
 - + url_parser_free_context(pnu->uctx);
 - +}
 - diff --git a/utils/mount/utils.h b/utils/mount/utils.h
 - index 224918ae..465c0a5e 100644
 - --- a/utils/mount/utils.h
 - +++ b/utils/mount/utils.h
 - @@ -24,13 +24,36 @@
 - #define _NFS_UTILS_MOUNT_UTILS_H
 - #include "parse_opt.h"
 - +#include "urlparser1.h"
 - +/* Boolean with three states: { not_set, false, true */
 - +typedef signed char tristate_bool;
 - +#define TRIS_BOOL_NOT_SET (-1)
 - +#define TRIS_BOOL_TRUE (1)
 - +#define TRIS_BOOL_FALSE (0)
 - +
 - +#define TRIS_BOOL_GET_VAL(tsb, tsb_default) \
 - + (((tsb)!=TRIS_BOOL_NOT_SET)?(tsb):(tsb_default))
 - +
 - +typedef struct _parsed_nfs_url {
 - + url_parser_context *uctx;
 - + struct {
 - + tristate_bool read_only;
 - + } mount_params;
 - +} parsed_nfs_url;
 - +
 - +/* Prototypes */
 - int discover_nfs_mount_data_version(int *string_ver);
 - void print_one(char *spec, char *node, char *type, char *opts);
 - void mount_usage(void);
 - void umount_usage(void);
 - int chk_mountpoint(const char *mount_point);
 - +char *utf8str2mbstr(const char *utf8_str);
 - +int is_spec_nfs_url(const char *spec);
 - int nfs_umount23(const char *devname, char *string);
 - +int mount_parse_nfs_url(const char *spec, parsed_nfs_url *pnu);
 - +void mount_free_parse_nfs_url(parsed_nfs_url *pnu);
 - +
 - #endif /* !_NFS_UTILS_MOUNT_UTILS_H */
 - --
 - 2.30.2
 
nfs-utils nfs://-URL support patch
Posted by Anonymous on Fri 6th Dec 2024 10:31
raw | new post
view followups (newest first): nfs-utils nfs://-URL support patch by Anonymous
modification of post by Anonymous (view diff)
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
 nrubsig.kpaste.net RSS