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.
13 * bb_getopt_ulflags();
16 /* Design notes: There is no spec for mount. Remind me to write one.
18 mount_main() calls singlemount() which calls mount_it_now().
20 mount_main() can loop through /etc/fstab for mount -a
21 singlemount() can loop through /etc/filesystems for fstype detection.
22 mount_it_now() does the actual mount.
28 /* Needed for nfs support only... */
30 #include <sys/utsname.h>
34 #include <rpc/pmap_prot.h>
35 #include <rpc/pmap_clnt.h>
38 // Not real flags, but we want to be able to check for this.
39 #define MOUNT_NOAUTO (1<<29)
40 #define MOUNT_SWAP (1<<30)
42 /* Standard mount options (from -o options or --options), with corresponding
48 } static mount_options[] = {
49 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
51 USE_FEATURE_MOUNT_LOOP(
55 USE_FEATURE_MOUNT_FSTAB(
58 {"noauto",MOUNT_NOAUTO},
62 USE_FEATURE_MOUNT_FLAGS(
64 {"nosuid", MS_NOSUID},
69 {"noexec", MS_NOEXEC},
70 {"sync", MS_SYNCHRONOUS},
71 {"async", ~MS_SYNCHRONOUS},
72 {"atime", ~MS_NOATIME},
73 {"noatime", MS_NOATIME},
74 {"diratime", ~MS_NODIRATIME},
75 {"nodiratime", MS_NODIRATIME},
82 {"shared", MS_SHARED},
84 {"private", MS_PRIVATE},
85 {"unbindable", MS_UNBINDABLE},
86 {"rshared", MS_SHARED|MS_RECURSIVE},
87 {"rslave", MS_SLAVE|MS_RECURSIVE},
88 {"rprivate", MS_SLAVE|MS_RECURSIVE},
89 {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
94 {"ro", MS_RDONLY}, // vfs flag
95 {"rw", ~MS_RDONLY}, // vfs flag
96 {"remount", MS_REMOUNT}, // action flag
101 /* Append mount options to string */
102 static void append_mount_options(char **oldopts, char *newopts)
104 if (*oldopts && **oldopts) {
105 char *temp = xasprintf("%s,%s",*oldopts,newopts);
109 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
110 *oldopts = xstrdup(newopts);
114 /* Use the mount_options list to parse options into flags.
115 * Also return list of unrecognized options if unrecognized!=NULL */
116 static int parse_mount_options(char *options, char **unrecognized)
118 int flags = MS_SILENT;
120 // Loop through options
123 char *comma = strchr(options, ',');
125 if (comma) *comma = 0;
127 // Find this option in mount_options
128 for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
129 if (!strcasecmp(mount_options[i].name, options)) {
130 long fl = mount_options[i].flags;
131 if (fl < 0) flags &= fl;
136 // If unrecognized not NULL, append unrecognized mount options */
138 && i == (sizeof(mount_options) / sizeof(*mount_options)))
140 // Add it to strflags, to pass on to kernel
141 i = *unrecognized ? strlen(*unrecognized) : 0;
142 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
144 // Comma separated if it's not the first one
145 if (i) (*unrecognized)[i++] = ',';
146 strcpy((*unrecognized)+i, options);
149 // Advance to next option, or finish
159 // Return a list of all block device backed filesystems
161 static llist_t *get_block_backed_filesystems(void)
164 *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
169 for (i = 0; filesystems[i]; i++) {
170 f = fopen(filesystems[i], "r");
173 for (fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
176 if (!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
178 while (isspace(*fs)) fs++;
179 if (*fs=='#' || *fs=='*') continue;
182 llist_add_to_end(&list,xstrdup(fs));
184 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
192 #if ENABLE_FEATURE_CLEAN_UP
193 static void delete_block_backed_filesystems(void)
195 llist_free(fslist, free);
198 void delete_block_backed_filesystems(void);
201 #if ENABLE_FEATURE_MTAB_SUPPORT
202 static int useMtab = 1;
209 // Perform actual mount of specific filesystem at specific location.
210 // NB: mp->xxx fields may be trashed on exit
211 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
215 if (fakeIt) return 0;
217 // Mount, with fallback to read-only if necessary.
220 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
221 vfsflags, filteropts);
222 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
224 bb_error_msg("%s is write-protected, mounting read-only",
226 vfsflags |= MS_RDONLY;
229 // Abort entirely if permission denied.
231 if (rc && errno == EPERM)
232 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
234 /* If the mount was successful, and we're maintaining an old-style
235 * mtab file by hand, add the new entry to it now. */
237 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
239 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
243 bb_error_msg("no %s",bb_path_mtab_file);
245 // Add vfs string flags
247 for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
248 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
249 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
251 // Remove trailing / (if any) from directory we mounted on
253 i = strlen(mp->mnt_dir) - 1;
254 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
256 // Convert to canonical pathnames as needed
258 mp->mnt_dir = dir = bb_simplify_path(mp->mnt_dir);
260 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
261 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
262 mp->mnt_type = "none";
264 mp->mnt_freq = mp->mnt_passno = 0;
268 addmntent(mountTable, mp);
269 endmntent(mountTable);
270 if (ENABLE_FEATURE_CLEAN_UP) {
279 #if ENABLE_FEATURE_MOUNT_NFS
283 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
285 * Licensed under GPLv2, see file LICENSE in this tarball for details.
287 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
288 * numbers to be specified on the command line.
290 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
291 * Omit the call to connect() for Linux version 1.3.11 or later.
293 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
294 * Implemented the "bg", "fg" and "retry" mount options for NFS.
296 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
297 * - added Native Language Support
299 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
303 /* This is just a warning of a common mistake. Possibly this should be a
304 * uclibc faq entry rather than in busybox... */
305 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
306 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
309 #define MOUNTPORT 635
310 #define MNTPATHLEN 1024
311 #define MNTNAMLEN 255
315 typedef char fhandle[FHSIZE];
318 unsigned int fhandle3_len;
330 MNT3ERR_NAMETOOLONG = 63,
331 MNT3ERR_NOTSUPP = 10004,
332 MNT3ERR_SERVERFAULT = 10006,
334 typedef enum mountstat3 mountstat3;
337 unsigned int fhs_status;
342 typedef struct fhstatus fhstatus;
344 struct mountres3_ok {
347 unsigned int auth_flavours_len;
348 char *auth_flavours_val;
351 typedef struct mountres3_ok mountres3_ok;
354 mountstat3 fhs_status;
356 mountres3_ok mountinfo;
359 typedef struct mountres3 mountres3;
361 typedef char *dirpath;
365 typedef struct mountbody *mountlist;
369 dirpath ml_directory;
372 typedef struct mountbody mountbody;
374 typedef struct groupnode *groups;
380 typedef struct groupnode groupnode;
382 typedef struct exportnode *exports;
389 typedef struct exportnode exportnode;
402 typedef struct ppathcnf ppathcnf;
404 #define MOUNTPROG 100005
407 #define MOUNTPROC_NULL 0
408 #define MOUNTPROC_MNT 1
409 #define MOUNTPROC_DUMP 2
410 #define MOUNTPROC_UMNT 3
411 #define MOUNTPROC_UMNTALL 4
412 #define MOUNTPROC_EXPORT 5
413 #define MOUNTPROC_EXPORTALL 6
415 #define MOUNTVERS_POSIX 2
417 #define MOUNTPROC_PATHCONF 7
421 #define MOUNTPROC3_NULL 0
422 #define MOUNTPROC3_MNT 1
423 #define MOUNTPROC3_DUMP 2
424 #define MOUNTPROC3_UMNT 3
425 #define MOUNTPROC3_UMNTALL 4
426 #define MOUNTPROC3_EXPORT 5
438 * We want to be able to compile mount on old kernels in such a way
439 * that the binary will work well on more recent kernels.
440 * Thus, if necessary we teach nfsmount.c the structure of new fields
441 * that will come later.
443 * Moreover, the new kernel includes conflict with glibc includes
444 * so it is easiest to ignore the kernel altogether (at compile time).
452 unsigned char data[64];
455 struct nfs_mount_data {
458 struct nfs2_fh old_root; /* 1 */
464 int acregmin; /* 1 */
465 int acregmax; /* 1 */
466 int acdirmin; /* 1 */
467 int acdirmax; /* 1 */
468 struct sockaddr_in addr; /* 1 */
469 char hostname[256]; /* 1 */
471 unsigned int bsize; /* 3 */
472 struct nfs3_fh root; /* 4 */
475 /* bits in the flags field */
477 NFS_MOUNT_SOFT = 0x0001, /* 1 */
478 NFS_MOUNT_INTR = 0x0002, /* 1 */
479 NFS_MOUNT_SECURE = 0x0004, /* 1 */
480 NFS_MOUNT_POSIX = 0x0008, /* 1 */
481 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
482 NFS_MOUNT_NOAC = 0x0020, /* 1 */
483 NFS_MOUNT_TCP = 0x0040, /* 2 */
484 NFS_MOUNT_VER3 = 0x0080, /* 3 */
485 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
486 NFS_MOUNT_NONLM = 0x0200 /* 3 */
491 * We need to translate between nfs status return values and
492 * the local errno values which may not be the same.
494 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
495 * "after #include <errno.h> the symbol errno is reserved for any use,
496 * it cannot even be used as a struct tag or field name".
500 #define EDQUOT ENOSPC
503 // Convert each NFSERR_BLAH into EBLAH
505 static const struct {
509 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
510 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
511 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
512 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
515 static char *nfs_strerror(int status)
518 static char buf[256];
520 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
521 if (nfs_errtbl[i].stat == status)
522 return strerror(nfs_errtbl[i].errnum);
524 sprintf(buf, "unknown nfs status return value: %d", status);
528 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
530 if (!xdr_opaque(xdrs, objp, FHSIZE))
535 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
537 if (!xdr_u_int(xdrs, &objp->fhs_status))
539 switch (objp->fhs_status) {
541 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
550 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
552 if (!xdr_string(xdrs, objp, MNTPATHLEN))
557 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
559 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
564 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
566 if (!xdr_fhandle3(xdrs, &objp->fhandle))
568 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
569 sizeof (int), (xdrproc_t) xdr_int))
574 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
576 if (!xdr_enum(xdrs, (enum_t *) objp))
581 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
583 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
585 switch (objp->fhs_status) {
587 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
596 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
599 * nfs_mount_version according to the sources seen at compile time.
601 static int nfs_mount_version;
602 static int kernel_version;
605 * Unfortunately, the kernel prints annoying console messages
606 * in case of an unexpected nfs mount version (instead of
607 * just returning some error). Therefore we'll have to try
608 * and figure out what version the kernel expects.
611 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
612 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
613 * nfs_mount_version: version this source and running kernel can handle
616 find_kernel_nfs_mount_version(void)
621 nfs_mount_version = 4; /* default */
623 kernel_version = get_linux_version_code();
624 if (kernel_version) {
625 if (kernel_version < KERNEL_VERSION(2,1,32))
626 nfs_mount_version = 1;
627 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
628 (kernel_version >= KERNEL_VERSION(2,3,0) &&
629 kernel_version < KERNEL_VERSION(2,3,99)))
630 nfs_mount_version = 3;
631 /* else v4 since 2.3.99pre4 */
636 get_mountport(struct sockaddr_in *server_addr,
638 long unsigned version,
642 struct pmaplist *pmap;
643 static struct pmap p = {0, 0, 0, 0};
645 server_addr->sin_port = PMAPPORT;
646 pmap = pmap_getmaps(server_addr);
648 if (version > MAX_NFSPROT)
649 version = MAX_NFSPROT;
658 if (pmap->pml_map.pm_prog != prog)
660 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
662 if (version > 2 && pmap->pml_map.pm_vers != version)
664 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
666 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
667 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
668 (port && pmap->pml_map.pm_port != port))
670 memcpy(&p, &pmap->pml_map, sizeof(p));
672 pmap = pmap->pml_next;
675 p.pm_vers = MOUNTVERS;
677 p.pm_port = MOUNTPORT;
679 p.pm_prot = IPPROTO_TCP;
683 static int daemonize(void)
687 if (pid < 0) /* error */
689 if (pid > 0) /* parent */
692 fd = xopen(bb_dev_null, O_RDWR);
696 if (fd > 2) close(fd);
698 openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
699 logmode = LOGMODE_SYSLOG;
704 static inline int we_saw_this_host_before(const char *hostname)
709 /* RPC strerror analogs are terminally idiotic:
710 * *mandatory* prefix and \n at end.
711 * This hopefully helps. Usage:
712 * error_msg_rpc(clnt_*error*(" ")) */
713 static void error_msg_rpc(const char *msg)
716 while (msg[0] == ' ' || msg[0] == ':') msg++;
718 while (len && msg[len-1] == '\n') len--;
719 bb_error_msg("%.*s", len, msg);
722 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
728 struct nfs_mount_data data;
731 struct sockaddr_in server_addr;
732 struct sockaddr_in mount_server_addr;
735 struct fhstatus nfsv2;
736 struct mountres3 nfsv3;
758 find_kernel_nfs_mount_version();
766 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
768 filteropts = xstrdup(filteropts); /* going to trash it later... */
770 hostname = xstrdup(mp->mnt_fsname);
771 /* mount_main() guarantees that ':' is there */
772 s = strchr(hostname, ':');
775 /* Ignore all but first hostname in replicated mounts
776 until they can be fully supported. (mack@sgi.com) */
777 s = strchr(hostname, ',');
780 bb_error_msg("warning: multiple hostnames not supported");
783 server_addr.sin_family = AF_INET;
784 if (!inet_aton(hostname, &server_addr.sin_addr)) {
785 hp = gethostbyname(hostname);
787 bb_herror_msg("%s", hostname);
790 if (hp->h_length > sizeof(struct in_addr)) {
791 bb_error_msg("got bad hp->h_length");
792 hp->h_length = sizeof(struct in_addr);
794 memcpy(&server_addr.sin_addr,
795 hp->h_addr, hp->h_length);
798 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
800 /* add IP address to mtab options for use when unmounting */
802 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
803 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
805 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
806 mp->mnt_opts[0] ? "," : "",
807 inet_ntoa(server_addr.sin_addr));
812 /* Set default options.
813 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
814 * let the kernel decide.
815 * timeo is filled in after we know whether it'll be TCP or UDP. */
816 memset(&data, 0, sizeof(data));
822 data.namlen = NAME_MAX;
831 retry = 10000; /* 10000 minutes ~ 1 week */
834 mountprog = MOUNTPROG;
843 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
844 char *opteq = strchr(opt, '=');
846 int val = atoi(opteq + 1);
848 if (!strcmp(opt, "rsize"))
850 else if (!strcmp(opt, "wsize"))
852 else if (!strcmp(opt, "timeo"))
854 else if (!strcmp(opt, "retrans"))
856 else if (!strcmp(opt, "acregmin"))
858 else if (!strcmp(opt, "acregmax"))
860 else if (!strcmp(opt, "acdirmin"))
862 else if (!strcmp(opt, "acdirmax"))
864 else if (!strcmp(opt, "actimeo")) {
870 else if (!strcmp(opt, "retry"))
872 else if (!strcmp(opt, "port"))
874 else if (!strcmp(opt, "mountport"))
876 else if (!strcmp(opt, "mounthost"))
877 mounthost = xstrndup(opteq+1,
878 strcspn(opteq+1," \t\n\r,"));
879 else if (!strcmp(opt, "mountprog"))
881 else if (!strcmp(opt, "mountvers"))
883 else if (!strcmp(opt, "nfsprog"))
885 else if (!strcmp(opt, "nfsvers") ||
886 !strcmp(opt, "vers"))
888 else if (!strcmp(opt, "proto")) {
889 if (!strncmp(opteq+1, "tcp", 3))
891 else if (!strncmp(opteq+1, "udp", 3))
894 bb_error_msg("warning: unrecognized proto= option");
895 } else if (!strcmp(opt, "namlen")) {
896 if (nfs_mount_version >= 2)
899 bb_error_msg("warning: option namlen is not supported\n");
900 } else if (!strcmp(opt, "addr"))
903 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
909 if (!strncmp(opt, "no", 2)) {
913 if (!strcmp(opt, "bg"))
915 else if (!strcmp(opt, "fg"))
917 else if (!strcmp(opt, "soft"))
919 else if (!strcmp(opt, "hard"))
921 else if (!strcmp(opt, "intr"))
923 else if (!strcmp(opt, "posix"))
925 else if (!strcmp(opt, "cto"))
927 else if (!strcmp(opt, "ac"))
929 else if (!strcmp(opt, "tcp"))
931 else if (!strcmp(opt, "udp"))
933 else if (!strcmp(opt, "lock")) {
934 if (nfs_mount_version >= 3)
937 bb_error_msg("warning: option nolock is not supported");
939 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
944 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
946 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
947 | (intr ? NFS_MOUNT_INTR : 0)
948 | (posix ? NFS_MOUNT_POSIX : 0)
949 | (nocto ? NFS_MOUNT_NOCTO : 0)
950 | (noac ? NFS_MOUNT_NOAC : 0);
951 if (nfs_mount_version >= 2)
952 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
953 if (nfs_mount_version >= 3)
954 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
955 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
956 bb_error_msg("NFSv%d not supported", nfsvers);
959 if (nfsvers && !mountvers)
960 mountvers = (nfsvers < 3) ? 1 : nfsvers;
961 if (nfsvers && nfsvers < mountvers) {
965 /* Adjust options if none specified */
967 data.timeo = tcp ? 70 : 7;
969 data.version = nfs_mount_version;
971 if (vfsflags & MS_REMOUNT)
975 * If the previous mount operation on the same host was
976 * backgrounded, and the "bg" for this mount is also set,
977 * give up immediately, to avoid the initial timeout.
979 if (bg && we_saw_this_host_before(hostname)) {
980 daemonized = daemonize(); /* parent or error */
981 if (daemonized <= 0) { /* parent or error */
982 retval = -daemonized;
987 /* create mount daemon client */
988 /* See if the nfs host = mount host. */
990 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
991 mount_server_addr.sin_family = AF_INET;
992 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
994 hp = gethostbyname(mounthost);
996 bb_herror_msg("%s", mounthost);
999 if (hp->h_length > sizeof(struct in_addr)) {
1000 bb_error_msg("got bad hp->h_length?");
1001 hp->h_length = sizeof(struct in_addr);
1003 mount_server_addr.sin_family = AF_INET;
1004 memcpy(&mount_server_addr.sin_addr,
1005 hp->h_addr, hp->h_length);
1011 * The following loop implements the mount retries. When the mount
1012 * times out, and the "bg" option is set, we background ourself
1013 * and continue trying.
1015 * The case where the mount point is not present and the "bg"
1016 * option is set, is treated as a timeout. This is done to
1017 * support nested mounts.
1019 * The "retry" count specified by the user is the number of
1020 * minutes to retry before giving up.
1023 struct timeval total_timeout;
1024 struct timeval retry_timeout;
1025 struct pmap* pm_mnt;
1030 retry_timeout.tv_sec = 3;
1031 retry_timeout.tv_usec = 0;
1032 total_timeout.tv_sec = 20;
1033 total_timeout.tv_usec = 0;
1034 timeout = time(NULL) + 60 * retry;
1038 /* be careful not to use too many CPU cycles */
1042 pm_mnt = get_mountport(&mount_server_addr,
1047 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1049 /* contact the mount daemon via TCP */
1050 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1051 msock = RPC_ANYSOCK;
1053 switch (pm_mnt->pm_prot) {
1055 mclient = clntudp_create(&mount_server_addr,
1062 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1063 msock = RPC_ANYSOCK;
1065 mclient = clnttcp_create(&mount_server_addr,
1074 if (!daemonized && prevt == 0)
1075 error_msg_rpc(clnt_spcreateerror(" "));
1077 enum clnt_stat clnt_stat;
1078 /* try to mount hostname:pathname */
1079 mclient->cl_auth = authunix_create_default();
1081 /* make pointers in xdr_mountres3 NULL so
1082 * that xdr_array allocates memory for us
1084 memset(&status, 0, sizeof(status));
1086 if (pm_mnt->pm_vers == 3)
1087 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1088 (xdrproc_t) xdr_dirpath,
1089 (caddr_t) &pathname,
1090 (xdrproc_t) xdr_mountres3,
1094 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1095 (xdrproc_t) xdr_dirpath,
1096 (caddr_t) &pathname,
1097 (xdrproc_t) xdr_fhstatus,
1101 if (clnt_stat == RPC_SUCCESS)
1102 goto prepare_kernel_data; /* we're done */
1103 if (errno != ECONNREFUSED) {
1104 error_msg_rpc(clnt_sperror(mclient, " "));
1105 goto fail; /* don't retry */
1107 /* Connection refused */
1108 if (!daemonized && prevt == 0) /* print just once */
1109 error_msg_rpc(clnt_sperror(mclient, " "));
1110 auth_destroy(mclient->cl_auth);
1111 clnt_destroy(mclient);
1116 /* Timeout. We are going to retry... maybe */
1121 daemonized = daemonize();
1122 if (daemonized <= 0) { /* parent or error */
1123 retval = -daemonized;
1130 /* TODO error message */
1136 prepare_kernel_data:
1139 if (status.nfsv2.fhs_status != 0) {
1140 bb_error_msg("%s:%s failed, reason given by server: %s",
1142 nfs_strerror(status.nfsv2.fhs_status));
1145 memcpy(data.root.data,
1146 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1148 data.root.size = NFS_FHSIZE;
1149 memcpy(data.old_root.data,
1150 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1153 fhandle3 *my_fhandle;
1154 if (status.nfsv3.fhs_status != 0) {
1155 bb_error_msg("%s:%s failed, reason given by server: %s",
1157 nfs_strerror(status.nfsv3.fhs_status));
1160 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1161 memset(data.old_root.data, 0, NFS_FHSIZE);
1162 memset(&data.root, 0, sizeof(data.root));
1163 data.root.size = my_fhandle->fhandle3_len;
1164 memcpy(data.root.data,
1165 (char *) my_fhandle->fhandle3_val,
1166 my_fhandle->fhandle3_len);
1168 data.flags |= NFS_MOUNT_VER3;
1171 /* create nfs socket for kernel */
1174 if (nfs_mount_version < 3) {
1175 bb_error_msg("NFS over TCP is not supported");
1178 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1180 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1182 bb_perror_msg("nfs socket");
1185 if (bindresvport(fsock, 0) < 0) {
1186 bb_perror_msg("nfs bindresvport");
1190 server_addr.sin_port = PMAPPORT;
1191 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1192 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1196 server_addr.sin_port = htons(port);
1198 /* prepare data structure for kernel */
1201 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1202 strncpy(data.hostname, hostname, sizeof(data.hostname));
1206 auth_destroy(mclient->cl_auth);
1207 clnt_destroy(mclient);
1211 /* We must wait until mount directory is available */
1212 struct stat statbuf;
1214 while (stat(mp->mnt_dir, &statbuf) == -1) {
1216 daemonized = daemonize();
1217 if (daemonized <= 0) { /* parent or error */
1218 retval = -daemonized;
1222 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1229 do_mount: /* perform actual mount */
1231 mp->mnt_type = "nfs";
1232 retval = mount_it_now(mp, vfsflags, (char*)&data);
1239 auth_destroy(mclient->cl_auth);
1240 clnt_destroy(mclient);
1254 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1256 /* Never called. Call should be optimized out. */
1257 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1259 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1261 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1262 // type detection. Returns 0 for success, nonzero for failure.
1264 static int singlemount(struct mntent *mp, int ignore_busy)
1266 int rc = -1, vfsflags;
1267 char *loopFile = 0, *filteropts = 0;
1271 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1273 // Treat fstype "auto" as unspecified.
1275 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1277 // Might this be an CIFS filesystem?
1279 if (ENABLE_FEATURE_MOUNT_CIFS &&
1280 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1281 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1287 // Replace '/' with '\' and verify that unc points to "//server/share".
1289 for (s = mp->mnt_fsname; *s; ++s)
1290 if (*s == '/') *s = '\\';
1294 s = strrchr(mp->mnt_fsname, '\\');
1295 if (s == mp->mnt_fsname+1) goto report_error;
1297 he = gethostbyname(mp->mnt_fsname+2);
1299 if (!he) goto report_error;
1301 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1303 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1304 he->h_addr[2], he->h_addr[3]);
1305 parse_mount_options(ip, &filteropts);
1307 // compose new unc '\\server-ip\share'
1309 s = xasprintf("\\\\%s%s",ip+3,strchr(mp->mnt_fsname+2,'\\'));
1310 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1314 vfsflags |= MS_MANDLOCK;
1316 mp->mnt_type = "cifs";
1317 rc = mount_it_now(mp, vfsflags, filteropts);
1321 // Might this be an NFS filesystem?
1323 if (ENABLE_FEATURE_MOUNT_NFS &&
1324 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1325 strchr(mp->mnt_fsname, ':') != NULL)
1327 rc = nfsmount(mp, vfsflags, filteropts);
1331 // Look at the file. (Not found isn't a failure for remount, or for
1332 // a synthetic filesystem like proc or sysfs.)
1334 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1336 // Do we need to allocate a loopback device for it?
1338 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1339 loopFile = bb_simplify_path(mp->mnt_fsname);
1341 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1346 bb_error_msg( errno == EPERM || errno == EACCES
1347 ? bb_msg_perm_denied_are_you_root
1348 : "cannot setup loop device");
1352 // Autodetect bind mounts
1354 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1355 vfsflags |= MS_BIND;
1358 /* If we know the fstype (or don't need to), jump straight
1359 * to the actual mount. */
1361 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1362 rc = mount_it_now(mp, vfsflags, filteropts);
1364 // Loop through filesystem types until mount succeeds or we run out
1368 /* Initialize list of block backed filesystems. This has to be
1369 * done here so that during "mount -a", mounts after /proc shows up
1370 * can autodetect. */
1373 fslist = get_block_backed_filesystems();
1374 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1375 atexit(delete_block_backed_filesystems);
1378 for (fl = fslist; fl; fl = fl->link) {
1379 mp->mnt_type = fl->data;
1381 rc = mount_it_now(mp,vfsflags, filteropts);
1388 // If mount failed, clean up loop file (if any).
1390 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1391 del_loop(mp->mnt_fsname);
1392 if (ENABLE_FEATURE_CLEAN_UP) {
1394 free(mp->mnt_fsname);
1399 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1401 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1403 /* perror here sometimes says "mounting ... on ... failed: Success" */
1404 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1409 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1410 // each directory to be mounted.
1412 int mount_main(int argc, char **argv)
1414 char *cmdopts = xstrdup(""), *fstabname, *fstype=0, *storage_path=0;
1416 int i, opt, all = FALSE, rc = 0;
1417 struct mntent mtpair[2], *mtcur = mtpair;
1419 /* parse long options, like --bind and --move. Note that -o option
1420 * and --option are synonymous. Yes, this means --remount,rw works. */
1422 for (i = opt = 0; i < argc; i++) {
1423 if (argv[i][0] == '-' && argv[i][1] == '-') {
1424 append_mount_options(&cmdopts,argv[i]+2);
1425 } else argv[opt++] = argv[i];
1429 // Parse remaining options
1431 while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) {
1434 append_mount_options(&cmdopts, optarg);
1440 append_mount_options(&cmdopts, "ro");
1443 append_mount_options(&cmdopts, "rw");
1449 USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;)
1452 USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;)
1461 // Three or more non-option arguments? Die with a usage message.
1463 if (optind-argc>2) bb_show_usage();
1465 // If we have no arguments, show currently mounted filesystems
1467 if (optind == argc) {
1469 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1471 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1473 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1474 sizeof(bb_common_bufsiz1)))
1476 // Don't show rootfs.
1477 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1479 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1480 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1481 mtpair->mnt_dir, mtpair->mnt_type,
1484 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1485 return EXIT_SUCCESS;
1487 } else storage_path = bb_simplify_path(argv[optind]);
1489 // When we have two arguments, the second is the directory and we can
1490 // skip looking at fstab entirely. We can always abspath() the directory
1491 // argument when we get it.
1493 if (optind+2 == argc) {
1494 mtpair->mnt_fsname = argv[optind];
1495 mtpair->mnt_dir = argv[optind+1];
1496 mtpair->mnt_type = fstype;
1497 mtpair->mnt_opts = cmdopts;
1498 rc = singlemount(mtpair, 0);
1502 // If we have a shared subtree flag, don't worry about fstab or mtab.
1503 i = parse_mount_options(cmdopts,0);
1504 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1505 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1507 rc = mount("", argv[optind], "", i, "");
1508 if (rc) bb_perror_msg_and_die("%s", argv[optind]);
1512 // Open either fstab or mtab
1514 if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
1515 fstabname = bb_path_mtab_file;
1516 else fstabname="/etc/fstab";
1518 fstab = setmntent(fstabname,"r");
1520 bb_perror_msg_and_die("cannot read %s",fstabname);
1522 // Loop through entries until we find what we're looking for.
1524 memset(mtpair,0,sizeof(mtpair));
1526 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1528 // Get next fstab entry
1530 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1531 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1532 sizeof(bb_common_bufsiz1)/2))
1534 // Were we looking for something specific?
1536 if (optind != argc) {
1538 // If we didn't find anything, complain.
1540 if (!mtnext->mnt_fsname)
1541 bb_error_msg_and_die("can't find %s in %s",
1542 argv[optind], fstabname);
1544 // Mount the last thing we found.
1547 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1548 append_mount_options(&(mtcur->mnt_opts),cmdopts);
1549 rc = singlemount(mtcur, 0);
1550 free(mtcur->mnt_opts);
1555 /* If we're trying to mount something specific and this isn't it,
1556 * skip it. Note we must match both the exact text in fstab (ala
1557 * "proc") or a full path from root */
1559 if (optind != argc) {
1561 // Is this what we're looking for?
1563 if (strcmp(argv[optind],mtcur->mnt_fsname) &&
1564 strcmp(storage_path,mtcur->mnt_fsname) &&
1565 strcmp(argv[optind],mtcur->mnt_dir) &&
1566 strcmp(storage_path,mtcur->mnt_dir)) continue;
1568 // Remember this entry. Something later may have overmounted
1569 // it, and we want the _last_ match.
1573 // If we're mounting all.
1577 // Do we need to match a filesystem type?
1578 if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
1580 // Skip noauto and swap anyway.
1582 if (parse_mount_options(mtcur->mnt_opts,0)
1583 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1585 // Mount this thing.
1587 if (singlemount(mtcur, 1)) {
1588 /* Count number of failed mounts */
1593 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1597 if (ENABLE_FEATURE_CLEAN_UP) {