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