1 /* vi: set sw=4 ts=4: */
3 * Mini mount implementation for busybox
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
13 * bb_getopt_ulflags();
16 /* Design notes: There is no spec for mount. Remind me to write one.
18 mount_main() calls singlemount() which calls mount_it_now().
20 mount_main() can loop through /etc/fstab for mount -a
21 singlemount() can loop through /etc/filesystems for fstype detection.
22 mount_it_now() does the actual mount.
28 /* Needed for nfs support only... */
30 #include <sys/utsname.h>
34 #include <rpc/pmap_prot.h>
35 #include <rpc/pmap_clnt.h>
38 // Not real flags, but we want to be able to check for this.
39 #define MOUNT_NOAUTO (1<<29)
40 #define MOUNT_SWAP (1<<30)
42 /* Standard mount options (from -o options or --options), with corresponding
48 } static mount_options[] = {
49 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
51 USE_FEATURE_MOUNT_LOOP(
55 USE_FEATURE_MOUNT_FSTAB(
58 {"noauto",MOUNT_NOAUTO},
62 USE_FEATURE_MOUNT_FLAGS(
64 {"nosuid", MS_NOSUID},
69 {"noexec", MS_NOEXEC},
70 {"sync", MS_SYNCHRONOUS},
71 {"async", ~MS_SYNCHRONOUS},
72 {"atime", ~MS_NOATIME},
73 {"noatime", MS_NOATIME},
74 {"diratime", ~MS_NODIRATIME},
75 {"nodiratime", MS_NODIRATIME},
82 {"shared", MS_SHARED},
84 {"private", MS_PRIVATE},
85 {"unbindable", MS_UNBINDABLE},
86 {"rshared", MS_SHARED|MS_RECURSIVE},
87 {"rslave", MS_SLAVE|MS_RECURSIVE},
88 {"rprivate", MS_SLAVE|MS_RECURSIVE},
89 {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
94 {"ro", MS_RDONLY}, // vfs flag
95 {"rw", ~MS_RDONLY}, // vfs flag
96 {"remount", MS_REMOUNT}, // action flag
101 /* Append mount options to string */
102 static void append_mount_options(char **oldopts, char *newopts)
104 if (*oldopts && **oldopts) {
105 char *temp = xasprintf("%s,%s",*oldopts,newopts);
109 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
110 *oldopts = xstrdup(newopts);
114 /* Use the mount_options list to parse options into flags.
115 * Also return list of unrecognized options if unrecognized!=NULL */
116 static int parse_mount_options(char *options, char **unrecognized)
118 int flags = MS_SILENT;
120 // Loop through options
123 char *comma = strchr(options, ',');
125 if (comma) *comma = 0;
127 // Find this option in mount_options
128 for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
129 if (!strcasecmp(mount_options[i].name, options)) {
130 long fl = mount_options[i].flags;
131 if (fl < 0) flags &= fl;
136 // If unrecognized not NULL, append unrecognized mount options */
138 && i == (sizeof(mount_options) / sizeof(*mount_options)))
140 // Add it to strflags, to pass on to kernel
141 i = *unrecognized ? strlen(*unrecognized) : 0;
142 *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
144 // Comma separated if it's not the first one
145 if (i) (*unrecognized)[i++] = ',';
146 strcpy((*unrecognized)+i, options);
149 // Advance to next option, or finish
159 // Return a list of all block device backed filesystems
161 static llist_t *get_block_backed_filesystems(void)
164 *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
169 for (i = 0; filesystems[i]; i++) {
170 f = fopen(filesystems[i], "r");
173 for (fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
176 if (!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
178 while (isspace(*fs)) fs++;
179 if (*fs=='#' || *fs=='*') continue;
182 llist_add_to_end(&list,xstrdup(fs));
184 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
192 #if ENABLE_FEATURE_CLEAN_UP
193 static void delete_block_backed_filesystems(void)
195 llist_free(fslist, free);
198 void delete_block_backed_filesystems(void);
201 #if ENABLE_FEATURE_MTAB_SUPPORT
202 static int useMtab = 1;
209 // Perform actual mount of specific filesystem at specific location.
210 // NB: mp->xxx fields may be trashed on exit
211 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
215 if (fakeIt) return 0;
217 // Mount, with fallback to read-only if necessary.
220 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
221 vfsflags, filteropts);
222 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
224 bb_error_msg("%s is write-protected, mounting read-only",
226 vfsflags |= MS_RDONLY;
229 // Abort entirely if permission denied.
231 if (rc && errno == EPERM)
232 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
234 /* If the mount was successful, and we're maintaining an old-style
235 * mtab file by hand, add the new entry to it now. */
237 if(ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
238 char dirbuf[PATH_MAX];
239 char srcbuf[PATH_MAX];
240 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
244 bb_error_msg("no %s",bb_path_mtab_file);
246 // Add vfs string flags
248 for(i=0; mount_options[i].flags != MS_REMOUNT; i++)
249 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
250 append_mount_options(&(mp->mnt_opts), mount_options[i].name);
252 // Remove trailing / (if any) from directory we mounted on
254 i = strlen(mp->mnt_dir) - 1;
255 if(i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
257 // Add full pathnames as needed
259 if (mp->mnt_dir[0] != '/') {
260 getcwd(dirbuf, sizeof(dirbuf));
262 /* strcat() would be unsafe here */
263 snprintf(dirbuf+i, sizeof(dirbuf)-i, "/%s", mp->mnt_dir);
264 mp->mnt_dir = dirbuf;
266 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
267 if (mp->mnt_fsname[0] != '/') {
268 getcwd(srcbuf, sizeof(srcbuf));
270 snprintf(srcbuf+i, sizeof(srcbuf)-i, "/%s",
272 mp->mnt_fsname = srcbuf;
274 mp->mnt_type = "none";
276 mp->mnt_freq = mp->mnt_passno = 0;
280 addmntent(mountTable, mp);
281 endmntent(mountTable);
287 #if ENABLE_FEATURE_MOUNT_NFS
291 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
293 * Licensed under GPLv2, see file LICENSE in this tarball for details.
295 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
296 * numbers to be specified on the command line.
298 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
299 * Omit the call to connect() for Linux version 1.3.11 or later.
301 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
302 * Implemented the "bg", "fg" and "retry" mount options for NFS.
304 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
305 * - added Native Language Support
307 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
311 /* This is just a warning of a common mistake. Possibly this should be a
312 * uclibc faq entry rather than in busybox... */
313 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
314 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
317 #define MOUNTPORT 635
318 #define MNTPATHLEN 1024
319 #define MNTNAMLEN 255
323 typedef char fhandle[FHSIZE];
326 unsigned int fhandle3_len;
338 MNT3ERR_NAMETOOLONG = 63,
339 MNT3ERR_NOTSUPP = 10004,
340 MNT3ERR_SERVERFAULT = 10006,
342 typedef enum mountstat3 mountstat3;
345 unsigned int fhs_status;
350 typedef struct fhstatus fhstatus;
352 struct mountres3_ok {
355 unsigned int auth_flavours_len;
356 char *auth_flavours_val;
359 typedef struct mountres3_ok mountres3_ok;
362 mountstat3 fhs_status;
364 mountres3_ok mountinfo;
367 typedef struct mountres3 mountres3;
369 typedef char *dirpath;
373 typedef struct mountbody *mountlist;
377 dirpath ml_directory;
380 typedef struct mountbody mountbody;
382 typedef struct groupnode *groups;
388 typedef struct groupnode groupnode;
390 typedef struct exportnode *exports;
397 typedef struct exportnode exportnode;
410 typedef struct ppathcnf ppathcnf;
412 #define MOUNTPROG 100005
415 #define MOUNTPROC_NULL 0
416 #define MOUNTPROC_MNT 1
417 #define MOUNTPROC_DUMP 2
418 #define MOUNTPROC_UMNT 3
419 #define MOUNTPROC_UMNTALL 4
420 #define MOUNTPROC_EXPORT 5
421 #define MOUNTPROC_EXPORTALL 6
423 #define MOUNTVERS_POSIX 2
425 #define MOUNTPROC_PATHCONF 7
429 #define MOUNTPROC3_NULL 0
430 #define MOUNTPROC3_MNT 1
431 #define MOUNTPROC3_DUMP 2
432 #define MOUNTPROC3_UMNT 3
433 #define MOUNTPROC3_UMNTALL 4
434 #define MOUNTPROC3_EXPORT 5
446 * We want to be able to compile mount on old kernels in such a way
447 * that the binary will work well on more recent kernels.
448 * Thus, if necessary we teach nfsmount.c the structure of new fields
449 * that will come later.
451 * Moreover, the new kernel includes conflict with glibc includes
452 * so it is easiest to ignore the kernel altogether (at compile time).
460 unsigned char data[64];
463 struct nfs_mount_data {
466 struct nfs2_fh old_root; /* 1 */
472 int acregmin; /* 1 */
473 int acregmax; /* 1 */
474 int acdirmin; /* 1 */
475 int acdirmax; /* 1 */
476 struct sockaddr_in addr; /* 1 */
477 char hostname[256]; /* 1 */
479 unsigned int bsize; /* 3 */
480 struct nfs3_fh root; /* 4 */
483 /* bits in the flags field */
485 NFS_MOUNT_SOFT = 0x0001, /* 1 */
486 NFS_MOUNT_INTR = 0x0002, /* 1 */
487 NFS_MOUNT_SECURE = 0x0004, /* 1 */
488 NFS_MOUNT_POSIX = 0x0008, /* 1 */
489 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
490 NFS_MOUNT_NOAC = 0x0020, /* 1 */
491 NFS_MOUNT_TCP = 0x0040, /* 2 */
492 NFS_MOUNT_VER3 = 0x0080, /* 3 */
493 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
494 NFS_MOUNT_NONLM = 0x0200 /* 3 */
499 * We need to translate between nfs status return values and
500 * the local errno values which may not be the same.
502 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
503 * "after #include <errno.h> the symbol errno is reserved for any use,
504 * it cannot even be used as a struct tag or field name".
508 #define EDQUOT ENOSPC
511 // Convert each NFSERR_BLAH into EBLAH
513 static const struct {
517 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
518 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
519 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
520 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
523 static char *nfs_strerror(int status)
526 static char buf[256];
528 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
529 if (nfs_errtbl[i].stat == status)
530 return strerror(nfs_errtbl[i].errnum);
532 sprintf(buf, "unknown nfs status return value: %d", status);
536 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
538 if (!xdr_opaque(xdrs, objp, FHSIZE))
543 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
545 if (!xdr_u_int(xdrs, &objp->fhs_status))
547 switch (objp->fhs_status) {
549 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
558 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
560 if (!xdr_string(xdrs, objp, MNTPATHLEN))
565 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
567 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
572 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
574 if (!xdr_fhandle3(xdrs, &objp->fhandle))
576 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
577 sizeof (int), (xdrproc_t) xdr_int))
582 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
584 if (!xdr_enum(xdrs, (enum_t *) objp))
589 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
591 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
593 switch (objp->fhs_status) {
595 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
604 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
607 * nfs_mount_version according to the sources seen at compile time.
609 static int nfs_mount_version;
610 static int kernel_version;
613 * Unfortunately, the kernel prints annoying console messages
614 * in case of an unexpected nfs mount version (instead of
615 * just returning some error). Therefore we'll have to try
616 * and figure out what version the kernel expects.
619 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
620 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
621 * nfs_mount_version: version this source and running kernel can handle
624 find_kernel_nfs_mount_version(void)
629 nfs_mount_version = 4; /* default */
631 kernel_version = get_linux_version_code();
632 if (kernel_version) {
633 if (kernel_version < KERNEL_VERSION(2,1,32))
634 nfs_mount_version = 1;
635 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
636 (kernel_version >= KERNEL_VERSION(2,3,0) &&
637 kernel_version < KERNEL_VERSION(2,3,99)))
638 nfs_mount_version = 3;
639 /* else v4 since 2.3.99pre4 */
644 get_mountport(struct sockaddr_in *server_addr,
646 long unsigned version,
650 struct pmaplist *pmap;
651 static struct pmap p = {0, 0, 0, 0};
653 server_addr->sin_port = PMAPPORT;
654 pmap = pmap_getmaps(server_addr);
656 if (version > MAX_NFSPROT)
657 version = MAX_NFSPROT;
666 if (pmap->pml_map.pm_prog != prog)
668 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
670 if (version > 2 && pmap->pml_map.pm_vers != version)
672 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
674 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
675 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
676 (port && pmap->pml_map.pm_port != port))
678 memcpy(&p, &pmap->pml_map, sizeof(p));
680 pmap = pmap->pml_next;
683 p.pm_vers = MOUNTVERS;
685 p.pm_port = MOUNTPORT;
687 p.pm_prot = IPPROTO_TCP;
691 static int daemonize(void)
695 if (pid < 0) /* error */
697 if (pid > 0) /* parent */
700 fd = xopen(bb_dev_null, O_RDWR);
704 if (fd > 2) close(fd);
706 openlog(bb_applet_name, LOG_PID, LOG_DAEMON);
707 logmode = LOGMODE_SYSLOG;
712 static inline int we_saw_this_host_before(const char *hostname)
717 /* RPC strerror analogs are terminally idiotic:
718 * *mandatory* prefix and \n at end.
719 * This hopefully helps. Usage:
720 * error_msg_rpc(clnt_*error*(" ")) */
721 static void error_msg_rpc(const char *msg)
724 while (msg[0] == ' ' || msg[0] == ':') msg++;
726 while (len && msg[len-1] == '\n') len--;
727 bb_error_msg("%.*s", len, msg);
730 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
736 struct nfs_mount_data data;
739 struct sockaddr_in server_addr;
740 struct sockaddr_in mount_server_addr;
743 struct fhstatus nfsv2;
744 struct mountres3 nfsv3;
766 find_kernel_nfs_mount_version();
774 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
776 filteropts = xstrdup(filteropts); /* going to trash it later... */
778 hostname = xstrdup(mp->mnt_fsname);
779 /* mount_main() guarantees that ':' is there */
780 s = strchr(hostname, ':');
783 /* Ignore all but first hostname in replicated mounts
784 until they can be fully supported. (mack@sgi.com) */
785 s = strchr(hostname, ',');
788 bb_error_msg("warning: multiple hostnames not supported");
791 server_addr.sin_family = AF_INET;
792 if (!inet_aton(hostname, &server_addr.sin_addr)) {
793 hp = gethostbyname(hostname);
795 bb_herror_msg("%s", hostname);
798 if (hp->h_length > sizeof(struct in_addr)) {
799 bb_error_msg("got bad hp->h_length");
800 hp->h_length = sizeof(struct in_addr);
802 memcpy(&server_addr.sin_addr,
803 hp->h_addr, hp->h_length);
806 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
808 /* add IP address to mtab options for use when unmounting */
810 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
811 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
813 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
814 mp->mnt_opts[0] ? "," : "",
815 inet_ntoa(server_addr.sin_addr));
820 /* Set default options.
821 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
822 * let the kernel decide.
823 * timeo is filled in after we know whether it'll be TCP or UDP. */
824 memset(&data, 0, sizeof(data));
830 data.namlen = NAME_MAX;
839 retry = 10000; /* 10000 minutes ~ 1 week */
842 mountprog = MOUNTPROG;
851 for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
852 char *opteq = strchr(opt, '=');
854 int val = atoi(opteq + 1);
856 if (!strcmp(opt, "rsize"))
858 else if (!strcmp(opt, "wsize"))
860 else if (!strcmp(opt, "timeo"))
862 else if (!strcmp(opt, "retrans"))
864 else if (!strcmp(opt, "acregmin"))
866 else if (!strcmp(opt, "acregmax"))
868 else if (!strcmp(opt, "acdirmin"))
870 else if (!strcmp(opt, "acdirmax"))
872 else if (!strcmp(opt, "actimeo")) {
878 else if (!strcmp(opt, "retry"))
880 else if (!strcmp(opt, "port"))
882 else if (!strcmp(opt, "mountport"))
884 else if (!strcmp(opt, "mounthost"))
885 mounthost = xstrndup(opteq+1,
886 strcspn(opteq+1," \t\n\r,"));
887 else if (!strcmp(opt, "mountprog"))
889 else if (!strcmp(opt, "mountvers"))
891 else if (!strcmp(opt, "nfsprog"))
893 else if (!strcmp(opt, "nfsvers") ||
894 !strcmp(opt, "vers"))
896 else if (!strcmp(opt, "proto")) {
897 if (!strncmp(opteq+1, "tcp", 3))
899 else if (!strncmp(opteq+1, "udp", 3))
902 bb_error_msg("warning: unrecognized proto= option");
903 } else if (!strcmp(opt, "namlen")) {
904 if (nfs_mount_version >= 2)
907 bb_error_msg("warning: option namlen is not supported\n");
908 } else if (!strcmp(opt, "addr"))
911 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
917 if (!strncmp(opt, "no", 2)) {
921 if (!strcmp(opt, "bg"))
923 else if (!strcmp(opt, "fg"))
925 else if (!strcmp(opt, "soft"))
927 else if (!strcmp(opt, "hard"))
929 else if (!strcmp(opt, "intr"))
931 else if (!strcmp(opt, "posix"))
933 else if (!strcmp(opt, "cto"))
935 else if (!strcmp(opt, "ac"))
937 else if (!strcmp(opt, "tcp"))
939 else if (!strcmp(opt, "udp"))
941 else if (!strcmp(opt, "lock")) {
942 if (nfs_mount_version >= 3)
945 bb_error_msg("warning: option nolock is not supported");
947 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
952 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
954 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
955 | (intr ? NFS_MOUNT_INTR : 0)
956 | (posix ? NFS_MOUNT_POSIX : 0)
957 | (nocto ? NFS_MOUNT_NOCTO : 0)
958 | (noac ? NFS_MOUNT_NOAC : 0);
959 if (nfs_mount_version >= 2)
960 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
961 if (nfs_mount_version >= 3)
962 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
963 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
964 bb_error_msg("NFSv%d not supported", nfsvers);
967 if (nfsvers && !mountvers)
968 mountvers = (nfsvers < 3) ? 1 : nfsvers;
969 if (nfsvers && nfsvers < mountvers) {
973 /* Adjust options if none specified */
975 data.timeo = tcp ? 70 : 7;
977 data.version = nfs_mount_version;
979 if (vfsflags & MS_REMOUNT)
983 * If the previous mount operation on the same host was
984 * backgrounded, and the "bg" for this mount is also set,
985 * give up immediately, to avoid the initial timeout.
987 if (bg && we_saw_this_host_before(hostname)) {
988 daemonized = daemonize(); /* parent or error */
989 if (daemonized <= 0) { /* parent or error */
990 retval = -daemonized;
995 /* create mount daemon client */
996 /* See if the nfs host = mount host. */
998 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
999 mount_server_addr.sin_family = AF_INET;
1000 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1002 hp = gethostbyname(mounthost);
1004 bb_herror_msg("%s", mounthost);
1007 if (hp->h_length > sizeof(struct in_addr)) {
1008 bb_error_msg("got bad hp->h_length?");
1009 hp->h_length = sizeof(struct in_addr);
1011 mount_server_addr.sin_family = AF_INET;
1012 memcpy(&mount_server_addr.sin_addr,
1013 hp->h_addr, hp->h_length);
1019 * The following loop implements the mount retries. When the mount
1020 * times out, and the "bg" option is set, we background ourself
1021 * and continue trying.
1023 * The case where the mount point is not present and the "bg"
1024 * option is set, is treated as a timeout. This is done to
1025 * support nested mounts.
1027 * The "retry" count specified by the user is the number of
1028 * minutes to retry before giving up.
1031 struct timeval total_timeout;
1032 struct timeval retry_timeout;
1033 struct pmap* pm_mnt;
1038 retry_timeout.tv_sec = 3;
1039 retry_timeout.tv_usec = 0;
1040 total_timeout.tv_sec = 20;
1041 total_timeout.tv_usec = 0;
1042 timeout = time(NULL) + 60 * retry;
1046 /* be careful not to use too many CPU cycles */
1050 pm_mnt = get_mountport(&mount_server_addr,
1055 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
1057 /* contact the mount daemon via TCP */
1058 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1059 msock = RPC_ANYSOCK;
1061 switch (pm_mnt->pm_prot) {
1063 mclient = clntudp_create(&mount_server_addr,
1070 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
1071 msock = RPC_ANYSOCK;
1073 mclient = clnttcp_create(&mount_server_addr,
1082 if (!daemonized && prevt == 0)
1083 error_msg_rpc(clnt_spcreateerror(" "));
1085 enum clnt_stat clnt_stat;
1086 /* try to mount hostname:pathname */
1087 mclient->cl_auth = authunix_create_default();
1089 /* make pointers in xdr_mountres3 NULL so
1090 * that xdr_array allocates memory for us
1092 memset(&status, 0, sizeof(status));
1094 if (pm_mnt->pm_vers == 3)
1095 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1096 (xdrproc_t) xdr_dirpath,
1097 (caddr_t) &pathname,
1098 (xdrproc_t) xdr_mountres3,
1102 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1103 (xdrproc_t) xdr_dirpath,
1104 (caddr_t) &pathname,
1105 (xdrproc_t) xdr_fhstatus,
1109 if (clnt_stat == RPC_SUCCESS)
1110 goto prepare_kernel_data; /* we're done */
1111 if (errno != ECONNREFUSED) {
1112 error_msg_rpc(clnt_sperror(mclient, " "));
1113 goto fail; /* don't retry */
1115 /* Connection refused */
1116 if (!daemonized && prevt == 0) /* print just once */
1117 error_msg_rpc(clnt_sperror(mclient, " "));
1118 auth_destroy(mclient->cl_auth);
1119 clnt_destroy(mclient);
1124 /* Timeout. We are going to retry... maybe */
1129 daemonized = daemonize();
1130 if (daemonized <= 0) { /* parent or error */
1131 retval = -daemonized;
1138 /* TODO error message */
1144 prepare_kernel_data:
1147 if (status.nfsv2.fhs_status != 0) {
1148 bb_error_msg("%s:%s failed, reason given by server: %s",
1150 nfs_strerror(status.nfsv2.fhs_status));
1153 memcpy(data.root.data,
1154 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1156 data.root.size = NFS_FHSIZE;
1157 memcpy(data.old_root.data,
1158 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1161 fhandle3 *my_fhandle;
1162 if (status.nfsv3.fhs_status != 0) {
1163 bb_error_msg("%s:%s failed, reason given by server: %s",
1165 nfs_strerror(status.nfsv3.fhs_status));
1168 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1169 memset(data.old_root.data, 0, NFS_FHSIZE);
1170 memset(&data.root, 0, sizeof(data.root));
1171 data.root.size = my_fhandle->fhandle3_len;
1172 memcpy(data.root.data,
1173 (char *) my_fhandle->fhandle3_val,
1174 my_fhandle->fhandle3_len);
1176 data.flags |= NFS_MOUNT_VER3;
1179 /* create nfs socket for kernel */
1182 if (nfs_mount_version < 3) {
1183 bb_error_msg("NFS over TCP is not supported");
1186 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1188 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1190 bb_perror_msg("nfs socket");
1193 if (bindresvport(fsock, 0) < 0) {
1194 bb_perror_msg("nfs bindresvport");
1198 server_addr.sin_port = PMAPPORT;
1199 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1200 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1204 server_addr.sin_port = htons(port);
1206 /* prepare data structure for kernel */
1209 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1210 strncpy(data.hostname, hostname, sizeof(data.hostname));
1214 auth_destroy(mclient->cl_auth);
1215 clnt_destroy(mclient);
1219 /* We must wait until mount directory is available */
1220 struct stat statbuf;
1222 while (stat(mp->mnt_dir, &statbuf) == -1) {
1224 daemonized = daemonize();
1225 if (daemonized <= 0) { /* parent or error */
1226 retval = -daemonized;
1230 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1237 do_mount: /* perform actual mount */
1239 mp->mnt_type = "nfs";
1240 retval = mount_it_now(mp, vfsflags, (char*)&data);
1247 auth_destroy(mclient->cl_auth);
1248 clnt_destroy(mclient);
1262 #else /* !ENABLE_FEATURE_MOUNT_NFS */
1264 /* Never called. Call should be optimized out. */
1265 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
1267 #endif /* !ENABLE_FEATURE_MOUNT_NFS */
1269 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1270 // type detection. Returns 0 for success, nonzero for failure.
1272 static int singlemount(struct mntent *mp, int ignore_busy)
1274 int rc = -1, vfsflags;
1275 char *loopFile = 0, *filteropts = 0;
1279 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1281 // Treat fstype "auto" as unspecified.
1283 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
1285 // Might this be an CIFS filesystem?
1287 if (ENABLE_FEATURE_MOUNT_CIFS &&
1288 (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) &&
1289 (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')))
1295 // Replace '/' with '\' and verify that unc points to "//server/share".
1297 for (s = mp->mnt_fsname; *s; ++s)
1298 if (*s == '/') *s = '\\';
1302 s = strrchr(mp->mnt_fsname, '\\');
1303 if (s == mp->mnt_fsname+1) goto report_error;
1305 he = gethostbyname(mp->mnt_fsname+2);
1307 if (!he) goto report_error;
1309 // Insert ip=... option into string flags. (NOTE: Add IPv6 support.)
1311 sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1],
1312 he->h_addr[2], he->h_addr[3]);
1313 parse_mount_options(ip, &filteropts);
1315 // compose new unc '\\server-ip\share'
1317 s = xasprintf("\\\\%s%s",ip+3,strchr(mp->mnt_fsname+2,'\\'));
1318 if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname);
1322 vfsflags |= MS_MANDLOCK;
1324 mp->mnt_type = "cifs";
1325 rc = mount_it_now(mp, vfsflags, filteropts);
1329 // Might this be an NFS filesystem?
1331 if (ENABLE_FEATURE_MOUNT_NFS &&
1332 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
1333 strchr(mp->mnt_fsname, ':') != NULL)
1335 rc = nfsmount(mp, vfsflags, filteropts);
1339 // Look at the file. (Not found isn't a failure for remount, or for
1340 // a synthetic filesystem like proc or sysfs.)
1342 if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1344 // Do we need to allocate a loopback device for it?
1346 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1347 loopFile = bb_simplify_path(mp->mnt_fsname);
1349 switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
1354 bb_error_msg( errno == EPERM || errno == EACCES
1355 ? bb_msg_perm_denied_are_you_root
1356 : "cannot setup loop device");
1360 // Autodetect bind mounts
1362 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1363 vfsflags |= MS_BIND;
1366 /* If we know the fstype (or don't need to), jump straight
1367 * to the actual mount. */
1369 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1370 rc = mount_it_now(mp, vfsflags, filteropts);
1372 // Loop through filesystem types until mount succeeds or we run out
1376 /* Initialize list of block backed filesystems. This has to be
1377 * done here so that during "mount -a", mounts after /proc shows up
1378 * can autodetect. */
1381 fslist = get_block_backed_filesystems();
1382 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1383 atexit(delete_block_backed_filesystems);
1386 for (fl = fslist; fl; fl = fl->link) {
1387 mp->mnt_type = fl->data;
1389 rc = mount_it_now(mp,vfsflags, filteropts);
1396 // If mount failed, clean up loop file (if any).
1398 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1399 del_loop(mp->mnt_fsname);
1400 if (ENABLE_FEATURE_CLEAN_UP) {
1402 free(mp->mnt_fsname);
1407 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
1409 if (rc && errno == EBUSY && ignore_busy) rc = 0;
1411 /* perror here sometimes says "mounting ... on ... failed: Success" */
1412 bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1417 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1418 // each directory to be mounted.
1420 int mount_main(int argc, char **argv)
1422 char *cmdopts = xstrdup(""), *fstabname, *fstype=0, *storage_path=0;
1424 int i, opt, all = FALSE, rc = 0;
1425 struct mntent mtpair[2], *mtcur = mtpair;
1427 /* parse long options, like --bind and --move. Note that -o option
1428 * and --option are synonymous. Yes, this means --remount,rw works. */
1430 for (i = opt = 0; i < argc; i++) {
1431 if (argv[i][0] == '-' && argv[i][1] == '-') {
1432 append_mount_options(&cmdopts,argv[i]+2);
1433 } else argv[opt++] = argv[i];
1437 // Parse remaining options
1439 while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) {
1442 append_mount_options(&cmdopts, optarg);
1448 append_mount_options(&cmdopts, "ro");
1451 append_mount_options(&cmdopts, "rw");
1457 USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;)
1460 USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;)
1469 // Three or more non-option arguments? Die with a usage message.
1471 if (optind-argc>2) bb_show_usage();
1473 // If we have no arguments, show currently mounted filesystems
1475 if (optind == argc) {
1477 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1479 if (!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file);
1481 while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
1482 sizeof(bb_common_bufsiz1)))
1484 // Don't show rootfs.
1485 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1487 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1488 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1489 mtpair->mnt_dir, mtpair->mnt_type,
1492 if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1493 return EXIT_SUCCESS;
1495 } else storage_path = bb_simplify_path(argv[optind]);
1497 // When we have two arguments, the second is the directory and we can
1498 // skip looking at fstab entirely. We can always abspath() the directory
1499 // argument when we get it.
1501 if (optind+2 == argc) {
1502 mtpair->mnt_fsname = argv[optind];
1503 mtpair->mnt_dir = argv[optind+1];
1504 mtpair->mnt_type = fstype;
1505 mtpair->mnt_opts = cmdopts;
1506 rc = singlemount(mtpair, 0);
1510 // If we have a shared subtree flag, don't worry about fstab or mtab.
1511 i = parse_mount_options(cmdopts,0);
1512 if (ENABLE_FEATURE_MOUNT_FLAGS &&
1513 (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE )))
1515 rc = mount("", argv[optind], "", i, "");
1516 if (rc) bb_perror_msg_and_die("%s", argv[optind]);
1520 // Open either fstab or mtab
1522 if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
1523 fstabname = bb_path_mtab_file;
1524 else fstabname="/etc/fstab";
1526 fstab = setmntent(fstabname,"r");
1528 bb_perror_msg_and_die("cannot read %s",fstabname);
1530 // Loop through entries until we find what we're looking for.
1532 memset(mtpair,0,sizeof(mtpair));
1534 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1536 // Get next fstab entry
1538 if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1539 + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1540 sizeof(bb_common_bufsiz1)/2))
1542 // Were we looking for something specific?
1544 if (optind != argc) {
1546 // If we didn't find anything, complain.
1548 if (!mtnext->mnt_fsname)
1549 bb_error_msg_and_die("can't find %s in %s",
1550 argv[optind], fstabname);
1552 // Mount the last thing we found.
1555 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1556 append_mount_options(&(mtcur->mnt_opts),cmdopts);
1557 rc = singlemount(mtcur, 0);
1558 free(mtcur->mnt_opts);
1563 /* If we're trying to mount something specific and this isn't it,
1564 * skip it. Note we must match both the exact text in fstab (ala
1565 * "proc") or a full path from root */
1567 if (optind != argc) {
1569 // Is this what we're looking for?
1571 if (strcmp(argv[optind],mtcur->mnt_fsname) &&
1572 strcmp(storage_path,mtcur->mnt_fsname) &&
1573 strcmp(argv[optind],mtcur->mnt_dir) &&
1574 strcmp(storage_path,mtcur->mnt_dir)) continue;
1576 // Remember this entry. Something later may have overmounted
1577 // it, and we want the _last_ match.
1581 // If we're mounting all.
1585 // Do we need to match a filesystem type?
1586 if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
1588 // Skip noauto and swap anyway.
1590 if (parse_mount_options(mtcur->mnt_opts,0)
1591 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1593 // Mount this thing.
1595 if (singlemount(mtcur, 1)) {
1596 /* Count number of failed mounts */
1601 if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1605 if (ENABLE_FEATURE_CLEAN_UP) {