pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


nfs-utils nfs://-URL support patch
Posted by Anonymous on Sat 2nd Nov 2024 13:39
raw | new post
view followups (newest first): nfs-utils nfs://-URL support patch by Anonymous
modification of post by Anonymous (view diff)

  1. diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
  2. index 5ff1148c..ad1d0884 100644
  3. --- a/utils/mount/Makefile.am
  4. +++ b/utils/mount/Makefile.am
  5. @@ -13,7 +13,8 @@ sbin_PROGRAMS = mount.nfs
  6.  EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS)
  7.  mount_common = error.c network.c token.c \
  8.                     parse_opt.c parse_dev.c \
  9. -                   nfsmount.c nfs4mount.c stropts.c\
  10. +                   nfsmount.c nfs4mount.c \
  11. +                   urlparser1.c urlparser1.h stropts.c \
  12.                     mount_constants.h error.h network.h token.h \
  13.                     parse_opt.h parse_dev.h \
  14.                     nfs4_mount.h stropts.h version.h \
  15. diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
  16. index 0fe142a7..8e4fbf30 100644
  17. --- a/utils/mount/nfs4mount.c
  18. +++ b/utils/mount/nfs4mount.c
  19. @@ -50,8 +50,10 @@
  20.  #include "mount_constants.h"
  21.  #include "nfs4_mount.h"
  22.  #include "nfs_mount.h"
  23. +#include "urlparser1.h"
  24.  #include "error.h"
  25.  #include "network.h"
  26. +#include "utils.h"
  27.  
  28.  #if defined(VAR_LOCK_DIR)
  29.  #define DEFAULT_DIR VAR_LOCK_DIR
  30. @@ -182,7 +184,7 @@ int nfs4mount(const char *spec, const char *node, int flags,
  31.         int num_flavour = 0;
  32.         int ip_addr_in_opts = 0;
  33.  
  34. -       char *hostname, *dirname, *old_opts;
  35. +       char *hostname, *dirname, *mb_dirname = NULL, *old_opts;
  36.         char new_opts[1024];
  37.         char *opt, *opteq;
  38.         char *s;
  39. @@ -192,15 +194,66 @@ int nfs4mount(const char *spec, const char *node, int flags,
  40.         int retry;
  41.         int retval = EX_FAIL;
  42.         time_t timeout, t;
  43. +       int nfs_port = NFS_PORT;
  44. +       parsed_nfs_url pnu;
  45. +
  46. +       (void)memset(&pnu, 0, sizeof(parsed_nfs_url));
  47.  
  48.         if (strlen(spec) >= sizeof(hostdir)) {
  49.                 nfs_error(_("%s: excessively long host:dir argument\n"),
  50.                                 progname);
  51.                 goto fail;
  52.         }
  53. -       strcpy(hostdir, spec);
  54. -       if (parse_devname(hostdir, &hostname, &dirname))
  55. -               goto fail;
  56. +
  57. +       /*
  58. +        * Support nfs://-URLS per RFC 2224 ("NFS URL
  59. +        * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
  60. +        * including custom port (nfs://hostname@port/path/...)
  61. +        * and URL parameter (e.g. nfs://.../?param1=val1&param2=val2
  62. +        * support
  63. +        */
  64. +       if (is_spec_nfs_url(spec)) {
  65. +               if (!mount_parse_nfs_url(spec, &pnu)) {
  66. +                       goto fail;
  67. +               }
  68. +
  69. +               /*
  70. +                * |pnu.uctx->path| is in UTF-8, but we need the data
  71. +                * in the current local locale's encoding, as mount(2)
  72. +                * does not have something like a |MS_UTF8_SPEC| flag
  73. +                * to indicate that the input path is in UTF-8,
  74. +                * independently of the current locale
  75. +                */
  76. +               hostname = pnu.uctx->hostport.hostname;
  77. +               dirname = mb_dirname = utf8str2mbstr(pnu.uctx->path);
  78. +
  79. +               (void)snprintf(hostdir, sizeof(hostdir), "%s:/%s",
  80. +                       hostname, dirname);
  81. +               spec = hostdir;
  82. +
  83. +               if (pnu.uctx->hostport.port != -1) {
  84. +                       nfs_port = pnu.uctx->hostport.port;
  85. +               }
  86. +
  87. +               /*
  88. +                * Values added here based on URL parameters
  89. +                * should be added the front of the list of options,
  90. +                * so users can override the nfs://-URL given default.
  91. +                *
  92. +                * FIXME: We do not do that here for |MS_RDONLY|!
  93. +                */
  94. +               if (pnu.mount_params.read_only != TRIS_BOOL_NOT_SET) {
  95. +                       if (pnu.mount_params.read_only)
  96. +                               flags |= MS_RDONLY;
  97. +                       else
  98. +                               flags &= ~MS_RDONLY;
  99. +               }
  100. +        } else {
  101. +               (void)strcpy(hostdir, spec);
  102. +
  103. +               if (parse_devname(hostdir, &hostname, &dirname))
  104. +                       goto fail;
  105. +       }
  106.  
  107.         if (fill_ipv4_sockaddr(hostname, &server_addr))
  108.                 goto fail;
  109. @@ -247,7 +300,7 @@ int nfs4mount(const char *spec, const char *node, int flags,
  110.         /*
  111.          * NFSv4 specifies that the default port should be 2049
  112.          */
  113. -       server_addr.sin_port = htons(NFS_PORT);
  114. +       server_addr.sin_port = htons(nfs_port);
  115.  
  116.         /* parse options */
  117.  
  118. @@ -474,8 +527,14 @@ int nfs4mount(const char *spec, const char *node, int flags,
  119.                 }
  120.         }
  121.  
  122. +       mount_free_parse_nfs_url(&pnu);
  123. +       free(mb_dirname);
  124. +
  125.         return EX_SUCCESS;
  126.  
  127.  fail:
  128. +       mount_free_parse_nfs_url(&pnu);
  129. +       free(mb_dirname);
  130. +
  131.         return retval;
  132.  }
  133. diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
  134. index a1c92fe8..e61d718a 100644
  135. --- a/utils/mount/nfsmount.c
  136. +++ b/utils/mount/nfsmount.c
  137. @@ -63,11 +63,13 @@
  138.  #include "xcommon.h"
  139.  #include "mount.h"
  140.  #include "nfs_mount.h"
  141. +#include "urlparser1.h"
  142.  #include "mount_constants.h"
  143.  #include "nls.h"
  144.  #include "error.h"
  145.  #include "network.h"
  146.  #include "version.h"
  147. +#include "utils.h"
  148.  
  149.  #ifdef HAVE_RPCSVC_NFS_PROT_H
  150.  #include <rpcsvc/nfs_prot.h>
  151. @@ -493,7 +495,7 @@ nfsmount(const char *spec, const char *node, int flags,
  152.          char **extra_opts, int fake, int running_bg)
  153.  {
  154.         char hostdir[1024];
  155. -       char *hostname, *dirname, *old_opts, *mounthost = NULL;
  156. +       char *hostname, *dirname, *mb_dirname = NULL, *old_opts, *mounthost = NULL;
  157.         char new_opts[1024], cbuf[1024];
  158.         static struct nfs_mount_data data;
  159.         int val;
  160. @@ -521,29 +523,79 @@ nfsmount(const char *spec, const char *node, int flags,
  161.         time_t t;
  162.         time_t prevt;
  163.         time_t timeout;
  164. +       int nfsurl_port = -1;
  165. +       parsed_nfs_url pnu;
  166. +
  167. +       (void)memset(&pnu, 0, sizeof(parsed_nfs_url));
  168.  
  169.         if (strlen(spec) >= sizeof(hostdir)) {
  170.                 nfs_error(_("%s: excessively long host:dir argument"),
  171.                                 progname);
  172.                 goto fail;
  173.         }
  174. -       strcpy(hostdir, spec);
  175. -       if ((s = strchr(hostdir, ':'))) {
  176. -               hostname = hostdir;
  177. -               dirname = s + 1;
  178. -               *s = '\0';
  179. -               /* Ignore all but first hostname in replicated mounts
  180. -                  until they can be fully supported. (mack@sgi.com) */
  181. -               if ((s = strchr(hostdir, ','))) {
  182. +
  183. +       /*
  184. +        * Support nfs://-URLS per RFC 2224 ("NFS URL
  185. +        * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
  186. +        * including custom port (nfs://hostname@port/path/...)
  187. +        * and URL parameter (e.g. nfs://.../?param1=val1&param2=val2
  188. +        * support
  189. +        */
  190. +       if (is_spec_nfs_url(spec)) {
  191. +               if (!mount_parse_nfs_url(spec, &pnu)) {
  192. +                       goto fail;
  193. +               }
  194. +
  195. +               /*
  196. +                * |pnu.uctx->path| is in UTF-8, but we need the data
  197. +                * in the current local locale's encoding, as mount(2)
  198. +                * does not have something like a |MS_UTF8_SPEC| flag
  199. +                * to indicate that the input path is in UTF-8,
  200. +                * independently of the current locale
  201. +                */
  202. +               hostname = pnu.uctx->hostport.hostname;
  203. +               dirname = mb_dirname = utf8str2mbstr(pnu.uctx->path);
  204. +
  205. +               (void)snprintf(hostdir, sizeof(hostdir), "%s:/%s",
  206. +                       hostname, dirname);
  207. +               spec = hostdir;
  208. +
  209. +               if (pnu.uctx->hostport.port != -1) {
  210. +                       nfsurl_port = pnu.uctx->hostport.port;
  211. +               }
  212. +
  213. +               /*
  214. +                * Values added here based on URL parameters
  215. +                * should be added the front of the list of options,
  216. +                * so users can override the nfs://-URL given default.
  217. +                *
  218. +                * FIXME: We do not do that here for |MS_RDONLY|!
  219. +                */
  220. +               if (pnu.mount_params.read_only != TRIS_BOOL_NOT_SET) {
  221. +                       if (pnu.mount_params.read_only)
  222. +                               flags |= MS_RDONLY;
  223. +                       else
  224. +                               flags &= ~MS_RDONLY;
  225. +               }
  226. +        } else {
  227. +               (void)strcpy(hostdir, spec);
  228. +               if ((s = strchr(hostdir, ':'))) {
  229. +                       hostname = hostdir;
  230. +                       dirname = s + 1;
  231.                         *s = '\0';
  232. -                       nfs_error(_("%s: warning: "
  233. -                                 "multiple hostnames not supported"),
  234. +                       /* Ignore all but first hostname in replicated mounts
  235. +                          until they can be fully supported. (mack@sgi.com) */
  236. +                       if ((s = strchr(hostdir, ','))) {
  237. +                               *s = '\0';
  238. +                               nfs_error(_("%s: warning: "
  239. +                                       "multiple hostnames not supported"),
  240.                                         progname);
  241. -               }
  242. -       } else {
  243. -               nfs_error(_("%s: directory to mount not in host:dir format"),
  244. +                       }
  245. +               } else {
  246. +                       nfs_error(_("%s: directory to mount not in host:dir format"),
  247.                                 progname);
  248. -               goto fail;
  249. +                       goto fail;
  250. +               }
  251.         }
  252.  
  253.         if (!nfs_gethostbyname(hostname, nfs_saddr))
  254. @@ -579,6 +631,14 @@ nfsmount(const char *spec, const char *node, int flags,
  255.         memset(nfs_pmap, 0, sizeof(*nfs_pmap));
  256.         nfs_pmap->pm_prog = NFS_PROGRAM;
  257.  
  258. +       if (nfsurl_port != -1) {
  259. +               /*
  260. +                * Set custom TCP port defined by a nfs://-URL here,
  261. +                * so $ mount -o port ... # can be used to override
  262. +                */
  263. +               nfs_pmap->pm_port = nfsurl_port;
  264. +       }
  265. +
  266.         /* parse options */
  267.         new_opts[0] = 0;
  268.         if (!parse_options(old_opts, &data, &bg, &retry, &mnt_server, &nfs_server,
  269. @@ -863,10 +923,13 @@ noauth_flavors:
  270.                 }
  271.         }
  272.  
  273. +       mount_free_parse_nfs_url(&pnu);
  274. +
  275.         return EX_SUCCESS;
  276.  
  277.         /* abort */
  278.   fail:
  279. +       mount_free_parse_nfs_url(&pnu);
  280.         if (fsock != -1)
  281.                 close(fsock);
  282.         return retval;
  283. diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c
  284. index 2ade5d5d..d9f8cf59 100644
  285. --- a/utils/mount/parse_dev.c
  286. +++ b/utils/mount/parse_dev.c
  287. @@ -27,6 +27,8 @@
  288.  #include "xcommon.h"
  289.  #include "nls.h"
  290.  #include "parse_dev.h"
  291. +#include "urlparser1.h"
  292. +#include "utils.h"
  293.  
  294.  #ifndef NFS_MAXHOSTNAME
  295.  #define NFS_MAXHOSTNAME                (255)
  296. @@ -179,17 +181,62 @@ static int nfs_parse_square_bracket(const char *dev,
  297.  }
  298.  
  299.  /*
  300. - * RFC 2224 says an NFS client must grok "public file handles" to
  301. - * support NFS URLs.  Linux doesn't do that yet.  Print a somewhat
  302. - * helpful error message in this case instead of pressing forward
  303. - * with the mount request and failing with a cryptic error message
  304. - * later.
  305. + * Support nfs://-URLS per RFC 2224 ("NFS URL
  306. + * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
  307. + * including port support (nfs://hostname@port/path/...)
  308.   */
  309. -static int nfs_parse_nfs_url(__attribute__((unused)) const char *dev,
  310. -                            __attribute__((unused)) char **hostname,
  311. -                            __attribute__((unused)) char **pathname)
  312. +static int nfs_parse_nfs_url(const char *dev,
  313. +                            char **out_hostname,
  314. +                            char **out_pathname)
  315.  {
  316. -       nfs_error(_("%s: NFS URLs are not supported"), progname);
  317. +       parsed_nfs_url pnu;
  318. +
  319. +       if (out_hostname)
  320. +               *out_hostname = NULL;
  321. +       if (out_pathname)
  322. +               *out_pathname = NULL;
  323. +
  324. +       /*
  325. +        * Support nfs://-URLS per RFC 2224 ("NFS URL
  326. +        * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
  327. +        * including custom port (nfs://hostname@port/path/...)
  328. +        * and URL parameter (e.g. nfs://.../?param1=val1&param2=val2
  329. +        * support
  330. +        */
  331. +       if (!mount_parse_nfs_url(dev, &pnu)) {
  332. +               goto fail;
  333. +       }
  334. +
  335. +       if (pnu.uctx->hostport.port != -1) {
  336. +               /* NOP here, unless we switch from hostname to hostport */
  337. +       }
  338. +
  339. +       if (out_hostname)
  340. +               *out_hostname = strdup(pnu.uctx->hostport.hostname);
  341. +       if (out_pathname)
  342. +               *out_pathname = utf8str2mbstr(pnu.uctx->path);
  343. +
  344. +       if (((out_hostname)?(*out_hostname == NULL):0) ||
  345. +               ((out_pathname)?(*out_pathname == NULL):0)) {
  346. +               nfs_error(_("%s: out of memory"),
  347. +                       progname);
  348. +               goto fail;
  349. +       }
  350. +
  351. +       mount_free_parse_nfs_url(&pnu);
  352. +
  353. +       return 1;
  354. +
  355. +fail:
  356. +       mount_free_parse_nfs_url(&pnu);
  357. +       if (out_hostname) {
  358. +               free(*out_hostname);
  359. +               *out_hostname = NULL;
  360. +       }
  361. +       if (out_pathname) {
  362. +               free(*out_pathname);
  363. +               *out_pathname = NULL;
  364. +       }
  365.         return 0;
  366.  }
  367.  
  368. @@ -223,7 +270,7 @@ int nfs_parse_devname(const char *devname,
  369.                 return nfs_pdn_nomem_err();
  370.         if (*dev == '[')
  371.                 result = nfs_parse_square_bracket(dev, hostname, pathname);
  372. -       else if (strncmp(dev, "nfs://", 6) == 0)
  373. +       else if (is_spec_nfs_url(dev))
  374.                 result = nfs_parse_nfs_url(dev, hostname, pathname);
  375.         else
  376.                 result = nfs_parse_simple_hostname(dev, hostname, pathname);
  377. diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
  378. index 2d5fa1f2..c234c1c9 100644
  379. --- a/utils/mount/stropts.c
  380. +++ b/utils/mount/stropts.c
  381. @@ -42,6 +42,7 @@
  382.  #include "nls.h"
  383.  #include "nfsrpc.h"
  384.  #include "mount_constants.h"
  385. +#include "urlparser1.h"
  386.  #include "stropts.h"
  387.  #include "error.h"
  388.  #include "network.h"
  389. @@ -50,6 +51,7 @@
  390.  #include "parse_dev.h"
  391.  #include "conffile.h"
  392.  #include "misc.h"
  393. +#include "utils.h"
  394.  
  395.  #ifndef NFS_PROGRAM
  396.  #define NFS_PROGRAM    (100003)
  397. @@ -643,24 +645,106 @@ static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
  398.  {
  399.         char *options = NULL;
  400.         int result;
  401. +       int nfs_port = 2049;
  402.  
  403.         if (mi->fake)
  404.                 return 1;
  405.  
  406. -       if (po_join(opts, &options) == PO_FAILED) {
  407. -               errno = EIO;
  408. -               return 0;
  409. -       }
  410. +       /*
  411. +        * Support nfs://-URLS per RFC 2224 ("NFS URL
  412. +        * SCHEME", see https://www.rfc-editor.org/rfc/rfc2224.html),
  413. +        * including custom port (nfs://hostname@port/path/...)
  414. +        * and URL parameter (e.g. nfs://.../?param1=val1&param2=val2
  415. +        * support
  416. +        */
  417. +       if (is_spec_nfs_url(mi->spec)) {
  418. +               parsed_nfs_url pnu;
  419. +               char *mb_path;
  420. +               char mount_source[1024];
  421. +
  422. +               if (!mount_parse_nfs_url(mi->spec, &pnu)) {
  423. +                       result = 1;
  424. +                       errno = EINVAL;
  425. +                       goto done;
  426. +               }
  427. +
  428. +               /*
  429. +                * |pnu.uctx->path| is in UTF-8, but we need the data
  430. +                * in the current local locale's encoding, as mount(2)
  431. +                * does not have something like a |MS_UTF8_SPEC| flag
  432. +                * to indicate that the input path is in UTF-8,
  433. +                * independently of the current locale
  434. +                */
  435. +               mb_path = utf8str2mbstr(pnu.uctx->path);
  436. +               if (!mb_path) {
  437. +                       nfs_error(_("%s: Could not convert path to local encoding."),
  438. +                               progname);
  439. +                       mount_free_parse_nfs_url(&pnu);
  440. +                       result = 1;
  441. +                       errno = EINVAL;
  442. +                       goto done;
  443. +               }
  444. +
  445. +               (void)snprintf(mount_source, sizeof(mount_source),
  446. +                       "%s:/%s",
  447. +                       pnu.uctx->hostport.hostname,
  448. +                       mb_path);
  449. +               free(mb_path);
  450. +
  451. +               if (pnu.uctx->hostport.port != -1) {
  452. +                       nfs_port = pnu.uctx->hostport.port;
  453. +               }
  454.  
  455. -       result = mount(mi->spec, mi->node, mi->type,
  456. +               /*
  457. +                * Insert "port=" option with the value from the nfs://
  458. +                * URL at the beginning of the list of options, so
  459. +                * users can override it with $ mount.nfs4 -o port= #,
  460. +                * e.g.
  461. +                * $ mount.nfs4 -o port=1234 nfs://10.49.202.230:400//bigdisk /mnt4 #
  462. +                * should use port 1234, and not port 400 as specified
  463. +                * in the URL.
  464. +                */
  465. +               char portoptbuf[5+32+1];
  466. +               (void)snprintf(portoptbuf, sizeof(portoptbuf), "port=%d", nfs_port);
  467. +               (void)po_insert(opts, portoptbuf);
  468. +
  469. +               if (pnu.mount_params.read_only != TRIS_BOOL_NOT_SET) {
  470. +                       if (pnu.mount_params.read_only)
  471. +                               mi->flags |= MS_RDONLY;
  472. +                       else
  473. +                               mi->flags &= ~MS_RDONLY;
  474. +               }
  475. +
  476. +               mount_free_parse_nfs_url(&pnu);
  477. +
  478. +               if (po_join(opts, &options) == PO_FAILED) {
  479. +                       errno = EIO;
  480. +                       result = 1;
  481. +                       goto done;
  482. +               }
  483. +
  484. +               result = mount(mount_source, mi->node, mi->type,
  485. +                       mi->flags & ~(MS_USER|MS_USERS), options);
  486. +               free(options);
  487. +       } else {
  488. +               if (po_join(opts, &options) == PO_FAILED) {
  489. +                       errno = EIO;
  490. +                       result = 1;
  491. +                       goto done;
  492. +               }
  493. +
  494. +               result = mount(mi->spec, mi->node, mi->type,
  495.                         mi->flags & ~(MS_USER|MS_USERS), options);
  496. -       free(options);
  497. +               free(options);
  498. +       }
  499.  
  500.         if (verbose && result) {
  501.                 int save = errno;
  502.                 nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
  503.                 errno = save;
  504.         }
  505. +
  506. +done:
  507.         return !result;
  508.  }
  509.  
  510. diff --git a/utils/mount/urlparser1.c b/utils/mount/urlparser1.c
  511. new file mode 100644
  512. index 00000000..ad32fde8
  513. --- /dev/null
  514. +++ b/utils/mount/urlparser1.c
  515. @@ -0,0 +1,334 @@
  516. +/*
  517. + * MIT License
  518. + *
  519. + * Copyright (c) 2024 Roland Mainz <roland.mainz@nrubsig.org>
  520. + *
  521. + * Permission is hereby granted, free of charge, to any person obtaining a copy
  522. + * of this software and associated documentation files (the "Software"), to deal
  523. + * in the Software without restriction, including without limitation the rights
  524. + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  525. + * copies of the Software, and to permit persons to whom the Software is
  526. + * furnished to do so, subject to the following conditions:
  527. + *
  528. + * The above copyright notice and this permission notice shall be included in all
  529. + * copies or substantial portions of the Software.
  530. + *
  531. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  532. + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  533. + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  534. + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  535. + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  536. + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  537. + * SOFTWARE.
  538. + */
  539. +
  540. +/* urlparser1.c - simple URL parser */
  541. +
  542. +#include <stdlib.h>
  543. +#include <stdbool.h>
  544. +#include <string.h>
  545. +#include <ctype.h>
  546. +#include <stdio.h>
  547. +
  548. +#include "urlparser1.h"
  549. +
  550. +typedef struct _url_parser_context_private {
  551. +       url_parser_context c;
  552. +
  553. +       /* Private data */
  554. +       char *parameter_string_buff;
  555. +} url_parser_context_private;
  556. +
  557. +#define MAX_URL_PARAMETERS 256
  558. +
  559. +/*
  560. + * Original extended regular expression:
  561. + *
  562. + * "^"
  563. + * "(.+?)"                             // scheme
  564. + * "://"                               // '://'
  565. + * "("                                 // login
  566. + *     "(?:"
  567. + *     "(.+?)"                         // user (optional)
  568. + *             "(?::(.+))?"            // password (optional)
  569. + *             "@"
  570. + *     ")?"
  571. + *     "("                             // hostport
  572. + *             "(.+?)"                 // host
  573. + *             "(?::([[:digit:]]+))?"  // port (optional)
  574. + *     ")"
  575. + * ")"
  576. + * "(?:/(.*?))?"                       // path (optional)
  577. + * "(?:\?(.*?))?"                      // URL parameters (optional)
  578. + * "$"
  579. + */
  580. +
  581. +#define DBGNULLSTR(s) (((s)!=NULL)?(s):"<NULL>")
  582. +#if 0 || defined(TEST_URLPARSER)
  583. +#define D(x) x
  584. +#else
  585. +#define D(x)
  586. +#endif
  587. +
  588. +static
  589. +void urldecodestr(char *outbuff, const char *buffer, size_t len)
  590. +{
  591. +       size_t i, j;
  592. +
  593. +       for (i = j = 0 ; i < len ; ) {
  594. +               switch (buffer[i]) {
  595. +                       case '%':
  596. +                               if ((i + 2) < len) {
  597. +                                       if (isxdigit((int)buffer[i+1]) && isxdigit((int)buffer[i+2])) {
  598. +                                               const char hexstr[3] = {
  599. +                                                       buffer[i+1],
  600. +                                                       buffer[i+2],
  601. +                                                       '\0'
  602. +                                               };
  603. +                                               outbuff[j++] = (unsigned char)strtol(hexstr, NULL, 16);
  604. +                                               i += 3;
  605. +                                       } else {
  606. +                                               /* invalid hex digit */
  607. +                                               outbuff[j++] = buffer[i];
  608. +                                               i++;
  609. +                                       }
  610. +                               } else {
  611. +                                       /* incomplete hex digit */
  612. +                                       outbuff[j++] = buffer[i];
  613. +                                       i++;
  614. +                               }
  615. +                               break;
  616. +                       case '+':
  617. +                               outbuff[j++] = ' ';
  618. +                               i++;
  619. +                               break;
  620. +                       default:
  621. +                               outbuff[j++] = buffer[i++];
  622. +                               break;
  623. +               }
  624. +       }
  625. +
  626. +       outbuff[j] = '\0';
  627. +}
  628. +
  629. +url_parser_context *url_parser_create_context(const char *in_url, unsigned int flags)
  630. +{
  631. +       url_parser_context_private *uctx;
  632. +       char *s;
  633. +       size_t in_url_len;
  634. +       size_t context_len;
  635. +
  636. +       /* |flags| is for future extensions */
  637. +       (void)flags;
  638. +
  639. +       if (!in_url)
  640. +               return NULL;
  641. +
  642. +       in_url_len = strlen(in_url);
  643. +
  644. +       context_len = sizeof(url_parser_context_private) +
  645. +               ((in_url_len+1)*6) +
  646. +               (sizeof(url_parser_name_value)*MAX_URL_PARAMETERS)+sizeof(void*);
  647. +       uctx = malloc(context_len);
  648. +       if (!uctx)
  649. +               return NULL;
  650. +
  651. +       s = (void *)(uctx+1);
  652. +       uctx->c.in_url = s;             s+= in_url_len+1;
  653. +       (void)strcpy(uctx->c.in_url, in_url);
  654. +       uctx->c.scheme = s;             s+= in_url_len+1;
  655. +       uctx->c.login.username = s;     s+= in_url_len+1;
  656. +       uctx->c.hostport.hostname = s;  s+= in_url_len+1;
  657. +       uctx->c.path = s;               s+= in_url_len+1;
  658. +       uctx->c.hostport.port = -1;
  659. +       uctx->c.num_parameters = -1;
  660. +       uctx->c.parameters = (void *)s;         s+= (sizeof(url_parser_name_value)*MAX_URL_PARAMETERS)+sizeof(void*);
  661. +       uctx->parameter_string_buff = s;        s+= in_url_len+1;
  662. +
  663. +       return &uctx->c;
  664. +}
  665. +
  666. +int url_parser_parse(url_parser_context *ctx)
  667. +{
  668. +       url_parser_context_private *uctx = (url_parser_context_private *)ctx;
  669. +
  670. +       D((void)fprintf(stderr, "## parser in_url='%s'\n", uctx->c.in_url));
  671. +
  672. +       char *s;
  673. +       const char *urlstr = uctx->c.in_url;
  674. +       size_t slen;
  675. +
  676. +       s = strstr(urlstr, "://");
  677. +       if (!s) {
  678. +               D((void)fprintf(stderr, "url_parser: Not an URL\n"));
  679. +               return -1;
  680. +       }
  681. +
  682. +       slen = s-urlstr;
  683. +       (void)memcpy(uctx->c.scheme, urlstr, slen);
  684. +       uctx->c.scheme[slen] = '\0';
  685. +       urlstr += slen + 3;
  686. +
  687. +       D((void)fprintf(stdout, "scheme='%s', rest='%s'\n", uctx->c.scheme, urlstr));
  688. +
  689. +       s = strstr(urlstr, "@");
  690. +       if (s) {
  691. +               /* URL has user/password */
  692. +               slen = s-urlstr;
  693. +               urldecodestr(uctx->c.login.username, urlstr, slen);
  694. +               urlstr += slen + 1;
  695. +
  696. +               s = strstr(uctx->c.login.username, ":");
  697. +               if (s) {
  698. +                       /* found passwd */
  699. +                       uctx->c.login.passwd = s+1;
  700. +                       *s = '\0';
  701. +               }
  702. +               else
  703. +               {
  704. +                       uctx->c.login.passwd = NULL;
  705. +               }
  706. +
  707. +               /* catch password-only URLs */
  708. +               if (uctx->c.login.username[0] == '\0')
  709. +                       uctx->c.login.username = NULL;
  710. +       }
  711. +       else
  712. +       {
  713. +               uctx->c.login.username = NULL;
  714. +               uctx->c.login.passwd = NULL;
  715. +       }
  716. +
  717. +       D((void)fprintf(stdout, "login='%s', passwd='%s', rest='%s'\n",
  718. +               DBGNULLSTR(uctx->c.login.username),
  719. +               DBGNULLSTR(uctx->c.login.passwd),
  720. +               DBGNULLSTR(urlstr)));
  721. +
  722. +       char *raw_parameters;
  723. +
  724. +       uctx->c.num_parameters = 0;
  725. +       raw_parameters = strstr(urlstr, "?");
  726. +       if (raw_parameters) {
  727. +               *raw_parameters++ = '\0';
  728. +               D((void)fprintf(stdout, "raw parameters = '%s'\n", raw_parameters));
  729. +
  730. +               char *ps = raw_parameters;
  731. +               char *pv; /* parameter value */
  732. +               char *na; /* next '&' */
  733. +               char *pb = uctx->parameter_string_buff;
  734. +               char *pname;
  735. +               char *pvalue;
  736. +               ssize_t pi;
  737. +
  738. +               for (pi = 0; pi < MAX_URL_PARAMETERS ; pi++) {
  739. +                       pname = ps;
  740. +
  741. +                       /*
  742. +                        * Handle parameters without value,
  743. +                        * e.g. "path?name1&name2=value2"
  744. +                        */
  745. +                       na = strstr(ps, "&");
  746. +                       pv = strstr(ps, "=");
  747. +                       if (pv && (na?(na > pv):true)) {
  748. +                               *pv++ = '\0';
  749. +                               pvalue = pv;
  750. +                               ps = pv;
  751. +                       }
  752. +                       else {
  753. +                               pvalue = NULL;
  754. +                       }
  755. +
  756. +                       if (na) {
  757. +                               *na++ = '\0';
  758. +                       }
  759. +
  760. +                       /* URLDecode parameter name */
  761. +                       urldecodestr(pb, pname, strlen(pname));
  762. +                       uctx->c.parameters[pi].name = pb;
  763. +                       pb += strlen(uctx->c.parameters[pi].name)+1;
  764. +
  765. +                       /* URLDecode parameter value */
  766. +                       if (pvalue) {
  767. +                               urldecodestr(pb, pvalue, strlen(pvalue));
  768. +                               uctx->c.parameters[pi].value = pb;
  769. +                               pb += strlen(uctx->c.parameters[pi].value)+1;
  770. +                       }
  771. +                       else {
  772. +                               uctx->c.parameters[pi].value = NULL;
  773. +                       }
  774. +
  775. +                       /* Next '&' ? */
  776. +                       if (!na)
  777. +                               break;
  778. +
  779. +                       ps = na;
  780. +               }
  781. +
  782. +               uctx->c.num_parameters = pi+1;
  783. +       }
  784. +
  785. +       s = strstr(urlstr, "/");
  786. +       if (s) {
  787. +               /* URL has hostport */
  788. +               slen = s-urlstr;
  789. +               urldecodestr(uctx->c.hostport.hostname, urlstr, slen);
  790. +               urlstr += slen + 1;
  791. +
  792. +               /*
  793. +                * check for addresses within '[' and ']', like
  794. +                * IPv6 addresses
  795. +                */
  796. +               s = uctx->c.hostport.hostname;
  797. +               if (s[0] == '[')
  798. +                       s = strstr(s, "]");
  799. +
  800. +               if (s == NULL) {
  801. +                       D((void)fprintf(stderr, "url_parser: Unmatched '[' in hostname\n"));
  802. +                       return -1;
  803. +               }
  804. +
  805. +               s = strstr(s, ":");
  806. +               if (s) {
  807. +                       /* found port number */
  808. +                       uctx->c.hostport.port = atoi(s+1);
  809. +                       *s = '\0';
  810. +               }
  811. +       }
  812. +       else
  813. +       {
  814. +               (void)strcpy(uctx->c.hostport.hostname, urlstr);
  815. +               uctx->c.path = NULL;
  816. +               urlstr = NULL;
  817. +       }
  818. +
  819. +       D((void)fprintf(stdout, "hostport='%s', port=%d, rest='%s', num_parameters=%d\n",
  820. +               DBGNULLSTR(uctx->c.hostport.hostname),
  821. +               uctx->c.hostport.port,
  822. +               DBGNULLSTR(urlstr),
  823. +               (int)uctx->c.num_parameters));
  824. +
  825. +       D(
  826. +               ssize_t dpi;
  827. +               for (dpi = 0 ; dpi < uctx->c.num_parameters ; dpi++) {
  828. +                       (void)fprintf(stdout, "param[%d]: name='%s'/value='%s'\n",
  829. +                               (int)dpi,
  830. +                               uctx->c.parameters[dpi].name,
  831. +                               DBGNULLSTR(uctx->c.parameters[dpi].value));
  832. +               }
  833. +       );
  834. +
  835. +       if (!urlstr) {
  836. +               goto done;
  837. +       }
  838. +
  839. +       urldecodestr(uctx->c.path, urlstr, strlen(urlstr));
  840. +       D((void)fprintf(stdout, "path='%s'\n", uctx->c.path));
  841. +
  842. +done:
  843. +       return 0;
  844. +}
  845. +
  846. +void url_parser_free_context(url_parser_context *c)
  847. +{
  848. +       free(c);
  849. +}
  850. diff --git a/utils/mount/urlparser1.h b/utils/mount/urlparser1.h
  851. new file mode 100644
  852. index 00000000..0b7b4c91
  853. --- /dev/null
  854. +++ b/utils/mount/urlparser1.h
  855. @@ -0,0 +1,64 @@
  856. +/*
  857. + * MIT License
  858. + *
  859. + * Copyright (c) 2024 Roland Mainz <roland.mainz@nrubsig.org>
  860. + *
  861. + * Permission is hereby granted, free of charge, to any person obtaining a copy
  862. + * of this software and associated documentation files (the "Software"), to deal
  863. + * in the Software without restriction, including without limitation the rights
  864. + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  865. + * copies of the Software, and to permit persons to whom the Software is
  866. + * furnished to do so, subject to the following conditions:
  867. + *
  868. + * The above copyright notice and this permission notice shall be included in all
  869. + * copies or substantial portions of the Software.
  870. + *
  871. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  872. + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  873. + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  874. + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  875. + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  876. + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  877. + * SOFTWARE.
  878. + */
  879. +
  880. +/* urlparser1.h - header for simple URL parser */
  881. +
  882. +#ifndef __URLPARSER1_H__
  883. +#define __URLPARSER1_H__
  884. +
  885. +#include <stdlib.h>
  886. +
  887. +#ifdef _MSC_VER
  888. +typedef signed long long ssize_t;
  889. +#endif
  890. +
  891. +typedef struct _url_parser_name_value {
  892. +       char *name;
  893. +       char *value;
  894. +} url_parser_name_value;
  895. +
  896. +typedef struct _url_parser_context {
  897. +       char *in_url;
  898. +
  899. +       char *scheme;
  900. +       struct {
  901. +               char *username;
  902. +               char *passwd;
  903. +       } login;
  904. +       struct {
  905. +               char *hostname;
  906. +               signed int port;
  907. +       } hostport;
  908. +       char *path;
  909. +
  910. +       ssize_t num_parameters;
  911. +       url_parser_name_value *parameters;
  912. +} url_parser_context;
  913. +
  914. +/* Prototypes */
  915. +url_parser_context *url_parser_create_context(const char *in_url, unsigned int flags);
  916. +int url_parser_parse(url_parser_context *uctx);
  917. +void url_parser_free_context(url_parser_context *c);
  918. +
  919. +#endif /* !__URLPARSER1_H__ */
  920. diff --git a/utils/mount/utils.c b/utils/mount/utils.c
  921. index b7562a47..2d4cfa5a 100644
  922. --- a/utils/mount/utils.c
  923. +++ b/utils/mount/utils.c
  924. @@ -28,6 +28,7 @@
  925.  #include <unistd.h>
  926.  #include <sys/types.h>
  927.  #include <sys/stat.h>
  928. +#include <iconv.h>
  929.  
  930.  #include "sockaddr.h"
  931.  #include "nfs_mount.h"
  932. @@ -173,3 +174,157 @@ int nfs_umount23(const char *devname, char *string)
  933.         free(dirname);
  934.         return result;
  935.  }
  936. +
  937. +/* Convert UTF-8 string to multibyte string in the current locale */
  938. +char *utf8str2mbstr(const char *utf8_str)
  939. +{
  940. +       iconv_t cd;
  941. +
  942. +       cd = iconv_open("UTF-8", "");
  943. +       if (cd == (iconv_t)-1) {
  944. +               perror("utf8str2mbstr: iconv_open failed");
  945. +               return NULL;
  946. +       }
  947. +
  948. +       size_t inbytesleft = strlen(utf8_str);
  949. +       char *inbuf = (char *)utf8_str;
  950. +       size_t outbytesleft = inbytesleft*4+1;
  951. +       char *outbuf = malloc(outbytesleft);
  952. +       char *outbuf_orig = outbuf;
  953. +
  954. +       if (!outbuf) {
  955. +               perror("utf8str2mbstr: out of memory");
  956. +               (void)iconv_close(cd);
  957. +               return NULL;
  958. +       }
  959. +
  960. +       int ret = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
  961. +       if (ret == -1) {
  962. +               perror("utf8str2mbstr: iconv() failed");
  963. +               free(outbuf_orig);
  964. +               (void)iconv_close(cd);
  965. +               return NULL;
  966. +       }
  967. +
  968. +       *outbuf = '\0';
  969. +
  970. +       (void)iconv_close(cd);
  971. +       return outbuf_orig;
  972. +}
  973. +
  974. +/* fixme: We should use |bool|! */
  975. +int is_spec_nfs_url(const char *spec)
  976. +{
  977. +       return (!strncmp(spec, "nfs://", 6));
  978. +}
  979. +
  980. +int mount_parse_nfs_url(const char *spec, parsed_nfs_url *pnu)
  981. +{
  982. +       int result = 1;
  983. +       url_parser_context *uctx = NULL;
  984. +
  985. +       (void)memset(pnu, 0, sizeof(parsed_nfs_url));
  986. +       pnu->mount_params.read_only = TRIS_BOOL_NOT_SET;
  987. +
  988. +       uctx = url_parser_create_context(spec, 0);
  989. +       if (!uctx) {
  990. +               nfs_error(_("%s: out of memory"),
  991. +                       progname);
  992. +               result = 1;
  993. +               goto done;
  994. +       }
  995. +
  996. +       if (url_parser_parse(uctx) < 0) {
  997. +               nfs_error(_("%s: Error parsing nfs://-URL."),
  998. +                       progname);
  999. +               result = 1;
  1000. +               goto done;
  1001. +       }
  1002. +       if (uctx->login.username || uctx->login.passwd) {
  1003. +               nfs_error(_("%s: Username/Password are not defined for nfs://-URL."),
  1004. +                       progname);
  1005. +               result = 1;
  1006. +               goto done;
  1007. +       }
  1008. +       if (!uctx->path) {
  1009. +               nfs_error(_("%s: Path missing in nfs://-URL."),
  1010. +                       progname);
  1011. +               result = 1;
  1012. +               goto done;
  1013. +       }
  1014. +       if (uctx->path[0] != '/') {
  1015. +               nfs_error(_("%s: Relative nfs://-URLs are not supported."),
  1016. +                       progname);
  1017. +               result = 1;
  1018. +               goto done;
  1019. +       }
  1020. +
  1021. +       if (uctx->num_parameters > 0) {
  1022. +               int pi;
  1023. +               const char *pname;
  1024. +               const char *pvalue;
  1025. +
  1026. +               /*
  1027. +                * Values added here based on URL parameters
  1028. +                * should be added the front of the list of options,
  1029. +                * so users can override the nfs://-URL given default.
  1030. +                */
  1031. +               for (pi = 0; pi < uctx->num_parameters ; pi++) {
  1032. +                       pname = uctx->parameters[pi].name;
  1033. +                       pvalue = uctx->parameters[pi].value;
  1034. +
  1035. +                       if (!strcmp(pname, "rw")) {
  1036. +                               if ((pvalue == NULL) || (!strcmp(pvalue, "1"))) {
  1037. +                                       pnu->mount_params.read_only = TRIS_BOOL_FALSE;
  1038. +                               }
  1039. +                               else if (!strcmp(pvalue, "0")) {
  1040. +                                       pnu->mount_params.read_only = TRIS_BOOL_TRUE;
  1041. +                               }
  1042. +                               else {
  1043. +                                       nfs_error(_("%s: Unsupported nfs://-URL "
  1044. +                                               "parameter '%s' value '%s'."),
  1045. +                                               progname, pname, pvalue);
  1046. +                                       result = 1;
  1047. +                                       goto done;
  1048. +                               }
  1049. +                       }
  1050. +                       else if (!strcmp(pname, "ro")) {
  1051. +                               if ((pvalue == NULL) || (!strcmp(pvalue, "1"))) {
  1052. +                                       pnu->mount_params.read_only = TRIS_BOOL_TRUE;
  1053. +                               }
  1054. +                               else if (!strcmp(pvalue, "0")) {
  1055. +                                       pnu->mount_params.read_only = TRIS_BOOL_FALSE;
  1056. +                               }
  1057. +                               else {
  1058. +                                       nfs_error(_("%s: Unsupported nfs://-URL "
  1059. +                                               "parameter '%s' value '%s'."),
  1060. +                                               progname, pname, pvalue);
  1061. +                                       result = 1;
  1062. +                                       goto done;
  1063. +                               }
  1064. +                       }
  1065. +                       else {
  1066. +                               nfs_error(_("%s: Unsupported nfs://-URL "
  1067. +                                               "parameter '%s'."),
  1068. +                                       progname, pname);
  1069. +                               result = 1;
  1070. +                               goto done;
  1071. +                       }
  1072. +               }
  1073. +       }
  1074. +
  1075. +       result = 0;
  1076. +done:
  1077. +       if (result != 0) {
  1078. +               url_parser_free_context(uctx);
  1079. +               return 0;
  1080. +       }
  1081. +
  1082. +       pnu->uctx = uctx;
  1083. +       return 1;
  1084. +}
  1085. +
  1086. +void mount_free_parse_nfs_url(parsed_nfs_url *pnu)
  1087. +{
  1088. +       url_parser_free_context(pnu->uctx);
  1089. +}
  1090. diff --git a/utils/mount/utils.h b/utils/mount/utils.h
  1091. index 224918ae..465c0a5e 100644
  1092. --- a/utils/mount/utils.h
  1093. +++ b/utils/mount/utils.h
  1094. @@ -24,13 +24,36 @@
  1095.  #define _NFS_UTILS_MOUNT_UTILS_H
  1096.  
  1097.  #include "parse_opt.h"
  1098. +#include "urlparser1.h"
  1099.  
  1100. +/* Boolean with three states: { not_set, false, true */
  1101. +typedef signed char tristate_bool;
  1102. +#define TRIS_BOOL_NOT_SET (-1)
  1103. +#define TRIS_BOOL_TRUE (1)
  1104. +#define TRIS_BOOL_FALSE (0)
  1105. +
  1106. +#define TRIS_BOOL_GET_VAL(tsb, tsb_default) \
  1107. +       (((tsb)!=TRIS_BOOL_NOT_SET)?(tsb):(tsb_default))
  1108. +
  1109. +typedef struct _parsed_nfs_url {
  1110. +       url_parser_context *uctx;
  1111. +       struct {
  1112. +               tristate_bool read_only;
  1113. +       } mount_params;
  1114. +} parsed_nfs_url;
  1115. +
  1116. +/* Prototypes */
  1117.  int discover_nfs_mount_data_version(int *string_ver);
  1118.  void print_one(char *spec, char *node, char *type, char *opts);
  1119.  void mount_usage(void);
  1120.  void umount_usage(void);
  1121.  int chk_mountpoint(const char *mount_point);
  1122. +char *utf8str2mbstr(const char *utf8_str);
  1123. +int is_spec_nfs_url(const char *spec);
  1124.  
  1125.  int nfs_umount23(const char *devname, char *string);
  1126.  
  1127. +int mount_parse_nfs_url(const char *spec, parsed_nfs_url *pnu);
  1128. +void mount_free_parse_nfs_url(parsed_nfs_url *pnu);
  1129. +
  1130.  #endif /* !_NFS_UTILS_MOUNT_UTILS_H */

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.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at