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