pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


nfs-utils nfs://-URL support patch
Posted by Anonymous on Thu 31st Oct 2024 16:22
raw | new post
view followups (newest first): nfs-utils nfs://-URL support patch by Anonymous

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

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