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