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 /* do not insert options which are already there */
108 int len = strlen(newopts);
109 p = strchr(newopts, ',');
110 if (p) len = p - newopts;
113 if (!strncmp(p,newopts,len) && (p[len]==',' || p[len]==0))
119 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
124 while (newopts[0] == ',') newopts++;
127 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
128 *oldopts = xstrdup(newopts);
132 /* Use the mount_options list to parse options into flags.
133 * Also return list of unrecognized options if unrecognized!=NULL */
134 static int parse_mount_options(char *options, char **unrecognized)
136 int flags = MS_SILENT;
138 // Loop through options
141 char *comma = strchr(options, ',');
143 if (comma) *comma = 0;
145 // Find this option in mount_options
146 for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
147 if (!strcasecmp(mount_options[i].name, options)) {
148 long fl = mount_options[i].flags;
149 if (fl < 0) flags &= fl;
154 // If unrecognized not NULL, append unrecognized mount options */
156 && i == (sizeof(mount_options) / sizeof(*mount_options)))
158 // Add it to strflags, to pass on to kernel
159 i = *unrecognized ? strlen(*unrecognized) : 0;
160 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
162 // Comma separated if it's not the first one
163 if (i) (*unrecognized)[i++] = ',';
164 strcpy((*unrecognized)+i, options);
167 // Advance to next option, or finish
177 // Return a list of all block device backed filesystems
179 static llist_t *get_block_backed_filesystems(void)
182 *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
187 for (i = 0; filesystems[i]; i++) {
188 f = fopen(filesystems[i], "r");
191 for (fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
194 if (!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
196 while (isspace(*fs)) fs++;
197 if (*fs=='#' || *fs=='*') continue;
200 llist_add_to_end(&list,xstrdup(fs));
202 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
210 #if ENABLE_FEATURE_CLEAN_UP
211 static void delete_block_backed_filesystems(void)
213 llist_free(fslist, free);
216 void delete_block_backed_filesystems(void);
219 #if ENABLE_FEATURE_MTAB_SUPPORT
220 static int useMtab = 1;
229 // Perform actual mount of specific filesystem at specific location.
230 // NB: mp->xxx fields may be trashed on exit
231 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
235 if (fakeIt) return 0;
237 // Mount, with fallback to read-only if necessary.
240 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
241 vfsflags, filteropts);
242 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
244 bb_error_msg("%s is write-protected, mounting read-only",
246 vfsflags |= MS_RDONLY;
249 // Abort entirely if permission denied.
251 if (rc && errno == EPERM)
252 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
254 /* If the mount was successful, and we're maintaining an old-style
255 * mtab file by hand, add the new entry to it now. */
257 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
259 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
263 bb_error_msg("no %s",bb_path_mtab_file);
265 // Add vfs string flags
267 for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
268 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
269 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
271 // Remove trailing / (if any) from directory we mounted on
273 i = strlen(mp->mnt_dir) - 1;
274 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
276 // Convert to canonical pathnames as needed
278 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
280 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
281 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
282 mp->mnt_type = "bind";
284 mp->mnt_freq = mp->mnt_passno = 0;
288 addmntent(mountTable, mp);
289 endmntent(mountTable);
290 if (ENABLE_FEATURE_CLEAN_UP) {
299 #if ENABLE_FEATURE_MOUNT_NFS
303 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
305 * Licensed under GPLv2, see file LICENSE in this tarball for details.
307 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
308 * numbers to be specified on the command line.
310 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
311 * Omit the call to connect() for Linux version 1.3.11 or later.
313 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
314 * Implemented the "bg", "fg" and "retry" mount options for NFS.
316 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
317 * - added Native Language Support
319 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
323 /* This is just a warning of a common mistake. Possibly this should be a
324 * uclibc faq entry rather than in busybox... */
325 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
326 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
329 #define MOUNTPORT 635
330 #define MNTPATHLEN 1024
331 #define MNTNAMLEN 255
335 typedef char fhandle[FHSIZE];
338 unsigned int fhandle3_len;
350 MNT3ERR_NAMETOOLONG = 63,
351 MNT3ERR_NOTSUPP = 10004,
352 MNT3ERR_SERVERFAULT = 10006,
354 typedef enum mountstat3 mountstat3;
357 unsigned int fhs_status;
362 typedef struct fhstatus fhstatus;
364 struct mountres3_ok {
367 unsigned int auth_flavours_len;
368 char *auth_flavours_val;
371 typedef struct mountres3_ok mountres3_ok;
374 mountstat3 fhs_status;
376 mountres3_ok mountinfo;
379 typedef struct mountres3 mountres3;
381 typedef char *dirpath;
385 typedef struct mountbody *mountlist;
389 dirpath ml_directory;
392 typedef struct mountbody mountbody;
394 typedef struct groupnode *groups;
400 typedef struct groupnode groupnode;
402 typedef struct exportnode *exports;
409 typedef struct exportnode exportnode;
422 typedef struct ppathcnf ppathcnf;
424 #define MOUNTPROG 100005
427 #define MOUNTPROC_NULL 0
428 #define MOUNTPROC_MNT 1
429 #define MOUNTPROC_DUMP 2
430 #define MOUNTPROC_UMNT 3
431 #define MOUNTPROC_UMNTALL 4
432 #define MOUNTPROC_EXPORT 5
433 #define MOUNTPROC_EXPORTALL 6
435 #define MOUNTVERS_POSIX 2
437 #define MOUNTPROC_PATHCONF 7
441 #define MOUNTPROC3_NULL 0
442 #define MOUNTPROC3_MNT 1
443 #define MOUNTPROC3_DUMP 2
444 #define MOUNTPROC3_UMNT 3
445 #define MOUNTPROC3_UMNTALL 4
446 #define MOUNTPROC3_EXPORT 5
458 * We want to be able to compile mount on old kernels in such a way
459 * that the binary will work well on more recent kernels.
460 * Thus, if necessary we teach nfsmount.c the structure of new fields
461 * that will come later.
463 * Moreover, the new kernel includes conflict with glibc includes
464 * so it is easiest to ignore the kernel altogether (at compile time).
472 unsigned char data[64];
475 struct nfs_mount_data {
478 struct nfs2_fh old_root; /* 1 */
484 int acregmin; /* 1 */
485 int acregmax; /* 1 */
486 int acdirmin; /* 1 */
487 int acdirmax; /* 1 */
488 struct sockaddr_in addr; /* 1 */
489 char hostname[256]; /* 1 */
491 unsigned int bsize; /* 3 */
492 struct nfs3_fh root; /* 4 */
495 /* bits in the flags field */
497 NFS_MOUNT_SOFT = 0x0001, /* 1 */
498 NFS_MOUNT_INTR = 0x0002, /* 1 */
499 NFS_MOUNT_SECURE = 0x0004, /* 1 */
500 NFS_MOUNT_POSIX = 0x0008, /* 1 */
501 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
502 NFS_MOUNT_NOAC = 0x0020, /* 1 */
503 NFS_MOUNT_TCP = 0x0040, /* 2 */
504 NFS_MOUNT_VER3 = 0x0080, /* 3 */
505 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
506 NFS_MOUNT_NONLM = 0x0200 /* 3 */
511 * We need to translate between nfs status return values and
512 * the local errno values which may not be the same.
514 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
515 * "after #include <errno.h> the symbol errno is reserved for any use,
516 * it cannot even be used as a struct tag or field name".
520 #define EDQUOT ENOSPC
523 // Convert each NFSERR_BLAH into EBLAH
525 static const struct {
529 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
530 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
531 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
532 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
535 static char *nfs_strerror(int status)
538 static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
540 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
541 if (nfs_errtbl[i].stat == status)
542 return strerror(nfs_errtbl[i].errnum);
544 sprintf(buf, "unknown nfs status return value: %d", status);
548 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
550 if (!xdr_opaque(xdrs, objp, FHSIZE))
555 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
557 if (!xdr_u_int(xdrs, &objp->fhs_status))
559 switch (objp->fhs_status) {
561 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
570 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
572 if (!xdr_string(xdrs, objp, MNTPATHLEN))
577 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
579 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
584 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
586 if (!xdr_fhandle3(xdrs, &objp->fhandle))
588 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
589 sizeof (int), (xdrproc_t) xdr_int))
594 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
596 if (!xdr_enum(xdrs, (enum_t *) objp))
601 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
603 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
605 switch (objp->fhs_status) {
607 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
616 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
619 * nfs_mount_version according to the sources seen at compile time.
621 static int nfs_mount_version;
622 static int kernel_version;
625 * Unfortunately, the kernel prints annoying console messages
626 * in case of an unexpected nfs mount version (instead of
627 * just returning some error). Therefore we'll have to try
628 * and figure out what version the kernel expects.
631 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
632 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
633 * nfs_mount_version: version this source and running kernel can handle
636 find_kernel_nfs_mount_version(void)
641 nfs_mount_version = 4; /* default */
643 kernel_version = get_linux_version_code();
644 if (kernel_version) {
645 if (kernel_version < KERNEL_VERSION(2,1,32))
646 nfs_mount_version = 1;
647 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
648 (kernel_version >= KERNEL_VERSION(2,3,0) &&
649 kernel_version < KERNEL_VERSION(2,3,99)))
650 nfs_mount_version = 3;
651 /* else v4 since 2.3.99pre4 */
656 get_mountport(struct sockaddr_in *server_addr,
658 long unsigned version,
662 struct pmaplist *pmap;
663 static struct pmap p = {0, 0, 0, 0};
665 server_addr->sin_port = PMAPPORT;
666 pmap = pmap_getmaps(server_addr);
668 if (version > MAX_NFSPROT)
669 version = MAX_NFSPROT;
678 if (pmap->pml_map.pm_prog != prog)
680 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
682 if (version > 2 && pmap->pml_map.pm_vers != version)
684 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
686 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
687 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
688 (port && pmap->pml_map.pm_port != port))
690 memcpy(&p, &pmap->pml_map, sizeof(p));
692 pmap = pmap->pml_next;
695 p.pm_vers = MOUNTVERS;
697 p.pm_port = MOUNTPORT;
699 p.pm_prot = IPPROTO_TCP;
703 static int daemonize(void)
707 if (pid < 0) /* error */
709 if (pid > 0) /* parent */
712 fd = xopen(bb_dev_null, O_RDWR);
716 if (fd > 2) close(fd);
718 openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
719 logmode = LOGMODE_SYSLOG;
724 static inline int we_saw_this_host_before(const char *hostname)
729 /* RPC strerror analogs are terminally idiotic:
730 * *mandatory* prefix and \n at end.
731 * This hopefully helps. Usage:
732 * error_msg_rpc(clnt_*error*(" ")) */
733 static void error_msg_rpc(const char *msg)
736 while (msg[0] == ' ' || msg[0] == ':') msg++;
738 while (len && msg[len-1] == '\n') len--;
739 bb_error_msg("%.*s", len, msg);
742 // NB: mp->xxx fields may be trashed on exit
743 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
749 struct nfs_mount_data data;
752 struct sockaddr_in server_addr;
753 struct sockaddr_in mount_server_addr;
756 struct fhstatus nfsv2;
757 struct mountres3 nfsv3;
779 find_kernel_nfs_mount_version();
787 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
789 filteropts = xstrdup(filteropts); /* going to trash it later... */
791 hostname = xstrdup(mp->mnt_fsname);
792 /* mount_main() guarantees that ':' is there */
793 s = strchr(hostname, ':');
796 /* Ignore all but first hostname in replicated mounts
797 until they can be fully supported. (mack@sgi.com) */
798 s = strchr(hostname, ',');
801 bb_error_msg("warning: multiple hostnames not supported");
804 server_addr.sin_family = AF_INET;
805 if (!inet_aton(hostname, &server_addr.sin_addr)) {
806 hp = gethostbyname(hostname);
808 bb_herror_msg("%s", hostname);
811 if (hp->h_length > sizeof(struct in_addr)) {
812 bb_error_msg("got bad hp->h_length");
813 hp->h_length = sizeof(struct in_addr);
815 memcpy(&server_addr.sin_addr,
816 hp->h_addr, hp->h_length);
819 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
821 /* add IP address to mtab options for use when unmounting */
823 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
824 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
826 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
827 mp->mnt_opts[0] ? "," : "",
828 inet_ntoa(server_addr.sin_addr));
833 /* Set default options.
834 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
835 * let the kernel decide.
836 * timeo is filled in after we know whether it'll be TCP or UDP. */
837 memset(&data, 0, sizeof(data));
843 data.namlen = NAME_MAX;
852 retry = 10000; /* 10000 minutes ~ 1 week */
855 mountprog = MOUNTPROG;
864 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
865 char *opteq = strchr(opt, '=');
867 int val = atoi(opteq + 1);
869 if (!strcmp(opt, "rsize"))
871 else if (!strcmp(opt, "wsize"))
873 else if (!strcmp(opt, "timeo"))
875 else if (!strcmp(opt, "retrans"))
877 else if (!strcmp(opt, "acregmin"))
879 else if (!strcmp(opt, "acregmax"))
881 else if (!strcmp(opt, "acdirmin"))
883 else if (!strcmp(opt, "acdirmax"))
885 else if (!strcmp(opt, "actimeo")) {
891 else if (!strcmp(opt, "retry"))
893 else if (!strcmp(opt, "port"))
895 else if (!strcmp(opt, "mountport"))
897 else if (!strcmp(opt, "mounthost"))
898 mounthost = xstrndup(opteq+1,
899 strcspn(opteq+1," \t\n\r,"));
900 else if (!strcmp(opt, "mountprog"))
902 else if (!strcmp(opt, "mountvers"))
904 else if (!strcmp(opt, "nfsprog"))
906 else if (!strcmp(opt, "nfsvers") ||
907 !strcmp(opt, "vers"))
909 else if (!strcmp(opt, "proto")) {
910 if (!strncmp(opteq+1, "tcp", 3))
912 else if (!strncmp(opteq+1, "udp", 3))
915 bb_error_msg("warning: unrecognized proto= option");
916 } else if (!strcmp(opt, "namlen")) {
917 if (nfs_mount_version >= 2)
920 bb_error_msg("warning: option namlen is not supported\n");
921 } else if (!strcmp(opt, "addr"))
924 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
930 if (!strncmp(opt, "no", 2)) {
934 if (!strcmp(opt, "bg"))
936 else if (!strcmp(opt, "fg"))
938 else if (!strcmp(opt, "soft"))
940 else if (!strcmp(opt, "hard"))
942 else if (!strcmp(opt, "intr"))
944 else if (!strcmp(opt, "posix"))
946 else if (!strcmp(opt, "cto"))
948 else if (!strcmp(opt, "ac"))
950 else if (!strcmp(opt, "tcp"))
952 else if (!strcmp(opt, "udp"))
954 else if (!strcmp(opt, "lock")) {
955 if (nfs_mount_version >= 3)
958 bb_error_msg("warning: option nolock is not supported");
960 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
965 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
967 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
968 | (intr ? NFS_MOUNT_INTR : 0)
969 | (posix ? NFS_MOUNT_POSIX : 0)
970 | (nocto ? NFS_MOUNT_NOCTO : 0)
971 | (noac ? NFS_MOUNT_NOAC : 0);
972 if (nfs_mount_version >= 2)
973 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
974 if (nfs_mount_version >= 3)
975 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
976 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
977 bb_error_msg("NFSv%d not supported", nfsvers);
980 if (nfsvers && !mountvers)
981 mountvers = (nfsvers < 3) ? 1 : nfsvers;
982 if (nfsvers && nfsvers < mountvers) {
986 /* Adjust options if none specified */
988 data.timeo = tcp ? 70 : 7;
990 data.version = nfs_mount_version;
992 if (vfsflags & MS_REMOUNT)
996 * If the previous mount operation on the same host was
997 * backgrounded, and the "bg" for this mount is also set,
998 * give up immediately, to avoid the initial timeout.
1000 if (bg && we_saw_this_host_before(hostname)) {
1001 daemonized = daemonize(); /* parent or error */
1002 if (daemonized <= 0) { /* parent or error */
1003 retval = -daemonized;
1008 /* create mount daemon client */
1009 /* See if the nfs host = mount host. */
1011 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1012 mount_server_addr.sin_family = AF_INET;
1013 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1015 hp = gethostbyname(mounthost);
1017 bb_herror_msg("%s", mounthost);
1020 if (hp->h_length > sizeof(struct in_addr)) {
1021 bb_error_msg("got bad hp->h_length?");
1022 hp->h_length = sizeof(struct in_addr);
1024 mount_server_addr.sin_family = AF_INET;
1025 memcpy(&mount_server_addr.sin_addr,
1026 hp->h_addr, hp->h_length);
1032 * The following loop implements the mount retries. When the mount
1033 * times out, and the "bg" option is set, we background ourself
1034 * and continue trying.
1036 * The case where the mount point is not present and the "bg"
1037 * option is set, is treated as a timeout. This is done to
1038 * support nested mounts.
1040 * The "retry" count specified by the user is the number of
1041 * minutes to retry before giving up.
1044 struct timeval total_timeout;
1045 struct timeval retry_timeout;
1046 struct pmap* pm_mnt;
1051 retry_timeout.tv_sec = 3;
1052 retry_timeout.tv_usec = 0;
1053 total_timeout.tv_sec = 20;
1054 total_timeout.tv_usec = 0;
1055 timeout = time(NULL) + 60 * retry;
1059 /* be careful not to use too many CPU cycles */
1063 pm_mnt = get_mountport(&mount_server_addr,
1068 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1070 /* contact the mount daemon via TCP */
1071 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1072 msock = RPC_ANYSOCK;
1074 switch (pm_mnt->pm_prot) {
1076 mclient = clntudp_create(&mount_server_addr,
1083 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1084 msock = RPC_ANYSOCK;
1086 mclient = clnttcp_create(&mount_server_addr,
1095 if (!daemonized && prevt == 0)
1096 error_msg_rpc(clnt_spcreateerror(" "));
1098 enum clnt_stat clnt_stat;
1099 /* try to mount hostname:pathname */
1100 mclient->cl_auth = authunix_create_default();
1102 /* make pointers in xdr_mountres3 NULL so
1103 * that xdr_array allocates memory for us
1105 memset(&status, 0, sizeof(status));
1107 if (pm_mnt->pm_vers == 3)
1108 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1109 (xdrproc_t) xdr_dirpath,
1110 (caddr_t) &pathname,
1111 (xdrproc_t) xdr_mountres3,
1115 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1116 (xdrproc_t) xdr_dirpath,
1117 (caddr_t) &pathname,
1118 (xdrproc_t) xdr_fhstatus,
1122 if (clnt_stat == RPC_SUCCESS)
1123 goto prepare_kernel_data; /* we're done */
1124 if (errno != ECONNREFUSED) {
1125 error_msg_rpc(clnt_sperror(mclient, " "));
1126 goto fail; /* don't retry */
1128 /* Connection refused */
1129 if (!daemonized && prevt == 0) /* print just once */
1130 error_msg_rpc(clnt_sperror(mclient, " "));
1131 auth_destroy(mclient->cl_auth);
1132 clnt_destroy(mclient);
1137 /* Timeout. We are going to retry... maybe */
1142 daemonized = daemonize();
1143 if (daemonized <= 0) { /* parent or error */
1144 retval = -daemonized;
1151 /* TODO error message */
1157 prepare_kernel_data:
1160 if (status.nfsv2.fhs_status != 0) {
1161 bb_error_msg("%s:%s failed, reason given by server: %s",
1163 nfs_strerror(status.nfsv2.fhs_status));
1166 memcpy(data.root.data,
1167 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1169 data.root.size = NFS_FHSIZE;
1170 memcpy(data.old_root.data,
1171 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1174 fhandle3 *my_fhandle;
1175 if (status.nfsv3.fhs_status != 0) {
1176 bb_error_msg("%s:%s failed, reason given by server: %s",
1178 nfs_strerror(status.nfsv3.fhs_status));
1181 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1182 memset(data.old_root.data, 0, NFS_FHSIZE);
1183 memset(&data.root, 0, sizeof(data.root));
1184 data.root.size = my_fhandle->fhandle3_len;
1185 memcpy(data.root.data,
1186 (char *) my_fhandle->fhandle3_val,
1187 my_fhandle->fhandle3_len);
1189 data.flags |= NFS_MOUNT_VER3;
1192 /* create nfs socket for kernel */
1195 if (nfs_mount_version < 3) {
1196 bb_error_msg("NFS over TCP is not supported");
1199 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1201 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1203 bb_perror_msg("nfs socket");
1206 if (bindresvport(fsock, 0) < 0) {
1207 bb_perror_msg("nfs bindresvport");
1211 server_addr.sin_port = PMAPPORT;
1212 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1213 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1217 server_addr.sin_port = htons(port);
1219 /* prepare data structure for kernel */
1222 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1223 strncpy(data.hostname, hostname, sizeof(data.hostname));
1227 auth_destroy(mclient->cl_auth);
1228 clnt_destroy(mclient);
1232 /* We must wait until mount directory is available */
1233 struct stat statbuf;
1235 while (stat(mp->mnt_dir, &statbuf) == -1) {
1237 daemonized = daemonize();
1238 if (daemonized <= 0) { /* parent or error */
1239 retval = -daemonized;
1243 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1250 do_mount: /* perform actual mount */
1252 mp->mnt_type = "nfs";
1253 retval = mount_it_now(mp, vfsflags, (char*)&data);
1260 auth_destroy(mclient->cl_auth);
1261 clnt_destroy(mclient);
1275 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1277 /* Never called. Call should be optimized out. */
1278 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1280 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1282 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1283 // type detection. Returns 0 for success, nonzero for failure.
1284 // NB: mp->xxx fields may be trashed on exit
1285 static int singlemount(struct mntent *mp, int ignore_busy)
1287 int rc = -1, vfsflags;
1288 char *loopFile = 0, *filteropts = 0;
1292 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1294 // Treat fstype "auto" as unspecified.
1296 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1298 // Might this be an CIFS filesystem?
1300 if (ENABLE_FEATURE_MOUNT_CIFS &&
1301 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1302 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1308 // Replace '/' with '\' and verify that unc points to "//server/share".
1310 for (s = mp->mnt_fsname; *s; ++s)
1311 if (*s == '/') *s = '\\';
1315 s = strrchr(mp->mnt_fsname, '\\');
1316 if (s == mp->mnt_fsname+1) goto report_error;
1318 he = gethostbyname(mp->mnt_fsname+2);
1320 if (!he) goto report_error;
1322 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1324 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1325 he->h_addr[2], he->h_addr[3]);
1326 parse_mount_options(ip, &filteropts);
1328 // compose new unc '\\server-ip\share'
1330 mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3,
1331 strchr(mp->mnt_fsname+2,'\\'));
1334 vfsflags |= MS_MANDLOCK;
1336 mp->mnt_type = "cifs";
1337 rc = mount_it_now(mp, vfsflags, filteropts);
1338 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1342 // Might this be an NFS filesystem?
1344 if (ENABLE_FEATURE_MOUNT_NFS &&
1345 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1346 strchr(mp->mnt_fsname, ':') != NULL)
1348 rc = nfsmount(mp, vfsflags, filteropts);
1352 // Look at the file. (Not found isn't a failure for remount, or for
1353 // a synthetic filesystem like proc or sysfs.)
1355 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1357 // Do we need to allocate a loopback device for it?
1359 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1360 loopFile = bb_simplify_path(mp->mnt_fsname);
1362 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1367 bb_error_msg( errno == EPERM || errno == EACCES
1368 ? bb_msg_perm_denied_are_you_root
1369 : "cannot setup loop device");
1373 // Autodetect bind mounts
1375 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1376 vfsflags |= MS_BIND;
1379 /* If we know the fstype (or don't need to), jump straight
1380 * to the actual mount. */
1382 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1383 rc = mount_it_now(mp, vfsflags, filteropts);
1385 // Loop through filesystem types until mount succeeds or we run out
1389 /* Initialize list of block backed filesystems. This has to be
1390 * done here so that during "mount -a", mounts after /proc shows up
1391 * can autodetect. */
1394 fslist = get_block_backed_filesystems();
1395 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1396 atexit(delete_block_backed_filesystems);
1399 for (fl = fslist; fl; fl = fl->link) {
1400 mp->mnt_type = fl->data;
1401 rc = mount_it_now(mp, vfsflags, filteropts);
1406 // If mount failed, clean up loop file (if any).
1408 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1409 del_loop(mp->mnt_fsname);
1410 if (ENABLE_FEATURE_CLEAN_UP) {
1412 free(mp->mnt_fsname);
1417 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1419 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1421 /* perror here sometimes says "mounting ... on ... failed: Success" */
1422 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1427 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1428 // each directory to be mounted.
1430 int mount_main(int argc, char **argv)
1432 enum { OPT_ALL = 0x8 };
1434 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1436 const char *fstabname;
1440 struct mntent mtpair[2], *mtcur = mtpair;
1442 /* parse long options, like --bind and --move. Note that -o option
1443 * and --option are synonymous. Yes, this means --remount,rw works. */
1445 for (i = j = 0; i < argc; i++) {
1446 if (argv[i][0] == '-' && argv[i][1] == '-') {
1447 append_mount_options(&cmdopts,argv[i]+2);
1448 } else argv[j++] = argv[i];
1452 // Parse remaining options
1454 opt = bb_getopt_ulflags(argc, argv, "o:t:rwavnf", &opt_o, &fstype);
1456 append_mount_options(&cmdopts, opt_o);
1457 //if (opt & 1) // -t
1459 append_mount_options(&cmdopts, "ro");
1461 append_mount_options(&cmdopts, "rw");
1462 //if (opt & 8) // -a
1463 if (opt & 0x10) // -n
1464 USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE);
1465 if (opt & 0x20) // -f
1466 USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE);
1467 //if (opt & 0x40) // ignore -v
1471 // Three or more non-option arguments? Die with a usage message.
1473 if (argc > 2) bb_show_usage();
1475 // If we have no arguments, show currently mounted filesystems
1478 if (!(opt & OPT_ALL)) {
1479 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1481 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1483 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1484 sizeof(bb_common_bufsiz1)))
1486 // Don't show rootfs.
1487 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1489 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1490 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1491 mtpair->mnt_dir, mtpair->mnt_type,
1494 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1495 return EXIT_SUCCESS;
1497 } else storage_path = bb_simplify_path(argv[0]);
1499 // When we have two arguments, the second is the directory and we can
1500 // skip looking at fstab entirely. We can always abspath() the directory
1501 // argument when we get it.
1504 mtpair->mnt_fsname = argv[0];
1505 mtpair->mnt_dir = argv[1];
1506 mtpair->mnt_type = fstype;
1507 mtpair->mnt_opts = cmdopts;
1508 rc = singlemount(mtpair, 0);
1512 // If we have a shared subtree flag, don't worry about fstab or mtab.
1513 i = parse_mount_options(cmdopts,0);
1514 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1515 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1517 rc = mount("", argv[0], "", i, "");
1518 if (rc) bb_perror_msg_and_die("%s", argv[0]);
1522 // Open either fstab or mtab
1524 if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
1525 fstabname = bb_path_mtab_file;
1526 else fstabname="/etc/fstab";
1528 fstab = setmntent(fstabname,"r");
1530 bb_perror_msg_and_die("cannot read %s", fstabname);
1532 // Loop through entries until we find what we're looking for.
1534 memset(mtpair,0,sizeof(mtpair));
1536 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1538 // Get next fstab entry
1540 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1541 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1542 sizeof(bb_common_bufsiz1)/2))
1544 // Were we looking for something specific?
1548 // If we didn't find anything, complain.
1550 if (!mtnext->mnt_fsname)
1551 bb_error_msg_and_die("can't find %s in %s",
1552 argv[0], fstabname);
1554 // Mount the last thing we found.
1557 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1558 append_mount_options(&(mtcur->mnt_opts),cmdopts);
1559 rc = singlemount(mtcur, 0);
1560 free(mtcur->mnt_opts);
1565 /* If we're trying to mount something specific and this isn't it,
1566 * skip it. Note we must match both the exact text in fstab (ala
1567 * "proc") or a full path from root */
1571 // Is this what we're looking for?
1573 if (strcmp(argv[0],mtcur->mnt_fsname) &&
1574 strcmp(storage_path,mtcur->mnt_fsname) &&
1575 strcmp(argv[0],mtcur->mnt_dir) &&
1576 strcmp(storage_path,mtcur->mnt_dir)) continue;
1578 // Remember this entry. Something later may have overmounted
1579 // it, and we want the _last_ match.
1583 // If we're mounting all.
1587 // Do we need to match a filesystem type?
1588 if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
1590 // Skip noauto and swap anyway.
1592 if (parse_mount_options(mtcur->mnt_opts,0)
1593 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1595 // Mount this thing.
1597 if (singlemount(mtcur, 1)) {
1598 /* Count number of failed mounts */
1603 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1607 if (ENABLE_FEATURE_CLEAN_UP) {