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