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.
35 #define MOUNT_NOAUTO (1<<29)
36 #define MOUNT_SWAP (1<<30)
38 /* Standard mount options (from -o options or --options), with corresponding
44 } static mount_options[] = {
45 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
47 USE_FEATURE_MOUNT_LOOP(
51 USE_FEATURE_MOUNT_FSTAB(
54 {"noauto",MOUNT_NOAUTO},
58 USE_FEATURE_MOUNT_FLAGS(
60 {"nosuid", MS_NOSUID},
65 {"noexec", MS_NOEXEC},
66 {"sync", MS_SYNCHRONOUS},
67 {"async", ~MS_SYNCHRONOUS},
68 {"atime", ~MS_NOATIME},
69 {"noatime", MS_NOATIME},
70 {"diratime", ~MS_NODIRATIME},
71 {"nodiratime", MS_NODIRATIME},
78 {"shared", MS_SHARED},
80 {"private", MS_PRIVATE},
81 {"unbindable", MS_UNBINDABLE},
82 {"rshared", MS_SHARED|MS_RECURSIVE},
83 {"rslave", MS_SLAVE|MS_RECURSIVE},
84 {"rprivate", MS_SLAVE|MS_RECURSIVE},
85 {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
90 {"ro", MS_RDONLY}, // vfs flag
91 {"rw", ~MS_RDONLY}, // vfs flag
92 {"remount", MS_REMOUNT}, // action flag
95 #define VECTOR_SIZE(v) (sizeof(v) / sizeof((v)[0]))
97 /* Append mount options to string */
98 static void append_mount_options(char **oldopts, char *newopts)
100 if (*oldopts && **oldopts) {
101 /* do not insert options which are already there */
104 int len = strlen(newopts);
105 p = strchr(newopts, ',');
106 if (p) len = p - newopts;
109 if (!strncmp(p,newopts,len) && (p[len]==',' || p[len]==0))
115 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
120 while (newopts[0] == ',') newopts++;
123 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
124 *oldopts = xstrdup(newopts);
128 /* Use the mount_options list to parse options into flags.
129 * Also return list of unrecognized options if unrecognized!=NULL */
130 static int parse_mount_options(char *options, char **unrecognized)
132 int flags = MS_SILENT;
134 // Loop through options
137 char *comma = strchr(options, ',');
139 if (comma) *comma = 0;
141 // Find this option in mount_options
142 for (i = 0; i < VECTOR_SIZE(mount_options); i++) {
143 if (!strcasecmp(mount_options[i].name, options)) {
144 long fl = mount_options[i].flags;
145 if (fl < 0) flags &= fl;
150 // If unrecognized not NULL, append unrecognized mount options */
151 if (unrecognized && i == VECTOR_SIZE(mount_options)) {
152 // Add it to strflags, to pass on to kernel
153 i = *unrecognized ? strlen(*unrecognized) : 0;
154 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
156 // Comma separated if it's not the first one
157 if (i) (*unrecognized)[i++] = ',';
158 strcpy((*unrecognized)+i, options);
161 // Advance to next option, or finish
171 // Return a list of all block device backed filesystems
173 static llist_t *get_block_backed_filesystems(void)
175 static const char *const filesystems[] = {
185 for (i = 0; filesystems[i]; i++) {
186 f = fopen(filesystems[i], "r");
189 while ((buf = bb_get_chomped_line_from_file(f)) != 0) {
190 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
193 while (isspace(*fs)) fs++;
194 if (*fs=='#' || *fs=='*' || !*fs) continue;
196 llist_add_to_end(&list, xstrdup(fs));
199 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
207 #if ENABLE_FEATURE_CLEAN_UP
208 static void delete_block_backed_filesystems(void)
210 llist_free(fslist, free);
213 void delete_block_backed_filesystems(void);
216 #if ENABLE_FEATURE_MTAB_SUPPORT
217 static int useMtab = 1;
224 // Perform actual mount of specific filesystem at specific location.
225 // NB: mp->xxx fields may be trashed on exit
226 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
230 if (fakeIt) goto mtab;
232 // Mount, with fallback to read-only if necessary.
235 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
236 vfsflags, filteropts);
237 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
239 bb_error_msg("%s is write-protected, mounting read-only",
241 vfsflags |= MS_RDONLY;
244 // Abort entirely if permission denied.
246 if (rc && errno == EPERM)
247 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
249 /* If the mount was successful, and we're maintaining an old-style
250 * mtab file by hand, add the new entry to it now. */
252 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
254 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
258 bb_error_msg("no %s",bb_path_mtab_file);
260 // Add vfs string flags
262 for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
263 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
264 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
266 // Remove trailing / (if any) from directory we mounted on
268 i = strlen(mp->mnt_dir) - 1;
269 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
271 // Convert to canonical pathnames as needed
273 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
275 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
276 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
277 mp->mnt_type = "bind";
279 mp->mnt_freq = mp->mnt_passno = 0;
283 addmntent(mountTable, mp);
284 endmntent(mountTable);
285 if (ENABLE_FEATURE_CLEAN_UP) {
294 #if ENABLE_FEATURE_MOUNT_NFS
298 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
300 * Licensed under GPLv2, see file LICENSE in this tarball for details.
302 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
303 * numbers to be specified on the command line.
305 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
306 * Omit the call to connect() for Linux version 1.3.11 or later.
308 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
309 * Implemented the "bg", "fg" and "retry" mount options for NFS.
311 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
312 * - added Native Language Support
314 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
318 /* This is just a warning of a common mistake. Possibly this should be a
319 * uclibc faq entry rather than in busybox... */
320 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
321 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
324 #define MOUNTPORT 635
325 #define MNTPATHLEN 1024
326 #define MNTNAMLEN 255
330 typedef char fhandle[FHSIZE];
333 unsigned int fhandle3_len;
345 MNT3ERR_NAMETOOLONG = 63,
346 MNT3ERR_NOTSUPP = 10004,
347 MNT3ERR_SERVERFAULT = 10006,
349 typedef enum mountstat3 mountstat3;
352 unsigned int fhs_status;
357 typedef struct fhstatus fhstatus;
359 struct mountres3_ok {
362 unsigned int auth_flavours_len;
363 char *auth_flavours_val;
366 typedef struct mountres3_ok mountres3_ok;
369 mountstat3 fhs_status;
371 mountres3_ok mountinfo;
374 typedef struct mountres3 mountres3;
376 typedef char *dirpath;
380 typedef struct mountbody *mountlist;
384 dirpath ml_directory;
387 typedef struct mountbody mountbody;
389 typedef struct groupnode *groups;
395 typedef struct groupnode groupnode;
397 typedef struct exportnode *exports;
404 typedef struct exportnode exportnode;
417 typedef struct ppathcnf ppathcnf;
419 #define MOUNTPROG 100005
422 #define MOUNTPROC_NULL 0
423 #define MOUNTPROC_MNT 1
424 #define MOUNTPROC_DUMP 2
425 #define MOUNTPROC_UMNT 3
426 #define MOUNTPROC_UMNTALL 4
427 #define MOUNTPROC_EXPORT 5
428 #define MOUNTPROC_EXPORTALL 6
430 #define MOUNTVERS_POSIX 2
432 #define MOUNTPROC_PATHCONF 7
436 #define MOUNTPROC3_NULL 0
437 #define MOUNTPROC3_MNT 1
438 #define MOUNTPROC3_DUMP 2
439 #define MOUNTPROC3_UMNT 3
440 #define MOUNTPROC3_UMNTALL 4
441 #define MOUNTPROC3_EXPORT 5
453 * We want to be able to compile mount on old kernels in such a way
454 * that the binary will work well on more recent kernels.
455 * Thus, if necessary we teach nfsmount.c the structure of new fields
456 * that will come later.
458 * Moreover, the new kernel includes conflict with glibc includes
459 * so it is easiest to ignore the kernel altogether (at compile time).
467 unsigned char data[64];
470 struct nfs_mount_data {
473 struct nfs2_fh old_root; /* 1 */
479 int acregmin; /* 1 */
480 int acregmax; /* 1 */
481 int acdirmin; /* 1 */
482 int acdirmax; /* 1 */
483 struct sockaddr_in addr; /* 1 */
484 char hostname[256]; /* 1 */
486 unsigned int bsize; /* 3 */
487 struct nfs3_fh root; /* 4 */
490 /* bits in the flags field */
492 NFS_MOUNT_SOFT = 0x0001, /* 1 */
493 NFS_MOUNT_INTR = 0x0002, /* 1 */
494 NFS_MOUNT_SECURE = 0x0004, /* 1 */
495 NFS_MOUNT_POSIX = 0x0008, /* 1 */
496 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
497 NFS_MOUNT_NOAC = 0x0020, /* 1 */
498 NFS_MOUNT_TCP = 0x0040, /* 2 */
499 NFS_MOUNT_VER3 = 0x0080, /* 3 */
500 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
501 NFS_MOUNT_NONLM = 0x0200 /* 3 */
506 * We need to translate between nfs status return values and
507 * the local errno values which may not be the same.
509 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
510 * "after #include <errno.h> the symbol errno is reserved for any use,
511 * it cannot even be used as a struct tag or field name".
515 #define EDQUOT ENOSPC
518 // Convert each NFSERR_BLAH into EBLAH
520 static const struct {
524 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
525 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
526 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
527 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
530 static char *nfs_strerror(int status)
533 static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
535 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
536 if (nfs_errtbl[i].stat == status)
537 return strerror(nfs_errtbl[i].errnum);
539 sprintf(buf, "unknown nfs status return value: %d", status);
543 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
545 if (!xdr_opaque(xdrs, objp, FHSIZE))
550 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
552 if (!xdr_u_int(xdrs, &objp->fhs_status))
554 switch (objp->fhs_status) {
556 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
565 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
567 if (!xdr_string(xdrs, objp, MNTPATHLEN))
572 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
574 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
579 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
581 if (!xdr_fhandle3(xdrs, &objp->fhandle))
583 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
584 sizeof (int), (xdrproc_t) xdr_int))
589 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
591 if (!xdr_enum(xdrs, (enum_t *) objp))
596 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
598 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
600 switch (objp->fhs_status) {
602 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
611 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
614 * nfs_mount_version according to the sources seen at compile time.
616 static int nfs_mount_version;
617 static int kernel_version;
620 * Unfortunately, the kernel prints annoying console messages
621 * in case of an unexpected nfs mount version (instead of
622 * just returning some error). Therefore we'll have to try
623 * and figure out what version the kernel expects.
626 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
627 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
628 * nfs_mount_version: version this source and running kernel can handle
631 find_kernel_nfs_mount_version(void)
636 nfs_mount_version = 4; /* default */
638 kernel_version = get_linux_version_code();
639 if (kernel_version) {
640 if (kernel_version < KERNEL_VERSION(2,1,32))
641 nfs_mount_version = 1;
642 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
643 (kernel_version >= KERNEL_VERSION(2,3,0) &&
644 kernel_version < KERNEL_VERSION(2,3,99)))
645 nfs_mount_version = 3;
646 /* else v4 since 2.3.99pre4 */
651 get_mountport(struct sockaddr_in *server_addr,
653 long unsigned version,
657 struct pmaplist *pmap;
658 static struct pmap p = {0, 0, 0, 0};
660 server_addr->sin_port = PMAPPORT;
661 pmap = pmap_getmaps(server_addr);
663 if (version > MAX_NFSPROT)
664 version = MAX_NFSPROT;
673 if (pmap->pml_map.pm_prog != prog)
675 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
677 if (version > 2 && pmap->pml_map.pm_vers != version)
679 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
681 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
682 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
683 (port && pmap->pml_map.pm_port != port))
685 memcpy(&p, &pmap->pml_map, sizeof(p));
687 pmap = pmap->pml_next;
690 p.pm_vers = MOUNTVERS;
692 p.pm_port = MOUNTPORT;
694 p.pm_prot = IPPROTO_TCP;
698 static int daemonize(void)
702 if (pid < 0) /* error */
704 if (pid > 0) /* parent */
707 fd = xopen(bb_dev_null, O_RDWR);
711 if (fd > 2) close(fd);
713 openlog(applet_name, LOG_PID, LOG_DAEMON);
714 logmode = LOGMODE_SYSLOG;
719 static inline int we_saw_this_host_before(const char *hostname)
724 /* RPC strerror analogs are terminally idiotic:
725 * *mandatory* prefix and \n at end.
726 * This hopefully helps. Usage:
727 * error_msg_rpc(clnt_*error*(" ")) */
728 static void error_msg_rpc(const char *msg)
731 while (msg[0] == ' ' || msg[0] == ':') msg++;
733 while (len && msg[len-1] == '\n') len--;
734 bb_error_msg("%.*s", len, msg);
737 // NB: mp->xxx fields may be trashed on exit
738 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
744 struct nfs_mount_data data;
747 struct sockaddr_in server_addr;
748 struct sockaddr_in mount_server_addr;
751 struct fhstatus nfsv2;
752 struct mountres3 nfsv3;
774 find_kernel_nfs_mount_version();
782 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
784 filteropts = xstrdup(filteropts); /* going to trash it later... */
786 hostname = xstrdup(mp->mnt_fsname);
787 /* mount_main() guarantees that ':' is there */
788 s = strchr(hostname, ':');
791 /* Ignore all but first hostname in replicated mounts
792 until they can be fully supported. (mack@sgi.com) */
793 s = strchr(hostname, ',');
796 bb_error_msg("warning: multiple hostnames not supported");
799 server_addr.sin_family = AF_INET;
800 if (!inet_aton(hostname, &server_addr.sin_addr)) {
801 hp = gethostbyname(hostname);
803 bb_herror_msg("%s", hostname);
806 if (hp->h_length > sizeof(struct in_addr)) {
807 bb_error_msg("got bad hp->h_length");
808 hp->h_length = sizeof(struct in_addr);
810 memcpy(&server_addr.sin_addr,
811 hp->h_addr, hp->h_length);
814 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
816 /* add IP address to mtab options for use when unmounting */
818 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
819 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
821 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
822 mp->mnt_opts[0] ? "," : "",
823 inet_ntoa(server_addr.sin_addr));
828 /* Set default options.
829 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
830 * let the kernel decide.
831 * timeo is filled in after we know whether it'll be TCP or UDP. */
832 memset(&data, 0, sizeof(data));
838 data.namlen = NAME_MAX;
847 retry = 10000; /* 10000 minutes ~ 1 week */
850 mountprog = MOUNTPROG;
859 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
860 char *opteq = strchr(opt, '=');
862 int val = xatoi_u(opteq + 1);
864 if (!strcmp(opt, "rsize"))
866 else if (!strcmp(opt, "wsize"))
868 else if (!strcmp(opt, "timeo"))
870 else if (!strcmp(opt, "retrans"))
872 else if (!strcmp(opt, "acregmin"))
874 else if (!strcmp(opt, "acregmax"))
876 else if (!strcmp(opt, "acdirmin"))
878 else if (!strcmp(opt, "acdirmax"))
880 else if (!strcmp(opt, "actimeo")) {
886 else if (!strcmp(opt, "retry"))
888 else if (!strcmp(opt, "port"))
890 else if (!strcmp(opt, "mountport"))
892 else if (!strcmp(opt, "mounthost"))
893 mounthost = xstrndup(opteq+1,
894 strcspn(opteq+1," \t\n\r,"));
895 else if (!strcmp(opt, "mountprog"))
897 else if (!strcmp(opt, "mountvers"))
899 else if (!strcmp(opt, "nfsprog"))
901 else if (!strcmp(opt, "nfsvers") ||
902 !strcmp(opt, "vers"))
904 else if (!strcmp(opt, "proto")) {
905 if (!strncmp(opteq+1, "tcp", 3))
907 else if (!strncmp(opteq+1, "udp", 3))
910 bb_error_msg("warning: unrecognized proto= option");
911 } else if (!strcmp(opt, "namlen")) {
912 if (nfs_mount_version >= 2)
915 bb_error_msg("warning: option namlen is not supported\n");
916 } else if (!strcmp(opt, "addr"))
919 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
925 if (!strncmp(opt, "no", 2)) {
929 if (!strcmp(opt, "bg"))
931 else if (!strcmp(opt, "fg"))
933 else if (!strcmp(opt, "soft"))
935 else if (!strcmp(opt, "hard"))
937 else if (!strcmp(opt, "intr"))
939 else if (!strcmp(opt, "posix"))
941 else if (!strcmp(opt, "cto"))
943 else if (!strcmp(opt, "ac"))
945 else if (!strcmp(opt, "tcp"))
947 else if (!strcmp(opt, "udp"))
949 else if (!strcmp(opt, "lock")) {
950 if (nfs_mount_version >= 3)
953 bb_error_msg("warning: option nolock is not supported");
955 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
960 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
962 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
963 | (intr ? NFS_MOUNT_INTR : 0)
964 | (posix ? NFS_MOUNT_POSIX : 0)
965 | (nocto ? NFS_MOUNT_NOCTO : 0)
966 | (noac ? NFS_MOUNT_NOAC : 0);
967 if (nfs_mount_version >= 2)
968 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
969 if (nfs_mount_version >= 3)
970 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
971 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
972 bb_error_msg("NFSv%d not supported", nfsvers);
975 if (nfsvers && !mountvers)
976 mountvers = (nfsvers < 3) ? 1 : nfsvers;
977 if (nfsvers && nfsvers < mountvers) {
981 /* Adjust options if none specified */
983 data.timeo = tcp ? 70 : 7;
985 data.version = nfs_mount_version;
987 if (vfsflags & MS_REMOUNT)
991 * If the previous mount operation on the same host was
992 * backgrounded, and the "bg" for this mount is also set,
993 * give up immediately, to avoid the initial timeout.
995 if (bg && we_saw_this_host_before(hostname)) {
996 daemonized = daemonize(); /* parent or error */
997 if (daemonized <= 0) { /* parent or error */
998 retval = -daemonized;
1003 /* create mount daemon client */
1004 /* See if the nfs host = mount host. */
1006 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1007 mount_server_addr.sin_family = AF_INET;
1008 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1010 hp = gethostbyname(mounthost);
1012 bb_herror_msg("%s", mounthost);
1015 if (hp->h_length > sizeof(struct in_addr)) {
1016 bb_error_msg("got bad hp->h_length?");
1017 hp->h_length = sizeof(struct in_addr);
1019 mount_server_addr.sin_family = AF_INET;
1020 memcpy(&mount_server_addr.sin_addr,
1021 hp->h_addr, hp->h_length);
1027 * The following loop implements the mount retries. When the mount
1028 * times out, and the "bg" option is set, we background ourself
1029 * and continue trying.
1031 * The case where the mount point is not present and the "bg"
1032 * option is set, is treated as a timeout. This is done to
1033 * support nested mounts.
1035 * The "retry" count specified by the user is the number of
1036 * minutes to retry before giving up.
1039 struct timeval total_timeout;
1040 struct timeval retry_timeout;
1041 struct pmap* pm_mnt;
1046 retry_timeout.tv_sec = 3;
1047 retry_timeout.tv_usec = 0;
1048 total_timeout.tv_sec = 20;
1049 total_timeout.tv_usec = 0;
1050 timeout = time(NULL) + 60 * retry;
1054 /* be careful not to use too many CPU cycles */
1058 pm_mnt = get_mountport(&mount_server_addr,
1063 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1065 /* contact the mount daemon via TCP */
1066 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1067 msock = RPC_ANYSOCK;
1069 switch (pm_mnt->pm_prot) {
1071 mclient = clntudp_create(&mount_server_addr,
1078 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1079 msock = RPC_ANYSOCK;
1081 mclient = clnttcp_create(&mount_server_addr,
1090 if (!daemonized && prevt == 0)
1091 error_msg_rpc(clnt_spcreateerror(" "));
1093 enum clnt_stat clnt_stat;
1094 /* try to mount hostname:pathname */
1095 mclient->cl_auth = authunix_create_default();
1097 /* make pointers in xdr_mountres3 NULL so
1098 * that xdr_array allocates memory for us
1100 memset(&status, 0, sizeof(status));
1102 if (pm_mnt->pm_vers == 3)
1103 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1104 (xdrproc_t) xdr_dirpath,
1105 (caddr_t) &pathname,
1106 (xdrproc_t) xdr_mountres3,
1110 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1111 (xdrproc_t) xdr_dirpath,
1112 (caddr_t) &pathname,
1113 (xdrproc_t) xdr_fhstatus,
1117 if (clnt_stat == RPC_SUCCESS)
1118 goto prepare_kernel_data; /* we're done */
1119 if (errno != ECONNREFUSED) {
1120 error_msg_rpc(clnt_sperror(mclient, " "));
1121 goto fail; /* don't retry */
1123 /* Connection refused */
1124 if (!daemonized && prevt == 0) /* print just once */
1125 error_msg_rpc(clnt_sperror(mclient, " "));
1126 auth_destroy(mclient->cl_auth);
1127 clnt_destroy(mclient);
1132 /* Timeout. We are going to retry... maybe */
1137 daemonized = daemonize();
1138 if (daemonized <= 0) { /* parent or error */
1139 retval = -daemonized;
1146 /* TODO error message */
1152 prepare_kernel_data:
1155 if (status.nfsv2.fhs_status != 0) {
1156 bb_error_msg("%s:%s failed, reason given by server: %s",
1158 nfs_strerror(status.nfsv2.fhs_status));
1161 memcpy(data.root.data,
1162 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1164 data.root.size = NFS_FHSIZE;
1165 memcpy(data.old_root.data,
1166 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1169 fhandle3 *my_fhandle;
1170 if (status.nfsv3.fhs_status != 0) {
1171 bb_error_msg("%s:%s failed, reason given by server: %s",
1173 nfs_strerror(status.nfsv3.fhs_status));
1176 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1177 memset(data.old_root.data, 0, NFS_FHSIZE);
1178 memset(&data.root, 0, sizeof(data.root));
1179 data.root.size = my_fhandle->fhandle3_len;
1180 memcpy(data.root.data,
1181 (char *) my_fhandle->fhandle3_val,
1182 my_fhandle->fhandle3_len);
1184 data.flags |= NFS_MOUNT_VER3;
1187 /* create nfs socket for kernel */
1190 if (nfs_mount_version < 3) {
1191 bb_error_msg("NFS over TCP is not supported");
1194 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1196 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1198 bb_perror_msg("nfs socket");
1201 if (bindresvport(fsock, 0) < 0) {
1202 bb_perror_msg("nfs bindresvport");
1206 server_addr.sin_port = PMAPPORT;
1207 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1208 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1212 server_addr.sin_port = htons(port);
1214 /* prepare data structure for kernel */
1217 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1218 strncpy(data.hostname, hostname, sizeof(data.hostname));
1222 auth_destroy(mclient->cl_auth);
1223 clnt_destroy(mclient);
1227 /* We must wait until mount directory is available */
1228 struct stat statbuf;
1230 while (stat(mp->mnt_dir, &statbuf) == -1) {
1232 daemonized = daemonize();
1233 if (daemonized <= 0) { /* parent or error */
1234 retval = -daemonized;
1238 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1245 do_mount: /* perform actual mount */
1247 mp->mnt_type = "nfs";
1248 retval = mount_it_now(mp, vfsflags, (char*)&data);
1255 auth_destroy(mclient->cl_auth);
1256 clnt_destroy(mclient);
1270 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1272 /* Never called. Call should be optimized out. */
1273 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1275 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1277 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1278 // type detection. Returns 0 for success, nonzero for failure.
1279 // NB: mp->xxx fields may be trashed on exit
1280 static int singlemount(struct mntent *mp, int ignore_busy)
1282 int rc = -1, vfsflags;
1283 char *loopFile = 0, *filteropts = 0;
1287 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1289 // Treat fstype "auto" as unspecified.
1291 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1293 // Might this be an CIFS filesystem?
1295 if (ENABLE_FEATURE_MOUNT_CIFS &&
1296 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1297 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1303 // Replace '/' with '\' and verify that unc points to "//server/share".
1305 for (s = mp->mnt_fsname; *s; ++s)
1306 if (*s == '/') *s = '\\';
1310 s = strrchr(mp->mnt_fsname, '\\');
1311 if (s == mp->mnt_fsname+1) goto report_error;
1313 he = gethostbyname(mp->mnt_fsname+2);
1315 if (!he) goto report_error;
1317 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1319 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1320 he->h_addr[2], he->h_addr[3]);
1321 parse_mount_options(ip, &filteropts);
1323 // compose new unc '\\server-ip\share'
1325 mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3,
1326 strchr(mp->mnt_fsname+2,'\\'));
1329 vfsflags |= MS_MANDLOCK;
1331 mp->mnt_type = "cifs";
1332 rc = mount_it_now(mp, vfsflags, filteropts);
1333 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1337 // Might this be an NFS filesystem?
1339 if (ENABLE_FEATURE_MOUNT_NFS &&
1340 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1341 strchr(mp->mnt_fsname, ':') != NULL)
1343 rc = nfsmount(mp, vfsflags, filteropts);
1347 // Look at the file. (Not found isn't a failure for remount, or for
1348 // a synthetic filesystem like proc or sysfs.)
1350 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1352 // Do we need to allocate a loopback device for it?
1354 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1355 loopFile = bb_simplify_path(mp->mnt_fsname);
1357 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1362 bb_error_msg( errno == EPERM || errno == EACCES
1363 ? bb_msg_perm_denied_are_you_root
1364 : "cannot setup loop device");
1368 // Autodetect bind mounts
1370 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1371 vfsflags |= MS_BIND;
1374 /* If we know the fstype (or don't need to), jump straight
1375 * to the actual mount. */
1377 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1378 rc = mount_it_now(mp, vfsflags, filteropts);
1380 // Loop through filesystem types until mount succeeds or we run out
1384 /* Initialize list of block backed filesystems. This has to be
1385 * done here so that during "mount -a", mounts after /proc shows up
1386 * can autodetect. */
1389 fslist = get_block_backed_filesystems();
1390 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1391 atexit(delete_block_backed_filesystems);
1394 for (fl = fslist; fl; fl = fl->link) {
1395 mp->mnt_type = fl->data;
1396 rc = mount_it_now(mp, vfsflags, filteropts);
1401 // If mount failed, clean up loop file (if any).
1403 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1404 del_loop(mp->mnt_fsname);
1405 if (ENABLE_FEATURE_CLEAN_UP) {
1407 free(mp->mnt_fsname);
1412 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1414 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1416 /* perror here sometimes says "mounting ... on ... failed: Success" */
1417 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1422 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1423 // each directory to be mounted.
1425 int mount_main(int argc, char **argv)
1427 enum { OPT_ALL = 0x10 };
1429 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1431 const char *fstabname;
1435 struct mntent mtpair[2], *mtcur = mtpair;
1437 /* parse long options, like --bind and --move. Note that -o option
1438 * and --option are synonymous. Yes, this means --remount,rw works. */
1440 for (i = j = 0; i < argc; i++) {
1441 if (argv[i][0] == '-' && argv[i][1] == '-') {
1442 append_mount_options(&cmdopts,argv[i]+2);
1443 } else argv[j++] = argv[i];
1448 // Parse remaining options
1450 opt = getopt32(argc, argv, "o:t:rwanfvs", &opt_o, &fstype);
1451 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
1452 //if (opt & 0x2) // -t
1453 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
1454 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
1455 //if (opt & 0x10) // -a
1456 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
1457 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
1458 //if (opt & 0x80) // -v: verbose (ignore)
1459 //if (opt & 0x100) // -s: sloppy (ignore)
1463 // Three or more non-option arguments? Die with a usage message.
1465 if (argc > 2) bb_show_usage();
1467 // If we have no arguments, show currently mounted filesystems
1470 if (!(opt & OPT_ALL)) {
1471 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1473 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1475 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1476 sizeof(bb_common_bufsiz1)))
1478 // Don't show rootfs.
1479 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1481 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1482 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1483 mtpair->mnt_dir, mtpair->mnt_type,
1486 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1487 return EXIT_SUCCESS;
1489 } else storage_path = bb_simplify_path(argv[0]);
1491 // When we have two arguments, the second is the directory and we can
1492 // skip looking at fstab entirely. We can always abspath() the directory
1493 // argument when we get it.
1496 mtpair->mnt_fsname = argv[0];
1497 mtpair->mnt_dir = argv[1];
1498 mtpair->mnt_type = fstype;
1499 mtpair->mnt_opts = cmdopts;
1500 rc = singlemount(mtpair, 0);
1504 i = parse_mount_options(cmdopts, 0);
1506 // If we have a shared subtree flag, don't worry about fstab or mtab.
1508 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1509 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1511 rc = mount("", argv[0], "", i, "");
1512 if (rc) bb_perror_msg_and_die("%s", argv[0]);
1516 // Open either fstab or mtab
1519 fstabname = bb_path_mtab_file;
1520 else fstabname = "/etc/fstab";
1521 fstab = setmntent(fstabname,"r");
1523 bb_perror_msg_and_die("cannot read %s", fstabname);
1525 // Loop through entries until we find what we're looking for.
1527 memset(mtpair, 0, sizeof(mtpair));
1529 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1531 // Get next fstab entry
1533 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1534 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1535 sizeof(bb_common_bufsiz1)/2))
1537 // Were we looking for something specific?
1541 // If we didn't find anything, complain.
1543 if (!mtnext->mnt_fsname)
1544 bb_error_msg_and_die("can't find %s in %s",
1545 argv[0], fstabname);
1547 // Mount the last thing we found.
1550 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1551 append_mount_options(&(mtcur->mnt_opts),cmdopts);
1552 rc = singlemount(mtcur, 0);
1553 free(mtcur->mnt_opts);
1558 /* If we're trying to mount something specific and this isn't it,
1559 * skip it. Note we must match both the exact text in fstab (ala
1560 * "proc") or a full path from root */
1564 // Is this what we're looking for?
1566 if (strcmp(argv[0],mtcur->mnt_fsname) &&
1567 strcmp(storage_path,mtcur->mnt_fsname) &&
1568 strcmp(argv[0],mtcur->mnt_dir) &&
1569 strcmp(storage_path,mtcur->mnt_dir)) continue;
1571 // Remember this entry. Something later may have overmounted
1572 // it, and we want the _last_ match.
1576 // If we're mounting all.
1580 // Do we need to match a filesystem type?
1581 if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
1583 // Skip noauto and swap anyway.
1585 if (parse_mount_options(mtcur->mnt_opts,0)
1586 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1588 // Mount this thing.
1590 if (singlemount(mtcur, 1)) {
1591 /* Count number of failed mounts */
1596 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1600 if (ENABLE_FEATURE_CLEAN_UP) {