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