findfs,mount: trivial build fix and whitespace fiexs
[oweals/busybox.git] / util-linux / mount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini mount implementation for busybox
4  *
5  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7  * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 /* Design notes: There is no spec for mount.  Remind me to write one.
13
14    mount_main() calls singlemount() which calls mount_it_now().
15
16    mount_main() can loop through /etc/fstab for mount -a
17    singlemount() can loop through /etc/filesystems for fstype detection.
18    mount_it_now() does the actual mount.
19 */
20
21 #include <mntent.h>
22 #include <syslog.h>
23 #include "libbb.h"
24
25 /* For FEATURE_MOUNT_LABEL only */
26 #include "volume_id.h"
27
28 /* Needed for nfs support only */
29 #include <sys/utsname.h>
30 #undef TRUE
31 #undef FALSE
32 #include <rpc/rpc.h>
33 #include <rpc/pmap_prot.h>
34 #include <rpc/pmap_clnt.h>
35
36 #ifndef MS_SILENT
37 #define MS_SILENT       (1 << 15)
38 #endif
39 /* Grab more as needed from util-linux's mount/mount_constants.h */
40 #ifndef MS_DIRSYNC
41 #define MS_DIRSYNC      128     /* Directory modifications are synchronous */
42 #endif
43
44
45 #if defined(__dietlibc__)
46 /* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
47  * dietlibc-0.30 does not have implementation of getmntent_r() */
48 static struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize)
49 {
50         struct mntent* ment = getmntent(stream);
51         memcpy(result, ment, sizeof(struct mntent));
52         return result;
53 }
54 #endif
55
56
57 // Not real flags, but we want to be able to check for this.
58 enum {
59         MOUNT_USERS  = (1<<28)*ENABLE_DESKTOP,
60         MOUNT_NOAUTO = (1<<29),
61         MOUNT_SWAP   = (1<<30),
62 };
63
64
65 #define OPTION_STR "o:t:rwanfvsi"
66 enum {
67         OPT_o = (1 << 0),
68         OPT_t = (1 << 1),
69         OPT_r = (1 << 2),
70         OPT_w = (1 << 3),
71         OPT_a = (1 << 4),
72         OPT_n = (1 << 5),
73         OPT_f = (1 << 6),
74         OPT_v = (1 << 7),
75         OPT_s = (1 << 8),
76         OPT_i = (1 << 9),
77 };
78
79 #if ENABLE_FEATURE_MTAB_SUPPORT
80 #define useMtab (!(option_mask32 & OPT_n))
81 #else
82 #define useMtab 0
83 #endif
84
85 #if ENABLE_FEATURE_MOUNT_FAKE
86 #define fakeIt (option_mask32 & OPT_f)
87 #else
88 #define fakeIt 0
89 #endif
90
91
92 // TODO: more "user" flag compatibility.
93 // "user" option (from mount manpage):
94 // Only the user that mounted a filesystem can unmount it again.
95 // If any user should be able to unmount, then use users instead of user
96 // in the fstab line.  The owner option is similar to the user option,
97 // with the restriction that the user must be the owner of the special file.
98 // This may be useful e.g. for /dev/fd if a login script makes
99 // the console user owner of this device.
100
101 /* Standard mount options (from -o options or --options), with corresponding
102  * flags */
103
104 static const int32_t mount_options[] = {
105         // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
106
107         USE_FEATURE_MOUNT_LOOP(
108                 /* "loop" */ 0,
109         )
110
111         USE_FEATURE_MOUNT_FSTAB(
112                 /* "defaults" */ 0,
113                 /* "quiet" 0 - do not filter out, vfat wants to see it */
114                 /* "noauto" */ MOUNT_NOAUTO,
115                 /* "sw"     */ MOUNT_SWAP,
116                 /* "swap"   */ MOUNT_SWAP,
117                 USE_DESKTOP(/* "user"  */ MOUNT_USERS,)
118                 USE_DESKTOP(/* "users" */ MOUNT_USERS,)
119                 /* "_netdev" */ 0,
120         )
121
122         USE_FEATURE_MOUNT_FLAGS(
123                 // vfs flags
124                 /* "nosuid"      */ MS_NOSUID,
125                 /* "suid"        */ ~MS_NOSUID,
126                 /* "dev"         */ ~MS_NODEV,
127                 /* "nodev"       */ MS_NODEV,
128                 /* "exec"        */ ~MS_NOEXEC,
129                 /* "noexec"      */ MS_NOEXEC,
130                 /* "sync"        */ MS_SYNCHRONOUS,
131                 /* "dirsync"     */ MS_DIRSYNC,
132                 /* "async"       */ ~MS_SYNCHRONOUS,
133                 /* "atime"       */ ~MS_NOATIME,
134                 /* "noatime"     */ MS_NOATIME,
135                 /* "diratime"    */ ~MS_NODIRATIME,
136                 /* "nodiratime"  */ MS_NODIRATIME,
137                 /* "loud"        */ ~MS_SILENT,
138
139                 // action flags
140                 /* "bind"        */ MS_BIND,
141                 /* "move"        */ MS_MOVE,
142                 /* "shared"      */ MS_SHARED,
143                 /* "slave"       */ MS_SLAVE,
144                 /* "private"     */ MS_PRIVATE,
145                 /* "unbindable"  */ MS_UNBINDABLE,
146                 /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
147                 /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
148                 /* "rprivate"    */ MS_SLAVE|MS_RECURSIVE,
149                 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
150         )
151
152         // Always understood.
153         /* "ro"      */ MS_RDONLY,  // vfs flag
154         /* "rw"      */ ~MS_RDONLY, // vfs flag
155         /* "remount" */ MS_REMOUNT  // action flag
156 };
157
158 static const char mount_option_str[] =
159         USE_FEATURE_MOUNT_LOOP(
160                 "loop" "\0"
161         )
162         USE_FEATURE_MOUNT_FSTAB(
163                 "defaults" "\0"
164                 /* "quiet" "\0" - do not filter out, vfat wants to see it */
165                 "noauto" "\0"
166                 "sw" "\0"
167                 "swap" "\0"
168                 USE_DESKTOP("user" "\0")
169                 USE_DESKTOP("users" "\0")
170                 "_netdev" "\0"
171         )
172         USE_FEATURE_MOUNT_FLAGS(
173                 // vfs flags
174                 "nosuid" "\0"
175                 "suid" "\0"
176                 "dev" "\0"
177                 "nodev" "\0"
178                 "exec" "\0"
179                 "noexec" "\0"
180                 "sync" "\0"
181                 "dirsync" "\0"
182                 "async" "\0"
183                 "atime" "\0"
184                 "noatime" "\0"
185                 "diratime" "\0"
186                 "nodiratime" "\0"
187                 "loud" "\0"
188
189                 // action flags
190                 "bind" "\0"
191                 "move" "\0"
192                 "shared" "\0"
193                 "slave" "\0"
194                 "private" "\0"
195                 "unbindable" "\0"
196                 "rshared" "\0"
197                 "rslave" "\0"
198                 "rprivate" "\0"
199                 "runbindable" "\0"
200         )
201
202         // Always understood.
203         "ro" "\0"        // vfs flag
204         "rw" "\0"        // vfs flag
205         "remount" "\0"   // action flag
206 ;
207
208
209 struct globals {
210 #if ENABLE_FEATURE_MOUNT_NFS
211         smalluint nfs_mount_version;
212 #endif
213 #if ENABLE_FEATURE_MOUNT_VERBOSE
214         unsigned verbose;
215 #endif
216         llist_t *fslist;
217         char getmntent_buf[sizeof(bb_common_bufsiz1) - 8*3];
218
219 };
220 #define G (*(struct globals*)&bb_common_bufsiz1)
221 #define nfs_mount_version (G.nfs_mount_version)
222 #if ENABLE_FEATURE_MOUNT_VERBOSE
223 #define verbose           (G.verbose          )
224 #else
225 #define verbose           0
226 #endif
227 #define fslist            (G.fslist           )
228 #define getmntent_buf     (G.getmntent_buf    )
229
230
231 #if ENABLE_FEATURE_MOUNT_VERBOSE
232 static int verbose_mount(const char *source, const char *target,
233                 const char *filesystemtype,
234                 unsigned long mountflags, const void *data)
235 {
236         int rc;
237
238         errno = 0;
239         rc = mount(source, target, filesystemtype, mountflags, data);
240         if (verbose >= 2)
241                 bb_perror_msg("would do mount('%s','%s','%s',0x%08lx,'%s'):%d"
242                                 + (sizeof("would do ")-1),
243                         source, target, filesystemtype,
244                         mountflags, (char*)data, rc);
245         return rc;
246 }
247 #else
248 #define verbose_mount(...) mount(__VA_ARGS__)
249 #endif
250
251 #if ENABLE_FEATURE_MOUNT_LABEL
252 static int resolve_mount_spec(char **fsname)
253 {
254         char *tmp = NULL;
255
256         if (!strncmp(*fsname, "UUID=", 5))
257                 tmp = get_devname_from_uuid(*fsname + 5);
258         else if (!strncmp(*fsname, "LABEL=", 6))
259                 tmp = get_devname_from_label(*fsname + 6);
260
261         if (tmp) {
262                 *fsname = tmp;
263                 return 1;
264         }
265         return 0;
266 }
267 #endif
268
269 /* Append mount options to string */
270 static void append_mount_options(char **oldopts, const char *newopts)
271 {
272         if (*oldopts && **oldopts) {
273                 /* do not insert options which are already there */
274                 while (newopts[0]) {
275                         char *p;
276                         int len = strlen(newopts);
277                         p = strchr(newopts, ',');
278                         if (p) len = p - newopts;
279                         p = *oldopts;
280                         while (1) {
281                                 if (!strncmp(p, newopts, len)
282                                  && (p[len] == ',' || p[len] == '\0'))
283                                         goto skip;
284                                 p = strchr(p,',');
285                                 if (!p) break;
286                                 p++;
287                         }
288                         p = xasprintf("%s,%.*s", *oldopts, len, newopts);
289                         free(*oldopts);
290                         *oldopts = p;
291  skip:
292                         newopts += len;
293                         while (newopts[0] == ',') newopts++;
294                 }
295         } else {
296                 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
297                 *oldopts = xstrdup(newopts);
298         }
299 }
300
301 /* Use the mount_options list to parse options into flags.
302  * Also return list of unrecognized options if unrecognized!=NULL */
303 static long parse_mount_options(char *options, char **unrecognized)
304 {
305         long flags = MS_SILENT;
306
307         // Loop through options
308         for (;;) {
309                 int i;
310                 char *comma = strchr(options, ',');
311                 const char *option_str = mount_option_str;
312
313                 if (comma) *comma = '\0';
314
315                 // Find this option in mount_options
316                 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
317                         if (!strcasecmp(option_str, options)) {
318                                 long fl = mount_options[i];
319                                 if (fl < 0) flags &= fl;
320                                 else flags |= fl;
321                                 break;
322                         }
323                         option_str += strlen(option_str) + 1;
324                 }
325                 // If unrecognized not NULL, append unrecognized mount options */
326                 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
327                         // Add it to strflags, to pass on to kernel
328                         i = *unrecognized ? strlen(*unrecognized) : 0;
329                         *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
330
331                         // Comma separated if it's not the first one
332                         if (i) (*unrecognized)[i++] = ',';
333                         strcpy((*unrecognized)+i, options);
334                 }
335
336                 if (!comma)
337                         break;
338                 // Advance to next option
339                 *comma = ',';
340                 options = ++comma;
341         }
342
343         return flags;
344 }
345
346 // Return a list of all block device backed filesystems
347
348 static llist_t *get_block_backed_filesystems(void)
349 {
350         static const char filesystems[2][sizeof("/proc/filesystems")] = {
351                 "/etc/filesystems",
352                 "/proc/filesystems",
353         };
354         char *fs, *buf;
355         llist_t *list = 0;
356         int i;
357         FILE *f;
358
359         for (i = 0; i < 2; i++) {
360                 f = fopen(filesystems[i], "r");
361                 if (!f) continue;
362
363                 while ((buf = xmalloc_getline(f)) != 0) {
364                         if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
365                                 continue;
366                         fs = skip_whitespace(buf);
367                         if (*fs=='#' || *fs=='*' || !*fs) continue;
368
369                         llist_add_to_end(&list, xstrdup(fs));
370                         free(buf);
371                 }
372                 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
373         }
374
375         return list;
376 }
377
378 #if ENABLE_FEATURE_CLEAN_UP
379 static void delete_block_backed_filesystems(void)
380 {
381         llist_free(fslist, free);
382 }
383 #else
384 void delete_block_backed_filesystems(void);
385 #endif
386
387 // Perform actual mount of specific filesystem at specific location.
388 // NB: mp->xxx fields may be trashed on exit
389 static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
390 {
391         int rc = 0;
392
393         if (fakeIt) {
394                 if (verbose >= 2)
395                         bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
396                                 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
397                                 vfsflags, filteropts);
398                 goto mtab;
399         }
400
401         // Mount, with fallback to read-only if necessary.
402         for (;;) {
403                 errno = 0;
404                 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
405                                 vfsflags, filteropts);
406
407                 // If mount failed, try
408                 // helper program <mnt_type>
409                 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
410                         char *args[6];
411                         int errno_save = errno;
412                         args[0] = mp->mnt_type;
413                         rc = 1;
414                         if (filteropts) {
415                                 args[rc++] = (char *)"-o";
416                                 args[rc++] = filteropts;
417                         }
418                         args[rc++] = mp->mnt_fsname;
419                         args[rc++] = mp->mnt_dir;
420                         args[rc] = NULL;
421                         rc = wait4pid(spawn(args));
422                         if (!rc)
423                                 break;
424                         errno = errno_save;
425                 }
426
427                 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
428                         break;
429                 if (!(vfsflags & MS_SILENT))
430                         bb_error_msg("%s is write-protected, mounting read-only",
431                                                 mp->mnt_fsname);
432                 vfsflags |= MS_RDONLY;
433         }
434
435         // Abort entirely if permission denied.
436
437         if (rc && errno == EPERM)
438                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
439
440         /* If the mount was successful, and we're maintaining an old-style
441          * mtab file by hand, add the new entry to it now. */
442  mtab:
443         if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
444                 char *fsname;
445                 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
446                 const char *option_str = mount_option_str;
447                 int i;
448
449                 if (!mountTable) {
450                         bb_error_msg("no %s", bb_path_mtab_file);
451                         goto ret;
452                 }
453
454                 // Add vfs string flags
455
456                 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
457                         if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
458                                 append_mount_options(&(mp->mnt_opts), option_str);
459                         option_str += strlen(option_str) + 1;
460                 }
461
462                 // Remove trailing / (if any) from directory we mounted on
463
464                 i = strlen(mp->mnt_dir) - 1;
465                 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
466
467                 // Convert to canonical pathnames as needed
468
469                 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
470                 fsname = 0;
471                 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
472                         mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
473                         mp->mnt_type = (char*)"bind";
474                 }
475                 mp->mnt_freq = mp->mnt_passno = 0;
476
477                 // Write and close.
478
479                 addmntent(mountTable, mp);
480                 endmntent(mountTable);
481                 if (ENABLE_FEATURE_CLEAN_UP) {
482                         free(mp->mnt_dir);
483                         free(fsname);
484                 }
485         }
486  ret:
487         return rc;
488 }
489
490 #if ENABLE_FEATURE_MOUNT_NFS
491
492 /*
493  * Linux NFS mount
494  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
495  *
496  * Licensed under GPLv2, see file LICENSE in this tarball for details.
497  *
498  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
499  * numbers to be specified on the command line.
500  *
501  * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
502  * Omit the call to connect() for Linux version 1.3.11 or later.
503  *
504  * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
505  * Implemented the "bg", "fg" and "retry" mount options for NFS.
506  *
507  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
508  * - added Native Language Support
509  *
510  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
511  * plus NFSv3 stuff.
512  */
513
514 /* This is just a warning of a common mistake.  Possibly this should be a
515  * uclibc faq entry rather than in busybox... */
516 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
517 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
518 #endif
519
520 #define MOUNTPORT 635
521 #define MNTPATHLEN 1024
522 #define MNTNAMLEN 255
523 #define FHSIZE 32
524 #define FHSIZE3 64
525
526 typedef char fhandle[FHSIZE];
527
528 typedef struct {
529         unsigned int fhandle3_len;
530         char *fhandle3_val;
531 } fhandle3;
532
533 enum mountstat3 {
534         MNT_OK = 0,
535         MNT3ERR_PERM = 1,
536         MNT3ERR_NOENT = 2,
537         MNT3ERR_IO = 5,
538         MNT3ERR_ACCES = 13,
539         MNT3ERR_NOTDIR = 20,
540         MNT3ERR_INVAL = 22,
541         MNT3ERR_NAMETOOLONG = 63,
542         MNT3ERR_NOTSUPP = 10004,
543         MNT3ERR_SERVERFAULT = 10006,
544 };
545 typedef enum mountstat3 mountstat3;
546
547 struct fhstatus {
548         unsigned int fhs_status;
549         union {
550                 fhandle fhs_fhandle;
551         } fhstatus_u;
552 };
553 typedef struct fhstatus fhstatus;
554
555 struct mountres3_ok {
556         fhandle3 fhandle;
557         struct {
558                 unsigned int auth_flavours_len;
559                 char *auth_flavours_val;
560         } auth_flavours;
561 };
562 typedef struct mountres3_ok mountres3_ok;
563
564 struct mountres3 {
565         mountstat3 fhs_status;
566         union {
567                 mountres3_ok mountinfo;
568         } mountres3_u;
569 };
570 typedef struct mountres3 mountres3;
571
572 typedef char *dirpath;
573
574 typedef char *name;
575
576 typedef struct mountbody *mountlist;
577
578 struct mountbody {
579         name ml_hostname;
580         dirpath ml_directory;
581         mountlist ml_next;
582 };
583 typedef struct mountbody mountbody;
584
585 typedef struct groupnode *groups;
586
587 struct groupnode {
588         name gr_name;
589         groups gr_next;
590 };
591 typedef struct groupnode groupnode;
592
593 typedef struct exportnode *exports;
594
595 struct exportnode {
596         dirpath ex_dir;
597         groups ex_groups;
598         exports ex_next;
599 };
600 typedef struct exportnode exportnode;
601
602 struct ppathcnf {
603         int pc_link_max;
604         short pc_max_canon;
605         short pc_max_input;
606         short pc_name_max;
607         short pc_path_max;
608         short pc_pipe_buf;
609         uint8_t pc_vdisable;
610         char pc_xxx;
611         short pc_mask[2];
612 };
613 typedef struct ppathcnf ppathcnf;
614
615 #define MOUNTPROG 100005
616 #define MOUNTVERS 1
617
618 #define MOUNTPROC_NULL 0
619 #define MOUNTPROC_MNT 1
620 #define MOUNTPROC_DUMP 2
621 #define MOUNTPROC_UMNT 3
622 #define MOUNTPROC_UMNTALL 4
623 #define MOUNTPROC_EXPORT 5
624 #define MOUNTPROC_EXPORTALL 6
625
626 #define MOUNTVERS_POSIX 2
627
628 #define MOUNTPROC_PATHCONF 7
629
630 #define MOUNT_V3 3
631
632 #define MOUNTPROC3_NULL 0
633 #define MOUNTPROC3_MNT 1
634 #define MOUNTPROC3_DUMP 2
635 #define MOUNTPROC3_UMNT 3
636 #define MOUNTPROC3_UMNTALL 4
637 #define MOUNTPROC3_EXPORT 5
638
639 enum {
640 #ifndef NFS_FHSIZE
641         NFS_FHSIZE = 32,
642 #endif
643 #ifndef NFS_PORT
644         NFS_PORT = 2049
645 #endif
646 };
647
648 /*
649  * We want to be able to compile mount on old kernels in such a way
650  * that the binary will work well on more recent kernels.
651  * Thus, if necessary we teach nfsmount.c the structure of new fields
652  * that will come later.
653  *
654  * Moreover, the new kernel includes conflict with glibc includes
655  * so it is easiest to ignore the kernel altogether (at compile time).
656  */
657
658 struct nfs2_fh {
659         char                    data[32];
660 };
661 struct nfs3_fh {
662         unsigned short          size;
663         unsigned char           data[64];
664 };
665
666 struct nfs_mount_data {
667         int             version;                /* 1 */
668         int             fd;                     /* 1 */
669         struct nfs2_fh  old_root;               /* 1 */
670         int             flags;                  /* 1 */
671         int             rsize;                  /* 1 */
672         int             wsize;                  /* 1 */
673         int             timeo;                  /* 1 */
674         int             retrans;                /* 1 */
675         int             acregmin;               /* 1 */
676         int             acregmax;               /* 1 */
677         int             acdirmin;               /* 1 */
678         int             acdirmax;               /* 1 */
679         struct sockaddr_in addr;                /* 1 */
680         char            hostname[256];          /* 1 */
681         int             namlen;                 /* 2 */
682         unsigned int    bsize;                  /* 3 */
683         struct nfs3_fh  root;                   /* 4 */
684 };
685
686 /* bits in the flags field */
687 enum {
688         NFS_MOUNT_SOFT = 0x0001,        /* 1 */
689         NFS_MOUNT_INTR = 0x0002,        /* 1 */
690         NFS_MOUNT_SECURE = 0x0004,      /* 1 */
691         NFS_MOUNT_POSIX = 0x0008,       /* 1 */
692         NFS_MOUNT_NOCTO = 0x0010,       /* 1 */
693         NFS_MOUNT_NOAC = 0x0020,        /* 1 */
694         NFS_MOUNT_TCP = 0x0040,         /* 2 */
695         NFS_MOUNT_VER3 = 0x0080,        /* 3 */
696         NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
697         NFS_MOUNT_NONLM = 0x0200        /* 3 */
698 };
699
700
701 /*
702  * We need to translate between nfs status return values and
703  * the local errno values which may not be the same.
704  *
705  * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
706  * "after #include <errno.h> the symbol errno is reserved for any use,
707  *  it cannot even be used as a struct tag or field name".
708  */
709
710 #ifndef EDQUOT
711 #define EDQUOT  ENOSPC
712 #endif
713
714 // Convert each NFSERR_BLAH into EBLAH
715
716 static const struct {
717         short stat;
718         short errnum;
719 } nfs_errtbl[] = {
720         {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
721         {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
722         {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
723         {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
724 };
725
726 static char *nfs_strerror(int status)
727 {
728         int i;
729
730         for (i = 0; nfs_errtbl[i].stat != -1; i++) {
731                 if (nfs_errtbl[i].stat == status)
732                         return strerror(nfs_errtbl[i].errnum);
733         }
734         return xasprintf("unknown nfs status return value: %d", status);
735 }
736
737 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
738 {
739         if (!xdr_opaque(xdrs, objp, FHSIZE))
740                  return FALSE;
741         return TRUE;
742 }
743
744 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
745 {
746         if (!xdr_u_int(xdrs, &objp->fhs_status))
747                  return FALSE;
748         switch (objp->fhs_status) {
749         case 0:
750                 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
751                          return FALSE;
752                 break;
753         default:
754                 break;
755         }
756         return TRUE;
757 }
758
759 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
760 {
761         if (!xdr_string(xdrs, objp, MNTPATHLEN))
762                  return FALSE;
763         return TRUE;
764 }
765
766 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
767 {
768         if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
769                  return FALSE;
770         return TRUE;
771 }
772
773 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
774 {
775         if (!xdr_fhandle3(xdrs, &objp->fhandle))
776                 return FALSE;
777         if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
778                                 sizeof (int), (xdrproc_t) xdr_int))
779                 return FALSE;
780         return TRUE;
781 }
782
783 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
784 {
785         if (!xdr_enum(xdrs, (enum_t *) objp))
786                  return FALSE;
787         return TRUE;
788 }
789
790 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
791 {
792         if (!xdr_mountstat3(xdrs, &objp->fhs_status))
793                 return FALSE;
794         switch (objp->fhs_status) {
795         case MNT_OK:
796                 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
797                          return FALSE;
798                 break;
799         default:
800                 break;
801         }
802         return TRUE;
803 }
804
805 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
806
807 /*
808  * Unfortunately, the kernel prints annoying console messages
809  * in case of an unexpected nfs mount version (instead of
810  * just returning some error).  Therefore we'll have to try
811  * and figure out what version the kernel expects.
812  *
813  * Variables:
814  *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
815  *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
816  *      nfs_mount_version: version this source and running kernel can handle
817  */
818 static void
819 find_kernel_nfs_mount_version(void)
820 {
821         int kernel_version;
822
823         if (nfs_mount_version)
824                 return;
825
826         nfs_mount_version = 4; /* default */
827
828         kernel_version = get_linux_version_code();
829         if (kernel_version) {
830                 if (kernel_version < KERNEL_VERSION(2,1,32))
831                         nfs_mount_version = 1;
832                 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
833                                 (kernel_version >= KERNEL_VERSION(2,3,0) &&
834                                  kernel_version < KERNEL_VERSION(2,3,99)))
835                         nfs_mount_version = 3;
836                 /* else v4 since 2.3.99pre4 */
837         }
838 }
839
840 static void
841 get_mountport(struct pmap *pm_mnt,
842         struct sockaddr_in *server_addr,
843         long unsigned prog,
844         long unsigned version,
845         long unsigned proto,
846         long unsigned port)
847 {
848         struct pmaplist *pmap;
849
850         server_addr->sin_port = PMAPPORT;
851 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
852  * I understand it like "IPv6 for this is not 100% ready" */
853         pmap = pmap_getmaps(server_addr);
854
855         if (version > MAX_NFSPROT)
856                 version = MAX_NFSPROT;
857         if (!prog)
858                 prog = MOUNTPROG;
859         pm_mnt->pm_prog = prog;
860         pm_mnt->pm_vers = version;
861         pm_mnt->pm_prot = proto;
862         pm_mnt->pm_port = port;
863
864         while (pmap) {
865                 if (pmap->pml_map.pm_prog != prog)
866                         goto next;
867                 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
868                         goto next;
869                 if (version > 2 && pmap->pml_map.pm_vers != version)
870                         goto next;
871                 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
872                         goto next;
873                 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
874                     (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
875                     (port && pmap->pml_map.pm_port != port))
876                         goto next;
877                 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
878  next:
879                 pmap = pmap->pml_next;
880         }
881         if (!pm_mnt->pm_vers)
882                 pm_mnt->pm_vers = MOUNTVERS;
883         if (!pm_mnt->pm_port)
884                 pm_mnt->pm_port = MOUNTPORT;
885         if (!pm_mnt->pm_prot)
886                 pm_mnt->pm_prot = IPPROTO_TCP;
887 }
888
889 #if BB_MMU
890 static int daemonize(void)
891 {
892         int fd;
893         int pid = fork();
894         if (pid < 0) /* error */
895                 return -errno;
896         if (pid > 0) /* parent */
897                 return 0;
898         /* child */
899         fd = xopen(bb_dev_null, O_RDWR);
900         dup2(fd, 0);
901         dup2(fd, 1);
902         dup2(fd, 2);
903         while (fd > 2) close(fd--);
904         setsid();
905         openlog(applet_name, LOG_PID, LOG_DAEMON);
906         logmode = LOGMODE_SYSLOG;
907         return 1;
908 }
909 #else
910 static inline int daemonize(void) { return -ENOSYS; }
911 #endif
912
913 // TODO
914 static inline int we_saw_this_host_before(const char *hostname)
915 {
916         return 0;
917 }
918
919 /* RPC strerror analogs are terminally idiotic:
920  * *mandatory* prefix and \n at end.
921  * This hopefully helps. Usage:
922  * error_msg_rpc(clnt_*error*(" ")) */
923 static void error_msg_rpc(const char *msg)
924 {
925         int len;
926         while (msg[0] == ' ' || msg[0] == ':') msg++;
927         len = strlen(msg);
928         while (len && msg[len-1] == '\n') len--;
929         bb_error_msg("%.*s", len, msg);
930 }
931
932 // NB: mp->xxx fields may be trashed on exit
933 static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
934 {
935         CLIENT *mclient;
936         char *hostname;
937         char *pathname;
938         char *mounthost;
939         struct nfs_mount_data data;
940         char *opt;
941         struct hostent *hp;
942         struct sockaddr_in server_addr;
943         struct sockaddr_in mount_server_addr;
944         int msock, fsock;
945         union {
946                 struct fhstatus nfsv2;
947                 struct mountres3 nfsv3;
948         } status;
949         int daemonized;
950         char *s;
951         int port;
952         int mountport;
953         int proto;
954 #if BB_MMU
955         int bg = 0;
956 #else
957         enum { bg = 0 };
958 #endif
959         int soft;
960         int intr;
961         int posix;
962         int nocto;
963         int noac;
964         int nolock;
965         int retry;
966         int tcp;
967         int mountprog;
968         int mountvers;
969         int nfsprog;
970         int nfsvers;
971         int retval;
972
973         find_kernel_nfs_mount_version();
974
975         daemonized = 0;
976         mounthost = NULL;
977         retval = ETIMEDOUT;
978         msock = fsock = -1;
979         mclient = NULL;
980
981         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
982
983         filteropts = xstrdup(filteropts); /* going to trash it later... */
984
985         hostname = xstrdup(mp->mnt_fsname);
986         /* mount_main() guarantees that ':' is there */
987         s = strchr(hostname, ':');
988         pathname = s + 1;
989         *s = '\0';
990         /* Ignore all but first hostname in replicated mounts
991            until they can be fully supported. (mack@sgi.com) */
992         s = strchr(hostname, ',');
993         if (s) {
994                 *s = '\0';
995                 bb_error_msg("warning: multiple hostnames not supported");
996         }
997
998         server_addr.sin_family = AF_INET;
999         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1000                 hp = gethostbyname(hostname);
1001                 if (hp == NULL) {
1002                         bb_herror_msg("%s", hostname);
1003                         goto fail;
1004                 }
1005                 if (hp->h_length > sizeof(struct in_addr)) {
1006                         bb_error_msg("got bad hp->h_length");
1007                         hp->h_length = sizeof(struct in_addr);
1008                 }
1009                 memcpy(&server_addr.sin_addr,
1010                                 hp->h_addr, hp->h_length);
1011         }
1012
1013         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1014
1015         /* add IP address to mtab options for use when unmounting */
1016
1017         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1018                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1019         } else {
1020                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1021                                         mp->mnt_opts[0] ? "," : "",
1022                                         inet_ntoa(server_addr.sin_addr));
1023                 free(mp->mnt_opts);
1024                 mp->mnt_opts = tmp;
1025         }
1026
1027         /* Set default options.
1028          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1029          * let the kernel decide.
1030          * timeo is filled in after we know whether it'll be TCP or UDP. */
1031         memset(&data, 0, sizeof(data));
1032         data.retrans    = 3;
1033         data.acregmin   = 3;
1034         data.acregmax   = 60;
1035         data.acdirmin   = 30;
1036         data.acdirmax   = 60;
1037         data.namlen     = NAME_MAX;
1038
1039         soft = 0;
1040         intr = 0;
1041         posix = 0;
1042         nocto = 0;
1043         nolock = 0;
1044         noac = 0;
1045         retry = 10000;          /* 10000 minutes ~ 1 week */
1046         tcp = 0;
1047
1048         mountprog = MOUNTPROG;
1049         mountvers = 0;
1050         port = 0;
1051         mountport = 0;
1052         nfsprog = 100003;
1053         nfsvers = 0;
1054
1055         /* parse options */
1056         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1057                 char *opteq = strchr(opt, '=');
1058                 if (opteq) {
1059                         static const char options[] ALIGN1 =
1060                                 /* 0 */ "rsize\0"
1061                                 /* 1 */ "wsize\0"
1062                                 /* 2 */ "timeo\0"
1063                                 /* 3 */ "retrans\0"
1064                                 /* 4 */ "acregmin\0"
1065                                 /* 5 */ "acregmax\0"
1066                                 /* 6 */ "acdirmin\0"
1067                                 /* 7 */ "acdirmax\0"
1068                                 /* 8 */ "actimeo\0"
1069                                 /* 9 */ "retry\0"
1070                                 /* 10 */ "port\0"
1071                                 /* 11 */ "mountport\0"
1072                                 /* 12 */ "mounthost\0"
1073                                 /* 13 */ "mountprog\0"
1074                                 /* 14 */ "mountvers\0"
1075                                 /* 15 */ "nfsprog\0"
1076                                 /* 16 */ "nfsvers\0"
1077                                 /* 17 */ "vers\0"
1078                                 /* 18 */ "proto\0"
1079                                 /* 19 */ "namlen\0"
1080                                 /* 20 */ "addr\0";
1081                         int val = xatoi_u(opteq + 1);
1082                         *opteq = '\0';
1083                         switch (index_in_strings(options, opt)) {
1084                         case 0: // "rsize"
1085                                 data.rsize = val;
1086                                 break;
1087                         case 1: // "wsize"
1088                                 data.wsize = val;
1089                                 break;
1090                         case 2: // "timeo"
1091                                 data.timeo = val;
1092                                 break;
1093                         case 3: // "retrans"
1094                                 data.retrans = val;
1095                                 break;
1096                         case 4: // "acregmin"
1097                                 data.acregmin = val;
1098                                 break;
1099                         case 5: // "acregmax"
1100                                 data.acregmax = val;
1101                                 break;
1102                         case 6: // "acdirmin"
1103                                 data.acdirmin = val;
1104                                 break;
1105                         case 7: // "acdirmax"
1106                                 data.acdirmax = val;
1107                                 break;
1108                         case 8: // "actimeo"
1109                                 data.acregmin = val;
1110                                 data.acregmax = val;
1111                                 data.acdirmin = val;
1112                                 data.acdirmax = val;
1113                                 break;
1114                         case 9: // "retry"
1115                                 retry = val;
1116                                 break;
1117                         case 10: // "port"
1118                                 port = val;
1119                                 break;
1120                         case 11: // "mountport"
1121                                 mountport = val;
1122                                 break;
1123                         case 12: // "mounthost"
1124                                 mounthost = xstrndup(opteq+1,
1125                                                 strcspn(opteq+1," \t\n\r,"));
1126                                 break;
1127                         case 13: // "mountprog"
1128                                 mountprog = val;
1129                                 break;
1130                         case 14: // "mountvers"
1131                                 mountvers = val;
1132                                 break;
1133                         case 15: // "nfsprog"
1134                                 nfsprog = val;
1135                                 break;
1136                         case 16: // "nfsvers"
1137                         case 17: // "vers"
1138                                 nfsvers = val;
1139                                 break;
1140                         case 18: // "proto"
1141                                 if (!strncmp(opteq+1, "tcp", 3))
1142                                         tcp = 1;
1143                                 else if (!strncmp(opteq+1, "udp", 3))
1144                                         tcp = 0;
1145                                 else
1146                                         bb_error_msg("warning: unrecognized proto= option");
1147                                 break;
1148                         case 19: // "namlen"
1149                                 if (nfs_mount_version >= 2)
1150                                         data.namlen = val;
1151                                 else
1152                                         bb_error_msg("warning: option namlen is not supported\n");
1153                                 break;
1154                         case 20: // "addr" - ignore
1155                                 break;
1156                         default:
1157                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1158                                 goto fail;
1159                         }
1160                 }
1161                 else {
1162                         static const char options[] ALIGN1 =
1163                                 "bg\0"
1164                                 "fg\0"
1165                                 "soft\0"
1166                                 "hard\0"
1167                                 "intr\0"
1168                                 "posix\0"
1169                                 "cto\0"
1170                                 "ac\0"
1171                                 "tcp\0"
1172                                 "udp\0"
1173                                 "lock\0";
1174                         int val = 1;
1175                         if (!strncmp(opt, "no", 2)) {
1176                                 val = 0;
1177                                 opt += 2;
1178                         }
1179                         switch (index_in_strings(options, opt)) {
1180                         case 0: // "bg"
1181 #if BB_MMU
1182                                 bg = val;
1183 #endif
1184                                 break;
1185                         case 1: // "fg"
1186 #if BB_MMU
1187                                 bg = !val;
1188 #endif
1189                                 break;
1190                         case 2: // "soft"
1191                                 soft = val;
1192                                 break;
1193                         case 3: // "hard"
1194                                 soft = !val;
1195                                 break;
1196                         case 4: // "intr"
1197                                 intr = val;
1198                                 break;
1199                         case 5: // "posix"
1200                                 posix = val;
1201                                 break;
1202                         case 6: // "cto"
1203                                 nocto = !val;
1204                                 break;
1205                         case 7: // "ac"
1206                                 noac = !val;
1207                                 break;
1208                         case 8: // "tcp"
1209                                 tcp = val;
1210                                 break;
1211                         case 9: // "udp"
1212                                 tcp = !val;
1213                                 break;
1214                         case 10: // "lock"
1215                                 if (nfs_mount_version >= 3)
1216                                         nolock = !val;
1217                                 else
1218                                         bb_error_msg("warning: option nolock is not supported");
1219                                 break;
1220                         default:
1221                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1222                                 goto fail;
1223                         }
1224                 }
1225         }
1226         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1227
1228         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1229                 | (intr ? NFS_MOUNT_INTR : 0)
1230                 | (posix ? NFS_MOUNT_POSIX : 0)
1231                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1232                 | (noac ? NFS_MOUNT_NOAC : 0);
1233         if (nfs_mount_version >= 2)
1234                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1235         if (nfs_mount_version >= 3)
1236                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1237         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1238                 bb_error_msg("NFSv%d not supported", nfsvers);
1239                 goto fail;
1240         }
1241         if (nfsvers && !mountvers)
1242                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1243         if (nfsvers && nfsvers < mountvers) {
1244                 mountvers = nfsvers;
1245         }
1246
1247         /* Adjust options if none specified */
1248         if (!data.timeo)
1249                 data.timeo = tcp ? 70 : 7;
1250
1251         data.version = nfs_mount_version;
1252
1253         if (vfsflags & MS_REMOUNT)
1254                 goto do_mount;
1255
1256         /*
1257          * If the previous mount operation on the same host was
1258          * backgrounded, and the "bg" for this mount is also set,
1259          * give up immediately, to avoid the initial timeout.
1260          */
1261         if (bg && we_saw_this_host_before(hostname)) {
1262                 daemonized = daemonize();
1263                 if (daemonized <= 0) { /* parent or error */
1264                         retval = -daemonized;
1265                         goto ret;
1266                 }
1267         }
1268
1269         /* create mount daemon client */
1270         /* See if the nfs host = mount host. */
1271         if (mounthost) {
1272                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1273                         mount_server_addr.sin_family = AF_INET;
1274                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1275                 } else {
1276                         hp = gethostbyname(mounthost);
1277                         if (hp == NULL) {
1278                                 bb_herror_msg("%s", mounthost);
1279                                 goto fail;
1280                         } else {
1281                                 if (hp->h_length > sizeof(struct in_addr)) {
1282                                         bb_error_msg("got bad hp->h_length?");
1283                                         hp->h_length = sizeof(struct in_addr);
1284                                 }
1285                                 mount_server_addr.sin_family = AF_INET;
1286                                 memcpy(&mount_server_addr.sin_addr,
1287                                                 hp->h_addr, hp->h_length);
1288                         }
1289                 }
1290         }
1291
1292         /*
1293          * The following loop implements the mount retries. When the mount
1294          * times out, and the "bg" option is set, we background ourself
1295          * and continue trying.
1296          *
1297          * The case where the mount point is not present and the "bg"
1298          * option is set, is treated as a timeout. This is done to
1299          * support nested mounts.
1300          *
1301          * The "retry" count specified by the user is the number of
1302          * minutes to retry before giving up.
1303          */
1304         {
1305                 struct timeval total_timeout;
1306                 struct timeval retry_timeout;
1307                 struct pmap pm_mnt;
1308                 time_t t;
1309                 time_t prevt;
1310                 time_t timeout;
1311
1312                 retry_timeout.tv_sec = 3;
1313                 retry_timeout.tv_usec = 0;
1314                 total_timeout.tv_sec = 20;
1315                 total_timeout.tv_usec = 0;
1316                 timeout = time(NULL) + 60 * retry;
1317                 prevt = 0;
1318                 t = 30;
1319  retry:
1320                 /* be careful not to use too many CPU cycles */
1321                 if (t - prevt < 30)
1322                         sleep(30);
1323
1324                 get_mountport(&pm_mnt, &mount_server_addr,
1325                                 mountprog,
1326                                 mountvers,
1327                                 proto,
1328                                 mountport);
1329                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1330
1331                 /* contact the mount daemon via TCP */
1332                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1333                 msock = RPC_ANYSOCK;
1334
1335                 switch (pm_mnt.pm_prot) {
1336                 case IPPROTO_UDP:
1337                         mclient = clntudp_create(&mount_server_addr,
1338                                                  pm_mnt.pm_prog,
1339                                                  pm_mnt.pm_vers,
1340                                                  retry_timeout,
1341                                                  &msock);
1342                         if (mclient)
1343                                 break;
1344                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1345                         msock = RPC_ANYSOCK;
1346                 case IPPROTO_TCP:
1347                         mclient = clnttcp_create(&mount_server_addr,
1348                                                  pm_mnt.pm_prog,
1349                                                  pm_mnt.pm_vers,
1350                                                  &msock, 0, 0);
1351                         break;
1352                 default:
1353                         mclient = NULL;
1354                 }
1355                 if (!mclient) {
1356                         if (!daemonized && prevt == 0)
1357                                 error_msg_rpc(clnt_spcreateerror(" "));
1358                 } else {
1359                         enum clnt_stat clnt_stat;
1360                         /* try to mount hostname:pathname */
1361                         mclient->cl_auth = authunix_create_default();
1362
1363                         /* make pointers in xdr_mountres3 NULL so
1364                          * that xdr_array allocates memory for us
1365                          */
1366                         memset(&status, 0, sizeof(status));
1367
1368                         if (pm_mnt.pm_vers == 3)
1369                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1370                                               (xdrproc_t) xdr_dirpath,
1371                                               (caddr_t) &pathname,
1372                                               (xdrproc_t) xdr_mountres3,
1373                                               (caddr_t) &status,
1374                                               total_timeout);
1375                         else
1376                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1377                                               (xdrproc_t) xdr_dirpath,
1378                                               (caddr_t) &pathname,
1379                                               (xdrproc_t) xdr_fhstatus,
1380                                               (caddr_t) &status,
1381                                               total_timeout);
1382
1383                         if (clnt_stat == RPC_SUCCESS)
1384                                 goto prepare_kernel_data; /* we're done */
1385                         if (errno != ECONNREFUSED) {
1386                                 error_msg_rpc(clnt_sperror(mclient, " "));
1387                                 goto fail;      /* don't retry */
1388                         }
1389                         /* Connection refused */
1390                         if (!daemonized && prevt == 0) /* print just once */
1391                                 error_msg_rpc(clnt_sperror(mclient, " "));
1392                         auth_destroy(mclient->cl_auth);
1393                         clnt_destroy(mclient);
1394                         mclient = NULL;
1395                         close(msock);
1396                         msock = -1;
1397                 }
1398
1399                 /* Timeout. We are going to retry... maybe */
1400
1401                 if (!bg)
1402                         goto fail;
1403                 if (!daemonized) {
1404                         daemonized = daemonize();
1405                         if (daemonized <= 0) { /* parent or error */
1406                                 retval = -daemonized;
1407                                 goto ret;
1408                         }
1409                 }
1410                 prevt = t;
1411                 t = time(NULL);
1412                 if (t >= timeout)
1413                         /* TODO error message */
1414                         goto fail;
1415
1416                 goto retry;
1417         }
1418
1419  prepare_kernel_data:
1420
1421         if (nfsvers == 2) {
1422                 if (status.nfsv2.fhs_status != 0) {
1423                         bb_error_msg("%s:%s failed, reason given by server: %s",
1424                                 hostname, pathname,
1425                                 nfs_strerror(status.nfsv2.fhs_status));
1426                         goto fail;
1427                 }
1428                 memcpy(data.root.data,
1429                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1430                                 NFS_FHSIZE);
1431                 data.root.size = NFS_FHSIZE;
1432                 memcpy(data.old_root.data,
1433                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1434                                 NFS_FHSIZE);
1435         } else {
1436                 fhandle3 *my_fhandle;
1437                 if (status.nfsv3.fhs_status != 0) {
1438                         bb_error_msg("%s:%s failed, reason given by server: %s",
1439                                 hostname, pathname,
1440                                 nfs_strerror(status.nfsv3.fhs_status));
1441                         goto fail;
1442                 }
1443                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1444                 memset(data.old_root.data, 0, NFS_FHSIZE);
1445                 memset(&data.root, 0, sizeof(data.root));
1446                 data.root.size = my_fhandle->fhandle3_len;
1447                 memcpy(data.root.data,
1448                                 (char *) my_fhandle->fhandle3_val,
1449                                 my_fhandle->fhandle3_len);
1450
1451                 data.flags |= NFS_MOUNT_VER3;
1452         }
1453
1454         /* create nfs socket for kernel */
1455
1456         if (tcp) {
1457                 if (nfs_mount_version < 3) {
1458                         bb_error_msg("NFS over TCP is not supported");
1459                         goto fail;
1460                 }
1461                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1462         } else
1463                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1464         if (fsock < 0) {
1465                 bb_perror_msg("nfs socket");
1466                 goto fail;
1467         }
1468         if (bindresvport(fsock, 0) < 0) {
1469                 bb_perror_msg("nfs bindresvport");
1470                 goto fail;
1471         }
1472         if (port == 0) {
1473                 server_addr.sin_port = PMAPPORT;
1474                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1475                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1476                 if (port == 0)
1477                         port = NFS_PORT;
1478         }
1479         server_addr.sin_port = htons(port);
1480
1481         /* prepare data structure for kernel */
1482
1483         data.fd = fsock;
1484         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1485         strncpy(data.hostname, hostname, sizeof(data.hostname));
1486
1487         /* clean up */
1488
1489         auth_destroy(mclient->cl_auth);
1490         clnt_destroy(mclient);
1491         close(msock);
1492         msock = -1;
1493
1494         if (bg) {
1495                 /* We must wait until mount directory is available */
1496                 struct stat statbuf;
1497                 int delay = 1;
1498                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1499                         if (!daemonized) {
1500                                 daemonized = daemonize();
1501                                 if (daemonized <= 0) { /* parent or error */
1502         // FIXME: parent doesn't close fsock - ??!
1503                                         retval = -daemonized;
1504                                         goto ret;
1505                                 }
1506                         }
1507                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1508                         delay *= 2;
1509                         if (delay > 30)
1510                                 delay = 30;
1511                 }
1512         }
1513
1514  do_mount: /* perform actual mount */
1515
1516         mp->mnt_type = (char*)"nfs";
1517         retval = mount_it_now(mp, vfsflags, (char*)&data);
1518         goto ret;
1519
1520  fail:  /* abort */
1521
1522         if (msock >= 0) {
1523                 if (mclient) {
1524                         auth_destroy(mclient->cl_auth);
1525                         clnt_destroy(mclient);
1526                 }
1527                 close(msock);
1528         }
1529         if (fsock >= 0)
1530                 close(fsock);
1531
1532  ret:
1533         free(hostname);
1534         free(mounthost);
1535         free(filteropts);
1536         return retval;
1537 }
1538
1539 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1540
1541 /* Never called. Call should be optimized out. */
1542 int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
1543
1544 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1545
1546 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1547 // type detection.  Returns 0 for success, nonzero for failure.
1548 // NB: mp->xxx fields may be trashed on exit
1549 static int singlemount(struct mntent *mp, int ignore_busy)
1550 {
1551         int rc = -1;
1552         long vfsflags;
1553         char *loopFile = 0, *filteropts = 0;
1554         llist_t *fl = 0;
1555         struct stat st;
1556
1557         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1558
1559         // Treat fstype "auto" as unspecified.
1560
1561         if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
1562                 mp->mnt_type = 0;
1563
1564         // Might this be a virtual filesystem?
1565
1566         if (ENABLE_FEATURE_MOUNT_HELPERS
1567          && (strchr(mp->mnt_fsname, '#'))
1568         ) {
1569                 char *s, *p, *args[35];
1570                 int n = 0;
1571                 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1572                         if (s[0] == '#' && s[1] != '#') {
1573                                 *s = '\0';
1574                                 args[n++] = p;
1575                                 p = s + 1;
1576                         }
1577                 }
1578                 args[n++] = p;
1579                 args[n++] = mp->mnt_dir;
1580                 args[n] = NULL;
1581                 rc = wait4pid(xspawn(args));
1582                 goto report_error;
1583         }
1584
1585         // Might this be an CIFS filesystem?
1586
1587         if (ENABLE_FEATURE_MOUNT_CIFS
1588          && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
1589          && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
1590          && mp->mnt_fsname[0]==mp->mnt_fsname[1]
1591         ) {
1592                 len_and_sockaddr *lsa;
1593                 char *ip, *dotted;
1594                 char *s;
1595
1596                 rc = 1;
1597                 // Replace '/' with '\' and verify that unc points to "//server/share".
1598
1599                 for (s = mp->mnt_fsname; *s; ++s)
1600                         if (*s == '/') *s = '\\';
1601
1602                 // get server IP
1603
1604                 s = strrchr(mp->mnt_fsname, '\\');
1605                 if (s <= mp->mnt_fsname+1) goto report_error;
1606                 *s = '\0';
1607                 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
1608                 *s = '\\';
1609                 if (!lsa) goto report_error;
1610
1611                 // insert ip=... option into string flags.
1612
1613                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1614                 ip = xasprintf("ip=%s", dotted);
1615                 parse_mount_options(ip, &filteropts);
1616
1617                 // compose new unc '\\server-ip\share'
1618                 // (s => slash after hostname)
1619
1620                 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
1621
1622                 // lock is required
1623                 vfsflags |= MS_MANDLOCK;
1624
1625                 mp->mnt_type = (char*)"cifs";
1626                 rc = mount_it_now(mp, vfsflags, filteropts);
1627                 if (ENABLE_FEATURE_CLEAN_UP) {
1628                         free(mp->mnt_fsname);
1629                         free(ip);
1630                         free(dotted);
1631                         free(lsa);
1632                 }
1633                 goto report_error;
1634         }
1635
1636         // Might this be an NFS filesystem?
1637
1638         if (ENABLE_FEATURE_MOUNT_NFS
1639          && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
1640          && strchr(mp->mnt_fsname, ':') != NULL
1641         ) {
1642                 rc = nfsmount(mp, vfsflags, filteropts);
1643                 goto report_error;
1644         }
1645
1646         // Look at the file.  (Not found isn't a failure for remount, or for
1647         // a synthetic filesystem like proc or sysfs.)
1648         // (We use stat, not lstat, in order to allow
1649         // mount symlink_to_file_or_blkdev dir)
1650
1651         if (!stat(mp->mnt_fsname, &st)
1652          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1653         ) {
1654                 // Do we need to allocate a loopback device for it?
1655
1656                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1657                         loopFile = bb_simplify_path(mp->mnt_fsname);
1658                         mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1659                         if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1660                                 if (errno == EPERM || errno == EACCES)
1661                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1662                                 else
1663                                         bb_perror_msg("cannot setup loop device");
1664                                 return errno;
1665                         }
1666
1667                 // Autodetect bind mounts
1668
1669                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1670                         vfsflags |= MS_BIND;
1671         }
1672
1673         /* If we know the fstype (or don't need to), jump straight
1674          * to the actual mount. */
1675
1676         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1677                 rc = mount_it_now(mp, vfsflags, filteropts);
1678         else {
1679                 // Loop through filesystem types until mount succeeds
1680                 // or we run out
1681
1682                 /* Initialize list of block backed filesystems.  This has to be
1683                  * done here so that during "mount -a", mounts after /proc shows up
1684                  * can autodetect. */
1685
1686                 if (!fslist) {
1687                         fslist = get_block_backed_filesystems();
1688                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1689                                 atexit(delete_block_backed_filesystems);
1690                 }
1691
1692                 for (fl = fslist; fl; fl = fl->link) {
1693                         mp->mnt_type = fl->data;
1694                         rc = mount_it_now(mp, vfsflags, filteropts);
1695                         if (!rc) break;
1696                 }
1697         }
1698
1699         // If mount failed, clean up loop file (if any).
1700
1701         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1702                 del_loop(mp->mnt_fsname);
1703                 if (ENABLE_FEATURE_CLEAN_UP) {
1704                         free(loopFile);
1705                         free(mp->mnt_fsname);
1706                 }
1707         }
1708
1709  report_error:
1710         if (ENABLE_FEATURE_CLEAN_UP)
1711                 free(filteropts);
1712
1713         if (errno == EBUSY && ignore_busy)
1714                 return 0;
1715         if (rc < 0)
1716                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1717         return rc;
1718 }
1719
1720 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1721 // each directory to be mounted.
1722
1723 static const char must_be_root[] ALIGN1 = "you must be root";
1724
1725 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1726 int mount_main(int argc, char **argv)
1727 {
1728         enum { OPT_ALL = 0x10 };
1729
1730         char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1731         char *opt_o;
1732         const char *fstabname;
1733         FILE *fstab;
1734         int i, j, rc = 0;
1735         unsigned opt;
1736         struct mntent mtpair[2], *mtcur = mtpair;
1737         SKIP_DESKTOP(const int nonroot = 0;)
1738
1739         USE_DESKTOP( int nonroot = ) sanitize_env_if_suid();
1740
1741         // Parse long options, like --bind and --move.  Note that -o option
1742         // and --option are synonymous.  Yes, this means --remount,rw works.
1743
1744         for (i = j = 0; i < argc; i++) {
1745                 if (argv[i][0] == '-' && argv[i][1] == '-') {
1746                         append_mount_options(&cmdopts, argv[i]+2);
1747                 } else argv[j++] = argv[i];
1748         }
1749         argv[j] = 0;
1750         argc = j;
1751
1752         // Parse remaining options
1753
1754 #if ENABLE_FEATURE_MOUNT_VERBOSE
1755         opt_complementary = "vv"; // -v is a counter
1756 #endif
1757         opt = getopt32(argv, OPTION_STR, &opt_o, &fstype
1758                         USE_FEATURE_MOUNT_VERBOSE(, &verbose));
1759         if (opt & OPT_o) append_mount_options(&cmdopts, opt_o); // -o
1760         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1761         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
1762         argv += optind;
1763         argc -= optind;
1764
1765         // Three or more non-option arguments?  Die with a usage message.
1766
1767         if (argc > 2) bb_show_usage();
1768
1769         // If we have no arguments, show currently mounted filesystems
1770
1771         if (!argc) {
1772                 if (!(opt & OPT_ALL)) {
1773                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1774
1775                         if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
1776
1777                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1778                                                                 sizeof(getmntent_buf)))
1779                         {
1780                                 // Don't show rootfs. FIXME: why??
1781                                 // util-linux 2.12a happily shows rootfs...
1782                                 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1783
1784                                 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1785                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1786                                                         mtpair->mnt_dir, mtpair->mnt_type,
1787                                                         mtpair->mnt_opts);
1788                         }
1789                         if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1790                         return EXIT_SUCCESS;
1791                 }
1792         } else storage_path = bb_simplify_path(argv[0]);
1793
1794         // When we have two arguments, the second is the directory and we can
1795         // skip looking at fstab entirely.  We can always abspath() the directory
1796         // argument when we get it.
1797
1798         if (argc == 2) {
1799                 if (nonroot)
1800                         bb_error_msg_and_die(must_be_root);
1801                 mtpair->mnt_fsname = argv[0];
1802                 mtpair->mnt_dir = argv[1];
1803                 mtpair->mnt_type = fstype;
1804                 mtpair->mnt_opts = cmdopts;
1805                 if (ENABLE_FEATURE_MOUNT_LABEL) {
1806                         resolve_mount_spec(&mtpair->mnt_fsname);
1807                 }
1808                 rc = singlemount(mtpair, 0);
1809                 goto clean_up;
1810         }
1811
1812         i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
1813         if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1814                 bb_error_msg_and_die(must_be_root);
1815
1816         // If we have a shared subtree flag, don't worry about fstab or mtab.
1817
1818         if (ENABLE_FEATURE_MOUNT_FLAGS
1819          && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1820         ) {
1821                 rc = verbose_mount("", argv[0], "", i, "");
1822                 if (rc) bb_simple_perror_msg_and_die(argv[0]);
1823                 goto clean_up;
1824         }
1825
1826         // Open either fstab or mtab
1827
1828         fstabname = "/etc/fstab";
1829         if (i & MS_REMOUNT) {
1830                 fstabname = bb_path_mtab_file;
1831         }
1832         fstab = setmntent(fstabname, "r");
1833         if (!fstab)
1834                 bb_perror_msg_and_die("cannot read %s", fstabname);
1835
1836         // Loop through entries until we find what we're looking for.
1837
1838         memset(mtpair, 0, sizeof(mtpair));
1839         for (;;) {
1840                 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1841
1842                 // Get next fstab entry
1843
1844                 if (!getmntent_r(fstab, mtcur, getmntent_buf
1845                                         + (mtcur==mtpair ? sizeof(getmntent_buf)/2 : 0),
1846                                 sizeof(getmntent_buf)/2))
1847                 {
1848                         // Were we looking for something specific?
1849
1850                         if (argc) {
1851
1852                                 // If we didn't find anything, complain.
1853
1854                                 if (!mtnext->mnt_fsname)
1855                                         bb_error_msg_and_die("can't find %s in %s",
1856                                                 argv[0], fstabname);
1857
1858                                 mtcur = mtnext;
1859                                 if (nonroot) {
1860                                         // fstab must have "users" or "user"
1861                                         if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1862                                                 bb_error_msg_and_die(must_be_root);
1863                                 }
1864
1865                                 // Mount the last thing we found.
1866
1867                                 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1868                                 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1869                                 if (ENABLE_FEATURE_MOUNT_LABEL) {
1870                                         resolve_mount_spec(&mtpair->mnt_fsname);
1871                                 }
1872                                 rc = singlemount(mtcur, 0);
1873                                 free(mtcur->mnt_opts);
1874                         }
1875                         goto clean_up;
1876                 }
1877
1878                 /* If we're trying to mount something specific and this isn't it,
1879                  * skip it.  Note we must match both the exact text in fstab (ala
1880                  * "proc") or a full path from root */
1881
1882                 if (argc) {
1883
1884                         // Is this what we're looking for?
1885
1886                         if (strcmp(argv[0], mtcur->mnt_fsname) &&
1887                            strcmp(storage_path, mtcur->mnt_fsname) &&
1888                            strcmp(argv[0], mtcur->mnt_dir) &&
1889                            strcmp(storage_path, mtcur->mnt_dir)) continue;
1890
1891                         // Remember this entry.  Something later may have overmounted
1892                         // it, and we want the _last_ match.
1893
1894                         mtcur = mtnext;
1895
1896                 // If we're mounting all.
1897
1898                 } else {
1899                         // Do we need to match a filesystem type?
1900                         if (fstype && match_fstype(mtcur, fstype)) continue;
1901
1902                         // Skip noauto and swap anyway.
1903
1904                         if (parse_mount_options(mtcur->mnt_opts, 0)
1905                                 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1906
1907                         // No, mount -a won't mount anything,
1908                         // even user mounts, for mere humans.
1909
1910                         if (nonroot)
1911                                 bb_error_msg_and_die(must_be_root);
1912
1913                         // Mount this thing.
1914                         if (ENABLE_FEATURE_MOUNT_LABEL) {
1915                                 resolve_mount_spec(&mtpair->mnt_fsname);
1916                         }
1917
1918                         // NFS mounts want this to be xrealloc-able
1919                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1920                         if (singlemount(mtcur, 1)) {
1921                                 /* Count number of failed mounts */
1922                                 rc++;
1923                         }
1924                         free(mtcur->mnt_opts);
1925                 }
1926         }
1927         if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1928
1929  clean_up:
1930
1931         if (ENABLE_FEATURE_CLEAN_UP) {
1932                 free(storage_path);
1933                 free(cmdopts);
1934         }
1935
1936         return rc;
1937 }