4e523b5f317b02f0bdcf88beab275bd44af793d2
[oweals/busybox.git] / util-linux / nfsmount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * nfsmount.c -- Linux NFS mount
4  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7  *
8  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
9  * numbers to be specified on the command line.
10  *
11  * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
12  * Omit the call to connect() for Linux version 1.3.11 or later.
13  *
14  * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
15  * Implemented the "bg", "fg" and "retry" mount options for NFS.
16  *
17  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
18  * - added Native Language Support
19  *
20  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
21  * plus NFSv3 stuff.
22  */
23
24 #include "busybox.h"
25 #include <syslog.h>
26 #include <mntent.h>
27 #include <sys/utsname.h>
28 #undef TRUE
29 #undef FALSE
30 #include <rpc/rpc.h>
31 #include <rpc/pmap_prot.h>
32 #include <rpc/pmap_clnt.h>
33
34 /* This is just a warning of a common mistake.  Possibly this should be a
35  * uclibc faq entry rather than in busybox... */
36 #if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
37 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
38 #endif
39
40 /* former nfsmount.h */
41
42 #define MOUNTPORT 635
43 #define MNTPATHLEN 1024
44 #define MNTNAMLEN 255
45 #define FHSIZE 32
46 #define FHSIZE3 64
47
48 typedef char fhandle[FHSIZE];
49
50 typedef struct {
51         unsigned int fhandle3_len;
52         char *fhandle3_val;
53 } fhandle3;
54
55 enum mountstat3 {
56         MNT_OK = 0,
57         MNT3ERR_PERM = 1,
58         MNT3ERR_NOENT = 2,
59         MNT3ERR_IO = 5,
60         MNT3ERR_ACCES = 13,
61         MNT3ERR_NOTDIR = 20,
62         MNT3ERR_INVAL = 22,
63         MNT3ERR_NAMETOOLONG = 63,
64         MNT3ERR_NOTSUPP = 10004,
65         MNT3ERR_SERVERFAULT = 10006,
66 };
67 typedef enum mountstat3 mountstat3;
68
69 struct fhstatus {
70         unsigned int fhs_status;
71         union {
72                 fhandle fhs_fhandle;
73         } fhstatus_u;
74 };
75 typedef struct fhstatus fhstatus;
76
77 struct mountres3_ok {
78         fhandle3 fhandle;
79         struct {
80                 unsigned int auth_flavours_len;
81                 char *auth_flavours_val;
82         } auth_flavours;
83 };
84 typedef struct mountres3_ok mountres3_ok;
85
86 struct mountres3 {
87         mountstat3 fhs_status;
88         union {
89                 mountres3_ok mountinfo;
90         } mountres3_u;
91 };
92 typedef struct mountres3 mountres3;
93
94 typedef char *dirpath;
95
96 typedef char *name;
97
98 typedef struct mountbody *mountlist;
99
100 struct mountbody {
101         name ml_hostname;
102         dirpath ml_directory;
103         mountlist ml_next;
104 };
105 typedef struct mountbody mountbody;
106
107 typedef struct groupnode *groups;
108
109 struct groupnode {
110         name gr_name;
111         groups gr_next;
112 };
113 typedef struct groupnode groupnode;
114
115 typedef struct exportnode *exports;
116
117 struct exportnode {
118         dirpath ex_dir;
119         groups ex_groups;
120         exports ex_next;
121 };
122 typedef struct exportnode exportnode;
123
124 struct ppathcnf {
125         int pc_link_max;
126         short pc_max_canon;
127         short pc_max_input;
128         short pc_name_max;
129         short pc_path_max;
130         short pc_pipe_buf;
131         u_char pc_vdisable;
132         char pc_xxx;
133         short pc_mask[2];
134 };
135 typedef struct ppathcnf ppathcnf;
136
137 #define MOUNTPROG 100005
138 #define MOUNTVERS 1
139
140 #define MOUNTPROC_NULL 0
141 #define MOUNTPROC_MNT 1
142 #define MOUNTPROC_DUMP 2
143 #define MOUNTPROC_UMNT 3
144 #define MOUNTPROC_UMNTALL 4
145 #define MOUNTPROC_EXPORT 5
146 #define MOUNTPROC_EXPORTALL 6
147
148 #define MOUNTVERS_POSIX 2
149
150 #define MOUNTPROC_PATHCONF 7
151
152 #define MOUNT_V3 3
153
154 #define MOUNTPROC3_NULL 0
155 #define MOUNTPROC3_MNT 1
156 #define MOUNTPROC3_DUMP 2
157 #define MOUNTPROC3_UMNT 3
158 #define MOUNTPROC3_UMNTALL 4
159 #define MOUNTPROC3_EXPORT 5
160
161 /* former nfsmount.h ends */
162
163 enum {
164 #ifndef NFS_FHSIZE
165         NFS_FHSIZE = 32,
166 #endif
167 #ifndef NFS_PORT
168         NFS_PORT = 2049
169 #endif
170 };
171
172 //enum {
173 //      S_QUOTA = 128,     /* Quota initialized for file/directory/symlink */
174 //};
175
176
177 /*
178  * We want to be able to compile mount on old kernels in such a way
179  * that the binary will work well on more recent kernels.
180  * Thus, if necessary we teach nfsmount.c the structure of new fields
181  * that will come later.
182  *
183  * Moreover, the new kernel includes conflict with glibc includes
184  * so it is easiest to ignore the kernel altogether (at compile time).
185  */
186
187 struct nfs2_fh {
188         char                    data[32];
189 };
190 struct nfs3_fh {
191         unsigned short          size;
192         unsigned char           data[64];
193 };
194
195 struct nfs_mount_data {
196         int             version;                /* 1 */
197         int             fd;                     /* 1 */
198         struct nfs2_fh  old_root;               /* 1 */
199         int             flags;                  /* 1 */
200         int             rsize;                  /* 1 */
201         int             wsize;                  /* 1 */
202         int             timeo;                  /* 1 */
203         int             retrans;                /* 1 */
204         int             acregmin;               /* 1 */
205         int             acregmax;               /* 1 */
206         int             acdirmin;               /* 1 */
207         int             acdirmax;               /* 1 */
208         struct sockaddr_in addr;                /* 1 */
209         char            hostname[256];          /* 1 */
210         int             namlen;                 /* 2 */
211         unsigned int    bsize;                  /* 3 */
212         struct nfs3_fh  root;                   /* 4 */
213 };
214
215 /* bits in the flags field */
216 enum {
217         NFS_MOUNT_SOFT = 0x0001,        /* 1 */
218         NFS_MOUNT_INTR = 0x0002,        /* 1 */
219         NFS_MOUNT_SECURE = 0x0004,      /* 1 */
220         NFS_MOUNT_POSIX = 0x0008,       /* 1 */
221         NFS_MOUNT_NOCTO = 0x0010,       /* 1 */
222         NFS_MOUNT_NOAC = 0x0020,        /* 1 */
223         NFS_MOUNT_TCP = 0x0040,         /* 2 */
224         NFS_MOUNT_VER3 = 0x0080,        /* 3 */
225         NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
226         NFS_MOUNT_NONLM = 0x0200        /* 3 */
227 };
228
229 #define HAVE_inet_aton
230
231 /*
232  * We need to translate between nfs status return values and
233  * the local errno values which may not be the same.
234  *
235  * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
236  * "after #include <errno.h> the symbol errno is reserved for any use,
237  *  it cannot even be used as a struct tag or field name".
238  */
239
240 #ifndef EDQUOT
241 #define EDQUOT  ENOSPC
242 #endif
243
244 // Convert each NFSERR_BLAH into EBLAH
245
246 static const struct {
247         int stat;
248         int errnum;
249 } nfs_errtbl[] = {
250         {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
251         {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
252         {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
253         {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
254 };
255
256 static char *nfs_strerror(int status)
257 {
258         int i;
259         static char buf[256];
260
261         for (i = 0; nfs_errtbl[i].stat != -1; i++) {
262                 if (nfs_errtbl[i].stat == status)
263                         return strerror(nfs_errtbl[i].errnum);
264         }
265         sprintf(buf, "unknown nfs status return value: %d", status);
266         return buf;
267 }
268
269 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
270 {
271         if (!xdr_opaque(xdrs, objp, FHSIZE))
272                  return FALSE;
273         return TRUE;
274 }
275
276 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
277 {
278         if (!xdr_u_int(xdrs, &objp->fhs_status))
279                  return FALSE;
280         switch (objp->fhs_status) {
281         case 0:
282                 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
283                          return FALSE;
284                 break;
285         default:
286                 break;
287         }
288         return TRUE;
289 }
290
291 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
292 {
293         if (!xdr_string(xdrs, objp, MNTPATHLEN))
294                  return FALSE;
295         return TRUE;
296 }
297
298 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
299 {
300         if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
301                  return FALSE;
302         return TRUE;
303 }
304
305 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
306 {
307         if (!xdr_fhandle3(xdrs, &objp->fhandle))
308                 return FALSE;
309         if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
310                                 sizeof (int), (xdrproc_t) xdr_int))
311                 return FALSE;
312         return TRUE;
313 }
314
315 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
316 {
317         if (!xdr_enum(xdrs, (enum_t *) objp))
318                  return FALSE;
319         return TRUE;
320 }
321
322 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
323 {
324         if (!xdr_mountstat3(xdrs, &objp->fhs_status))
325                 return FALSE;
326         switch (objp->fhs_status) {
327         case MNT_OK:
328                 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
329                          return FALSE;
330                 break;
331         default:
332                 break;
333         }
334         return TRUE;
335 }
336
337 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
338
339 /*
340  * nfs_mount_version according to the sources seen at compile time.
341  */
342 static int nfs_mount_version;
343 static int kernel_version;
344
345 /*
346  * Unfortunately, the kernel prints annoying console messages
347  * in case of an unexpected nfs mount version (instead of
348  * just returning some error).  Therefore we'll have to try
349  * and figure out what version the kernel expects.
350  *
351  * Variables:
352  *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
353  *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
354  *      nfs_mount_version: version this source and running kernel can handle
355  */
356 static void
357 find_kernel_nfs_mount_version(void)
358 {
359         if (kernel_version)
360                 return;
361
362         nfs_mount_version = 4; /* default */
363
364         kernel_version = get_linux_version_code();
365         if (kernel_version) {
366                 if (kernel_version < KERNEL_VERSION(2,1,32))
367                         nfs_mount_version = 1;
368                 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
369                                 (kernel_version >= KERNEL_VERSION(2,3,0) &&
370                                  kernel_version < KERNEL_VERSION(2,3,99)))
371                         nfs_mount_version = 3;
372                 /* else v4 since 2.3.99pre4 */
373         }
374 }
375
376 static struct pmap *
377 get_mountport(struct sockaddr_in *server_addr,
378         long unsigned prog,
379         long unsigned version,
380         long unsigned proto,
381         long unsigned port)
382 {
383         struct pmaplist *pmap;
384         static struct pmap p = {0, 0, 0, 0};
385
386         server_addr->sin_port = PMAPPORT;
387         pmap = pmap_getmaps(server_addr);
388
389         if (version > MAX_NFSPROT)
390                 version = MAX_NFSPROT;
391         if (!prog)
392                 prog = MOUNTPROG;
393         p.pm_prog = prog;
394         p.pm_vers = version;
395         p.pm_prot = proto;
396         p.pm_port = port;
397         
398         while (pmap) {
399                 if (pmap->pml_map.pm_prog != prog)
400                         goto next;
401                 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
402                         goto next;
403                 if (version > 2 && pmap->pml_map.pm_vers != version)
404                         goto next;
405                 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
406                         goto next;
407                 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
408                     (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
409                     (port && pmap->pml_map.pm_port != port))
410                         goto next;
411                 memcpy(&p, &pmap->pml_map, sizeof(p));
412 next:
413                 pmap = pmap->pml_next;
414         }
415         if (!p.pm_vers)
416                 p.pm_vers = MOUNTVERS;
417         if (!p.pm_port)
418                 p.pm_port = MOUNTPORT;
419         if (!p.pm_prot)
420                 p.pm_prot = IPPROTO_TCP;
421         return &p;
422 }
423
424 static int daemonize(void)
425 {
426         int fd;
427         int pid = fork();
428         if (pid < 0) /* error */
429                 return -errno;
430         if (pid > 0) /* parent */
431                 return 0;
432         /* child */
433         fd = xopen(bb_dev_null, O_RDWR);
434         dup2(fd, 0);
435         dup2(fd, 1);
436         dup2(fd, 2);
437         if (fd > 2) close(fd);
438         setsid();
439         openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
440         logmode = LOGMODE_SYSLOG;
441         return 1;
442 }
443
444 // TODO
445 static inline int we_saw_this_host_before(const char *hostname)
446 {
447         return 0;
448 }
449
450 /* RPC strerror analogs are terminally idiotic:
451  * *mandatory* prefix and \n at end.
452  * This hopefully helps. Usage:
453  * error_msg_rpc(clnt_*error*(" ")) */
454 static void error_msg_rpc(const char *msg)
455 {
456         size_t len;
457         while (msg[0] == ' ' || msg[0] == ':') msg++;
458         len = strlen(msg);
459         while (len && msg[len-1] == '\n') len--;
460         bb_error_msg("%.*s", len, msg);
461 }
462
463 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
464 {
465         CLIENT *mclient;
466         char *hostname;
467         char *pathname;
468         char *mounthost;
469         struct nfs_mount_data data;
470         char *opt;
471         struct hostent *hp;
472         struct sockaddr_in server_addr;
473         struct sockaddr_in mount_server_addr;
474         int msock, fsock;
475         union {
476                 struct fhstatus nfsv2;
477                 struct mountres3 nfsv3;
478         } status;
479         int daemonized;
480         char *s;
481         int port;
482         int mountport;
483         int proto;
484         int bg;
485         int soft;
486         int intr;
487         int posix;
488         int nocto;
489         int noac;
490         int nolock;
491         int retry;
492         int tcp;
493         int mountprog;
494         int mountvers;
495         int nfsprog;
496         int nfsvers;
497         int retval;
498
499         find_kernel_nfs_mount_version();
500
501         daemonized = 0;
502         mounthost = NULL;
503         retval = ETIMEDOUT;
504         msock = fsock = -1;
505         mclient = NULL;
506
507         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
508
509         filteropts = xstrdup(filteropts); /* going to trash it later... */
510
511         hostname = xstrdup(mp->mnt_fsname);
512         /* mount_main() guarantees that ':' is there */
513         s = strchr(hostname, ':');
514         pathname = s + 1;
515         *s = '\0';
516         /* Ignore all but first hostname in replicated mounts
517            until they can be fully supported. (mack@sgi.com) */
518         s = strchr(hostname, ',');
519         if (s) {
520                 *s = '\0';
521                 bb_error_msg("warning: multiple hostnames not supported");
522         }
523
524         server_addr.sin_family = AF_INET;
525 #ifdef HAVE_inet_aton
526         if (!inet_aton(hostname, &server_addr.sin_addr))
527 #endif
528         {
529                 hp = gethostbyname(hostname);
530                 if (hp == NULL) {
531                         bb_herror_msg("%s", hostname);
532                         goto fail;
533                 }
534                 if (hp->h_length > sizeof(struct in_addr)) {
535                         bb_error_msg("got bad hp->h_length");
536                         hp->h_length = sizeof(struct in_addr);
537                 }
538                 memcpy(&server_addr.sin_addr,
539                                 hp->h_addr, hp->h_length);
540         }
541
542         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
543
544         /* add IP address to mtab options for use when unmounting */
545
546         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
547                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
548         } else {
549                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
550                                         mp->mnt_opts[0] ? "," : "",
551                                         inet_ntoa(server_addr.sin_addr));
552                 free(mp->mnt_opts);
553                 mp->mnt_opts = tmp;
554         }
555
556         /* Set default options.
557          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
558          * let the kernel decide.
559          * timeo is filled in after we know whether it'll be TCP or UDP. */
560         memset(&data, 0, sizeof(data));
561         data.retrans    = 3;
562         data.acregmin   = 3;
563         data.acregmax   = 60;
564         data.acdirmin   = 30;
565         data.acdirmax   = 60;
566         data.namlen     = NAME_MAX;
567
568         bg = 0;
569         soft = 0;
570         intr = 0;
571         posix = 0;
572         nocto = 0;
573         nolock = 0;
574         noac = 0;
575         retry = 10000;          /* 10000 minutes ~ 1 week */
576         tcp = 0;
577
578         mountprog = MOUNTPROG;
579         mountvers = 0;
580         port = 0;
581         mountport = 0;
582         nfsprog = 100003;
583         nfsvers = 0;
584
585         /* parse options */
586
587         for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
588                 char *opteq = strchr(opt, '=');
589                 if (opteq) {
590                         int val = atoi(opteq + 1);
591                         *opteq = '\0';
592                         if (!strcmp(opt, "rsize"))
593                                 data.rsize = val;
594                         else if (!strcmp(opt, "wsize"))
595                                 data.wsize = val;
596                         else if (!strcmp(opt, "timeo"))
597                                 data.timeo = val;
598                         else if (!strcmp(opt, "retrans"))
599                                 data.retrans = val;
600                         else if (!strcmp(opt, "acregmin"))
601                                 data.acregmin = val;
602                         else if (!strcmp(opt, "acregmax"))
603                                 data.acregmax = val;
604                         else if (!strcmp(opt, "acdirmin"))
605                                 data.acdirmin = val;
606                         else if (!strcmp(opt, "acdirmax"))
607                                 data.acdirmax = val;
608                         else if (!strcmp(opt, "actimeo")) {
609                                 data.acregmin = val;
610                                 data.acregmax = val;
611                                 data.acdirmin = val;
612                                 data.acdirmax = val;
613                         }
614                         else if (!strcmp(opt, "retry"))
615                                 retry = val;
616                         else if (!strcmp(opt, "port"))
617                                 port = val;
618                         else if (!strcmp(opt, "mountport"))
619                                 mountport = val;
620                         else if (!strcmp(opt, "mounthost"))
621                                 mounthost = xstrndup(opteq+1,
622                                                   strcspn(opteq+1," \t\n\r,"));
623                         else if (!strcmp(opt, "mountprog"))
624                                 mountprog = val;
625                         else if (!strcmp(opt, "mountvers"))
626                                 mountvers = val;
627                         else if (!strcmp(opt, "nfsprog"))
628                                 nfsprog = val;
629                         else if (!strcmp(opt, "nfsvers") ||
630                                  !strcmp(opt, "vers"))
631                                 nfsvers = val;
632                         else if (!strcmp(opt, "proto")) {
633                                 if (!strncmp(opteq+1, "tcp", 3))
634                                         tcp = 1;
635                                 else if (!strncmp(opteq+1, "udp", 3))
636                                         tcp = 0;
637                                 else
638                                         bb_error_msg("warning: unrecognized proto= option");
639                         } else if (!strcmp(opt, "namlen")) {
640                                 if (nfs_mount_version >= 2)
641                                         data.namlen = val;
642                                 else
643                                         bb_error_msg("warning: option namlen is not supported\n");
644                         } else if (!strcmp(opt, "addr"))
645                                 /* ignore */;
646                         else {
647                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
648                                 goto fail;
649                         }
650                 }
651                 else {
652                         int val = 1;
653                         if (!strncmp(opt, "no", 2)) {
654                                 val = 0;
655                                 opt += 2;
656                         }
657                         if (!strcmp(opt, "bg"))
658                                 bg = val;
659                         else if (!strcmp(opt, "fg"))
660                                 bg = !val;
661                         else if (!strcmp(opt, "soft"))
662                                 soft = val;
663                         else if (!strcmp(opt, "hard"))
664                                 soft = !val;
665                         else if (!strcmp(opt, "intr"))
666                                 intr = val;
667                         else if (!strcmp(opt, "posix"))
668                                 posix = val;
669                         else if (!strcmp(opt, "cto"))
670                                 nocto = !val;
671                         else if (!strcmp(opt, "ac"))
672                                 noac = !val;
673                         else if (!strcmp(opt, "tcp"))
674                                 tcp = val;
675                         else if (!strcmp(opt, "udp"))
676                                 tcp = !val;
677                         else if (!strcmp(opt, "lock")) {
678                                 if (nfs_mount_version >= 3)
679                                         nolock = !val;
680                                 else
681                                         bb_error_msg("warning: option nolock is not supported");
682                         } else {
683                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
684                                 goto fail;
685                         }
686                 }
687         }
688         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
689
690         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
691                 | (intr ? NFS_MOUNT_INTR : 0)
692                 | (posix ? NFS_MOUNT_POSIX : 0)
693                 | (nocto ? NFS_MOUNT_NOCTO : 0)
694                 | (noac ? NFS_MOUNT_NOAC : 0);
695         if (nfs_mount_version >= 2)
696                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
697         if (nfs_mount_version >= 3)
698                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
699         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
700                 bb_error_msg("NFSv%d not supported", nfsvers);
701                 goto fail;
702         }
703         if (nfsvers && !mountvers)
704                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
705         if (nfsvers && nfsvers < mountvers) {
706                 mountvers = nfsvers;
707         }
708
709         /* Adjust options if none specified */
710         if (!data.timeo)
711                 data.timeo = tcp ? 70 : 7;
712
713 #ifdef NFS_MOUNT_DEBUG
714         printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
715                 data.rsize, data.wsize, data.timeo, data.retrans);
716         printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
717                 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
718         printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
719                 port, bg, retry, data.flags);
720         printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
721                 mountprog, mountvers, nfsprog, nfsvers);
722         printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
723                 (data.flags & NFS_MOUNT_SOFT) != 0,
724                 (data.flags & NFS_MOUNT_INTR) != 0,
725                 (data.flags & NFS_MOUNT_POSIX) != 0,
726                 (data.flags & NFS_MOUNT_NOCTO) != 0,
727                 (data.flags & NFS_MOUNT_NOAC) != 0);
728         printf("tcp = %d\n",
729                 (data.flags & NFS_MOUNT_TCP) != 0);
730 #endif
731
732         data.version = nfs_mount_version;
733
734         if (vfsflags & MS_REMOUNT)
735                 goto do_mount;
736
737         /*
738          * If the previous mount operation on the same host was
739          * backgrounded, and the "bg" for this mount is also set,
740          * give up immediately, to avoid the initial timeout.
741          */
742         if (bg && we_saw_this_host_before(hostname)) {
743                 daemonized = daemonize(); /* parent or error */
744                 if (daemonized <= 0) { /* parent or error */
745                         retval = -daemonized;
746                         goto ret;
747                 }
748         }
749
750         /* create mount daemon client */
751         /* See if the nfs host = mount host. */
752         if (mounthost) {
753                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
754                         mount_server_addr.sin_family = AF_INET;
755                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
756                 } else {
757                         hp = gethostbyname(mounthost);
758                         if (hp == NULL) {
759                                 bb_herror_msg("%s", mounthost);
760                                 goto fail;
761                         } else {
762                                 if (hp->h_length > sizeof(struct in_addr)) {
763                                         bb_error_msg("got bad hp->h_length?");
764                                         hp->h_length = sizeof(struct in_addr);
765                                 }
766                                 mount_server_addr.sin_family = AF_INET;
767                                 memcpy(&mount_server_addr.sin_addr,
768                                                 hp->h_addr, hp->h_length);
769                         }
770                 }
771         }
772
773         /*
774          * The following loop implements the mount retries. When the mount
775          * times out, and the "bg" option is set, we background ourself
776          * and continue trying.
777          *
778          * The case where the mount point is not present and the "bg"
779          * option is set, is treated as a timeout. This is done to
780          * support nested mounts.
781          *
782          * The "retry" count specified by the user is the number of
783          * minutes to retry before giving up.
784          */
785         {
786                 struct timeval total_timeout;
787                 struct timeval retry_timeout;
788                 struct pmap* pm_mnt;
789                 time_t t;
790                 time_t prevt;
791                 time_t timeout;
792
793                 retry_timeout.tv_sec = 3;
794                 retry_timeout.tv_usec = 0;
795                 total_timeout.tv_sec = 20;
796                 total_timeout.tv_usec = 0;
797                 timeout = time(NULL) + 60 * retry;
798                 prevt = 0;
799                 t = 30;
800 retry:
801                 /* be careful not to use too many CPU cycles */
802                 if (t - prevt < 30)
803                         sleep(30);
804
805                 pm_mnt = get_mountport(&mount_server_addr,
806                                 mountprog,
807                                 mountvers,
808                                 proto,
809                                 mountport);
810                 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
811
812                 /* contact the mount daemon via TCP */
813                 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
814                 msock = RPC_ANYSOCK;
815
816                 switch (pm_mnt->pm_prot) {
817                 case IPPROTO_UDP:
818                         mclient = clntudp_create(&mount_server_addr,
819                                                  pm_mnt->pm_prog,
820                                                  pm_mnt->pm_vers,
821                                                  retry_timeout,
822                                                  &msock);
823                         if (mclient)
824                                 break;
825                         mount_server_addr.sin_port = htons(pm_mnt->pm_port);
826                         msock = RPC_ANYSOCK;
827                 case IPPROTO_TCP:
828                         mclient = clnttcp_create(&mount_server_addr,
829                                                  pm_mnt->pm_prog,
830                                                  pm_mnt->pm_vers,
831                                                  &msock, 0, 0);
832                         break;
833                 default:
834                         mclient = 0;
835                 }
836                 if (!mclient) {
837                         if (!daemonized && prevt == 0)
838                                 error_msg_rpc(clnt_spcreateerror(" "));
839                 } else {
840                         enum clnt_stat clnt_stat;
841                         /* try to mount hostname:pathname */
842                         mclient->cl_auth = authunix_create_default();
843
844                         /* make pointers in xdr_mountres3 NULL so
845                          * that xdr_array allocates memory for us
846                          */
847                         memset(&status, 0, sizeof(status));
848
849                         if (pm_mnt->pm_vers == 3)
850                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
851                                               (xdrproc_t) xdr_dirpath,
852                                               (caddr_t) &pathname,
853                                               (xdrproc_t) xdr_mountres3,
854                                               (caddr_t) &status,
855                                               total_timeout);
856                         else
857                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
858                                               (xdrproc_t) xdr_dirpath,
859                                               (caddr_t) &pathname,
860                                               (xdrproc_t) xdr_fhstatus,
861                                               (caddr_t) &status,
862                                               total_timeout);
863
864                         if (clnt_stat == RPC_SUCCESS)
865                                 goto prepare_kernel_data; /* we're done */
866                         if (errno != ECONNREFUSED) {
867                                 error_msg_rpc(clnt_sperror(mclient, " "));
868                                 goto fail;      /* don't retry */
869                         }
870                         /* Connection refused */
871                         if (!daemonized && prevt == 0) /* print just once */
872                                 error_msg_rpc(clnt_sperror(mclient, " "));
873                         auth_destroy(mclient->cl_auth);
874                         clnt_destroy(mclient);
875                         mclient = 0;
876                         close(msock);
877                 }
878
879                 /* Timeout. We are going to retry... maybe */
880
881                 if (!bg)
882                         goto fail;
883                 if (!daemonized) {
884                         daemonized = daemonize();
885                         if (daemonized <= 0) { /* parent or error */
886                                 retval = -daemonized;
887                                 goto ret;
888                         }
889                 }
890                 prevt = t;
891                 t = time(NULL);
892                 if (t >= timeout)
893                         /* TODO error message */
894                         goto fail;
895
896                 goto retry;
897         }
898
899 prepare_kernel_data:
900
901         if (nfsvers == 2) {
902                 if (status.nfsv2.fhs_status != 0) {
903                         bb_error_msg("%s:%s failed, reason given by server: %s",
904                                 hostname, pathname,
905                                 nfs_strerror(status.nfsv2.fhs_status));
906                         goto fail;
907                 }
908                 memcpy(data.root.data,
909                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
910                                 NFS_FHSIZE);
911                 data.root.size = NFS_FHSIZE;
912                 memcpy(data.old_root.data,
913                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
914                                 NFS_FHSIZE);
915         } else {
916                 fhandle3 *my_fhandle;
917                 if (status.nfsv3.fhs_status != 0) {
918                         bb_error_msg("%s:%s failed, reason given by server: %s",
919                                 hostname, pathname,
920                                 nfs_strerror(status.nfsv3.fhs_status));
921                         goto fail;
922                 }
923                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
924                 memset(data.old_root.data, 0, NFS_FHSIZE);
925                 memset(&data.root, 0, sizeof(data.root));
926                 data.root.size = my_fhandle->fhandle3_len;
927                 memcpy(data.root.data,
928                                 (char *) my_fhandle->fhandle3_val,
929                                 my_fhandle->fhandle3_len);
930
931                 data.flags |= NFS_MOUNT_VER3;
932         }
933
934         /* create nfs socket for kernel */
935
936         if (tcp) {
937                 if (nfs_mount_version < 3) {
938                         bb_error_msg("NFS over TCP is not supported");
939                         goto fail;
940                 }
941                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
942         } else
943                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
944         if (fsock < 0) {
945                 bb_perror_msg("nfs socket");
946                 goto fail;
947         }
948         if (bindresvport(fsock, 0) < 0) {
949                 bb_perror_msg("nfs bindresvport");
950                 goto fail;
951         }
952         if (port == 0) {
953                 server_addr.sin_port = PMAPPORT;
954                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
955                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
956                 if (port == 0)
957                         port = NFS_PORT;
958 #ifdef NFS_MOUNT_DEBUG
959                 else
960                         printf("used portmapper to find NFS port\n");
961 #endif
962         }
963 #ifdef NFS_MOUNT_DEBUG
964         printf("using port %d for nfs daemon\n", port);
965 #endif
966         server_addr.sin_port = htons(port);
967
968         /* prepare data structure for kernel */
969
970         data.fd = fsock;
971         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
972         strncpy(data.hostname, hostname, sizeof(data.hostname));
973
974         /* clean up */
975
976         auth_destroy(mclient->cl_auth);
977         clnt_destroy(mclient);
978         close(msock);
979
980         if (bg) {
981                 /* We must wait until mount directory is available */
982                 struct stat statbuf;
983                 int delay = 1;
984                 while (stat(mp->mnt_dir, &statbuf) == -1) {
985                         if (!daemonized) {
986                                 daemonized = daemonize();
987                                 if (daemonized <= 0) { /* parent or error */
988                                         retval = -daemonized;
989                                         goto ret;
990                                 }
991                         }
992                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
993                         delay *= 2;
994                         if (delay > 30)
995                                 delay = 30;
996                 }
997         }
998
999 do_mount: /* perform actual mount */
1000
1001         mp->mnt_type = "nfs";
1002         retval = mount_it_now(mp, vfsflags, (char*)&data);
1003         goto ret;
1004
1005 fail:   /* abort */
1006
1007         if (msock != -1) {
1008                 if (mclient) {
1009                         auth_destroy(mclient->cl_auth);
1010                         clnt_destroy(mclient);
1011                 }
1012                 close(msock);
1013         }
1014         if (fsock != -1)
1015                 close(fsock);
1016
1017 ret:
1018         free(hostname);
1019         free(mounthost);
1020         free(filteropts);
1021         return retval;
1022 }