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