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 // NB: mp->xxx fields may be trashed on exit
723 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
729 struct nfs_mount_data data;
732 struct sockaddr_in server_addr;
733 struct sockaddr_in mount_server_addr;
736 struct fhstatus nfsv2;
737 struct mountres3 nfsv3;
759 find_kernel_nfs_mount_version();
767 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
769 filteropts = xstrdup(filteropts); /* going to trash it later... */
771 hostname = xstrdup(mp->mnt_fsname);
772 /* mount_main() guarantees that ':' is there */
773 s = strchr(hostname, ':');
776 /* Ignore all but first hostname in replicated mounts
777 until they can be fully supported. (mack@sgi.com) */
778 s = strchr(hostname, ',');
781 bb_error_msg("warning: multiple hostnames not supported");
784 server_addr.sin_family = AF_INET;
785 if (!inet_aton(hostname, &server_addr.sin_addr)) {
786 hp = gethostbyname(hostname);
788 bb_herror_msg("%s", hostname);
791 if (hp->h_length > sizeof(struct in_addr)) {
792 bb_error_msg("got bad hp->h_length");
793 hp->h_length = sizeof(struct in_addr);
795 memcpy(&server_addr.sin_addr,
796 hp->h_addr, hp->h_length);
799 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
801 /* add IP address to mtab options for use when unmounting */
803 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
804 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
806 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
807 mp->mnt_opts[0] ? "," : "",
808 inet_ntoa(server_addr.sin_addr));
813 /* Set default options.
814 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
815 * let the kernel decide.
816 * timeo is filled in after we know whether it'll be TCP or UDP. */
817 memset(&data, 0, sizeof(data));
823 data.namlen = NAME_MAX;
832 retry = 10000; /* 10000 minutes ~ 1 week */
835 mountprog = MOUNTPROG;
844 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
845 char *opteq = strchr(opt, '=');
847 int val = atoi(opteq + 1);
849 if (!strcmp(opt, "rsize"))
851 else if (!strcmp(opt, "wsize"))
853 else if (!strcmp(opt, "timeo"))
855 else if (!strcmp(opt, "retrans"))
857 else if (!strcmp(opt, "acregmin"))
859 else if (!strcmp(opt, "acregmax"))
861 else if (!strcmp(opt, "acdirmin"))
863 else if (!strcmp(opt, "acdirmax"))
865 else if (!strcmp(opt, "actimeo")) {
871 else if (!strcmp(opt, "retry"))
873 else if (!strcmp(opt, "port"))
875 else if (!strcmp(opt, "mountport"))
877 else if (!strcmp(opt, "mounthost"))
878 mounthost = xstrndup(opteq+1,
879 strcspn(opteq+1," \t\n\r,"));
880 else if (!strcmp(opt, "mountprog"))
882 else if (!strcmp(opt, "mountvers"))
884 else if (!strcmp(opt, "nfsprog"))
886 else if (!strcmp(opt, "nfsvers") ||
887 !strcmp(opt, "vers"))
889 else if (!strcmp(opt, "proto")) {
890 if (!strncmp(opteq+1, "tcp", 3))
892 else if (!strncmp(opteq+1, "udp", 3))
895 bb_error_msg("warning: unrecognized proto= option");
896 } else if (!strcmp(opt, "namlen")) {
897 if (nfs_mount_version >= 2)
900 bb_error_msg("warning: option namlen is not supported\n");
901 } else if (!strcmp(opt, "addr"))
904 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
910 if (!strncmp(opt, "no", 2)) {
914 if (!strcmp(opt, "bg"))
916 else if (!strcmp(opt, "fg"))
918 else if (!strcmp(opt, "soft"))
920 else if (!strcmp(opt, "hard"))
922 else if (!strcmp(opt, "intr"))
924 else if (!strcmp(opt, "posix"))
926 else if (!strcmp(opt, "cto"))
928 else if (!strcmp(opt, "ac"))
930 else if (!strcmp(opt, "tcp"))
932 else if (!strcmp(opt, "udp"))
934 else if (!strcmp(opt, "lock")) {
935 if (nfs_mount_version >= 3)
938 bb_error_msg("warning: option nolock is not supported");
940 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
945 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
947 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
948 | (intr ? NFS_MOUNT_INTR : 0)
949 | (posix ? NFS_MOUNT_POSIX : 0)
950 | (nocto ? NFS_MOUNT_NOCTO : 0)
951 | (noac ? NFS_MOUNT_NOAC : 0);
952 if (nfs_mount_version >= 2)
953 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
954 if (nfs_mount_version >= 3)
955 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
956 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
957 bb_error_msg("NFSv%d not supported", nfsvers);
960 if (nfsvers && !mountvers)
961 mountvers = (nfsvers < 3) ? 1 : nfsvers;
962 if (nfsvers && nfsvers < mountvers) {
966 /* Adjust options if none specified */
968 data.timeo = tcp ? 70 : 7;
970 data.version = nfs_mount_version;
972 if (vfsflags & MS_REMOUNT)
976 * If the previous mount operation on the same host was
977 * backgrounded, and the "bg" for this mount is also set,
978 * give up immediately, to avoid the initial timeout.
980 if (bg && we_saw_this_host_before(hostname)) {
981 daemonized = daemonize(); /* parent or error */
982 if (daemonized <= 0) { /* parent or error */
983 retval = -daemonized;
988 /* create mount daemon client */
989 /* See if the nfs host = mount host. */
991 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
992 mount_server_addr.sin_family = AF_INET;
993 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
995 hp = gethostbyname(mounthost);
997 bb_herror_msg("%s", mounthost);
1000 if (hp->h_length > sizeof(struct in_addr)) {
1001 bb_error_msg("got bad hp->h_length?");
1002 hp->h_length = sizeof(struct in_addr);
1004 mount_server_addr.sin_family = AF_INET;
1005 memcpy(&mount_server_addr.sin_addr,
1006 hp->h_addr, hp->h_length);
1012 * The following loop implements the mount retries. When the mount
1013 * times out, and the "bg" option is set, we background ourself
1014 * and continue trying.
1016 * The case where the mount point is not present and the "bg"
1017 * option is set, is treated as a timeout. This is done to
1018 * support nested mounts.
1020 * The "retry" count specified by the user is the number of
1021 * minutes to retry before giving up.
1024 struct timeval total_timeout;
1025 struct timeval retry_timeout;
1026 struct pmap* pm_mnt;
1031 retry_timeout.tv_sec = 3;
1032 retry_timeout.tv_usec = 0;
1033 total_timeout.tv_sec = 20;
1034 total_timeout.tv_usec = 0;
1035 timeout = time(NULL) + 60 * retry;
1039 /* be careful not to use too many CPU cycles */
1043 pm_mnt = get_mountport(&mount_server_addr,
1048 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1050 /* contact the mount daemon via TCP */
1051 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1052 msock = RPC_ANYSOCK;
1054 switch (pm_mnt->pm_prot) {
1056 mclient = clntudp_create(&mount_server_addr,
1063 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1064 msock = RPC_ANYSOCK;
1066 mclient = clnttcp_create(&mount_server_addr,
1075 if (!daemonized && prevt == 0)
1076 error_msg_rpc(clnt_spcreateerror(" "));
1078 enum clnt_stat clnt_stat;
1079 /* try to mount hostname:pathname */
1080 mclient->cl_auth = authunix_create_default();
1082 /* make pointers in xdr_mountres3 NULL so
1083 * that xdr_array allocates memory for us
1085 memset(&status, 0, sizeof(status));
1087 if (pm_mnt->pm_vers == 3)
1088 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1089 (xdrproc_t) xdr_dirpath,
1090 (caddr_t) &pathname,
1091 (xdrproc_t) xdr_mountres3,
1095 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1096 (xdrproc_t) xdr_dirpath,
1097 (caddr_t) &pathname,
1098 (xdrproc_t) xdr_fhstatus,
1102 if (clnt_stat == RPC_SUCCESS)
1103 goto prepare_kernel_data; /* we're done */
1104 if (errno != ECONNREFUSED) {
1105 error_msg_rpc(clnt_sperror(mclient, " "));
1106 goto fail; /* don't retry */
1108 /* Connection refused */
1109 if (!daemonized && prevt == 0) /* print just once */
1110 error_msg_rpc(clnt_sperror(mclient, " "));
1111 auth_destroy(mclient->cl_auth);
1112 clnt_destroy(mclient);
1117 /* Timeout. We are going to retry... maybe */
1122 daemonized = daemonize();
1123 if (daemonized <= 0) { /* parent or error */
1124 retval = -daemonized;
1131 /* TODO error message */
1137 prepare_kernel_data:
1140 if (status.nfsv2.fhs_status != 0) {
1141 bb_error_msg("%s:%s failed, reason given by server: %s",
1143 nfs_strerror(status.nfsv2.fhs_status));
1146 memcpy(data.root.data,
1147 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1149 data.root.size = NFS_FHSIZE;
1150 memcpy(data.old_root.data,
1151 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1154 fhandle3 *my_fhandle;
1155 if (status.nfsv3.fhs_status != 0) {
1156 bb_error_msg("%s:%s failed, reason given by server: %s",
1158 nfs_strerror(status.nfsv3.fhs_status));
1161 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1162 memset(data.old_root.data, 0, NFS_FHSIZE);
1163 memset(&data.root, 0, sizeof(data.root));
1164 data.root.size = my_fhandle->fhandle3_len;
1165 memcpy(data.root.data,
1166 (char *) my_fhandle->fhandle3_val,
1167 my_fhandle->fhandle3_len);
1169 data.flags |= NFS_MOUNT_VER3;
1172 /* create nfs socket for kernel */
1175 if (nfs_mount_version < 3) {
1176 bb_error_msg("NFS over TCP is not supported");
1179 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1181 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1183 bb_perror_msg("nfs socket");
1186 if (bindresvport(fsock, 0) < 0) {
1187 bb_perror_msg("nfs bindresvport");
1191 server_addr.sin_port = PMAPPORT;
1192 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1193 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1197 server_addr.sin_port = htons(port);
1199 /* prepare data structure for kernel */
1202 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1203 strncpy(data.hostname, hostname, sizeof(data.hostname));
1207 auth_destroy(mclient->cl_auth);
1208 clnt_destroy(mclient);
1212 /* We must wait until mount directory is available */
1213 struct stat statbuf;
1215 while (stat(mp->mnt_dir, &statbuf) == -1) {
1217 daemonized = daemonize();
1218 if (daemonized <= 0) { /* parent or error */
1219 retval = -daemonized;
1223 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1230 do_mount: /* perform actual mount */
1232 mp->mnt_type = "nfs";
1233 retval = mount_it_now(mp, vfsflags, (char*)&data);
1240 auth_destroy(mclient->cl_auth);
1241 clnt_destroy(mclient);
1255 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1257 /* Never called. Call should be optimized out. */
1258 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1260 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1262 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1263 // type detection. Returns 0 for success, nonzero for failure.
1264 // NB: mp->xxx fields may be trashed on exit
1265 static int singlemount(struct mntent *mp, int ignore_busy)
1267 int rc = -1, vfsflags;
1268 char *loopFile = 0, *filteropts = 0;
1272 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1274 // Treat fstype "auto" as unspecified.
1276 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1278 // Might this be an CIFS filesystem?
1280 if (ENABLE_FEATURE_MOUNT_CIFS &&
1281 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1282 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1288 // Replace '/' with '\' and verify that unc points to "//server/share".
1290 for (s = mp->mnt_fsname; *s; ++s)
1291 if (*s == '/') *s = '\\';
1295 s = strrchr(mp->mnt_fsname, '\\');
1296 if (s == mp->mnt_fsname+1) goto report_error;
1298 he = gethostbyname(mp->mnt_fsname+2);
1300 if (!he) goto report_error;
1302 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1304 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1305 he->h_addr[2], he->h_addr[3]);
1306 parse_mount_options(ip, &filteropts);
1308 // compose new unc '\\server-ip\share'
1310 mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3,
1311 strchr(mp->mnt_fsname+2,'\\'));
1314 vfsflags |= MS_MANDLOCK;
1316 mp->mnt_type = "cifs";
1317 rc = mount_it_now(mp, vfsflags, filteropts);
1318 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1322 // Might this be an NFS filesystem?
1324 if (ENABLE_FEATURE_MOUNT_NFS &&
1325 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1326 strchr(mp->mnt_fsname, ':') != NULL)
1328 rc = nfsmount(mp, vfsflags, filteropts);
1332 // Look at the file. (Not found isn't a failure for remount, or for
1333 // a synthetic filesystem like proc or sysfs.)
1335 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1337 // Do we need to allocate a loopback device for it?
1339 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1340 loopFile = bb_simplify_path(mp->mnt_fsname);
1342 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1347 bb_error_msg( errno == EPERM || errno == EACCES
1348 ? bb_msg_perm_denied_are_you_root
1349 : "cannot setup loop device");
1353 // Autodetect bind mounts
1355 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1356 vfsflags |= MS_BIND;
1359 /* If we know the fstype (or don't need to), jump straight
1360 * to the actual mount. */
1362 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1363 rc = mount_it_now(mp, vfsflags, filteropts);
1365 // Loop through filesystem types until mount succeeds or we run out
1369 /* Initialize list of block backed filesystems. This has to be
1370 * done here so that during "mount -a", mounts after /proc shows up
1371 * can autodetect. */
1374 fslist = get_block_backed_filesystems();
1375 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1376 atexit(delete_block_backed_filesystems);
1379 for (fl = fslist; fl; fl = fl->link) {
1380 mp->mnt_type = fl->data;
1381 rc = mount_it_now(mp, vfsflags, filteropts);
1386 // If mount failed, clean up loop file (if any).
1388 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1389 del_loop(mp->mnt_fsname);
1390 if (ENABLE_FEATURE_CLEAN_UP) {
1392 free(mp->mnt_fsname);
1397 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1399 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1401 /* perror here sometimes says "mounting ... on ... failed: Success" */
1402 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1407 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1408 // each directory to be mounted.
1410 int mount_main(int argc, char **argv)
1412 enum { OPT_ALL = 0x8 };
1414 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1415 char *opt_o, *fstabname;
1419 struct mntent mtpair[2], *mtcur = mtpair;
1421 /* parse long options, like --bind and --move. Note that -o option
1422 * and --option are synonymous. Yes, this means --remount,rw works. */
1424 for (i = j = 0; i < argc; i++) {
1425 if (argv[i][0] == '-' && argv[i][1] == '-') {
1426 append_mount_options(&cmdopts,argv[i]+2);
1427 } else argv[j++] = argv[i];
1431 // Parse remaining options
1433 opt = bb_getopt_ulflags(argc, argv, "o:t:rwavnf", &opt_o, &fstype);
1435 append_mount_options(&cmdopts, opt_o);
1436 //if (opt & 1) // -t
1438 append_mount_options(&cmdopts, "ro");
1440 append_mount_options(&cmdopts, "rw");
1441 //if (opt & 8) // -a
1442 if (opt & 0x10) // -n
1443 USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE);
1444 if (opt & 0x20) // -f
1445 USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE);
1446 //if (opt & 0x40) // ignore -v
1450 // Three or more non-option arguments? Die with a usage message.
1452 if (argc > 2) bb_show_usage();
1454 // If we have no arguments, show currently mounted filesystems
1457 if (!(opt & OPT_ALL)) {
1458 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1460 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1462 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1463 sizeof(bb_common_bufsiz1)))
1465 // Don't show rootfs.
1466 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1468 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1469 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1470 mtpair->mnt_dir, mtpair->mnt_type,
1473 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1474 return EXIT_SUCCESS;
1476 } else storage_path = bb_simplify_path(argv[0]);
1478 // When we have two arguments, the second is the directory and we can
1479 // skip looking at fstab entirely. We can always abspath() the directory
1480 // argument when we get it.
1483 mtpair->mnt_fsname = argv[0];
1484 mtpair->mnt_dir = argv[1];
1485 mtpair->mnt_type = fstype;
1486 mtpair->mnt_opts = cmdopts;
1487 rc = singlemount(mtpair, 0);
1491 // If we have a shared subtree flag, don't worry about fstab or mtab.
1492 i = parse_mount_options(cmdopts,0);
1493 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1494 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1496 rc = mount("", argv[0], "", i, "");
1497 if (rc) bb_perror_msg_and_die("%s", argv[0]);
1501 // Open either fstab or mtab
1503 if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
1504 fstabname = bb_path_mtab_file;
1505 else fstabname="/etc/fstab";
1507 fstab = setmntent(fstabname,"r");
1509 bb_perror_msg_and_die("cannot read %s",fstabname);
1511 // Loop through entries until we find what we're looking for.
1513 memset(mtpair,0,sizeof(mtpair));
1515 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1517 // Get next fstab entry
1519 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1520 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1521 sizeof(bb_common_bufsiz1)/2))
1523 // Were we looking for something specific?
1527 // If we didn't find anything, complain.
1529 if (!mtnext->mnt_fsname)
1530 bb_error_msg_and_die("can't find %s in %s",
1531 argv[0], fstabname);
1533 // Mount the last thing we found.
1536 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1537 append_mount_options(&(mtcur->mnt_opts),cmdopts);
1538 rc = singlemount(mtcur, 0);
1539 free(mtcur->mnt_opts);
1544 /* If we're trying to mount something specific and this isn't it,
1545 * skip it. Note we must match both the exact text in fstab (ala
1546 * "proc") or a full path from root */
1550 // Is this what we're looking for?
1552 if (strcmp(argv[0],mtcur->mnt_fsname) &&
1553 strcmp(storage_path,mtcur->mnt_fsname) &&
1554 strcmp(argv[0],mtcur->mnt_dir) &&
1555 strcmp(storage_path,mtcur->mnt_dir)) continue;
1557 // Remember this entry. Something later may have overmounted
1558 // it, and we want the _last_ match.
1562 // If we're mounting all.
1566 // Do we need to match a filesystem type?
1567 if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
1569 // Skip noauto and swap anyway.
1571 if (parse_mount_options(mtcur->mnt_opts,0)
1572 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1574 // Mount this thing.
1576 if (singlemount(mtcur, 1)) {
1577 /* Count number of failed mounts */
1582 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1586 if (ENABLE_FEATURE_CLEAN_UP) {