1 /* vi: set sw=4 ts=4: */
3 * Mini mount implementation for busybox
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>
9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
12 /* Design notes: There is no spec for mount. Remind me to write one.
14 mount_main() calls singlemount() which calls mount_it_now().
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.
24 /* Needed for nfs support only... */
26 #include <sys/utsname.h>
30 #include <rpc/pmap_prot.h>
31 #include <rpc/pmap_clnt.h>
34 // Not real flags, but we want to be able to check for this.
36 MOUNT_USERS = (1<<28)*ENABLE_DESKTOP,
37 MOUNT_NOAUTO = (1<<29),
40 // TODO: more "user" flag compatibility.
41 // "user" option (from mount manpage):
42 // Only the user that mounted a filesystem can unmount it again.
43 // If any user should be able to unmount, then use users instead of user
44 // in the fstab line. The owner option is similar to the user option,
45 // with the restriction that the user must be the owner of the special file.
46 // This may be useful e.g. for /dev/fd if a login script makes
47 // the console user owner of this device.
49 /* Standard mount options (from -o options or --options), with corresponding
55 } static mount_options[] = {
56 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
58 USE_FEATURE_MOUNT_LOOP(
62 USE_FEATURE_MOUNT_FSTAB(
65 {"noauto", MOUNT_NOAUTO},
67 USE_DESKTOP({"user", MOUNT_USERS},)
68 USE_DESKTOP({"users", MOUNT_USERS},)
71 USE_FEATURE_MOUNT_FLAGS(
73 {"nosuid", MS_NOSUID},
78 {"noexec", MS_NOEXEC},
79 {"sync", MS_SYNCHRONOUS},
80 {"async", ~MS_SYNCHRONOUS},
81 {"atime", ~MS_NOATIME},
82 {"noatime", MS_NOATIME},
83 {"diratime", ~MS_NODIRATIME},
84 {"nodiratime", MS_NODIRATIME},
91 {"shared", MS_SHARED},
93 {"private", MS_PRIVATE},
94 {"unbindable", MS_UNBINDABLE},
95 {"rshared", MS_SHARED|MS_RECURSIVE},
96 {"rslave", MS_SLAVE|MS_RECURSIVE},
97 {"rprivate", MS_SLAVE|MS_RECURSIVE},
98 {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
101 // Always understood.
103 {"ro", MS_RDONLY}, // vfs flag
104 {"rw", ~MS_RDONLY}, // vfs flag
105 {"remount", MS_REMOUNT}, // action flag
108 #define VECTOR_SIZE(v) (sizeof(v) / sizeof((v)[0]))
110 /* Append mount options to string */
111 static void append_mount_options(char **oldopts, char *newopts)
113 if (*oldopts && **oldopts) {
114 /* do not insert options which are already there */
117 int len = strlen(newopts);
118 p = strchr(newopts, ',');
119 if (p) len = p - newopts;
122 if (!strncmp(p, newopts, len)
123 && (p[len]==',' || p[len]==0))
129 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
134 while (newopts[0] == ',') newopts++;
137 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
138 *oldopts = xstrdup(newopts);
142 /* Use the mount_options list to parse options into flags.
143 * Also return list of unrecognized options if unrecognized!=NULL */
144 static int parse_mount_options(char *options, char **unrecognized)
146 int flags = MS_SILENT;
148 // Loop through options
151 char *comma = strchr(options, ',');
153 if (comma) *comma = 0;
155 // Find this option in mount_options
156 for (i = 0; i < VECTOR_SIZE(mount_options); i++) {
157 if (!strcasecmp(mount_options[i].name, options)) {
158 long fl = mount_options[i].flags;
159 if (fl < 0) flags &= fl;
164 // If unrecognized not NULL, append unrecognized mount options */
165 if (unrecognized && i == VECTOR_SIZE(mount_options)) {
166 // Add it to strflags, to pass on to kernel
167 i = *unrecognized ? strlen(*unrecognized) : 0;
168 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
170 // Comma separated if it's not the first one
171 if (i) (*unrecognized)[i++] = ',';
172 strcpy((*unrecognized)+i, options);
175 // Advance to next option, or finish
185 // Return a list of all block device backed filesystems
187 static llist_t *get_block_backed_filesystems(void)
189 static const char *const filesystems[] = {
199 for (i = 0; filesystems[i]; i++) {
200 f = fopen(filesystems[i], "r");
203 while ((buf = xmalloc_getline(f)) != 0) {
204 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
207 while (isspace(*fs)) fs++;
208 if (*fs=='#' || *fs=='*' || !*fs) continue;
210 llist_add_to_end(&list, xstrdup(fs));
213 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
221 #if ENABLE_FEATURE_CLEAN_UP
222 static void delete_block_backed_filesystems(void)
224 llist_free(fslist, free);
227 void delete_block_backed_filesystems(void);
230 #if ENABLE_FEATURE_MTAB_SUPPORT
231 static int useMtab = 1;
238 // Perform actual mount of specific filesystem at specific location.
239 // NB: mp->xxx fields may be trashed on exit
240 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
244 if (fakeIt) goto mtab;
246 // Mount, with fallback to read-only if necessary.
249 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
250 vfsflags, filteropts);
251 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
253 bb_error_msg("%s is write-protected, mounting read-only",
255 vfsflags |= MS_RDONLY;
258 // Abort entirely if permission denied.
260 if (rc && errno == EPERM)
261 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
263 /* If the mount was successful, and we're maintaining an old-style
264 * mtab file by hand, add the new entry to it now. */
266 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
268 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
272 bb_error_msg("no %s",bb_path_mtab_file);
274 // Add vfs string flags
276 for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
277 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
278 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
280 // Remove trailing / (if any) from directory we mounted on
282 i = strlen(mp->mnt_dir) - 1;
283 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
285 // Convert to canonical pathnames as needed
287 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
289 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
290 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
291 mp->mnt_type = "bind";
293 mp->mnt_freq = mp->mnt_passno = 0;
297 addmntent(mountTable, mp);
298 endmntent(mountTable);
299 if (ENABLE_FEATURE_CLEAN_UP) {
308 #if ENABLE_FEATURE_MOUNT_NFS
312 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
314 * Licensed under GPLv2, see file LICENSE in this tarball for details.
316 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
317 * numbers to be specified on the command line.
319 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
320 * Omit the call to connect() for Linux version 1.3.11 or later.
322 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
323 * Implemented the "bg", "fg" and "retry" mount options for NFS.
325 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
326 * - added Native Language Support
328 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
332 /* This is just a warning of a common mistake. Possibly this should be a
333 * uclibc faq entry rather than in busybox... */
334 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
335 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
338 #define MOUNTPORT 635
339 #define MNTPATHLEN 1024
340 #define MNTNAMLEN 255
344 typedef char fhandle[FHSIZE];
347 unsigned int fhandle3_len;
359 MNT3ERR_NAMETOOLONG = 63,
360 MNT3ERR_NOTSUPP = 10004,
361 MNT3ERR_SERVERFAULT = 10006,
363 typedef enum mountstat3 mountstat3;
366 unsigned int fhs_status;
371 typedef struct fhstatus fhstatus;
373 struct mountres3_ok {
376 unsigned int auth_flavours_len;
377 char *auth_flavours_val;
380 typedef struct mountres3_ok mountres3_ok;
383 mountstat3 fhs_status;
385 mountres3_ok mountinfo;
388 typedef struct mountres3 mountres3;
390 typedef char *dirpath;
394 typedef struct mountbody *mountlist;
398 dirpath ml_directory;
401 typedef struct mountbody mountbody;
403 typedef struct groupnode *groups;
409 typedef struct groupnode groupnode;
411 typedef struct exportnode *exports;
418 typedef struct exportnode exportnode;
431 typedef struct ppathcnf ppathcnf;
433 #define MOUNTPROG 100005
436 #define MOUNTPROC_NULL 0
437 #define MOUNTPROC_MNT 1
438 #define MOUNTPROC_DUMP 2
439 #define MOUNTPROC_UMNT 3
440 #define MOUNTPROC_UMNTALL 4
441 #define MOUNTPROC_EXPORT 5
442 #define MOUNTPROC_EXPORTALL 6
444 #define MOUNTVERS_POSIX 2
446 #define MOUNTPROC_PATHCONF 7
450 #define MOUNTPROC3_NULL 0
451 #define MOUNTPROC3_MNT 1
452 #define MOUNTPROC3_DUMP 2
453 #define MOUNTPROC3_UMNT 3
454 #define MOUNTPROC3_UMNTALL 4
455 #define MOUNTPROC3_EXPORT 5
467 * We want to be able to compile mount on old kernels in such a way
468 * that the binary will work well on more recent kernels.
469 * Thus, if necessary we teach nfsmount.c the structure of new fields
470 * that will come later.
472 * Moreover, the new kernel includes conflict with glibc includes
473 * so it is easiest to ignore the kernel altogether (at compile time).
481 unsigned char data[64];
484 struct nfs_mount_data {
487 struct nfs2_fh old_root; /* 1 */
493 int acregmin; /* 1 */
494 int acregmax; /* 1 */
495 int acdirmin; /* 1 */
496 int acdirmax; /* 1 */
497 struct sockaddr_in addr; /* 1 */
498 char hostname[256]; /* 1 */
500 unsigned int bsize; /* 3 */
501 struct nfs3_fh root; /* 4 */
504 /* bits in the flags field */
506 NFS_MOUNT_SOFT = 0x0001, /* 1 */
507 NFS_MOUNT_INTR = 0x0002, /* 1 */
508 NFS_MOUNT_SECURE = 0x0004, /* 1 */
509 NFS_MOUNT_POSIX = 0x0008, /* 1 */
510 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
511 NFS_MOUNT_NOAC = 0x0020, /* 1 */
512 NFS_MOUNT_TCP = 0x0040, /* 2 */
513 NFS_MOUNT_VER3 = 0x0080, /* 3 */
514 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
515 NFS_MOUNT_NONLM = 0x0200 /* 3 */
520 * We need to translate between nfs status return values and
521 * the local errno values which may not be the same.
523 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
524 * "after #include <errno.h> the symbol errno is reserved for any use,
525 * it cannot even be used as a struct tag or field name".
529 #define EDQUOT ENOSPC
532 // Convert each NFSERR_BLAH into EBLAH
534 static const struct {
538 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
539 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
540 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
541 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
544 static char *nfs_strerror(int status)
547 static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
549 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
550 if (nfs_errtbl[i].stat == status)
551 return strerror(nfs_errtbl[i].errnum);
553 sprintf(buf, "unknown nfs status return value: %d", status);
557 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
559 if (!xdr_opaque(xdrs, objp, FHSIZE))
564 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
566 if (!xdr_u_int(xdrs, &objp->fhs_status))
568 switch (objp->fhs_status) {
570 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
579 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
581 if (!xdr_string(xdrs, objp, MNTPATHLEN))
586 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
588 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
593 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
595 if (!xdr_fhandle3(xdrs, &objp->fhandle))
597 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
598 sizeof (int), (xdrproc_t) xdr_int))
603 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
605 if (!xdr_enum(xdrs, (enum_t *) objp))
610 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
612 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
614 switch (objp->fhs_status) {
616 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
625 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
628 * nfs_mount_version according to the sources seen at compile time.
630 static int nfs_mount_version;
631 static int kernel_version;
634 * Unfortunately, the kernel prints annoying console messages
635 * in case of an unexpected nfs mount version (instead of
636 * just returning some error). Therefore we'll have to try
637 * and figure out what version the kernel expects.
640 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
641 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
642 * nfs_mount_version: version this source and running kernel can handle
645 find_kernel_nfs_mount_version(void)
650 nfs_mount_version = 4; /* default */
652 kernel_version = get_linux_version_code();
653 if (kernel_version) {
654 if (kernel_version < KERNEL_VERSION(2,1,32))
655 nfs_mount_version = 1;
656 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
657 (kernel_version >= KERNEL_VERSION(2,3,0) &&
658 kernel_version < KERNEL_VERSION(2,3,99)))
659 nfs_mount_version = 3;
660 /* else v4 since 2.3.99pre4 */
665 get_mountport(struct sockaddr_in *server_addr,
667 long unsigned version,
671 struct pmaplist *pmap;
672 static struct pmap p = {0, 0, 0, 0};
674 server_addr->sin_port = PMAPPORT;
675 pmap = pmap_getmaps(server_addr);
677 if (version > MAX_NFSPROT)
678 version = MAX_NFSPROT;
687 if (pmap->pml_map.pm_prog != prog)
689 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
691 if (version > 2 && pmap->pml_map.pm_vers != version)
693 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
695 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
696 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
697 (port && pmap->pml_map.pm_port != port))
699 memcpy(&p, &pmap->pml_map, sizeof(p));
701 pmap = pmap->pml_next;
704 p.pm_vers = MOUNTVERS;
706 p.pm_port = MOUNTPORT;
708 p.pm_prot = IPPROTO_TCP;
712 static int daemonize(void)
716 if (pid < 0) /* error */
718 if (pid > 0) /* parent */
721 fd = xopen(bb_dev_null, O_RDWR);
725 if (fd > 2) close(fd);
727 openlog(applet_name, LOG_PID, LOG_DAEMON);
728 logmode = LOGMODE_SYSLOG;
733 static inline int we_saw_this_host_before(const char *hostname)
738 /* RPC strerror analogs are terminally idiotic:
739 * *mandatory* prefix and \n at end.
740 * This hopefully helps. Usage:
741 * error_msg_rpc(clnt_*error*(" ")) */
742 static void error_msg_rpc(const char *msg)
745 while (msg[0] == ' ' || msg[0] == ':') msg++;
747 while (len && msg[len-1] == '\n') len--;
748 bb_error_msg("%.*s", len, msg);
751 // NB: mp->xxx fields may be trashed on exit
752 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
758 struct nfs_mount_data data;
761 struct sockaddr_in server_addr;
762 struct sockaddr_in mount_server_addr;
765 struct fhstatus nfsv2;
766 struct mountres3 nfsv3;
788 find_kernel_nfs_mount_version();
796 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
798 filteropts = xstrdup(filteropts); /* going to trash it later... */
800 hostname = xstrdup(mp->mnt_fsname);
801 /* mount_main() guarantees that ':' is there */
802 s = strchr(hostname, ':');
805 /* Ignore all but first hostname in replicated mounts
806 until they can be fully supported. (mack@sgi.com) */
807 s = strchr(hostname, ',');
810 bb_error_msg("warning: multiple hostnames not supported");
813 server_addr.sin_family = AF_INET;
814 if (!inet_aton(hostname, &server_addr.sin_addr)) {
815 hp = gethostbyname(hostname);
817 bb_herror_msg("%s", hostname);
820 if (hp->h_length > sizeof(struct in_addr)) {
821 bb_error_msg("got bad hp->h_length");
822 hp->h_length = sizeof(struct in_addr);
824 memcpy(&server_addr.sin_addr,
825 hp->h_addr, hp->h_length);
828 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
830 /* add IP address to mtab options for use when unmounting */
832 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
833 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
835 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
836 mp->mnt_opts[0] ? "," : "",
837 inet_ntoa(server_addr.sin_addr));
842 /* Set default options.
843 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
844 * let the kernel decide.
845 * timeo is filled in after we know whether it'll be TCP or UDP. */
846 memset(&data, 0, sizeof(data));
852 data.namlen = NAME_MAX;
861 retry = 10000; /* 10000 minutes ~ 1 week */
864 mountprog = MOUNTPROG;
873 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
874 char *opteq = strchr(opt, '=');
876 int val = xatoi_u(opteq + 1);
878 if (!strcmp(opt, "rsize"))
880 else if (!strcmp(opt, "wsize"))
882 else if (!strcmp(opt, "timeo"))
884 else if (!strcmp(opt, "retrans"))
886 else if (!strcmp(opt, "acregmin"))
888 else if (!strcmp(opt, "acregmax"))
890 else if (!strcmp(opt, "acdirmin"))
892 else if (!strcmp(opt, "acdirmax"))
894 else if (!strcmp(opt, "actimeo")) {
900 else if (!strcmp(opt, "retry"))
902 else if (!strcmp(opt, "port"))
904 else if (!strcmp(opt, "mountport"))
906 else if (!strcmp(opt, "mounthost"))
907 mounthost = xstrndup(opteq+1,
908 strcspn(opteq+1," \t\n\r,"));
909 else if (!strcmp(opt, "mountprog"))
911 else if (!strcmp(opt, "mountvers"))
913 else if (!strcmp(opt, "nfsprog"))
915 else if (!strcmp(opt, "nfsvers") ||
916 !strcmp(opt, "vers"))
918 else if (!strcmp(opt, "proto")) {
919 if (!strncmp(opteq+1, "tcp", 3))
921 else if (!strncmp(opteq+1, "udp", 3))
924 bb_error_msg("warning: unrecognized proto= option");
925 } else if (!strcmp(opt, "namlen")) {
926 if (nfs_mount_version >= 2)
929 bb_error_msg("warning: option namlen is not supported\n");
930 } else if (!strcmp(opt, "addr"))
933 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
939 if (!strncmp(opt, "no", 2)) {
943 if (!strcmp(opt, "bg"))
945 else if (!strcmp(opt, "fg"))
947 else if (!strcmp(opt, "soft"))
949 else if (!strcmp(opt, "hard"))
951 else if (!strcmp(opt, "intr"))
953 else if (!strcmp(opt, "posix"))
955 else if (!strcmp(opt, "cto"))
957 else if (!strcmp(opt, "ac"))
959 else if (!strcmp(opt, "tcp"))
961 else if (!strcmp(opt, "udp"))
963 else if (!strcmp(opt, "lock")) {
964 if (nfs_mount_version >= 3)
967 bb_error_msg("warning: option nolock is not supported");
969 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
974 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
976 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
977 | (intr ? NFS_MOUNT_INTR : 0)
978 | (posix ? NFS_MOUNT_POSIX : 0)
979 | (nocto ? NFS_MOUNT_NOCTO : 0)
980 | (noac ? NFS_MOUNT_NOAC : 0);
981 if (nfs_mount_version >= 2)
982 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
983 if (nfs_mount_version >= 3)
984 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
985 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
986 bb_error_msg("NFSv%d not supported", nfsvers);
989 if (nfsvers && !mountvers)
990 mountvers = (nfsvers < 3) ? 1 : nfsvers;
991 if (nfsvers && nfsvers < mountvers) {
995 /* Adjust options if none specified */
997 data.timeo = tcp ? 70 : 7;
999 data.version = nfs_mount_version;
1001 if (vfsflags & MS_REMOUNT)
1005 * If the previous mount operation on the same host was
1006 * backgrounded, and the "bg" for this mount is also set,
1007 * give up immediately, to avoid the initial timeout.
1009 if (bg && we_saw_this_host_before(hostname)) {
1010 daemonized = daemonize(); /* parent or error */
1011 if (daemonized <= 0) { /* parent or error */
1012 retval = -daemonized;
1017 /* create mount daemon client */
1018 /* See if the nfs host = mount host. */
1020 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1021 mount_server_addr.sin_family = AF_INET;
1022 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1024 hp = gethostbyname(mounthost);
1026 bb_herror_msg("%s", mounthost);
1029 if (hp->h_length > sizeof(struct in_addr)) {
1030 bb_error_msg("got bad hp->h_length?");
1031 hp->h_length = sizeof(struct in_addr);
1033 mount_server_addr.sin_family = AF_INET;
1034 memcpy(&mount_server_addr.sin_addr,
1035 hp->h_addr, hp->h_length);
1041 * The following loop implements the mount retries. When the mount
1042 * times out, and the "bg" option is set, we background ourself
1043 * and continue trying.
1045 * The case where the mount point is not present and the "bg"
1046 * option is set, is treated as a timeout. This is done to
1047 * support nested mounts.
1049 * The "retry" count specified by the user is the number of
1050 * minutes to retry before giving up.
1053 struct timeval total_timeout;
1054 struct timeval retry_timeout;
1055 struct pmap* pm_mnt;
1060 retry_timeout.tv_sec = 3;
1061 retry_timeout.tv_usec = 0;
1062 total_timeout.tv_sec = 20;
1063 total_timeout.tv_usec = 0;
1064 timeout = time(NULL) + 60 * retry;
1068 /* be careful not to use too many CPU cycles */
1072 pm_mnt = get_mountport(&mount_server_addr,
1077 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1079 /* contact the mount daemon via TCP */
1080 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1081 msock = RPC_ANYSOCK;
1083 switch (pm_mnt->pm_prot) {
1085 mclient = clntudp_create(&mount_server_addr,
1092 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1093 msock = RPC_ANYSOCK;
1095 mclient = clnttcp_create(&mount_server_addr,
1104 if (!daemonized && prevt == 0)
1105 error_msg_rpc(clnt_spcreateerror(" "));
1107 enum clnt_stat clnt_stat;
1108 /* try to mount hostname:pathname */
1109 mclient->cl_auth = authunix_create_default();
1111 /* make pointers in xdr_mountres3 NULL so
1112 * that xdr_array allocates memory for us
1114 memset(&status, 0, sizeof(status));
1116 if (pm_mnt->pm_vers == 3)
1117 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1118 (xdrproc_t) xdr_dirpath,
1119 (caddr_t) &pathname,
1120 (xdrproc_t) xdr_mountres3,
1124 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1125 (xdrproc_t) xdr_dirpath,
1126 (caddr_t) &pathname,
1127 (xdrproc_t) xdr_fhstatus,
1131 if (clnt_stat == RPC_SUCCESS)
1132 goto prepare_kernel_data; /* we're done */
1133 if (errno != ECONNREFUSED) {
1134 error_msg_rpc(clnt_sperror(mclient, " "));
1135 goto fail; /* don't retry */
1137 /* Connection refused */
1138 if (!daemonized && prevt == 0) /* print just once */
1139 error_msg_rpc(clnt_sperror(mclient, " "));
1140 auth_destroy(mclient->cl_auth);
1141 clnt_destroy(mclient);
1146 /* Timeout. We are going to retry... maybe */
1151 daemonized = daemonize();
1152 if (daemonized <= 0) { /* parent or error */
1153 retval = -daemonized;
1160 /* TODO error message */
1166 prepare_kernel_data:
1169 if (status.nfsv2.fhs_status != 0) {
1170 bb_error_msg("%s:%s failed, reason given by server: %s",
1172 nfs_strerror(status.nfsv2.fhs_status));
1175 memcpy(data.root.data,
1176 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1178 data.root.size = NFS_FHSIZE;
1179 memcpy(data.old_root.data,
1180 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1183 fhandle3 *my_fhandle;
1184 if (status.nfsv3.fhs_status != 0) {
1185 bb_error_msg("%s:%s failed, reason given by server: %s",
1187 nfs_strerror(status.nfsv3.fhs_status));
1190 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1191 memset(data.old_root.data, 0, NFS_FHSIZE);
1192 memset(&data.root, 0, sizeof(data.root));
1193 data.root.size = my_fhandle->fhandle3_len;
1194 memcpy(data.root.data,
1195 (char *) my_fhandle->fhandle3_val,
1196 my_fhandle->fhandle3_len);
1198 data.flags |= NFS_MOUNT_VER3;
1201 /* create nfs socket for kernel */
1204 if (nfs_mount_version < 3) {
1205 bb_error_msg("NFS over TCP is not supported");
1208 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1210 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1212 bb_perror_msg("nfs socket");
1215 if (bindresvport(fsock, 0) < 0) {
1216 bb_perror_msg("nfs bindresvport");
1220 server_addr.sin_port = PMAPPORT;
1221 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1222 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1226 server_addr.sin_port = htons(port);
1228 /* prepare data structure for kernel */
1231 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1232 strncpy(data.hostname, hostname, sizeof(data.hostname));
1236 auth_destroy(mclient->cl_auth);
1237 clnt_destroy(mclient);
1241 /* We must wait until mount directory is available */
1242 struct stat statbuf;
1244 while (stat(mp->mnt_dir, &statbuf) == -1) {
1246 daemonized = daemonize();
1247 if (daemonized <= 0) { /* parent or error */
1248 retval = -daemonized;
1252 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1259 do_mount: /* perform actual mount */
1261 mp->mnt_type = "nfs";
1262 retval = mount_it_now(mp, vfsflags, (char*)&data);
1269 auth_destroy(mclient->cl_auth);
1270 clnt_destroy(mclient);
1284 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1286 /* Never called. Call should be optimized out. */
1287 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1289 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1291 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1292 // type detection. Returns 0 for success, nonzero for failure.
1293 // NB: mp->xxx fields may be trashed on exit
1294 static int singlemount(struct mntent *mp, int ignore_busy)
1296 int rc = -1, vfsflags;
1297 char *loopFile = 0, *filteropts = 0;
1301 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1303 // Treat fstype "auto" as unspecified.
1305 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1307 // Might this be an CIFS filesystem?
1309 if (ENABLE_FEATURE_MOUNT_CIFS &&
1310 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1311 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1317 // Replace '/' with '\' and verify that unc points to "//server/share".
1319 for (s = mp->mnt_fsname; *s; ++s)
1320 if (*s == '/') *s = '\\';
1324 s = strrchr(mp->mnt_fsname, '\\');
1325 if (s == mp->mnt_fsname+1) goto report_error;
1327 he = gethostbyname(mp->mnt_fsname+2);
1329 if (!he) goto report_error;
1331 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1333 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1334 he->h_addr[2], he->h_addr[3]);
1335 parse_mount_options(ip, &filteropts);
1337 // compose new unc '\\server-ip\share'
1339 mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3,
1340 strchr(mp->mnt_fsname+2,'\\'));
1343 vfsflags |= MS_MANDLOCK;
1345 mp->mnt_type = "cifs";
1346 rc = mount_it_now(mp, vfsflags, filteropts);
1347 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1351 // Might this be an NFS filesystem?
1353 if (ENABLE_FEATURE_MOUNT_NFS &&
1354 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1355 strchr(mp->mnt_fsname, ':') != NULL)
1357 rc = nfsmount(mp, vfsflags, filteropts);
1361 // Look at the file. (Not found isn't a failure for remount, or for
1362 // a synthetic filesystem like proc or sysfs.)
1364 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1366 // Do we need to allocate a loopback device for it?
1368 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1369 loopFile = bb_simplify_path(mp->mnt_fsname);
1371 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1376 bb_error_msg( errno == EPERM || errno == EACCES
1377 ? bb_msg_perm_denied_are_you_root
1378 : "cannot setup loop device");
1382 // Autodetect bind mounts
1384 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1385 vfsflags |= MS_BIND;
1388 /* If we know the fstype (or don't need to), jump straight
1389 * to the actual mount. */
1391 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1392 rc = mount_it_now(mp, vfsflags, filteropts);
1394 // Loop through filesystem types until mount succeeds or we run out
1398 /* Initialize list of block backed filesystems. This has to be
1399 * done here so that during "mount -a", mounts after /proc shows up
1400 * can autodetect. */
1403 fslist = get_block_backed_filesystems();
1404 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1405 atexit(delete_block_backed_filesystems);
1408 for (fl = fslist; fl; fl = fl->link) {
1409 mp->mnt_type = fl->data;
1410 rc = mount_it_now(mp, vfsflags, filteropts);
1415 // If mount failed, clean up loop file (if any).
1417 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1418 del_loop(mp->mnt_fsname);
1419 if (ENABLE_FEATURE_CLEAN_UP) {
1421 free(mp->mnt_fsname);
1426 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1428 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1430 /* perror here sometimes says "mounting ... on ... failed: Success" */
1431 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1436 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1437 // each directory to be mounted.
1439 const char must_be_root[] = "you must be root";
1441 int mount_main(int argc, char **argv)
1443 enum { OPT_ALL = 0x10 };
1445 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1447 const char *fstabname;
1451 struct mntent mtpair[2], *mtcur = mtpair;
1452 SKIP_DESKTOP(const int nonroot = 0;)
1453 USE_DESKTOP( int nonroot = (getuid() != 0);)
1455 /* parse long options, like --bind and --move. Note that -o option
1456 * and --option are synonymous. Yes, this means --remount,rw works. */
1458 for (i = j = 0; i < argc; i++) {
1459 if (argv[i][0] == '-' && argv[i][1] == '-') {
1460 append_mount_options(&cmdopts, argv[i]+2);
1461 } else argv[j++] = argv[i];
1466 // Parse remaining options
1468 opt = getopt32(argc, argv, "o:t:rwanfvs", &opt_o, &fstype);
1469 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
1470 //if (opt & 0x2) // -t
1471 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
1472 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
1473 //if (opt & 0x10) // -a
1474 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
1475 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
1476 //if (opt & 0x80) // -v: verbose (ignore)
1477 //if (opt & 0x100) // -s: sloppy (ignore)
1481 // Three or more non-option arguments? Die with a usage message.
1483 if (argc > 2) bb_show_usage();
1485 // If we have no arguments, show currently mounted filesystems
1488 if (!(opt & OPT_ALL)) {
1489 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1491 if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
1493 while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1,
1494 sizeof(bb_common_bufsiz1)))
1496 // Don't show rootfs. FIXME: why??
1497 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1499 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1500 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1501 mtpair->mnt_dir, mtpair->mnt_type,
1504 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1505 return EXIT_SUCCESS;
1507 } else storage_path = bb_simplify_path(argv[0]);
1509 // When we have two arguments, the second is the directory and we can
1510 // skip looking at fstab entirely. We can always abspath() the directory
1511 // argument when we get it.
1515 bb_error_msg_and_die(must_be_root);
1516 mtpair->mnt_fsname = argv[0];
1517 mtpair->mnt_dir = argv[1];
1518 mtpair->mnt_type = fstype;
1519 mtpair->mnt_opts = cmdopts;
1520 rc = singlemount(mtpair, 0);
1524 i = parse_mount_options(cmdopts, 0);
1525 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1526 bb_error_msg_and_die(must_be_root);
1528 // If we have a shared subtree flag, don't worry about fstab or mtab.
1530 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1531 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)))
1533 rc = mount("", argv[0], "", i, "");
1534 if (rc) bb_perror_msg_and_die("%s", argv[0]);
1538 // Open either fstab or mtab
1540 fstabname = "/etc/fstab";
1541 if (i & MS_REMOUNT) {
1542 fstabname = bb_path_mtab_file;
1544 fstab = setmntent(fstabname, "r");
1546 bb_perror_msg_and_die("cannot read %s", fstabname);
1548 // Loop through entries until we find what we're looking for.
1550 memset(mtpair, 0, sizeof(mtpair));
1552 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1554 // Get next fstab entry
1556 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1557 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1558 sizeof(bb_common_bufsiz1)/2))
1560 // Were we looking for something specific?
1564 // If we didn't find anything, complain.
1566 if (!mtnext->mnt_fsname)
1567 bb_error_msg_and_die("can't find %s in %s",
1568 argv[0], fstabname);
1572 // fstab must have "users" or "user"
1573 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1574 bb_error_msg_and_die(must_be_root);
1577 // Mount the last thing we found.
1579 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1580 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1581 rc = singlemount(mtcur, 0);
1582 free(mtcur->mnt_opts);
1587 /* If we're trying to mount something specific and this isn't it,
1588 * skip it. Note we must match both the exact text in fstab (ala
1589 * "proc") or a full path from root */
1593 // Is this what we're looking for?
1595 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1596 strcmp(storage_path, mtcur->mnt_fsname) &&
1597 strcmp(argv[0], mtcur->mnt_dir) &&
1598 strcmp(storage_path, mtcur->mnt_dir)) continue;
1600 // Remember this entry. Something later may have overmounted
1601 // it, and we want the _last_ match.
1605 // If we're mounting all.
1608 // Do we need to match a filesystem type?
1609 // TODO: support "-t type1,type2"; "-t notype1,type2"
1611 if (fstype && strcmp(mtcur->mnt_type, fstype)) continue;
1613 // Skip noauto and swap anyway.
1615 if (parse_mount_options(mtcur->mnt_opts, 0)
1616 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1618 // No, mount -a won't mount anything,
1619 // even user mounts, for mere humans.
1622 bb_error_msg_and_die(must_be_root);
1624 // Mount this thing.
1626 if (singlemount(mtcur, 1)) {
1627 /* Count number of failed mounts */
1632 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1636 if (ENABLE_FEATURE_CLEAN_UP) {