mount: code shrink without logic changes. Added a comment
[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 static int daemonize(void)
894 {
895         int fd;
896         int pid = fork();
897         if (pid < 0) /* error */
898                 return -errno;
899         if (pid > 0) /* parent */
900                 return 0;
901         /* child */
902         fd = xopen(bb_dev_null, O_RDWR);
903         dup2(fd, 0);
904         dup2(fd, 1);
905         dup2(fd, 2);
906         while (fd > 2) close(fd--);
907         setsid();
908         openlog(applet_name, LOG_PID, LOG_DAEMON);
909         logmode = LOGMODE_SYSLOG;
910         return 1;
911 }
912 #else
913 static inline int daemonize(void) { return -ENOSYS; }
914 #endif
915
916 // TODO
917 static inline int we_saw_this_host_before(const char *hostname ATTRIBUTE_UNUSED)
918 {
919         return 0;
920 }
921
922 /* RPC strerror analogs are terminally idiotic:
923  * *mandatory* prefix and \n at end.
924  * This hopefully helps. Usage:
925  * error_msg_rpc(clnt_*error*(" ")) */
926 static void error_msg_rpc(const char *msg)
927 {
928         int len;
929         while (msg[0] == ' ' || msg[0] == ':') msg++;
930         len = strlen(msg);
931         while (len && msg[len-1] == '\n') len--;
932         bb_error_msg("%.*s", len, msg);
933 }
934
935 // NB: mp->xxx fields may be trashed on exit
936 static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
937 {
938         CLIENT *mclient;
939         char *hostname;
940         char *pathname;
941         char *mounthost;
942         struct nfs_mount_data data;
943         char *opt;
944         struct hostent *hp;
945         struct sockaddr_in server_addr;
946         struct sockaddr_in mount_server_addr;
947         int msock, fsock;
948         union {
949                 struct fhstatus nfsv2;
950                 struct mountres3 nfsv3;
951         } status;
952         int daemonized;
953         char *s;
954         int port;
955         int mountport;
956         int proto;
957 #if BB_MMU
958         int bg = 0;
959 #else
960         enum { bg = 0 };
961 #endif
962         int soft;
963         int intr;
964         int posix;
965         int nocto;
966         int noac;
967         int nolock;
968         int retry;
969         int tcp;
970         int mountprog;
971         int mountvers;
972         int nfsprog;
973         int nfsvers;
974         int retval;
975
976         find_kernel_nfs_mount_version();
977
978         daemonized = 0;
979         mounthost = NULL;
980         retval = ETIMEDOUT;
981         msock = fsock = -1;
982         mclient = NULL;
983
984         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
985
986         filteropts = xstrdup(filteropts); /* going to trash it later... */
987
988         hostname = xstrdup(mp->mnt_fsname);
989         /* mount_main() guarantees that ':' is there */
990         s = strchr(hostname, ':');
991         pathname = s + 1;
992         *s = '\0';
993         /* Ignore all but first hostname in replicated mounts
994            until they can be fully supported. (mack@sgi.com) */
995         s = strchr(hostname, ',');
996         if (s) {
997                 *s = '\0';
998                 bb_error_msg("warning: multiple hostnames not supported");
999         }
1000
1001         server_addr.sin_family = AF_INET;
1002         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1003                 hp = gethostbyname(hostname);
1004                 if (hp == NULL) {
1005                         bb_herror_msg("%s", hostname);
1006                         goto fail;
1007                 }
1008                 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1009                         bb_error_msg("got bad hp->h_length");
1010                         hp->h_length = sizeof(struct in_addr);
1011                 }
1012                 memcpy(&server_addr.sin_addr,
1013                                 hp->h_addr, hp->h_length);
1014         }
1015
1016         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1017
1018         /* add IP address to mtab options for use when unmounting */
1019
1020         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1021                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1022         } else {
1023                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1024                                         mp->mnt_opts[0] ? "," : "",
1025                                         inet_ntoa(server_addr.sin_addr));
1026                 free(mp->mnt_opts);
1027                 mp->mnt_opts = tmp;
1028         }
1029
1030         /* Set default options.
1031          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1032          * let the kernel decide.
1033          * timeo is filled in after we know whether it'll be TCP or UDP. */
1034         memset(&data, 0, sizeof(data));
1035         data.retrans    = 3;
1036         data.acregmin   = 3;
1037         data.acregmax   = 60;
1038         data.acdirmin   = 30;
1039         data.acdirmax   = 60;
1040         data.namlen     = NAME_MAX;
1041
1042         soft = 0;
1043         intr = 0;
1044         posix = 0;
1045         nocto = 0;
1046         nolock = 0;
1047         noac = 0;
1048         retry = 10000;          /* 10000 minutes ~ 1 week */
1049         tcp = 0;
1050
1051         mountprog = MOUNTPROG;
1052         mountvers = 0;
1053         port = 0;
1054         mountport = 0;
1055         nfsprog = 100003;
1056         nfsvers = 0;
1057
1058         /* parse options */
1059         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1060                 char *opteq = strchr(opt, '=');
1061                 if (opteq) {
1062                         static const char options[] ALIGN1 =
1063                                 /* 0 */ "rsize\0"
1064                                 /* 1 */ "wsize\0"
1065                                 /* 2 */ "timeo\0"
1066                                 /* 3 */ "retrans\0"
1067                                 /* 4 */ "acregmin\0"
1068                                 /* 5 */ "acregmax\0"
1069                                 /* 6 */ "acdirmin\0"
1070                                 /* 7 */ "acdirmax\0"
1071                                 /* 8 */ "actimeo\0"
1072                                 /* 9 */ "retry\0"
1073                                 /* 10 */ "port\0"
1074                                 /* 11 */ "mountport\0"
1075                                 /* 12 */ "mounthost\0"
1076                                 /* 13 */ "mountprog\0"
1077                                 /* 14 */ "mountvers\0"
1078                                 /* 15 */ "nfsprog\0"
1079                                 /* 16 */ "nfsvers\0"
1080                                 /* 17 */ "vers\0"
1081                                 /* 18 */ "proto\0"
1082                                 /* 19 */ "namlen\0"
1083                                 /* 20 */ "addr\0";
1084                         int val = xatoi_u(opteq + 1);
1085                         *opteq = '\0';
1086                         switch (index_in_strings(options, opt)) {
1087                         case 0: // "rsize"
1088                                 data.rsize = val;
1089                                 break;
1090                         case 1: // "wsize"
1091                                 data.wsize = val;
1092                                 break;
1093                         case 2: // "timeo"
1094                                 data.timeo = val;
1095                                 break;
1096                         case 3: // "retrans"
1097                                 data.retrans = val;
1098                                 break;
1099                         case 4: // "acregmin"
1100                                 data.acregmin = val;
1101                                 break;
1102                         case 5: // "acregmax"
1103                                 data.acregmax = val;
1104                                 break;
1105                         case 6: // "acdirmin"
1106                                 data.acdirmin = val;
1107                                 break;
1108                         case 7: // "acdirmax"
1109                                 data.acdirmax = val;
1110                                 break;
1111                         case 8: // "actimeo"
1112                                 data.acregmin = val;
1113                                 data.acregmax = val;
1114                                 data.acdirmin = val;
1115                                 data.acdirmax = val;
1116                                 break;
1117                         case 9: // "retry"
1118                                 retry = val;
1119                                 break;
1120                         case 10: // "port"
1121                                 port = val;
1122                                 break;
1123                         case 11: // "mountport"
1124                                 mountport = val;
1125                                 break;
1126                         case 12: // "mounthost"
1127                                 mounthost = xstrndup(opteq+1,
1128                                                 strcspn(opteq+1," \t\n\r,"));
1129                                 break;
1130                         case 13: // "mountprog"
1131                                 mountprog = val;
1132                                 break;
1133                         case 14: // "mountvers"
1134                                 mountvers = val;
1135                                 break;
1136                         case 15: // "nfsprog"
1137                                 nfsprog = val;
1138                                 break;
1139                         case 16: // "nfsvers"
1140                         case 17: // "vers"
1141                                 nfsvers = val;
1142                                 break;
1143                         case 18: // "proto"
1144                                 if (!strncmp(opteq+1, "tcp", 3))
1145                                         tcp = 1;
1146                                 else if (!strncmp(opteq+1, "udp", 3))
1147                                         tcp = 0;
1148                                 else
1149                                         bb_error_msg("warning: unrecognized proto= option");
1150                                 break;
1151                         case 19: // "namlen"
1152                                 if (nfs_mount_version >= 2)
1153                                         data.namlen = val;
1154                                 else
1155                                         bb_error_msg("warning: option namlen is not supported\n");
1156                                 break;
1157                         case 20: // "addr" - ignore
1158                                 break;
1159                         default:
1160                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1161                                 goto fail;
1162                         }
1163                 }
1164                 else {
1165                         static const char options[] ALIGN1 =
1166                                 "bg\0"
1167                                 "fg\0"
1168                                 "soft\0"
1169                                 "hard\0"
1170                                 "intr\0"
1171                                 "posix\0"
1172                                 "cto\0"
1173                                 "ac\0"
1174                                 "tcp\0"
1175                                 "udp\0"
1176                                 "lock\0";
1177                         int val = 1;
1178                         if (!strncmp(opt, "no", 2)) {
1179                                 val = 0;
1180                                 opt += 2;
1181                         }
1182                         switch (index_in_strings(options, opt)) {
1183                         case 0: // "bg"
1184 #if BB_MMU
1185                                 bg = val;
1186 #endif
1187                                 break;
1188                         case 1: // "fg"
1189 #if BB_MMU
1190                                 bg = !val;
1191 #endif
1192                                 break;
1193                         case 2: // "soft"
1194                                 soft = val;
1195                                 break;
1196                         case 3: // "hard"
1197                                 soft = !val;
1198                                 break;
1199                         case 4: // "intr"
1200                                 intr = val;
1201                                 break;
1202                         case 5: // "posix"
1203                                 posix = val;
1204                                 break;
1205                         case 6: // "cto"
1206                                 nocto = !val;
1207                                 break;
1208                         case 7: // "ac"
1209                                 noac = !val;
1210                                 break;
1211                         case 8: // "tcp"
1212                                 tcp = val;
1213                                 break;
1214                         case 9: // "udp"
1215                                 tcp = !val;
1216                                 break;
1217                         case 10: // "lock"
1218                                 if (nfs_mount_version >= 3)
1219                                         nolock = !val;
1220                                 else
1221                                         bb_error_msg("warning: option nolock is not supported");
1222                                 break;
1223                         default:
1224                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1225                                 goto fail;
1226                         }
1227                 }
1228         }
1229         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1230
1231         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1232                 | (intr ? NFS_MOUNT_INTR : 0)
1233                 | (posix ? NFS_MOUNT_POSIX : 0)
1234                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1235                 | (noac ? NFS_MOUNT_NOAC : 0);
1236         if (nfs_mount_version >= 2)
1237                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1238         if (nfs_mount_version >= 3)
1239                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1240         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1241                 bb_error_msg("NFSv%d not supported", nfsvers);
1242                 goto fail;
1243         }
1244         if (nfsvers && !mountvers)
1245                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1246         if (nfsvers && nfsvers < mountvers) {
1247                 mountvers = nfsvers;
1248         }
1249
1250         /* Adjust options if none specified */
1251         if (!data.timeo)
1252                 data.timeo = tcp ? 70 : 7;
1253
1254         data.version = nfs_mount_version;
1255
1256         if (vfsflags & MS_REMOUNT)
1257                 goto do_mount;
1258
1259         /*
1260          * If the previous mount operation on the same host was
1261          * backgrounded, and the "bg" for this mount is also set,
1262          * give up immediately, to avoid the initial timeout.
1263          */
1264         if (bg && we_saw_this_host_before(hostname)) {
1265                 daemonized = daemonize();
1266                 if (daemonized <= 0) { /* parent or error */
1267                         retval = -daemonized;
1268                         goto ret;
1269                 }
1270         }
1271
1272         /* create mount daemon client */
1273         /* See if the nfs host = mount host. */
1274         if (mounthost) {
1275                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1276                         mount_server_addr.sin_family = AF_INET;
1277                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1278                 } else {
1279                         hp = gethostbyname(mounthost);
1280                         if (hp == NULL) {
1281                                 bb_herror_msg("%s", mounthost);
1282                                 goto fail;
1283                         }
1284                         if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1285                                 bb_error_msg("got bad hp->h_length");
1286                                 hp->h_length = sizeof(struct in_addr);
1287                         }
1288                         mount_server_addr.sin_family = AF_INET;
1289                         memcpy(&mount_server_addr.sin_addr,
1290                                                 hp->h_addr, hp->h_length);
1291                 }
1292         }
1293
1294         /*
1295          * The following loop implements the mount retries. When the mount
1296          * times out, and the "bg" option is set, we background ourself
1297          * and continue trying.
1298          *
1299          * The case where the mount point is not present and the "bg"
1300          * option is set, is treated as a timeout. This is done to
1301          * support nested mounts.
1302          *
1303          * The "retry" count specified by the user is the number of
1304          * minutes to retry before giving up.
1305          */
1306         {
1307                 struct timeval total_timeout;
1308                 struct timeval retry_timeout;
1309                 struct pmap pm_mnt;
1310                 time_t t;
1311                 time_t prevt;
1312                 time_t timeout;
1313
1314                 retry_timeout.tv_sec = 3;
1315                 retry_timeout.tv_usec = 0;
1316                 total_timeout.tv_sec = 20;
1317                 total_timeout.tv_usec = 0;
1318                 timeout = time(NULL) + 60 * retry;
1319                 prevt = 0;
1320                 t = 30;
1321  retry:
1322                 /* be careful not to use too many CPU cycles */
1323                 if (t - prevt < 30)
1324                         sleep(30);
1325
1326                 get_mountport(&pm_mnt, &mount_server_addr,
1327                                 mountprog,
1328                                 mountvers,
1329                                 proto,
1330                                 mountport);
1331                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1332
1333                 /* contact the mount daemon via TCP */
1334                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1335                 msock = RPC_ANYSOCK;
1336
1337                 switch (pm_mnt.pm_prot) {
1338                 case IPPROTO_UDP:
1339                         mclient = clntudp_create(&mount_server_addr,
1340                                                  pm_mnt.pm_prog,
1341                                                  pm_mnt.pm_vers,
1342                                                  retry_timeout,
1343                                                  &msock);
1344                         if (mclient)
1345                                 break;
1346                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1347                         msock = RPC_ANYSOCK;
1348                 case IPPROTO_TCP:
1349                         mclient = clnttcp_create(&mount_server_addr,
1350                                                  pm_mnt.pm_prog,
1351                                                  pm_mnt.pm_vers,
1352                                                  &msock, 0, 0);
1353                         break;
1354                 default:
1355                         mclient = NULL;
1356                 }
1357                 if (!mclient) {
1358                         if (!daemonized && prevt == 0)
1359                                 error_msg_rpc(clnt_spcreateerror(" "));
1360                 } else {
1361                         enum clnt_stat clnt_stat;
1362                         /* try to mount hostname:pathname */
1363                         mclient->cl_auth = authunix_create_default();
1364
1365                         /* make pointers in xdr_mountres3 NULL so
1366                          * that xdr_array allocates memory for us
1367                          */
1368                         memset(&status, 0, sizeof(status));
1369
1370                         if (pm_mnt.pm_vers == 3)
1371                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1372                                               (xdrproc_t) xdr_dirpath,
1373                                               (caddr_t) &pathname,
1374                                               (xdrproc_t) xdr_mountres3,
1375                                               (caddr_t) &status,
1376                                               total_timeout);
1377                         else
1378                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1379                                               (xdrproc_t) xdr_dirpath,
1380                                               (caddr_t) &pathname,
1381                                               (xdrproc_t) xdr_fhstatus,
1382                                               (caddr_t) &status,
1383                                               total_timeout);
1384
1385                         if (clnt_stat == RPC_SUCCESS)
1386                                 goto prepare_kernel_data; /* we're done */
1387                         if (errno != ECONNREFUSED) {
1388                                 error_msg_rpc(clnt_sperror(mclient, " "));
1389                                 goto fail;      /* don't retry */
1390                         }
1391                         /* Connection refused */
1392                         if (!daemonized && prevt == 0) /* print just once */
1393                                 error_msg_rpc(clnt_sperror(mclient, " "));
1394                         auth_destroy(mclient->cl_auth);
1395                         clnt_destroy(mclient);
1396                         mclient = NULL;
1397                         close(msock);
1398                         msock = -1;
1399                 }
1400
1401                 /* Timeout. We are going to retry... maybe */
1402
1403                 if (!bg)
1404                         goto fail;
1405                 if (!daemonized) {
1406                         daemonized = daemonize();
1407                         if (daemonized <= 0) { /* parent or error */
1408                                 retval = -daemonized;
1409                                 goto ret;
1410                         }
1411                 }
1412                 prevt = t;
1413                 t = time(NULL);
1414                 if (t >= timeout)
1415                         /* TODO error message */
1416                         goto fail;
1417
1418                 goto retry;
1419         }
1420
1421  prepare_kernel_data:
1422
1423         if (nfsvers == 2) {
1424                 if (status.nfsv2.fhs_status != 0) {
1425                         bb_error_msg("%s:%s failed, reason given by server: %s",
1426                                 hostname, pathname,
1427                                 nfs_strerror(status.nfsv2.fhs_status));
1428                         goto fail;
1429                 }
1430                 memcpy(data.root.data,
1431                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1432                                 NFS_FHSIZE);
1433                 data.root.size = NFS_FHSIZE;
1434                 memcpy(data.old_root.data,
1435                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1436                                 NFS_FHSIZE);
1437         } else {
1438                 fhandle3 *my_fhandle;
1439                 if (status.nfsv3.fhs_status != 0) {
1440                         bb_error_msg("%s:%s failed, reason given by server: %s",
1441                                 hostname, pathname,
1442                                 nfs_strerror(status.nfsv3.fhs_status));
1443                         goto fail;
1444                 }
1445                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1446                 memset(data.old_root.data, 0, NFS_FHSIZE);
1447                 memset(&data.root, 0, sizeof(data.root));
1448                 data.root.size = my_fhandle->fhandle3_len;
1449                 memcpy(data.root.data,
1450                                 (char *) my_fhandle->fhandle3_val,
1451                                 my_fhandle->fhandle3_len);
1452
1453                 data.flags |= NFS_MOUNT_VER3;
1454         }
1455
1456         /* create nfs socket for kernel */
1457
1458         if (tcp) {
1459                 if (nfs_mount_version < 3) {
1460                         bb_error_msg("NFS over TCP is not supported");
1461                         goto fail;
1462                 }
1463                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1464         } else
1465                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1466         if (fsock < 0) {
1467                 bb_perror_msg("nfs socket");
1468                 goto fail;
1469         }
1470         if (bindresvport(fsock, 0) < 0) {
1471                 bb_perror_msg("nfs bindresvport");
1472                 goto fail;
1473         }
1474         if (port == 0) {
1475                 server_addr.sin_port = PMAPPORT;
1476                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1477                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1478                 if (port == 0)
1479                         port = NFS_PORT;
1480         }
1481         server_addr.sin_port = htons(port);
1482
1483         /* prepare data structure for kernel */
1484
1485         data.fd = fsock;
1486         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1487         strncpy(data.hostname, hostname, sizeof(data.hostname));
1488
1489         /* clean up */
1490
1491         auth_destroy(mclient->cl_auth);
1492         clnt_destroy(mclient);
1493         close(msock);
1494         msock = -1;
1495
1496         if (bg) {
1497                 /* We must wait until mount directory is available */
1498                 struct stat statbuf;
1499                 int delay = 1;
1500                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1501                         if (!daemonized) {
1502                                 daemonized = daemonize();
1503                                 if (daemonized <= 0) { /* parent or error */
1504         // FIXME: parent doesn't close fsock - ??!
1505                                         retval = -daemonized;
1506                                         goto ret;
1507                                 }
1508                         }
1509                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1510                         delay *= 2;
1511                         if (delay > 30)
1512                                 delay = 30;
1513                 }
1514         }
1515
1516  do_mount: /* perform actual mount */
1517
1518         mp->mnt_type = (char*)"nfs";
1519         retval = mount_it_now(mp, vfsflags, (char*)&data);
1520         goto ret;
1521
1522  fail:  /* abort */
1523
1524         if (msock >= 0) {
1525                 if (mclient) {
1526                         auth_destroy(mclient->cl_auth);
1527                         clnt_destroy(mclient);
1528                 }
1529                 close(msock);
1530         }
1531         if (fsock >= 0)
1532                 close(fsock);
1533
1534  ret:
1535         free(hostname);
1536         free(mounthost);
1537         free(filteropts);
1538         return retval;
1539 }
1540
1541 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1542
1543 /* Never called. Call should be optimized out. */
1544 int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
1545
1546 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1547
1548 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1549 // type detection.  Returns 0 for success, nonzero for failure.
1550 // NB: mp->xxx fields may be trashed on exit
1551 static int singlemount(struct mntent *mp, int ignore_busy)
1552 {
1553         int rc = -1;
1554         long vfsflags;
1555         char *loopFile = 0, *filteropts = 0;
1556         llist_t *fl = 0;
1557         struct stat st;
1558
1559         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1560
1561         // Treat fstype "auto" as unspecified.
1562
1563         if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1564                 mp->mnt_type = NULL;
1565
1566         // Might this be a virtual filesystem?
1567
1568         if (ENABLE_FEATURE_MOUNT_HELPERS
1569          && (strchr(mp->mnt_fsname, '#'))
1570         ) {
1571                 char *s, *p, *args[35];
1572                 int n = 0;
1573 // FIXME: does it allow execution of arbitrary commands?!
1574 // What args[0] can end up with?
1575                 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1576                         if (s[0] == '#' && s[1] != '#') {
1577                                 *s = '\0';
1578                                 args[n++] = p;
1579                                 p = s + 1;
1580                         }
1581                 }
1582                 args[n++] = p;
1583                 args[n++] = mp->mnt_dir;
1584                 args[n] = NULL;
1585                 rc = wait4pid(xspawn(args));
1586                 goto report_error;
1587         }
1588
1589         // Might this be an CIFS filesystem?
1590
1591         if (ENABLE_FEATURE_MOUNT_CIFS
1592          && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1593          && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1594          && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1595         ) {
1596                 len_and_sockaddr *lsa;
1597                 char *ip, *dotted;
1598                 char *s;
1599
1600                 rc = 1;
1601                 // Replace '/' with '\' and verify that unc points to "//server/share".
1602
1603                 for (s = mp->mnt_fsname; *s; ++s)
1604                         if (*s == '/') *s = '\\';
1605
1606                 // get server IP
1607
1608                 s = strrchr(mp->mnt_fsname, '\\');
1609                 if (s <= mp->mnt_fsname+1) goto report_error;
1610                 *s = '\0';
1611                 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
1612                 *s = '\\';
1613                 if (!lsa) goto report_error;
1614
1615                 // insert ip=... option into string flags.
1616
1617                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1618                 ip = xasprintf("ip=%s", dotted);
1619                 parse_mount_options(ip, &filteropts);
1620
1621                 // compose new unc '\\server-ip\share'
1622                 // (s => slash after hostname)
1623
1624                 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
1625
1626                 // lock is required
1627                 vfsflags |= MS_MANDLOCK;
1628
1629                 mp->mnt_type = (char*)"cifs";
1630                 rc = mount_it_now(mp, vfsflags, filteropts);
1631                 if (ENABLE_FEATURE_CLEAN_UP) {
1632                         free(mp->mnt_fsname);
1633                         free(ip);
1634                         free(dotted);
1635                         free(lsa);
1636                 }
1637                 goto report_error;
1638         }
1639
1640         // Might this be an NFS filesystem?
1641
1642         if (ENABLE_FEATURE_MOUNT_NFS
1643          && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
1644          && strchr(mp->mnt_fsname, ':') != NULL
1645         ) {
1646                 rc = nfsmount(mp, vfsflags, filteropts);
1647                 goto report_error;
1648         }
1649
1650         // Look at the file.  (Not found isn't a failure for remount, or for
1651         // a synthetic filesystem like proc or sysfs.)
1652         // (We use stat, not lstat, in order to allow
1653         // mount symlink_to_file_or_blkdev dir)
1654
1655         if (!stat(mp->mnt_fsname, &st)
1656          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1657         ) {
1658                 // Do we need to allocate a loopback device for it?
1659
1660                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1661                         loopFile = bb_simplify_path(mp->mnt_fsname);
1662                         mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1663                         if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1664                                 if (errno == EPERM || errno == EACCES)
1665                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1666                                 else
1667                                         bb_perror_msg("cannot setup loop device");
1668                                 return errno;
1669                         }
1670
1671                 // Autodetect bind mounts
1672
1673                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1674                         vfsflags |= MS_BIND;
1675         }
1676
1677         /* If we know the fstype (or don't need to), jump straight
1678          * to the actual mount. */
1679
1680         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1681                 rc = mount_it_now(mp, vfsflags, filteropts);
1682         else {
1683                 // Loop through filesystem types until mount succeeds
1684                 // or we run out
1685
1686                 /* Initialize list of block backed filesystems.  This has to be
1687                  * done here so that during "mount -a", mounts after /proc shows up
1688                  * can autodetect. */
1689
1690                 if (!fslist) {
1691                         fslist = get_block_backed_filesystems();
1692                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1693                                 atexit(delete_block_backed_filesystems);
1694                 }
1695
1696                 for (fl = fslist; fl; fl = fl->link) {
1697                         mp->mnt_type = fl->data;
1698                         rc = mount_it_now(mp, vfsflags, filteropts);
1699                         if (!rc) break;
1700                 }
1701         }
1702
1703         // If mount failed, clean up loop file (if any).
1704
1705         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1706                 del_loop(mp->mnt_fsname);
1707                 if (ENABLE_FEATURE_CLEAN_UP) {
1708                         free(loopFile);
1709                         free(mp->mnt_fsname);
1710                 }
1711         }
1712
1713  report_error:
1714         if (ENABLE_FEATURE_CLEAN_UP)
1715                 free(filteropts);
1716
1717         if (errno == EBUSY && ignore_busy)
1718                 return 0;
1719         if (rc < 0)
1720                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1721         return rc;
1722 }
1723
1724 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1725 // each directory to be mounted.
1726
1727 static const char must_be_root[] ALIGN1 = "you must be root";
1728
1729 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1730 int mount_main(int argc ATTRIBUTE_UNUSED, char **argv)
1731 {
1732         char *cmdopts = xstrdup("");
1733         char *fstype = NULL;
1734         char *storage_path;
1735         char *opt_o;
1736         const char *fstabname;
1737         FILE *fstab;
1738         int i, j, rc = 0;
1739         unsigned opt;
1740         struct mntent mtpair[2], *mtcur = mtpair;
1741         SKIP_DESKTOP(const int nonroot = 0;)
1742
1743         USE_DESKTOP(int nonroot = ) sanitize_env_if_suid();
1744
1745         // Parse long options, like --bind and --move.  Note that -o option
1746         // and --option are synonymous.  Yes, this means --remount,rw works.
1747         for (i = j = 1; argv[i]; i++) {
1748                 if (argv[i][0] == '-' && argv[i][1] == '-')
1749                         append_mount_options(&cmdopts, argv[i] + 2);
1750                 else
1751                         argv[j++] = argv[i];
1752         }
1753         argv[j] = NULL;
1754
1755         // Parse remaining options
1756         // Max 2 params; -v is a counter
1757         opt_complementary = "?2" USE_FEATURE_MOUNT_VERBOSE(":vv");
1758         opt = getopt32(argv, OPTION_STR, &opt_o, &fstype
1759                         USE_FEATURE_MOUNT_VERBOSE(, &verbose));
1760         if (opt & OPT_o) append_mount_options(&cmdopts, opt_o); // -o
1761         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1762         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
1763         argv += optind;
1764
1765         // If we have no arguments, show currently mounted filesystems
1766         if (!argv[0]) {
1767                 if (!(opt & OPT_a)) {
1768                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1769
1770                         if (!mountTable)
1771                                 bb_error_msg_and_die("no %s", bb_path_mtab_file);
1772
1773                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
1774                                                                 GETMNTENT_BUFSIZE))
1775                         {
1776                                 // Don't show rootfs. FIXME: why??
1777                                 // util-linux 2.12a happily shows rootfs...
1778                                 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1779
1780                                 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1781                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1782                                                         mtpair->mnt_dir, mtpair->mnt_type,
1783                                                         mtpair->mnt_opts);
1784                         }
1785                         if (ENABLE_FEATURE_CLEAN_UP)
1786                                 endmntent(mountTable);
1787                         return EXIT_SUCCESS;
1788                 }
1789                 storage_path = NULL;
1790         } else {
1791                 // When we have two arguments, the second is the directory and we can
1792                 // skip looking at fstab entirely.  We can always abspath() the directory
1793                 // argument when we get it.
1794                 if (argv[1]) {
1795                         if (nonroot)
1796                                 bb_error_msg_and_die(must_be_root);
1797                         mtpair->mnt_fsname = argv[0];
1798                         mtpair->mnt_dir = argv[1];
1799                         mtpair->mnt_type = fstype;
1800                         mtpair->mnt_opts = cmdopts;
1801                         if (ENABLE_FEATURE_MOUNT_LABEL) {
1802                                 resolve_mount_spec(&mtpair->mnt_fsname);
1803                         }
1804                         rc = singlemount(mtpair, 0);
1805                         return rc;
1806                 }
1807                 storage_path = bb_simplify_path(argv[0]); // malloced
1808         }
1809
1810         // Past this point, we are handling either "mount -a [opts]"
1811         // or "mount [opts] single_param"
1812
1813         i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
1814         if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1815                 bb_error_msg_and_die(must_be_root);
1816
1817         // If we have a shared subtree flag, don't worry about fstab or mtab.
1818         if (ENABLE_FEATURE_MOUNT_FLAGS
1819          && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1820         ) {
1821                 rc = verbose_mount(/*source:*/ "", /*target:*/ argv[0],
1822                                 /*type:*/ "", /*flags:*/ i, /*data:*/ "");
1823                 if (rc)
1824                         bb_simple_perror_msg_and_die(argv[0]);
1825                 return rc;
1826         }
1827
1828         // Open either fstab or mtab
1829         fstabname = "/etc/fstab";
1830         if (i & MS_REMOUNT) {
1831                 // WARNING. I am not sure this matches util-linux's
1832                 // behavior. It's possible util-linux does not
1833                 // take -o opts from mtab (takes only mount source).
1834                 fstabname = bb_path_mtab_file;
1835         }
1836         fstab = setmntent(fstabname, "r");
1837         if (!fstab)
1838                 bb_perror_msg_and_die("cannot read %s", fstabname);
1839
1840         // Loop through entries until we find what we're looking for
1841         memset(mtpair, 0, sizeof(mtpair));
1842         for (;;) {
1843                 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
1844
1845                 // Get next fstab entry
1846                 if (!getmntent_r(fstab, mtcur, getmntent_buf
1847                                         + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
1848                                 GETMNTENT_BUFSIZE/2)
1849                 ) { // End of fstab/mtab is reached
1850                         mtcur = mtother; // the thing we found last time
1851                         break;
1852                 }
1853
1854                 // If we're trying to mount something specific and this isn't it,
1855                 // skip it.  Note we must match the exact text in fstab (ala
1856                 // "proc") or a full path from root
1857                 if (argv[0]) {
1858
1859                         // Is this what we're looking for?
1860                         if (strcmp(argv[0], mtcur->mnt_fsname) &&
1861                            strcmp(storage_path, mtcur->mnt_fsname) &&
1862                            strcmp(argv[0], mtcur->mnt_dir) &&
1863                            strcmp(storage_path, mtcur->mnt_dir)) continue;
1864
1865                         // Remember this entry.  Something later may have
1866                         // overmounted it, and we want the _last_ match.
1867                         mtcur = mtother;
1868
1869                 // If we're mounting all
1870                 } else {
1871                         // Do we need to match a filesystem type?
1872                         if (fstype && match_fstype(mtcur, fstype))
1873                                 continue;
1874
1875                         // Skip noauto and swap anyway.
1876                         if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP))
1877                                 continue;
1878
1879                         // No, mount -a won't mount anything,
1880                         // even user mounts, for mere humans
1881                         if (nonroot)
1882                                 bb_error_msg_and_die(must_be_root);
1883
1884                         // Mount this thing
1885                         if (ENABLE_FEATURE_MOUNT_LABEL)
1886                                 resolve_mount_spec(&mtpair->mnt_fsname);
1887
1888                         // NFS mounts want this to be xrealloc-able
1889                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1890                         if (singlemount(mtcur, 1)) {
1891                                 // Count number of failed mounts
1892                                 rc++;
1893                         }
1894                         free(mtcur->mnt_opts);
1895                 }
1896         }
1897
1898         // End of fstab/mtab is reached.
1899         // Were we looking for something specific?
1900         if (argv[0]) {
1901                 // If we didn't find anything, complain
1902                 if (!mtcur->mnt_fsname)
1903                         bb_error_msg_and_die("can't find %s in %s",
1904                                 argv[0], fstabname);
1905                 if (nonroot) {
1906                         // fstab must have "users" or "user"
1907                         if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1908                                 bb_error_msg_and_die(must_be_root);
1909                 }
1910
1911                 // Mount the last thing we found
1912                 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1913                 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1914                 if (ENABLE_FEATURE_MOUNT_LABEL) {
1915                         resolve_mount_spec(&mtpair->mnt_fsname);
1916                 }
1917                 rc = singlemount(mtcur, 0);
1918                 if (ENABLE_FEATURE_CLEAN_UP)
1919                         free(mtcur->mnt_opts);
1920         }
1921
1922         if (ENABLE_FEATURE_CLEAN_UP)
1923                 endmntent(fstab);
1924         if (ENABLE_FEATURE_CLEAN_UP) {
1925                 free(storage_path);
1926                 free(cmdopts);
1927         }
1928         return rc;
1929 }