1 /* vi: set sw=4 ts=4: */
3 * Mini mount implementation for busybox
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
12 /* Design notes: There is no spec for mount. Remind me to write one.
14 mount_main() calls singlemount() which calls mount_it_now().
16 mount_main() can loop through /etc/fstab for mount -a
17 singlemount() can loop through /etc/filesystems for fstype detection.
18 mount_it_now() does the actual mount.
24 /* Needed for nfs support only... */
26 #include <sys/utsname.h>
30 #include <rpc/pmap_prot.h>
31 #include <rpc/pmap_clnt.h>
34 // Not real flags, but we want to be able to check for this.
35 #define MOUNT_NOAUTO (1<<29)
36 #define MOUNT_SWAP (1<<30)
38 /* Standard mount options (from -o options or --options), with corresponding
44 } static mount_options[] = {
45 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
47 USE_FEATURE_MOUNT_LOOP(
51 USE_FEATURE_MOUNT_FSTAB(
54 {"noauto",MOUNT_NOAUTO},
58 USE_FEATURE_MOUNT_FLAGS(
60 {"nosuid", MS_NOSUID},
65 {"noexec", MS_NOEXEC},
66 {"sync", MS_SYNCHRONOUS},
67 {"async", ~MS_SYNCHRONOUS},
68 {"atime", ~MS_NOATIME},
69 {"noatime", MS_NOATIME},
70 {"diratime", ~MS_NODIRATIME},
71 {"nodiratime", MS_NODIRATIME},
78 {"shared", MS_SHARED},
80 {"private", MS_PRIVATE},
81 {"unbindable", MS_UNBINDABLE},
82 {"rshared", MS_SHARED|MS_RECURSIVE},
83 {"rslave", MS_SLAVE|MS_RECURSIVE},
84 {"rprivate", MS_SLAVE|MS_RECURSIVE},
85 {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
90 {"ro", MS_RDONLY}, // vfs flag
91 {"rw", ~MS_RDONLY}, // vfs flag
92 {"remount", MS_REMOUNT}, // action flag
95 #define VECTOR_SIZE(v) (sizeof(v) / sizeof((v)[0]))
97 /* Append mount options to string */
98 static void append_mount_options(char **oldopts, char *newopts)
100 if (*oldopts && **oldopts) {
101 /* do not insert options which are already there */
104 int len = strlen(newopts);
105 p = strchr(newopts, ',');
106 if (p) len = p - newopts;
109 if (!strncmp(p,newopts,len) && (p[len]==',' || p[len]==0))
115 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
120 while (newopts[0] == ',') newopts++;
123 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
124 *oldopts = xstrdup(newopts);
128 /* Use the mount_options list to parse options into flags.
129 * Also return list of unrecognized options if unrecognized!=NULL */
130 static int parse_mount_options(char *options, char **unrecognized)
132 int flags = MS_SILENT;
134 // Loop through options
137 char *comma = strchr(options, ',');
139 if (comma) *comma = 0;
141 // Find this option in mount_options
142 for (i = 0; i < VECTOR_SIZE(mount_options); i++) {
143 if (!strcasecmp(mount_options[i].name, options)) {
144 long fl = mount_options[i].flags;
145 if (fl < 0) flags &= fl;
150 // If unrecognized not NULL, append unrecognized mount options */
151 if (unrecognized && i == VECTOR_SIZE(mount_options)) {
152 // Add it to strflags, to pass on to kernel
153 i = *unrecognized ? strlen(*unrecognized) : 0;
154 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
156 // Comma separated if it's not the first one
157 if (i) (*unrecognized)[i++] = ',';
158 strcpy((*unrecognized)+i, options);
161 // Advance to next option, or finish
171 // Return a list of all block device backed filesystems
173 static llist_t *get_block_backed_filesystems(void)
176 *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
181 for (i = 0; filesystems[i]; i++) {
182 f = fopen(filesystems[i], "r");
185 for (fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
188 if (!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
190 while (isspace(*fs)) fs++;
191 if (*fs=='#' || *fs=='*') continue;
194 llist_add_to_end(&list,xstrdup(fs));
196 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
204 #if ENABLE_FEATURE_CLEAN_UP
205 static void delete_block_backed_filesystems(void)
207 llist_free(fslist, free);
210 void delete_block_backed_filesystems(void);
213 #if ENABLE_FEATURE_MTAB_SUPPORT
214 static int useMtab = 1;
221 // Perform actual mount of specific filesystem at specific location.
222 // NB: mp->xxx fields may be trashed on exit
223 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
227 if (fakeIt) goto mtab;
229 // Mount, with fallback to read-only if necessary.
232 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
233 vfsflags, filteropts);
234 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
236 bb_error_msg("%s is write-protected, mounting read-only",
238 vfsflags |= MS_RDONLY;
241 // Abort entirely if permission denied.
243 if (rc && errno == EPERM)
244 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
246 /* If the mount was successful, and we're maintaining an old-style
247 * mtab file by hand, add the new entry to it now. */
249 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
251 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
255 bb_error_msg("no %s",bb_path_mtab_file);
257 // Add vfs string flags
259 for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
260 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
261 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
263 // Remove trailing / (if any) from directory we mounted on
265 i = strlen(mp->mnt_dir) - 1;
266 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
268 // Convert to canonical pathnames as needed
270 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
272 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
273 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
274 mp->mnt_type = "bind";
276 mp->mnt_freq = mp->mnt_passno = 0;
280 addmntent(mountTable, mp);
281 endmntent(mountTable);
282 if (ENABLE_FEATURE_CLEAN_UP) {
291 #if ENABLE_FEATURE_MOUNT_NFS
295 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
297 * Licensed under GPLv2, see file LICENSE in this tarball for details.
299 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
300 * numbers to be specified on the command line.
302 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
303 * Omit the call to connect() for Linux version 1.3.11 or later.
305 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
306 * Implemented the "bg", "fg" and "retry" mount options for NFS.
308 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
309 * - added Native Language Support
311 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
315 /* This is just a warning of a common mistake. Possibly this should be a
316 * uclibc faq entry rather than in busybox... */
317 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
318 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
321 #define MOUNTPORT 635
322 #define MNTPATHLEN 1024
323 #define MNTNAMLEN 255
327 typedef char fhandle[FHSIZE];
330 unsigned int fhandle3_len;
342 MNT3ERR_NAMETOOLONG = 63,
343 MNT3ERR_NOTSUPP = 10004,
344 MNT3ERR_SERVERFAULT = 10006,
346 typedef enum mountstat3 mountstat3;
349 unsigned int fhs_status;
354 typedef struct fhstatus fhstatus;
356 struct mountres3_ok {
359 unsigned int auth_flavours_len;
360 char *auth_flavours_val;
363 typedef struct mountres3_ok mountres3_ok;
366 mountstat3 fhs_status;
368 mountres3_ok mountinfo;
371 typedef struct mountres3 mountres3;
373 typedef char *dirpath;
377 typedef struct mountbody *mountlist;
381 dirpath ml_directory;
384 typedef struct mountbody mountbody;
386 typedef struct groupnode *groups;
392 typedef struct groupnode groupnode;
394 typedef struct exportnode *exports;
401 typedef struct exportnode exportnode;
414 typedef struct ppathcnf ppathcnf;
416 #define MOUNTPROG 100005
419 #define MOUNTPROC_NULL 0
420 #define MOUNTPROC_MNT 1
421 #define MOUNTPROC_DUMP 2
422 #define MOUNTPROC_UMNT 3
423 #define MOUNTPROC_UMNTALL 4
424 #define MOUNTPROC_EXPORT 5
425 #define MOUNTPROC_EXPORTALL 6
427 #define MOUNTVERS_POSIX 2
429 #define MOUNTPROC_PATHCONF 7
433 #define MOUNTPROC3_NULL 0
434 #define MOUNTPROC3_MNT 1
435 #define MOUNTPROC3_DUMP 2
436 #define MOUNTPROC3_UMNT 3
437 #define MOUNTPROC3_UMNTALL 4
438 #define MOUNTPROC3_EXPORT 5
450 * We want to be able to compile mount on old kernels in such a way
451 * that the binary will work well on more recent kernels.
452 * Thus, if necessary we teach nfsmount.c the structure of new fields
453 * that will come later.
455 * Moreover, the new kernel includes conflict with glibc includes
456 * so it is easiest to ignore the kernel altogether (at compile time).
464 unsigned char data[64];
467 struct nfs_mount_data {
470 struct nfs2_fh old_root; /* 1 */
476 int acregmin; /* 1 */
477 int acregmax; /* 1 */
478 int acdirmin; /* 1 */
479 int acdirmax; /* 1 */
480 struct sockaddr_in addr; /* 1 */
481 char hostname[256]; /* 1 */
483 unsigned int bsize; /* 3 */
484 struct nfs3_fh root; /* 4 */
487 /* bits in the flags field */
489 NFS_MOUNT_SOFT = 0x0001, /* 1 */
490 NFS_MOUNT_INTR = 0x0002, /* 1 */
491 NFS_MOUNT_SECURE = 0x0004, /* 1 */
492 NFS_MOUNT_POSIX = 0x0008, /* 1 */
493 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
494 NFS_MOUNT_NOAC = 0x0020, /* 1 */
495 NFS_MOUNT_TCP = 0x0040, /* 2 */
496 NFS_MOUNT_VER3 = 0x0080, /* 3 */
497 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
498 NFS_MOUNT_NONLM = 0x0200 /* 3 */
503 * We need to translate between nfs status return values and
504 * the local errno values which may not be the same.
506 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
507 * "after #include <errno.h> the symbol errno is reserved for any use,
508 * it cannot even be used as a struct tag or field name".
512 #define EDQUOT ENOSPC
515 // Convert each NFSERR_BLAH into EBLAH
517 static const struct {
521 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
522 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
523 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
524 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
527 static char *nfs_strerror(int status)
530 static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
532 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
533 if (nfs_errtbl[i].stat == status)
534 return strerror(nfs_errtbl[i].errnum);
536 sprintf(buf, "unknown nfs status return value: %d", status);
540 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
542 if (!xdr_opaque(xdrs, objp, FHSIZE))
547 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
549 if (!xdr_u_int(xdrs, &objp->fhs_status))
551 switch (objp->fhs_status) {
553 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
562 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
564 if (!xdr_string(xdrs, objp, MNTPATHLEN))
569 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
571 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
576 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
578 if (!xdr_fhandle3(xdrs, &objp->fhandle))
580 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
581 sizeof (int), (xdrproc_t) xdr_int))
586 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
588 if (!xdr_enum(xdrs, (enum_t *) objp))
593 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
595 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
597 switch (objp->fhs_status) {
599 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
608 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
611 * nfs_mount_version according to the sources seen at compile time.
613 static int nfs_mount_version;
614 static int kernel_version;
617 * Unfortunately, the kernel prints annoying console messages
618 * in case of an unexpected nfs mount version (instead of
619 * just returning some error). Therefore we'll have to try
620 * and figure out what version the kernel expects.
623 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
624 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
625 * nfs_mount_version: version this source and running kernel can handle
628 find_kernel_nfs_mount_version(void)
633 nfs_mount_version = 4; /* default */
635 kernel_version = get_linux_version_code();
636 if (kernel_version) {
637 if (kernel_version < KERNEL_VERSION(2,1,32))
638 nfs_mount_version = 1;
639 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
640 (kernel_version >= KERNEL_VERSION(2,3,0) &&
641 kernel_version < KERNEL_VERSION(2,3,99)))
642 nfs_mount_version = 3;
643 /* else v4 since 2.3.99pre4 */
648 get_mountport(struct sockaddr_in *server_addr,
650 long unsigned version,
654 struct pmaplist *pmap;
655 static struct pmap p = {0, 0, 0, 0};
657 server_addr->sin_port = PMAPPORT;
658 pmap = pmap_getmaps(server_addr);
660 if (version > MAX_NFSPROT)
661 version = MAX_NFSPROT;
670 if (pmap->pml_map.pm_prog != prog)
672 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
674 if (version > 2 && pmap->pml_map.pm_vers != version)
676 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
678 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
679 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
680 (port && pmap->pml_map.pm_port != port))
682 memcpy(&p, &pmap->pml_map, sizeof(p));
684 pmap = pmap->pml_next;
687 p.pm_vers = MOUNTVERS;
689 p.pm_port = MOUNTPORT;
691 p.pm_prot = IPPROTO_TCP;
695 static int daemonize(void)
699 if (pid < 0) /* error */
701 if (pid > 0) /* parent */
704 fd = xopen(bb_dev_null, O_RDWR);
708 if (fd > 2) close(fd);
710 openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
711 logmode = LOGMODE_SYSLOG;
716 static inline int we_saw_this_host_before(const char *hostname)
721 /* RPC strerror analogs are terminally idiotic:
722 * *mandatory* prefix and \n at end.
723 * This hopefully helps. Usage:
724 * error_msg_rpc(clnt_*error*(" ")) */
725 static void error_msg_rpc(const char *msg)
728 while (msg[0] == ' ' || msg[0] == ':') msg++;
730 while (len && msg[len-1] == '\n') len--;
731 bb_error_msg("%.*s", len, msg);
734 // NB: mp->xxx fields may be trashed on exit
735 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
741 struct nfs_mount_data data;
744 struct sockaddr_in server_addr;
745 struct sockaddr_in mount_server_addr;
748 struct fhstatus nfsv2;
749 struct mountres3 nfsv3;
771 find_kernel_nfs_mount_version();
779 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
781 filteropts = xstrdup(filteropts); /* going to trash it later... */
783 hostname = xstrdup(mp->mnt_fsname);
784 /* mount_main() guarantees that ':' is there */
785 s = strchr(hostname, ':');
788 /* Ignore all but first hostname in replicated mounts
789 until they can be fully supported. (mack@sgi.com) */
790 s = strchr(hostname, ',');
793 bb_error_msg("warning: multiple hostnames not supported");
796 server_addr.sin_family = AF_INET;
797 if (!inet_aton(hostname, &server_addr.sin_addr)) {
798 hp = gethostbyname(hostname);
800 bb_herror_msg("%s", hostname);
803 if (hp->h_length > sizeof(struct in_addr)) {
804 bb_error_msg("got bad hp->h_length");
805 hp->h_length = sizeof(struct in_addr);
807 memcpy(&server_addr.sin_addr,
808 hp->h_addr, hp->h_length);
811 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
813 /* add IP address to mtab options for use when unmounting */
815 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
816 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
818 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
819 mp->mnt_opts[0] ? "," : "",
820 inet_ntoa(server_addr.sin_addr));
825 /* Set default options.
826 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
827 * let the kernel decide.
828 * timeo is filled in after we know whether it'll be TCP or UDP. */
829 memset(&data, 0, sizeof(data));
835 data.namlen = NAME_MAX;
844 retry = 10000; /* 10000 minutes ~ 1 week */
847 mountprog = MOUNTPROG;
856 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
857 char *opteq = strchr(opt, '=');
859 int val = atoi(opteq + 1);
861 if (!strcmp(opt, "rsize"))
863 else if (!strcmp(opt, "wsize"))
865 else if (!strcmp(opt, "timeo"))
867 else if (!strcmp(opt, "retrans"))
869 else if (!strcmp(opt, "acregmin"))
871 else if (!strcmp(opt, "acregmax"))
873 else if (!strcmp(opt, "acdirmin"))
875 else if (!strcmp(opt, "acdirmax"))
877 else if (!strcmp(opt, "actimeo")) {
883 else if (!strcmp(opt, "retry"))
885 else if (!strcmp(opt, "port"))
887 else if (!strcmp(opt, "mountport"))
889 else if (!strcmp(opt, "mounthost"))
890 mounthost = xstrndup(opteq+1,
891 strcspn(opteq+1," \t\n\r,"));
892 else if (!strcmp(opt, "mountprog"))
894 else if (!strcmp(opt, "mountvers"))
896 else if (!strcmp(opt, "nfsprog"))
898 else if (!strcmp(opt, "nfsvers") ||
899 !strcmp(opt, "vers"))
901 else if (!strcmp(opt, "proto")) {
902 if (!strncmp(opteq+1, "tcp", 3))
904 else if (!strncmp(opteq+1, "udp", 3))
907 bb_error_msg("warning: unrecognized proto= option");
908 } else if (!strcmp(opt, "namlen")) {
909 if (nfs_mount_version >= 2)
912 bb_error_msg("warning: option namlen is not supported\n");
913 } else if (!strcmp(opt, "addr"))
916 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
922 if (!strncmp(opt, "no", 2)) {
926 if (!strcmp(opt, "bg"))
928 else if (!strcmp(opt, "fg"))
930 else if (!strcmp(opt, "soft"))
932 else if (!strcmp(opt, "hard"))
934 else if (!strcmp(opt, "intr"))
936 else if (!strcmp(opt, "posix"))
938 else if (!strcmp(opt, "cto"))
940 else if (!strcmp(opt, "ac"))
942 else if (!strcmp(opt, "tcp"))
944 else if (!strcmp(opt, "udp"))
946 else if (!strcmp(opt, "lock")) {
947 if (nfs_mount_version >= 3)
950 bb_error_msg("warning: option nolock is not supported");
952 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
957 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
959 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
960 | (intr ? NFS_MOUNT_INTR : 0)
961 | (posix ? NFS_MOUNT_POSIX : 0)
962 | (nocto ? NFS_MOUNT_NOCTO : 0)
963 | (noac ? NFS_MOUNT_NOAC : 0);
964 if (nfs_mount_version >= 2)
965 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
966 if (nfs_mount_version >= 3)
967 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
968 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
969 bb_error_msg("NFSv%d not supported", nfsvers);
972 if (nfsvers && !mountvers)
973 mountvers = (nfsvers < 3) ? 1 : nfsvers;
974 if (nfsvers && nfsvers < mountvers) {
978 /* Adjust options if none specified */
980 data.timeo = tcp ? 70 : 7;
982 data.version = nfs_mount_version;
984 if (vfsflags & MS_REMOUNT)
988 * If the previous mount operation on the same host was
989 * backgrounded, and the "bg" for this mount is also set,
990 * give up immediately, to avoid the initial timeout.
992 if (bg && we_saw_this_host_before(hostname)) {
993 daemonized = daemonize(); /* parent or error */
994 if (daemonized <= 0) { /* parent or error */
995 retval = -daemonized;
1000 /* create mount daemon client */
1001 /* See if the nfs host = mount host. */
1003 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1004 mount_server_addr.sin_family = AF_INET;
1005 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1007 hp = gethostbyname(mounthost);
1009 bb_herror_msg("%s", mounthost);
1012 if (hp->h_length > sizeof(struct in_addr)) {
1013 bb_error_msg("got bad hp->h_length?");
1014 hp->h_length = sizeof(struct in_addr);
1016 mount_server_addr.sin_family = AF_INET;
1017 memcpy(&mount_server_addr.sin_addr,
1018 hp->h_addr, hp->h_length);
1024 * The following loop implements the mount retries. When the mount
1025 * times out, and the "bg" option is set, we background ourself
1026 * and continue trying.
1028 * The case where the mount point is not present and the "bg"
1029 * option is set, is treated as a timeout. This is done to
1030 * support nested mounts.
1032 * The "retry" count specified by the user is the number of
1033 * minutes to retry before giving up.
1036 struct timeval total_timeout;
1037 struct timeval retry_timeout;
1038 struct pmap* pm_mnt;
1043 retry_timeout.tv_sec = 3;
1044 retry_timeout.tv_usec = 0;
1045 total_timeout.tv_sec = 20;
1046 total_timeout.tv_usec = 0;
1047 timeout = time(NULL) + 60 * retry;
1051 /* be careful not to use too many CPU cycles */
1055 pm_mnt = get_mountport(&mount_server_addr,
1060 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1062 /* contact the mount daemon via TCP */
1063 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1064 msock = RPC_ANYSOCK;
1066 switch (pm_mnt->pm_prot) {
1068 mclient = clntudp_create(&mount_server_addr,
1075 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1076 msock = RPC_ANYSOCK;
1078 mclient = clnttcp_create(&mount_server_addr,
1087 if (!daemonized && prevt == 0)
1088 error_msg_rpc(clnt_spcreateerror(" "));
1090 enum clnt_stat clnt_stat;
1091 /* try to mount hostname:pathname */
1092 mclient->cl_auth = authunix_create_default();
1094 /* make pointers in xdr_mountres3 NULL so
1095 * that xdr_array allocates memory for us
1097 memset(&status, 0, sizeof(status));
1099 if (pm_mnt->pm_vers == 3)
1100 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1101 (xdrproc_t) xdr_dirpath,
1102 (caddr_t) &pathname,
1103 (xdrproc_t) xdr_mountres3,
1107 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1108 (xdrproc_t) xdr_dirpath,
1109 (caddr_t) &pathname,
1110 (xdrproc_t) xdr_fhstatus,
1114 if (clnt_stat == RPC_SUCCESS)
1115 goto prepare_kernel_data; /* we're done */
1116 if (errno != ECONNREFUSED) {
1117 error_msg_rpc(clnt_sperror(mclient, " "));
1118 goto fail; /* don't retry */
1120 /* Connection refused */
1121 if (!daemonized && prevt == 0) /* print just once */
1122 error_msg_rpc(clnt_sperror(mclient, " "));
1123 auth_destroy(mclient->cl_auth);
1124 clnt_destroy(mclient);
1129 /* Timeout. We are going to retry... maybe */
1134 daemonized = daemonize();
1135 if (daemonized <= 0) { /* parent or error */
1136 retval = -daemonized;
1143 /* TODO error message */
1149 prepare_kernel_data:
1152 if (status.nfsv2.fhs_status != 0) {
1153 bb_error_msg("%s:%s failed, reason given by server: %s",
1155 nfs_strerror(status.nfsv2.fhs_status));
1158 memcpy(data.root.data,
1159 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1161 data.root.size = NFS_FHSIZE;
1162 memcpy(data.old_root.data,
1163 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1166 fhandle3 *my_fhandle;
1167 if (status.nfsv3.fhs_status != 0) {
1168 bb_error_msg("%s:%s failed, reason given by server: %s",
1170 nfs_strerror(status.nfsv3.fhs_status));
1173 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1174 memset(data.old_root.data, 0, NFS_FHSIZE);
1175 memset(&data.root, 0, sizeof(data.root));
1176 data.root.size = my_fhandle->fhandle3_len;
1177 memcpy(data.root.data,
1178 (char *) my_fhandle->fhandle3_val,
1179 my_fhandle->fhandle3_len);
1181 data.flags |= NFS_MOUNT_VER3;
1184 /* create nfs socket for kernel */
1187 if (nfs_mount_version < 3) {
1188 bb_error_msg("NFS over TCP is not supported");
1191 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1193 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1195 bb_perror_msg("nfs socket");
1198 if (bindresvport(fsock, 0) < 0) {
1199 bb_perror_msg("nfs bindresvport");
1203 server_addr.sin_port = PMAPPORT;
1204 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1205 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1209 server_addr.sin_port = htons(port);
1211 /* prepare data structure for kernel */
1214 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1215 strncpy(data.hostname, hostname, sizeof(data.hostname));
1219 auth_destroy(mclient->cl_auth);
1220 clnt_destroy(mclient);
1224 /* We must wait until mount directory is available */
1225 struct stat statbuf;
1227 while (stat(mp->mnt_dir, &statbuf) == -1) {
1229 daemonized = daemonize();
1230 if (daemonized <= 0) { /* parent or error */
1231 retval = -daemonized;
1235 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1242 do_mount: /* perform actual mount */
1244 mp->mnt_type = "nfs";
1245 retval = mount_it_now(mp, vfsflags, (char*)&data);
1252 auth_destroy(mclient->cl_auth);
1253 clnt_destroy(mclient);
1267 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1269 /* Never called. Call should be optimized out. */
1270 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1272 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1274 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1275 // type detection. Returns 0 for success, nonzero for failure.
1276 // NB: mp->xxx fields may be trashed on exit
1277 static int singlemount(struct mntent *mp, int ignore_busy)
1279 int rc = -1, vfsflags;
1280 char *loopFile = 0, *filteropts = 0;
1284 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1286 // Treat fstype "auto" as unspecified.
1288 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1290 // Might this be an CIFS filesystem?
1292 if (ENABLE_FEATURE_MOUNT_CIFS &&
1293 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1294 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1300 // Replace '/' with '\' and verify that unc points to "//server/share".
1302 for (s = mp->mnt_fsname; *s; ++s)
1303 if (*s == '/') *s = '\\';
1307 s = strrchr(mp->mnt_fsname, '\\');
1308 if (s == mp->mnt_fsname+1) goto report_error;
1310 he = gethostbyname(mp->mnt_fsname+2);
1312 if (!he) goto report_error;
1314 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1316 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1317 he->h_addr[2], he->h_addr[3]);
1318 parse_mount_options(ip, &filteropts);
1320 // compose new unc '\\server-ip\share'
1322 mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3,
1323 strchr(mp->mnt_fsname+2,'\\'));
1326 vfsflags |= MS_MANDLOCK;
1328 mp->mnt_type = "cifs";
1329 rc = mount_it_now(mp, vfsflags, filteropts);
1330 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1334 // Might this be an NFS filesystem?
1336 if (ENABLE_FEATURE_MOUNT_NFS &&
1337 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1338 strchr(mp->mnt_fsname, ':') != NULL)
1340 rc = nfsmount(mp, vfsflags, filteropts);
1344 // Look at the file. (Not found isn't a failure for remount, or for
1345 // a synthetic filesystem like proc or sysfs.)
1347 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1349 // Do we need to allocate a loopback device for it?
1351 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1352 loopFile = bb_simplify_path(mp->mnt_fsname);
1354 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1359 bb_error_msg( errno == EPERM || errno == EACCES
1360 ? bb_msg_perm_denied_are_you_root
1361 : "cannot setup loop device");
1365 // Autodetect bind mounts
1367 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1368 vfsflags |= MS_BIND;
1371 /* If we know the fstype (or don't need to), jump straight
1372 * to the actual mount. */
1374 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1375 rc = mount_it_now(mp, vfsflags, filteropts);
1377 // Loop through filesystem types until mount succeeds or we run out
1381 /* Initialize list of block backed filesystems. This has to be
1382 * done here so that during "mount -a", mounts after /proc shows up
1383 * can autodetect. */
1386 fslist = get_block_backed_filesystems();
1387 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1388 atexit(delete_block_backed_filesystems);
1391 for (fl = fslist; fl; fl = fl->link) {
1392 mp->mnt_type = fl->data;
1393 rc = mount_it_now(mp, vfsflags, filteropts);
1398 // If mount failed, clean up loop file (if any).
1400 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1401 del_loop(mp->mnt_fsname);
1402 if (ENABLE_FEATURE_CLEAN_UP) {
1404 free(mp->mnt_fsname);
1409 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1411 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1413 /* perror here sometimes says "mounting ... on ... failed: Success" */
1414 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1419 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1420 // each directory to be mounted.
1422 int mount_main(int argc, char **argv)
1424 enum { OPT_ALL = 0x10 };
1426 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1428 const char *fstabname;
1432 struct mntent mtpair[2], *mtcur = mtpair;
1434 /* parse long options, like --bind and --move. Note that -o option
1435 * and --option are synonymous. Yes, this means --remount,rw works. */
1437 for (i = j = 0; i < argc; i++) {
1438 if (argv[i][0] == '-' && argv[i][1] == '-') {
1439 append_mount_options(&cmdopts,argv[i]+2);
1440 } else argv[j++] = argv[i];
1445 // Parse remaining options
1447 opt = getopt32(argc, argv, "o:t:rwanfvs", &opt_o, &fstype);
1448 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
1449 //if (opt & 0x2) // -t
1450 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
1451 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
1452 //if (opt & 0x10) // -a
1453 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
1454 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
1455 //if (opt & 0x80) // -v: verbose (ignore)
1456 //if (opt & 0x100) // -s: sloppy (ignore)
1460 // Three or more non-option arguments? Die with a usage message.
1462 if (argc > 2) bb_show_usage();
1464 // If we have no arguments, show currently mounted filesystems
1467 if (!(opt & OPT_ALL)) {
1468 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1470 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1472 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1473 sizeof(bb_common_bufsiz1)))
1475 // Don't show rootfs.
1476 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1478 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1479 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1480 mtpair->mnt_dir, mtpair->mnt_type,
1483 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1484 return EXIT_SUCCESS;
1486 } else storage_path = bb_simplify_path(argv[0]);
1488 // When we have two arguments, the second is the directory and we can
1489 // skip looking at fstab entirely. We can always abspath() the directory
1490 // argument when we get it.
1493 mtpair->mnt_fsname = argv[0];
1494 mtpair->mnt_dir = argv[1];
1495 mtpair->mnt_type = fstype;
1496 mtpair->mnt_opts = cmdopts;
1497 rc = singlemount(mtpair, 0);
1501 i = parse_mount_options(cmdopts, 0);
1503 // If we have a shared subtree flag, don't worry about fstab or mtab.
1505 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1506 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1508 rc = mount("", argv[0], "", i, "");
1509 if (rc) bb_perror_msg_and_die("%s", argv[0]);
1513 // Open either fstab or mtab
1516 fstabname = bb_path_mtab_file;
1517 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?
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[0], 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 */
1561 // Is this what we're looking for?
1563 if (strcmp(argv[0],mtcur->mnt_fsname) &&
1564 strcmp(storage_path,mtcur->mnt_fsname) &&
1565 strcmp(argv[0],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) {