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
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 < (sizeof(mount_options) / sizeof(*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 */
152 && i == (sizeof(mount_options) / sizeof(*mount_options)))
154 // Add it to strflags, to pass on to kernel
155 i = *unrecognized ? strlen(*unrecognized) : 0;
156 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
158 // Comma separated if it's not the first one
159 if (i) (*unrecognized)[i++] = ',';
160 strcpy((*unrecognized)+i, options);
163 // Advance to next option, or finish
173 // Return a list of all block device backed filesystems
175 static llist_t *get_block_backed_filesystems(void)
178 *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
183 for (i = 0; filesystems[i]; i++) {
184 f = fopen(filesystems[i], "r");
187 for (fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
190 if (!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
192 while (isspace(*fs)) fs++;
193 if (*fs=='#' || *fs=='*') continue;
196 llist_add_to_end(&list,xstrdup(fs));
198 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
206 #if ENABLE_FEATURE_CLEAN_UP
207 static void delete_block_backed_filesystems(void)
209 llist_free(fslist, free);
212 void delete_block_backed_filesystems(void);
215 #if ENABLE_FEATURE_MTAB_SUPPORT
216 static int useMtab = 1;
223 // Perform actual mount of specific filesystem at specific location.
224 // NB: mp->xxx fields may be trashed on exit
225 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
229 if (fakeIt) return 0;
231 // Mount, with fallback to read-only if necessary.
234 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
235 vfsflags, filteropts);
236 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
238 bb_error_msg("%s is write-protected, mounting read-only",
240 vfsflags |= MS_RDONLY;
243 // Abort entirely if permission denied.
245 if (rc && errno == EPERM)
246 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
248 /* If the mount was successful, and we're maintaining an old-style
249 * mtab file by hand, add the new entry to it now. */
251 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
253 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
257 bb_error_msg("no %s",bb_path_mtab_file);
259 // Add vfs string flags
261 for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
262 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
263 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
265 // Remove trailing / (if any) from directory we mounted on
267 i = strlen(mp->mnt_dir) - 1;
268 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
270 // Convert to canonical pathnames as needed
272 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
274 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
275 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
276 mp->mnt_type = "bind";
278 mp->mnt_freq = mp->mnt_passno = 0;
282 addmntent(mountTable, mp);
283 endmntent(mountTable);
284 if (ENABLE_FEATURE_CLEAN_UP) {
293 #if ENABLE_FEATURE_MOUNT_NFS
297 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
299 * Licensed under GPLv2, see file LICENSE in this tarball for details.
301 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
302 * numbers to be specified on the command line.
304 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
305 * Omit the call to connect() for Linux version 1.3.11 or later.
307 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
308 * Implemented the "bg", "fg" and "retry" mount options for NFS.
310 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
311 * - added Native Language Support
313 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
317 /* This is just a warning of a common mistake. Possibly this should be a
318 * uclibc faq entry rather than in busybox... */
319 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
320 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
323 #define MOUNTPORT 635
324 #define MNTPATHLEN 1024
325 #define MNTNAMLEN 255
329 typedef char fhandle[FHSIZE];
332 unsigned int fhandle3_len;
344 MNT3ERR_NAMETOOLONG = 63,
345 MNT3ERR_NOTSUPP = 10004,
346 MNT3ERR_SERVERFAULT = 10006,
348 typedef enum mountstat3 mountstat3;
351 unsigned int fhs_status;
356 typedef struct fhstatus fhstatus;
358 struct mountres3_ok {
361 unsigned int auth_flavours_len;
362 char *auth_flavours_val;
365 typedef struct mountres3_ok mountres3_ok;
368 mountstat3 fhs_status;
370 mountres3_ok mountinfo;
373 typedef struct mountres3 mountres3;
375 typedef char *dirpath;
379 typedef struct mountbody *mountlist;
383 dirpath ml_directory;
386 typedef struct mountbody mountbody;
388 typedef struct groupnode *groups;
394 typedef struct groupnode groupnode;
396 typedef struct exportnode *exports;
403 typedef struct exportnode exportnode;
416 typedef struct ppathcnf ppathcnf;
418 #define MOUNTPROG 100005
421 #define MOUNTPROC_NULL 0
422 #define MOUNTPROC_MNT 1
423 #define MOUNTPROC_DUMP 2
424 #define MOUNTPROC_UMNT 3
425 #define MOUNTPROC_UMNTALL 4
426 #define MOUNTPROC_EXPORT 5
427 #define MOUNTPROC_EXPORTALL 6
429 #define MOUNTVERS_POSIX 2
431 #define MOUNTPROC_PATHCONF 7
435 #define MOUNTPROC3_NULL 0
436 #define MOUNTPROC3_MNT 1
437 #define MOUNTPROC3_DUMP 2
438 #define MOUNTPROC3_UMNT 3
439 #define MOUNTPROC3_UMNTALL 4
440 #define MOUNTPROC3_EXPORT 5
452 * We want to be able to compile mount on old kernels in such a way
453 * that the binary will work well on more recent kernels.
454 * Thus, if necessary we teach nfsmount.c the structure of new fields
455 * that will come later.
457 * Moreover, the new kernel includes conflict with glibc includes
458 * so it is easiest to ignore the kernel altogether (at compile time).
466 unsigned char data[64];
469 struct nfs_mount_data {
472 struct nfs2_fh old_root; /* 1 */
478 int acregmin; /* 1 */
479 int acregmax; /* 1 */
480 int acdirmin; /* 1 */
481 int acdirmax; /* 1 */
482 struct sockaddr_in addr; /* 1 */
483 char hostname[256]; /* 1 */
485 unsigned int bsize; /* 3 */
486 struct nfs3_fh root; /* 4 */
489 /* bits in the flags field */
491 NFS_MOUNT_SOFT = 0x0001, /* 1 */
492 NFS_MOUNT_INTR = 0x0002, /* 1 */
493 NFS_MOUNT_SECURE = 0x0004, /* 1 */
494 NFS_MOUNT_POSIX = 0x0008, /* 1 */
495 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
496 NFS_MOUNT_NOAC = 0x0020, /* 1 */
497 NFS_MOUNT_TCP = 0x0040, /* 2 */
498 NFS_MOUNT_VER3 = 0x0080, /* 3 */
499 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
500 NFS_MOUNT_NONLM = 0x0200 /* 3 */
505 * We need to translate between nfs status return values and
506 * the local errno values which may not be the same.
508 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
509 * "after #include <errno.h> the symbol errno is reserved for any use,
510 * it cannot even be used as a struct tag or field name".
514 #define EDQUOT ENOSPC
517 // Convert each NFSERR_BLAH into EBLAH
519 static const struct {
523 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
524 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
525 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
526 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
529 static char *nfs_strerror(int status)
532 static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
534 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
535 if (nfs_errtbl[i].stat == status)
536 return strerror(nfs_errtbl[i].errnum);
538 sprintf(buf, "unknown nfs status return value: %d", status);
542 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
544 if (!xdr_opaque(xdrs, objp, FHSIZE))
549 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
551 if (!xdr_u_int(xdrs, &objp->fhs_status))
553 switch (objp->fhs_status) {
555 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
564 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
566 if (!xdr_string(xdrs, objp, MNTPATHLEN))
571 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
573 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
578 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
580 if (!xdr_fhandle3(xdrs, &objp->fhandle))
582 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
583 sizeof (int), (xdrproc_t) xdr_int))
588 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
590 if (!xdr_enum(xdrs, (enum_t *) objp))
595 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
597 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
599 switch (objp->fhs_status) {
601 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
610 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
613 * nfs_mount_version according to the sources seen at compile time.
615 static int nfs_mount_version;
616 static int kernel_version;
619 * Unfortunately, the kernel prints annoying console messages
620 * in case of an unexpected nfs mount version (instead of
621 * just returning some error). Therefore we'll have to try
622 * and figure out what version the kernel expects.
625 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
626 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
627 * nfs_mount_version: version this source and running kernel can handle
630 find_kernel_nfs_mount_version(void)
635 nfs_mount_version = 4; /* default */
637 kernel_version = get_linux_version_code();
638 if (kernel_version) {
639 if (kernel_version < KERNEL_VERSION(2,1,32))
640 nfs_mount_version = 1;
641 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
642 (kernel_version >= KERNEL_VERSION(2,3,0) &&
643 kernel_version < KERNEL_VERSION(2,3,99)))
644 nfs_mount_version = 3;
645 /* else v4 since 2.3.99pre4 */
650 get_mountport(struct sockaddr_in *server_addr,
652 long unsigned version,
656 struct pmaplist *pmap;
657 static struct pmap p = {0, 0, 0, 0};
659 server_addr->sin_port = PMAPPORT;
660 pmap = pmap_getmaps(server_addr);
662 if (version > MAX_NFSPROT)
663 version = MAX_NFSPROT;
672 if (pmap->pml_map.pm_prog != prog)
674 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
676 if (version > 2 && pmap->pml_map.pm_vers != version)
678 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
680 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
681 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
682 (port && pmap->pml_map.pm_port != port))
684 memcpy(&p, &pmap->pml_map, sizeof(p));
686 pmap = pmap->pml_next;
689 p.pm_vers = MOUNTVERS;
691 p.pm_port = MOUNTPORT;
693 p.pm_prot = IPPROTO_TCP;
697 static int daemonize(void)
701 if (pid < 0) /* error */
703 if (pid > 0) /* parent */
706 fd = xopen(bb_dev_null, O_RDWR);
710 if (fd > 2) close(fd);
712 openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
713 logmode = LOGMODE_SYSLOG;
718 static inline int we_saw_this_host_before(const char *hostname)
723 /* RPC strerror analogs are terminally idiotic:
724 * *mandatory* prefix and \n at end.
725 * This hopefully helps. Usage:
726 * error_msg_rpc(clnt_*error*(" ")) */
727 static void error_msg_rpc(const char *msg)
730 while (msg[0] == ' ' || msg[0] == ':') msg++;
732 while (len && msg[len-1] == '\n') len--;
733 bb_error_msg("%.*s", len, msg);
736 // NB: mp->xxx fields may be trashed on exit
737 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
743 struct nfs_mount_data data;
746 struct sockaddr_in server_addr;
747 struct sockaddr_in mount_server_addr;
750 struct fhstatus nfsv2;
751 struct mountres3 nfsv3;
773 find_kernel_nfs_mount_version();
781 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
783 filteropts = xstrdup(filteropts); /* going to trash it later... */
785 hostname = xstrdup(mp->mnt_fsname);
786 /* mount_main() guarantees that ':' is there */
787 s = strchr(hostname, ':');
790 /* Ignore all but first hostname in replicated mounts
791 until they can be fully supported. (mack@sgi.com) */
792 s = strchr(hostname, ',');
795 bb_error_msg("warning: multiple hostnames not supported");
798 server_addr.sin_family = AF_INET;
799 if (!inet_aton(hostname, &server_addr.sin_addr)) {
800 hp = gethostbyname(hostname);
802 bb_herror_msg("%s", hostname);
805 if (hp->h_length > sizeof(struct in_addr)) {
806 bb_error_msg("got bad hp->h_length");
807 hp->h_length = sizeof(struct in_addr);
809 memcpy(&server_addr.sin_addr,
810 hp->h_addr, hp->h_length);
813 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
815 /* add IP address to mtab options for use when unmounting */
817 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
818 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
820 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
821 mp->mnt_opts[0] ? "," : "",
822 inet_ntoa(server_addr.sin_addr));
827 /* Set default options.
828 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
829 * let the kernel decide.
830 * timeo is filled in after we know whether it'll be TCP or UDP. */
831 memset(&data, 0, sizeof(data));
837 data.namlen = NAME_MAX;
846 retry = 10000; /* 10000 minutes ~ 1 week */
849 mountprog = MOUNTPROG;
858 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
859 char *opteq = strchr(opt, '=');
861 int val = atoi(opteq + 1);
863 if (!strcmp(opt, "rsize"))
865 else if (!strcmp(opt, "wsize"))
867 else if (!strcmp(opt, "timeo"))
869 else if (!strcmp(opt, "retrans"))
871 else if (!strcmp(opt, "acregmin"))
873 else if (!strcmp(opt, "acregmax"))
875 else if (!strcmp(opt, "acdirmin"))
877 else if (!strcmp(opt, "acdirmax"))
879 else if (!strcmp(opt, "actimeo")) {
885 else if (!strcmp(opt, "retry"))
887 else if (!strcmp(opt, "port"))
889 else if (!strcmp(opt, "mountport"))
891 else if (!strcmp(opt, "mounthost"))
892 mounthost = xstrndup(opteq+1,
893 strcspn(opteq+1," \t\n\r,"));
894 else if (!strcmp(opt, "mountprog"))
896 else if (!strcmp(opt, "mountvers"))
898 else if (!strcmp(opt, "nfsprog"))
900 else if (!strcmp(opt, "nfsvers") ||
901 !strcmp(opt, "vers"))
903 else if (!strcmp(opt, "proto")) {
904 if (!strncmp(opteq+1, "tcp", 3))
906 else if (!strncmp(opteq+1, "udp", 3))
909 bb_error_msg("warning: unrecognized proto= option");
910 } else if (!strcmp(opt, "namlen")) {
911 if (nfs_mount_version >= 2)
914 bb_error_msg("warning: option namlen is not supported\n");
915 } else if (!strcmp(opt, "addr"))
918 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
924 if (!strncmp(opt, "no", 2)) {
928 if (!strcmp(opt, "bg"))
930 else if (!strcmp(opt, "fg"))
932 else if (!strcmp(opt, "soft"))
934 else if (!strcmp(opt, "hard"))
936 else if (!strcmp(opt, "intr"))
938 else if (!strcmp(opt, "posix"))
940 else if (!strcmp(opt, "cto"))
942 else if (!strcmp(opt, "ac"))
944 else if (!strcmp(opt, "tcp"))
946 else if (!strcmp(opt, "udp"))
948 else if (!strcmp(opt, "lock")) {
949 if (nfs_mount_version >= 3)
952 bb_error_msg("warning: option nolock is not supported");
954 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
959 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
961 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
962 | (intr ? NFS_MOUNT_INTR : 0)
963 | (posix ? NFS_MOUNT_POSIX : 0)
964 | (nocto ? NFS_MOUNT_NOCTO : 0)
965 | (noac ? NFS_MOUNT_NOAC : 0);
966 if (nfs_mount_version >= 2)
967 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
968 if (nfs_mount_version >= 3)
969 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
970 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
971 bb_error_msg("NFSv%d not supported", nfsvers);
974 if (nfsvers && !mountvers)
975 mountvers = (nfsvers < 3) ? 1 : nfsvers;
976 if (nfsvers && nfsvers < mountvers) {
980 /* Adjust options if none specified */
982 data.timeo = tcp ? 70 : 7;
984 data.version = nfs_mount_version;
986 if (vfsflags & MS_REMOUNT)
990 * If the previous mount operation on the same host was
991 * backgrounded, and the "bg" for this mount is also set,
992 * give up immediately, to avoid the initial timeout.
994 if (bg && we_saw_this_host_before(hostname)) {
995 daemonized = daemonize(); /* parent or error */
996 if (daemonized <= 0) { /* parent or error */
997 retval = -daemonized;
1002 /* create mount daemon client */
1003 /* See if the nfs host = mount host. */
1005 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1006 mount_server_addr.sin_family = AF_INET;
1007 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1009 hp = gethostbyname(mounthost);
1011 bb_herror_msg("%s", mounthost);
1014 if (hp->h_length > sizeof(struct in_addr)) {
1015 bb_error_msg("got bad hp->h_length?");
1016 hp->h_length = sizeof(struct in_addr);
1018 mount_server_addr.sin_family = AF_INET;
1019 memcpy(&mount_server_addr.sin_addr,
1020 hp->h_addr, hp->h_length);
1026 * The following loop implements the mount retries. When the mount
1027 * times out, and the "bg" option is set, we background ourself
1028 * and continue trying.
1030 * The case where the mount point is not present and the "bg"
1031 * option is set, is treated as a timeout. This is done to
1032 * support nested mounts.
1034 * The "retry" count specified by the user is the number of
1035 * minutes to retry before giving up.
1038 struct timeval total_timeout;
1039 struct timeval retry_timeout;
1040 struct pmap* pm_mnt;
1045 retry_timeout.tv_sec = 3;
1046 retry_timeout.tv_usec = 0;
1047 total_timeout.tv_sec = 20;
1048 total_timeout.tv_usec = 0;
1049 timeout = time(NULL) + 60 * retry;
1053 /* be careful not to use too many CPU cycles */
1057 pm_mnt = get_mountport(&mount_server_addr,
1062 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1064 /* contact the mount daemon via TCP */
1065 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1066 msock = RPC_ANYSOCK;
1068 switch (pm_mnt->pm_prot) {
1070 mclient = clntudp_create(&mount_server_addr,
1077 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1078 msock = RPC_ANYSOCK;
1080 mclient = clnttcp_create(&mount_server_addr,
1089 if (!daemonized && prevt == 0)
1090 error_msg_rpc(clnt_spcreateerror(" "));
1092 enum clnt_stat clnt_stat;
1093 /* try to mount hostname:pathname */
1094 mclient->cl_auth = authunix_create_default();
1096 /* make pointers in xdr_mountres3 NULL so
1097 * that xdr_array allocates memory for us
1099 memset(&status, 0, sizeof(status));
1101 if (pm_mnt->pm_vers == 3)
1102 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1103 (xdrproc_t) xdr_dirpath,
1104 (caddr_t) &pathname,
1105 (xdrproc_t) xdr_mountres3,
1109 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1110 (xdrproc_t) xdr_dirpath,
1111 (caddr_t) &pathname,
1112 (xdrproc_t) xdr_fhstatus,
1116 if (clnt_stat == RPC_SUCCESS)
1117 goto prepare_kernel_data; /* we're done */
1118 if (errno != ECONNREFUSED) {
1119 error_msg_rpc(clnt_sperror(mclient, " "));
1120 goto fail; /* don't retry */
1122 /* Connection refused */
1123 if (!daemonized && prevt == 0) /* print just once */
1124 error_msg_rpc(clnt_sperror(mclient, " "));
1125 auth_destroy(mclient->cl_auth);
1126 clnt_destroy(mclient);
1131 /* Timeout. We are going to retry... maybe */
1136 daemonized = daemonize();
1137 if (daemonized <= 0) { /* parent or error */
1138 retval = -daemonized;
1145 /* TODO error message */
1151 prepare_kernel_data:
1154 if (status.nfsv2.fhs_status != 0) {
1155 bb_error_msg("%s:%s failed, reason given by server: %s",
1157 nfs_strerror(status.nfsv2.fhs_status));
1160 memcpy(data.root.data,
1161 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1163 data.root.size = NFS_FHSIZE;
1164 memcpy(data.old_root.data,
1165 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1168 fhandle3 *my_fhandle;
1169 if (status.nfsv3.fhs_status != 0) {
1170 bb_error_msg("%s:%s failed, reason given by server: %s",
1172 nfs_strerror(status.nfsv3.fhs_status));
1175 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1176 memset(data.old_root.data, 0, NFS_FHSIZE);
1177 memset(&data.root, 0, sizeof(data.root));
1178 data.root.size = my_fhandle->fhandle3_len;
1179 memcpy(data.root.data,
1180 (char *) my_fhandle->fhandle3_val,
1181 my_fhandle->fhandle3_len);
1183 data.flags |= NFS_MOUNT_VER3;
1186 /* create nfs socket for kernel */
1189 if (nfs_mount_version < 3) {
1190 bb_error_msg("NFS over TCP is not supported");
1193 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1195 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1197 bb_perror_msg("nfs socket");
1200 if (bindresvport(fsock, 0) < 0) {
1201 bb_perror_msg("nfs bindresvport");
1205 server_addr.sin_port = PMAPPORT;
1206 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1207 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1211 server_addr.sin_port = htons(port);
1213 /* prepare data structure for kernel */
1216 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1217 strncpy(data.hostname, hostname, sizeof(data.hostname));
1221 auth_destroy(mclient->cl_auth);
1222 clnt_destroy(mclient);
1226 /* We must wait until mount directory is available */
1227 struct stat statbuf;
1229 while (stat(mp->mnt_dir, &statbuf) == -1) {
1231 daemonized = daemonize();
1232 if (daemonized <= 0) { /* parent or error */
1233 retval = -daemonized;
1237 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1244 do_mount: /* perform actual mount */
1246 mp->mnt_type = "nfs";
1247 retval = mount_it_now(mp, vfsflags, (char*)&data);
1254 auth_destroy(mclient->cl_auth);
1255 clnt_destroy(mclient);
1269 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1271 /* Never called. Call should be optimized out. */
1272 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1274 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1276 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1277 // type detection. Returns 0 for success, nonzero for failure.
1278 // NB: mp->xxx fields may be trashed on exit
1279 static int singlemount(struct mntent *mp, int ignore_busy)
1281 int rc = -1, vfsflags;
1282 char *loopFile = 0, *filteropts = 0;
1286 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1288 // Treat fstype "auto" as unspecified.
1290 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1292 // Might this be an CIFS filesystem?
1294 if (ENABLE_FEATURE_MOUNT_CIFS &&
1295 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1296 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1302 // Replace '/' with '\' and verify that unc points to "//server/share".
1304 for (s = mp->mnt_fsname; *s; ++s)
1305 if (*s == '/') *s = '\\';
1309 s = strrchr(mp->mnt_fsname, '\\');
1310 if (s == mp->mnt_fsname+1) goto report_error;
1312 he = gethostbyname(mp->mnt_fsname+2);
1314 if (!he) goto report_error;
1316 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1318 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1319 he->h_addr[2], he->h_addr[3]);
1320 parse_mount_options(ip, &filteropts);
1322 // compose new unc '\\server-ip\share'
1324 mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3,
1325 strchr(mp->mnt_fsname+2,'\\'));
1328 vfsflags |= MS_MANDLOCK;
1330 mp->mnt_type = "cifs";
1331 rc = mount_it_now(mp, vfsflags, filteropts);
1332 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1336 // Might this be an NFS filesystem?
1338 if (ENABLE_FEATURE_MOUNT_NFS &&
1339 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1340 strchr(mp->mnt_fsname, ':') != NULL)
1342 rc = nfsmount(mp, vfsflags, filteropts);
1346 // Look at the file. (Not found isn't a failure for remount, or for
1347 // a synthetic filesystem like proc or sysfs.)
1349 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1351 // Do we need to allocate a loopback device for it?
1353 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1354 loopFile = bb_simplify_path(mp->mnt_fsname);
1356 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1361 bb_error_msg( errno == EPERM || errno == EACCES
1362 ? bb_msg_perm_denied_are_you_root
1363 : "cannot setup loop device");
1367 // Autodetect bind mounts
1369 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1370 vfsflags |= MS_BIND;
1373 /* If we know the fstype (or don't need to), jump straight
1374 * to the actual mount. */
1376 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1377 rc = mount_it_now(mp, vfsflags, filteropts);
1379 // Loop through filesystem types until mount succeeds or we run out
1383 /* Initialize list of block backed filesystems. This has to be
1384 * done here so that during "mount -a", mounts after /proc shows up
1385 * can autodetect. */
1388 fslist = get_block_backed_filesystems();
1389 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1390 atexit(delete_block_backed_filesystems);
1393 for (fl = fslist; fl; fl = fl->link) {
1394 mp->mnt_type = fl->data;
1395 rc = mount_it_now(mp, vfsflags, filteropts);
1400 // If mount failed, clean up loop file (if any).
1402 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1403 del_loop(mp->mnt_fsname);
1404 if (ENABLE_FEATURE_CLEAN_UP) {
1406 free(mp->mnt_fsname);
1411 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1413 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1415 /* perror here sometimes says "mounting ... on ... failed: Success" */
1416 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1421 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1422 // each directory to be mounted.
1424 int mount_main(int argc, char **argv)
1426 enum { OPT_ALL = 0x10 };
1428 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1430 const char *fstabname;
1434 struct mntent mtpair[2], *mtcur = mtpair;
1436 /* parse long options, like --bind and --move. Note that -o option
1437 * and --option are synonymous. Yes, this means --remount,rw works. */
1439 for (i = j = 0; i < argc; i++) {
1440 if (argv[i][0] == '-' && argv[i][1] == '-') {
1441 append_mount_options(&cmdopts,argv[i]+2);
1442 } else argv[j++] = argv[i];
1447 // Parse remaining options
1449 opt = bb_getopt_ulflags(argc, argv, "o:t:rwanfv", &opt_o, &fstype);
1450 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
1451 //if (opt & 0x2) // -t
1452 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
1453 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
1454 //if (opt & 0x10) // -a
1455 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE); // -n
1456 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE); // -f
1457 //if (opt & 0x80) // -v: ignore
1461 // Three or more non-option arguments? Die with a usage message.
1463 if (argc > 2) bb_show_usage();
1465 // If we have no arguments, show currently mounted filesystems
1468 if (!(opt & OPT_ALL)) {
1469 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1471 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1473 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1474 sizeof(bb_common_bufsiz1)))
1476 // Don't show rootfs.
1477 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1479 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1480 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1481 mtpair->mnt_dir, mtpair->mnt_type,
1484 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1485 return EXIT_SUCCESS;
1487 } else storage_path = bb_simplify_path(argv[0]);
1489 // When we have two arguments, the second is the directory and we can
1490 // skip looking at fstab entirely. We can always abspath() the directory
1491 // argument when we get it.
1494 mtpair->mnt_fsname = argv[0];
1495 mtpair->mnt_dir = argv[1];
1496 mtpair->mnt_type = fstype;
1497 mtpair->mnt_opts = cmdopts;
1498 rc = singlemount(mtpair, 0);
1502 // If we have a shared subtree flag, don't worry about fstab or mtab.
1503 i = parse_mount_options(cmdopts,0);
1504 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1505 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1507 rc = mount("", argv[0], "", i, "");
1508 if (rc) bb_perror_msg_and_die("%s", argv[0]);
1512 // Open either fstab or mtab
1514 if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
1515 fstabname = bb_path_mtab_file;
1516 else fstabname="/etc/fstab";
1518 fstab = setmntent(fstabname,"r");
1520 bb_perror_msg_and_die("cannot read %s", fstabname);
1522 // Loop through entries until we find what we're looking for.
1524 memset(mtpair,0,sizeof(mtpair));
1526 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1528 // Get next fstab entry
1530 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1531 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1532 sizeof(bb_common_bufsiz1)/2))
1534 // Were we looking for something specific?
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) {