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