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