]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/stropts.c
c5b92fc12f4f58633d5fd17b37acf68aa23e8b4e
[nfs-utils.git] / utils / mount / stropts.c
1 /*
2  * stropts.c -- NFS mount using C string to pass options to kernel
3  *
4  * Copyright (C) 2007 Oracle.  All rights reserved.
5  * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 021110-1307, USA.
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <unistd.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <time.h>
32
33 #include <sys/socket.h>
34 #include <sys/mount.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37
38 #include "xcommon.h"
39 #include "mount.h"
40 #include "nls.h"
41 #include "nfsrpc.h"
42 #include "mount_constants.h"
43 #include "stropts.h"
44 #include "error.h"
45 #include "network.h"
46 #include "parse_opt.h"
47 #include "version.h"
48 #include "parse_dev.h"
49 #include "conffile.h"
50
51 #ifndef NFS_PROGRAM
52 #define NFS_PROGRAM     (100003)
53 #endif
54
55 #ifndef NFS_PORT
56 #define NFS_PORT        (2049)
57 #endif
58
59 #ifndef NFS_MAXHOSTNAME
60 #define NFS_MAXHOSTNAME         (255)
61 #endif
62
63 #ifndef NFS_MAXPATHNAME
64 #define NFS_MAXPATHNAME         (1024)
65 #endif
66
67 #ifndef NFS_DEF_FG_TIMEOUT_MINUTES
68 #define NFS_DEF_FG_TIMEOUT_MINUTES      (2u)
69 #endif
70
71 #ifndef NFS_DEF_BG_TIMEOUT_MINUTES
72 #define NFS_DEF_BG_TIMEOUT_MINUTES      (10000u)
73 #endif
74
75 extern int nfs_mount_data_version;
76 extern char *progname;
77 extern int verbose;
78 extern int sloppy;
79
80 union nfs_sockaddr {
81         struct sockaddr         sa;
82         struct sockaddr_in      s4;
83         struct sockaddr_in6     s6;
84 };
85
86 struct nfsmount_info {
87         const char              *spec,          /* server:/path */
88                                 *node,          /* mounted-on dir */
89                                 *type;          /* "nfs" or "nfs4" */
90         char                    *hostname;      /* server's hostname */
91         union nfs_sockaddr      address;
92         socklen_t               salen;          /* size of server's address */
93
94         struct mount_options    *options;       /* parsed mount options */
95         char                    **extra_opts;   /* string for /etc/mtab */
96
97         unsigned long           version;        /* NFS version */
98         int                     flags,          /* MS_ flags */
99                                 fake,           /* actually do the mount? */
100                                 child;          /* forked bg child? */
101 };
102
103 #ifdef MOUNT_CONFIG
104 static void nfs_default_version(struct nfsmount_info *mi);
105
106 static void nfs_default_version(struct nfsmount_info *mi)
107 {
108         extern unsigned long config_default_vers;
109         /*
110          * Use the default value set in the config file when
111          * the version has not been explicitly set.
112          */
113         if (mi->version == 0 && config_default_vers) {
114                 if (config_default_vers < 4)
115                         mi->version = config_default_vers;
116         }
117 }
118 #else
119 inline void nfs_default_version(struct nfsmount_info *mi) {}
120 #endif /* MOUNT_CONFIG */
121
122 /*
123  * Obtain a retry timeout value based on the value of the "retry=" option.
124  *
125  * Returns a time_t timeout timestamp, in seconds.
126  */
127 static time_t nfs_parse_retry_option(struct mount_options *options,
128                                      unsigned int timeout_minutes)
129 {
130         long tmp;
131
132         switch (po_get_numeric(options, "retry", &tmp)) {
133         case PO_NOT_FOUND:
134                 break;
135         case PO_FOUND:
136                 if (tmp >= 0) {
137                         timeout_minutes = tmp;
138                         break;
139                 }
140         case PO_BAD_VALUE:
141                 if (verbose)
142                         nfs_error(_("%s: invalid retry timeout was specified; "
143                                         "using default timeout"), progname);
144                 break;
145         }
146
147         return time(NULL) + (time_t)(timeout_minutes * 60);
148 }
149
150 /*
151  * Convert the passed-in sockaddr-style address to presentation
152  * format, then append an option of the form "keyword=address".
153  *
154  * Returns 1 if the option was appended successfully; otherwise zero.
155  */
156 static int nfs_append_generic_address_option(const struct sockaddr *sap,
157                                              const socklen_t salen,
158                                              const char *keyword,
159                                              struct mount_options *options)
160 {
161         char address[NI_MAXHOST];
162         char new_option[512];
163         int len;
164
165         if (!nfs_present_sockaddr(sap, salen, address, sizeof(address)))
166                 goto out_err;
167
168         len = snprintf(new_option, sizeof(new_option), "%s=%s",
169                                                 keyword, address);
170         if (len < 0 || (size_t)len >= sizeof(new_option))
171                 goto out_err;
172
173         if (po_append(options, new_option) != PO_SUCCEEDED)
174                 goto out_err;
175
176         return 1;
177
178 out_err:
179         nfs_error(_("%s: failed to construct %s option"), progname, keyword);
180         return 0;
181 }
182
183 /*
184  * Append the 'addr=' option to the options string to pass a resolved
185  * server address to the kernel.  After a successful mount, this address
186  * is also added to /etc/mtab for use when unmounting.
187  *
188  * If 'addr=' is already present, we strip it out.  This prevents users
189  * from setting a bogus 'addr=' option themselves, and also allows bg
190  * retries to recompute the server's address, in case it has changed.
191  *
192  * Returns 1 if 'addr=' option appended successfully;
193  * otherwise zero.
194  */
195 static int nfs_append_addr_option(const struct sockaddr *sap,
196                                   socklen_t salen,
197                                   struct mount_options *options)
198 {
199         po_remove_all(options, "addr");
200         return nfs_append_generic_address_option(sap, salen, "addr", options);
201 }
202
203 /*
204  * Called to discover our address and append an appropriate 'clientaddr='
205  * option to the options string.
206  *
207  * Returns 1 if 'clientaddr=' option created successfully or if
208  * 'clientaddr=' option is already present; otherwise zero.
209  */
210 static int nfs_append_clientaddr_option(const struct sockaddr *sap,
211                                         socklen_t salen,
212                                         struct mount_options *options)
213 {
214         union nfs_sockaddr address;
215         struct sockaddr *my_addr = &address.sa;
216         socklen_t my_len = sizeof(address);
217
218         if (po_contains(options, "clientaddr") == PO_FOUND)
219                 return 1;
220
221         nfs_callback_address(sap, salen, my_addr, &my_len);
222
223         return nfs_append_generic_address_option(my_addr, my_len,
224                                                         "clientaddr", options);
225 }
226
227 /*
228  * Resolve the 'mounthost=' hostname and append a new option using
229  * the resulting address.
230  */
231 static int nfs_fix_mounthost_option(struct mount_options *options)
232 {
233         union nfs_sockaddr address;
234         struct sockaddr *sap = &address.sa;
235         socklen_t salen = sizeof(address);
236         char *mounthost;
237
238         mounthost = po_get(options, "mounthost");
239         if (!mounthost)
240                 return 1;
241
242         if (!nfs_name_to_address(mounthost, sap, &salen)) {
243                 nfs_error(_("%s: unable to determine mount server's address"),
244                                 progname);
245                 return 0;
246         }
247
248         return nfs_append_generic_address_option(sap, salen,
249                                                         "mountaddr", options);
250 }
251
252 /*
253  * Returns zero if the "lock" option is in effect, but statd
254  * can't be started.  Otherwise, returns 1.
255  */
256 static const char *nfs_lock_opttbl[] = {
257         "nolock",
258         "lock",
259         NULL,
260 };
261
262 static int nfs_verify_lock_option(struct mount_options *options)
263 {
264         if (po_rightmost(options, nfs_lock_opttbl) == 0)
265                 return 1;
266
267         if (!start_statd()) {
268                 nfs_error(_("%s: rpc.statd is not running but is "
269                             "required for remote locking."), progname);
270                 nfs_error(_("%s: Either use '-o nolock' to keep "
271                             "locks local, or start statd."), progname);
272                 return 0;
273         }
274
275         return 1;
276 }
277
278 static int nfs_append_sloppy_option(struct mount_options *options)
279 {
280         if (!sloppy || linux_version_code() < MAKE_VERSION(2, 6, 27))
281                 return 1;
282
283         if (po_append(options, "sloppy") == PO_FAILED)
284                 return 0;
285         return 1;
286 }
287
288 static int nfs_set_version(struct nfsmount_info *mi)
289 {
290         if (!nfs_nfs_version(mi->options, &mi->version))
291                 return 0;
292
293         if (strncmp(mi->type, "nfs4", 4) == 0)
294                 mi->version = 4;
295         else {
296                 char *option = po_get(mi->options, "proto");
297                 if (option && strcmp(option, "rdma") == 0)
298                         mi->version = 3;
299         }
300
301         /*
302          * If we still don't know, check for version-specific
303          * mount options.
304          */
305         if (mi->version == 0) {
306                 if (po_contains(mi->options, "mounthost") ||
307                     po_contains(mi->options, "mountaddr") ||
308                     po_contains(mi->options, "mountvers") ||
309                     po_contains(mi->options, "mountproto"))
310                         mi->version = 3;
311         }
312
313         /*
314          * If enabled, see if the default version was
315          * set in the config file
316          */
317         nfs_default_version(mi);
318         
319         return 1;
320 }
321
322 /*
323  * Set up mandatory non-version specific NFS mount options.
324  *
325  * Returns 1 if successful; otherwise zero.
326  */
327 static int nfs_validate_options(struct nfsmount_info *mi)
328 {
329         struct sockaddr *sap = &mi->address.sa;
330
331         if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
332                 return 0;
333
334         mi->salen = sizeof(mi->address);
335         if (!nfs_name_to_address(mi->hostname, sap, &mi->salen))
336                 return 0;
337
338         if (!nfs_set_version(mi))
339                 return 0;
340
341         if (!nfs_append_sloppy_option(mi->options))
342                 return 0;
343
344         if (!nfs_append_addr_option(sap, mi->salen, mi->options))
345                 return 0;
346
347         return 1;
348 }
349
350 /*
351  * Get NFS/mnt server addresses from mount options
352  *
353  * Returns 1 and fills in @nfs_saddr, @nfs_salen, @mnt_saddr, and @mnt_salen
354  * if all goes well; otherwise zero.
355  */
356 static int nfs_extract_server_addresses(struct mount_options *options,
357                                         struct sockaddr *nfs_saddr,
358                                         socklen_t *nfs_salen,
359                                         struct sockaddr *mnt_saddr,
360                                         socklen_t *mnt_salen)
361 {
362         char *option;
363
364         option = po_get(options, "addr");
365         if (option == NULL)
366                 return 0;
367         if (!nfs_string_to_sockaddr(option, nfs_saddr, nfs_salen))
368                 return 0;
369
370         option = po_get(options, "mountaddr");
371         if (option == NULL) {
372                 memcpy(mnt_saddr, nfs_saddr, *nfs_salen);
373                 *mnt_salen = *nfs_salen;
374         } else if (!nfs_string_to_sockaddr(option, mnt_saddr, mnt_salen))
375                 return 0;
376
377         return 1;
378 }
379
380 static int nfs_construct_new_options(struct mount_options *options,
381                                      struct sockaddr *nfs_saddr,
382                                      struct pmap *nfs_pmap,
383                                      struct sockaddr *mnt_saddr,
384                                      struct pmap *mnt_pmap)
385 {
386         char new_option[64];
387         char *netid;
388
389         po_remove_all(options, "nfsprog");
390         po_remove_all(options, "mountprog");
391
392         po_remove_all(options, "v2");
393         po_remove_all(options, "v3");
394         po_remove_all(options, "vers");
395         po_remove_all(options, "nfsvers");
396         snprintf(new_option, sizeof(new_option) - 1,
397                  "vers=%lu", nfs_pmap->pm_vers);
398         if (po_append(options, new_option) == PO_FAILED)
399                 return 0;
400
401         po_remove_all(options, "proto");
402         po_remove_all(options, "udp");
403         po_remove_all(options, "tcp");
404         netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
405         if (netid == NULL)
406                 return 0;
407         snprintf(new_option, sizeof(new_option) - 1,
408                          "proto=%s", netid);
409         free(netid);
410         if (po_append(options, new_option) == PO_FAILED)
411                 return 0;
412
413         po_remove_all(options, "port");
414         if (nfs_pmap->pm_port != NFS_PORT) {
415                 snprintf(new_option, sizeof(new_option) - 1,
416                          "port=%lu", nfs_pmap->pm_port);
417                 if (po_append(options, new_option) == PO_FAILED)
418                         return 0;
419         }
420
421         po_remove_all(options, "mountvers");
422         snprintf(new_option, sizeof(new_option) - 1,
423                  "mountvers=%lu", mnt_pmap->pm_vers);
424         if (po_append(options, new_option) == PO_FAILED)
425                 return 0;
426
427         po_remove_all(options, "mountproto");
428         netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
429         if (netid == NULL)
430                 return 0;
431         snprintf(new_option, sizeof(new_option) - 1,
432                          "mountproto=%s", netid);
433         free(netid);
434         if (po_append(options, new_option) == PO_FAILED)
435                 return 0;
436
437         po_remove_all(options, "mountport");
438         snprintf(new_option, sizeof(new_option) - 1,
439                  "mountport=%lu", mnt_pmap->pm_port);
440         if (po_append(options, new_option) == PO_FAILED)
441                 return 0;
442
443         return 1;
444 }
445
446 /*
447  * Reconstruct the mount option string based on a portmapper probe
448  * of the server.  Returns one if the server's portmapper returned
449  * something we can use, otherwise zero.
450  *
451  * To handle version and transport protocol fallback properly, we
452  * need to parse some of the mount options in order to set up a
453  * portmap probe.  Mount options that nfs_rewrite_pmap_mount_options()
454  * doesn't recognize are left alone.
455  *
456  * Returns TRUE if rewriting was successful; otherwise
457  * FALSE is returned if some failure occurred.
458  */
459 static int
460 nfs_rewrite_pmap_mount_options(struct mount_options *options)
461 {
462         union nfs_sockaddr nfs_address;
463         struct sockaddr *nfs_saddr = &nfs_address.sa;
464         socklen_t nfs_salen = sizeof(nfs_address);
465         struct pmap nfs_pmap;
466         union nfs_sockaddr mnt_address;
467         struct sockaddr *mnt_saddr = &mnt_address.sa;
468         socklen_t mnt_salen = sizeof(mnt_address);
469         struct pmap mnt_pmap;
470         char *option;
471
472         /*
473          * Skip option negotiation for proto=rdma mounts.
474          */
475         option = po_get(options, "proto");
476         if (option && strcmp(option, "rdma") == 0)
477                 goto out;
478
479         /*
480          * Extract just the options needed to contact server.
481          * Bail now if any of these have bad values.
482          */
483         if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen,
484                                                 mnt_saddr, &mnt_salen)) {
485                 errno = EINVAL;
486                 return 0;
487         }
488         if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) {
489                 errno = EINVAL;
490                 return 0;
491         }
492
493         /*
494          * The kernel NFS client doesn't support changing the RPC
495          * program number for these services, so force the value of
496          * these fields before probing the server's ports.
497          */
498         nfs_pmap.pm_prog = NFS_PROGRAM;
499         mnt_pmap.pm_prog = MOUNTPROG;
500
501         /*
502          * If the server's rpcbind service isn't available, we can't
503          * negotiate.  Bail now if we can't contact it.
504          */
505         if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
506                                  nfs_saddr, nfs_salen, &nfs_pmap)) {
507                 errno = ESPIPE;
508                 return 0;
509         }
510
511         if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
512                                         mnt_saddr, &mnt_pmap)) {
513                 errno = EINVAL;
514                 return 0;
515         }
516
517 out:
518         errno = 0;
519         return 1;
520 }
521
522 /*
523  * Do the mount(2) system call.
524  *
525  * Returns TRUE if successful, otherwise FALSE.
526  * "errno" is set to reflect the individual error.
527  */
528 static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
529 {
530         char *options = NULL;
531         int result;
532
533         if (po_join(opts, &options) == PO_FAILED) {
534                 errno = EIO;
535                 return 0;
536         }
537
538         if (verbose)
539                 printf(_("%s: trying text-based options '%s'\n"),
540                         progname, options);
541
542         if (mi->fake)
543                 return 1;
544
545         result = mount(mi->spec, mi->node, mi->type,
546                         mi->flags & ~(MS_USER|MS_USERS), options);
547         if (verbose && result) {
548                 int save = errno;
549                 nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
550                 errno = save;
551         }
552         return !result;
553 }
554
555 /*
556  * For "-t nfs vers=2" or "-t nfs vers=3" mounts.
557  */
558 static int nfs_try_mount_v3v2(struct nfsmount_info *mi)
559 {
560         struct mount_options *options = po_dup(mi->options);
561         int result = 0;
562
563         if (!options) {
564                 errno = ENOMEM;
565                 return result;
566         }
567
568         if (!nfs_fix_mounthost_option(options)) {
569                 errno = EINVAL;
570                 goto out_fail;
571         }
572         if (!mi->fake && !nfs_verify_lock_option(options)) {
573                 errno = EINVAL;
574                 goto out_fail;
575         }
576
577         /*
578          * Options we negotiate below may be stale by the time this
579          * file system is unmounted.  In order to force umount.nfs
580          * to renegotiate with the server, only write the user-
581          * specified options, and not negotiated options, to /etc/mtab.
582          */
583         if (po_join(options, mi->extra_opts) == PO_FAILED) {
584                 errno = ENOMEM;
585                 goto out_fail;
586         }
587
588         if (!nfs_rewrite_pmap_mount_options(options))
589                 goto out_fail;
590
591         result = nfs_sys_mount(mi, options);
592
593 out_fail:
594         po_destroy(options);
595         return result;
596 }
597
598 /*
599  * For "-t nfs -o vers=4" or "-t nfs4" mounts.
600  */
601 static int nfs_try_mount_v4(struct nfsmount_info *mi)
602 {
603         struct sockaddr *sap = &mi->address.sa;
604         struct mount_options *options = po_dup(mi->options);
605         int result = 0;
606
607         if (!options) {
608                 errno = ENOMEM;
609                 return result;
610         }
611
612         if (mi->version == 0) {
613                 if (po_contains(options, "mounthost") ||
614                         po_contains(options, "mountaddr") ||
615                         po_contains(options, "mountvers") ||
616                         po_contains(options, "mountproto")) {
617                 /*
618                  * Since these mountd options are set assume version 3
619                  * is wanted so error out with EPROTONOSUPPORT so the
620                  * protocol negation starts with v3.
621                  */
622                         errno = EPROTONOSUPPORT;
623                         goto out_fail;
624                 }
625                 if (po_append(options, "vers=4") == PO_FAILED) {
626                         errno = EINVAL;
627                         goto out_fail;
628                 }
629         }
630
631         if (!nfs_append_clientaddr_option(sap, mi->salen, options)) {
632                 errno = EINVAL;
633                 goto out_fail;
634         }
635
636         /*
637          * Update option string to be recorded in /etc/mtab.
638          */
639         if (po_join(options, mi->extra_opts) == PO_FAILED) {
640                 errno = ENOMEM;
641                 goto out_fail;
642         }
643
644         result = nfs_sys_mount(mi, options);
645
646 out_fail:
647         po_destroy(options);
648         return result;
649 }
650
651 /*
652  * This is a single pass through the fg/bg loop.
653  *
654  * Returns TRUE if successful, otherwise FALSE.
655  * "errno" is set to reflect the individual error.
656  */
657 static int nfs_try_mount(struct nfsmount_info *mi)
658 {
659         int result = 0;
660
661         switch (mi->version) {
662         case 0:
663                 if (linux_version_code() > MAKE_VERSION(2, 6, 31)) {
664                         errno = 0;
665                         result = nfs_try_mount_v4(mi);
666                         if (errno != EPROTONOSUPPORT) {
667                                 /* 
668                                  * To deal with legacy Linux servers that don't
669                                  * automatically export a pseudo root, retry
670                                  * ENOENT errors using version 3. And for
671                                  * Linux servers prior to 2.6.25, retry EPERM
672                                  */
673                                 if (errno != ENOENT && errno != EPERM)
674                                         break;
675                         }
676                 }
677         case 2:
678         case 3:
679                 result = nfs_try_mount_v3v2(mi);
680                 break;
681         case 4:
682                 result = nfs_try_mount_v4(mi);
683                 break;
684         default:
685                 errno = EIO;
686         }
687
688         return result;
689 }
690
691 /*
692  * Distinguish between permanent and temporary errors.
693  *
694  * Basically, we retry if communication with the server has
695  * failed so far, but fail immediately if there is a local
696  * error (like a bad mount option).
697  *
698  * ESTALE is also a temporary error because some servers
699  * return ESTALE when a share is temporarily offline.
700  *
701  * Returns 1 if we should fail immediately, or 0 if we
702  * should retry.
703  */
704 static int nfs_is_permanent_error(int error)
705 {
706         switch (error) {
707         case ESTALE:
708         case ETIMEDOUT:
709         case ECONNREFUSED:
710                 return 0;       /* temporary */
711         default:
712                 return 1;       /* permanent */
713         }
714 }
715
716 /*
717  * Handle "foreground" NFS mounts.
718  *
719  * Retry the mount request for as long as the 'retry=' option says.
720  *
721  * Returns a valid mount command exit code.
722  */
723 static int nfsmount_fg(struct nfsmount_info *mi)
724 {
725         unsigned int secs = 1;
726         time_t timeout;
727
728         timeout = nfs_parse_retry_option(mi->options,
729                                          NFS_DEF_FG_TIMEOUT_MINUTES);
730         if (verbose)
731                 printf(_("%s: timeout set for %s"),
732                         progname, ctime(&timeout));
733
734         for (;;) {
735                 if (nfs_try_mount(mi))
736                         return EX_SUCCESS;
737
738                 if (nfs_is_permanent_error(errno))
739                         break;
740
741                 if (time(NULL) > timeout) {
742                         errno = ETIMEDOUT;
743                         break;
744                 }
745
746                 if (errno != ETIMEDOUT) {
747                         if (sleep(secs))
748                                 break;
749                         secs <<= 1;
750                         if (secs > 10)
751                                 secs = 10;
752                 }
753         };
754
755         mount_error(mi->spec, mi->node, errno);
756         return EX_FAIL;
757 }
758
759 /*
760  * Handle "background" NFS mount [first try]
761  *
762  * Returns a valid mount command exit code.
763  *
764  * EX_BG should cause the caller to fork and invoke nfsmount_child.
765  */
766 static int nfsmount_parent(struct nfsmount_info *mi)
767 {
768         if (nfs_try_mount(mi))
769                 return EX_SUCCESS;
770
771         if (nfs_is_permanent_error(errno)) {
772                 mount_error(mi->spec, mi->node, errno);
773                 return EX_FAIL;
774         }
775
776         sys_mount_errors(mi->hostname, errno, 1, 1);
777         return EX_BG;
778 }
779
780 /*
781  * Handle "background" NFS mount [retry daemon]
782  *
783  * Returns a valid mount command exit code: EX_SUCCESS if successful,
784  * EX_FAIL if a failure occurred.  There's nothing to catch the
785  * error return, though, so we use sys_mount_errors to log the
786  * failure.
787  */
788 static int nfsmount_child(struct nfsmount_info *mi)
789 {
790         unsigned int secs = 1;
791         time_t timeout;
792
793         timeout = nfs_parse_retry_option(mi->options,
794                                          NFS_DEF_BG_TIMEOUT_MINUTES);
795
796         for (;;) {
797                 if (sleep(secs))
798                         break;
799                 secs <<= 1;
800                 if (secs > 120)
801                         secs = 120;
802
803                 if (nfs_try_mount(mi))
804                         return EX_SUCCESS;
805
806                 if (nfs_is_permanent_error(errno))
807                         break;
808
809                 if (time(NULL) > timeout)
810                         break;
811
812                 sys_mount_errors(mi->hostname, errno, 1, 1);
813         };
814
815         sys_mount_errors(mi->hostname, errno, 1, 0);
816         return EX_FAIL;
817 }
818
819 /*
820  * Handle "background" NFS mount
821  *
822  * Returns a valid mount command exit code.
823  */
824 static int nfsmount_bg(struct nfsmount_info *mi)
825 {
826         if (!mi->child)
827                 return nfsmount_parent(mi);
828         else
829                 return nfsmount_child(mi);
830 }
831
832 /*
833  * Process mount options and try a mount system call.
834  *
835  * Returns a valid mount command exit code.
836  */
837 static const char *nfs_background_opttbl[] = {
838         "bg",
839         "fg",
840         NULL,
841 };
842
843 static int nfsmount_start(struct nfsmount_info *mi)
844 {
845         if (!nfs_validate_options(mi))
846                 return EX_FAIL;
847
848         if (po_rightmost(mi->options, nfs_background_opttbl) == 0)
849                 return nfsmount_bg(mi);
850         else
851                 return nfsmount_fg(mi);
852 }
853
854 /**
855  * nfsmount_string - Mount an NFS file system using C string options
856  * @spec: C string specifying remote share to mount ("hostname:path")
857  * @node: C string pathname of local mounted-on directory
858  * @type: C string that represents file system type ("nfs" or "nfs4")
859  * @flags: MS_ style mount flags
860  * @extra_opts: pointer to C string containing fs-specific mount options
861  *              (input and output argument)
862  * @fake: flag indicating whether to carry out the whole operation
863  * @child: one if this is a mount daemon (bg)
864  */
865 int nfsmount_string(const char *spec, const char *node, const char *type,
866                     int flags, char **extra_opts, int fake, int child)
867 {
868         struct nfsmount_info mi = {
869                 .spec           = spec,
870                 .node           = node,
871                 .type           = type,
872                 .extra_opts     = extra_opts,
873                 .flags          = flags,
874                 .fake           = fake,
875                 .child          = child,
876         };
877         int retval = EX_FAIL;
878
879         mi.options = po_split(*extra_opts);
880         if (mi.options) {
881                 retval = nfsmount_start(&mi);
882                 po_destroy(mi.options);
883         } else
884                 nfs_error(_("%s: internal option parsing error"), progname);
885
886         free(mi.hostname);
887         return retval;
888 }