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