From 30a64cdc95eaa435c00a9aeccdc1b8f9da727875 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Fri, 15 Sep 2006 15:12:00 +0000 Subject: mount: reorder things, fix NFS-less mount. --- util-linux/mount.c | 2334 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1157 insertions(+), 1177 deletions(-) (limited to 'util-linux') diff --git a/util-linux/mount.c b/util-linux/mount.c index 4660b27..5219842 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -25,6 +25,16 @@ #include "busybox.h" #include +/* Needed for nfs support only... */ +#include +#include +#undef TRUE +#undef FALSE +#include +#include +#include + + // Not real flags, but we want to be able to check for this. #define MOUNT_NOAUTO (1<<29) #define MOUNT_SWAP (1<<30) @@ -86,7 +96,7 @@ struct { {"remount", MS_REMOUNT}, // action flag }; -static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts); + /* Append mount options to string */ static void append_mount_options(char **oldopts, char *newopts) @@ -272,1357 +282,1327 @@ static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts) return rc; } -// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem -// type detection. Returns 0 for success, nonzero for failure. - -static int singlemount(struct mntent *mp, int ignore_busy) -{ - int rc = -1, vfsflags; - char *loopFile = 0, *filteropts = 0; - llist_t *fl = 0; - struct stat st; - - vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); - - // Treat fstype "auto" as unspecified. - - if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0; - - // Might this be an CIFS filesystem? +#if ENABLE_FEATURE_MOUNT_NFS - if(ENABLE_FEATURE_MOUNT_CIFS && - (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) && - (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\'))) - { - struct hostent *he; - char ip[32], *s; +/* + * Linux NFS mount + * Copyright (C) 1993 Rick Sladkey + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + * + * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port + * numbers to be specified on the command line. + * + * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler : + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - added Native Language Support + * + * Modified by Olaf Kirch and Trond Myklebust for new NFS code, + * plus NFSv3 stuff. + */ - rc = 1; - // Replace '/' with '\' and verify that unc points to "//server/share". +/* This is just a warning of a common mistake. Possibly this should be a + * uclibc faq entry rather than in busybox... */ +#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) +#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support." +#endif - for (s = mp->mnt_fsname; *s; ++s) - if (*s == '/') *s = '\\'; +#define MOUNTPORT 635 +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 +#define FHSIZE3 64 - // get server IP +typedef char fhandle[FHSIZE]; - s = strrchr(mp->mnt_fsname, '\\'); - if (s == mp->mnt_fsname+1) goto report_error; - *s = 0; - he = gethostbyname(mp->mnt_fsname+2); - *s = '\\'; - if (!he) goto report_error; +typedef struct { + unsigned int fhandle3_len; + char *fhandle3_val; +} fhandle3; - // Insert ip=... option into string flags. (NOTE: Add IPv6 support.) +enum mountstat3 { + MNT_OK = 0, + MNT3ERR_PERM = 1, + MNT3ERR_NOENT = 2, + MNT3ERR_IO = 5, + MNT3ERR_ACCES = 13, + MNT3ERR_NOTDIR = 20, + MNT3ERR_INVAL = 22, + MNT3ERR_NAMETOOLONG = 63, + MNT3ERR_NOTSUPP = 10004, + MNT3ERR_SERVERFAULT = 10006, +}; +typedef enum mountstat3 mountstat3; - sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1], - he->h_addr[2], he->h_addr[3]); - parse_mount_options(ip, &filteropts); +struct fhstatus { + unsigned int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; - // compose new unc '\\server-ip\share' +struct mountres3_ok { + fhandle3 fhandle; + struct { + unsigned int auth_flavours_len; + char *auth_flavours_val; + } auth_flavours; +}; +typedef struct mountres3_ok mountres3_ok; - s = xasprintf("\\\\%s%s",ip+3,strchr(mp->mnt_fsname+2,'\\')); - if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname); - mp->mnt_fsname = s; +struct mountres3 { + mountstat3 fhs_status; + union { + mountres3_ok mountinfo; + } mountres3_u; +}; +typedef struct mountres3 mountres3; - // lock is required - vfsflags |= MS_MANDLOCK; +typedef char *dirpath; - mp->mnt_type = "cifs"; - rc = mount_it_now(mp, vfsflags, filteropts); - goto report_error; - } +typedef char *name; - // Might this be an NFS filesystem? +typedef struct mountbody *mountlist; - if (ENABLE_FEATURE_MOUNT_NFS && - (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) && - strchr(mp->mnt_fsname, ':') != NULL) - { - rc = nfsmount(mp, vfsflags, filteropts); - goto report_error; - } +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; +typedef struct mountbody mountbody; - // Look at the file. (Not found isn't a failure for remount, or for - // a synthetic filesystem like proc or sysfs.) +typedef struct groupnode *groups; - if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) - { - // Do we need to allocate a loopback device for it? +struct groupnode { + name gr_name; + groups gr_next; +}; +typedef struct groupnode groupnode; - if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { - loopFile = bb_simplify_path(mp->mnt_fsname); - mp->mnt_fsname = 0; - switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) { - case 0: - case 1: - break; - default: - bb_error_msg( errno == EPERM || errno == EACCES - ? bb_msg_perm_denied_are_you_root - : "cannot setup loop device"); - return errno; - } +typedef struct exportnode *exports; - // Autodetect bind mounts +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; +typedef struct exportnode exportnode; - } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) - vfsflags |= MS_BIND; - } +struct ppathcnf { + int pc_link_max; + short pc_max_canon; + short pc_max_input; + short pc_name_max; + short pc_path_max; + short pc_pipe_buf; + u_char pc_vdisable; + char pc_xxx; + short pc_mask[2]; +}; +typedef struct ppathcnf ppathcnf; - /* If we know the fstype (or don't need to), jump straight - * to the actual mount. */ +#define MOUNTPROG 100005 +#define MOUNTVERS 1 - if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) - rc = mount_it_now(mp, vfsflags, filteropts); +#define MOUNTPROC_NULL 0 +#define MOUNTPROC_MNT 1 +#define MOUNTPROC_DUMP 2 +#define MOUNTPROC_UMNT 3 +#define MOUNTPROC_UMNTALL 4 +#define MOUNTPROC_EXPORT 5 +#define MOUNTPROC_EXPORTALL 6 - // Loop through filesystem types until mount succeeds or we run out +#define MOUNTVERS_POSIX 2 - else { +#define MOUNTPROC_PATHCONF 7 - /* Initialize list of block backed filesystems. This has to be - * done here so that during "mount -a", mounts after /proc shows up - * can autodetect. */ +#define MOUNT_V3 3 - if (!fslist) { - fslist = get_block_backed_filesystems(); - if (ENABLE_FEATURE_CLEAN_UP && fslist) - atexit(delete_block_backed_filesystems); - } +#define MOUNTPROC3_NULL 0 +#define MOUNTPROC3_MNT 1 +#define MOUNTPROC3_DUMP 2 +#define MOUNTPROC3_UMNT 3 +#define MOUNTPROC3_UMNTALL 4 +#define MOUNTPROC3_EXPORT 5 - for (fl = fslist; fl; fl = fl->link) { - mp->mnt_type = fl->data; +enum { +#ifndef NFS_FHSIZE + NFS_FHSIZE = 32, +#endif +#ifndef NFS_PORT + NFS_PORT = 2049 +#endif +}; - if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break; - mp->mnt_type = 0; - } - } +/* + * We want to be able to compile mount on old kernels in such a way + * that the binary will work well on more recent kernels. + * Thus, if necessary we teach nfsmount.c the structure of new fields + * that will come later. + * + * Moreover, the new kernel includes conflict with glibc includes + * so it is easiest to ignore the kernel altogether (at compile time). + */ - // If mount failed, clean up loop file (if any). +struct nfs2_fh { + char data[32]; +}; +struct nfs3_fh { + unsigned short size; + unsigned char data[64]; +}; - if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { - del_loop(mp->mnt_fsname); - if (ENABLE_FEATURE_CLEAN_UP) { - free(loopFile); - free(mp->mnt_fsname); - } - } +struct nfs_mount_data { + int version; /* 1 */ + int fd; /* 1 */ + struct nfs2_fh old_root; /* 1 */ + int flags; /* 1 */ + int rsize; /* 1 */ + int wsize; /* 1 */ + int timeo; /* 1 */ + int retrans; /* 1 */ + int acregmin; /* 1 */ + int acregmax; /* 1 */ + int acdirmin; /* 1 */ + int acdirmax; /* 1 */ + struct sockaddr_in addr; /* 1 */ + char hostname[256]; /* 1 */ + int namlen; /* 2 */ + unsigned int bsize; /* 3 */ + struct nfs3_fh root; /* 4 */ +}; -report_error: - if (ENABLE_FEATURE_CLEAN_UP) free(filteropts); +/* bits in the flags field */ +enum { + NFS_MOUNT_SOFT = 0x0001, /* 1 */ + NFS_MOUNT_INTR = 0x0002, /* 1 */ + NFS_MOUNT_SECURE = 0x0004, /* 1 */ + NFS_MOUNT_POSIX = 0x0008, /* 1 */ + NFS_MOUNT_NOCTO = 0x0010, /* 1 */ + NFS_MOUNT_NOAC = 0x0020, /* 1 */ + NFS_MOUNT_TCP = 0x0040, /* 2 */ + NFS_MOUNT_VER3 = 0x0080, /* 3 */ + NFS_MOUNT_KERBEROS = 0x0100, /* 3 */ + NFS_MOUNT_NONLM = 0x0200 /* 3 */ +}; - if (rc && errno == EBUSY && ignore_busy) rc = 0; - if (rc < 0) - /* perror here sometimes says "mounting ... on ... failed: Success" */ - bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); - return rc; -} +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + * + * Andreas Schwab : change errno: + * "after #include the symbol errno is reserved for any use, + * it cannot even be used as a struct tag or field name". + */ -// Parse options, if necessary parse fstab/mtab, and call singlemount for -// each directory to be mounted. +#ifndef EDQUOT +#define EDQUOT ENOSPC +#endif -int mount_main(int argc, char **argv) -{ - char *cmdopts = xstrdup(""), *fstabname, *fstype=0, *storage_path=0; - FILE *fstab; - int i, opt, all = FALSE, rc = 0; - struct mntent mtpair[2], *mtcur = mtpair; +// Convert each NFSERR_BLAH into EBLAH - /* parse long options, like --bind and --move. Note that -o option - * and --option are synonymous. Yes, this means --remount,rw works. */ +static const struct { + int stat; + int errnum; +} nfs_errtbl[] = { + {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST}, + {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG}, + {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT}, + {70,ESTALE}, {71,EREMOTE}, {-1,EIO} +}; - for (i = opt = 0; i < argc; i++) { - if (argv[i][0] == '-' && argv[i][1] == '-') { - append_mount_options(&cmdopts,argv[i]+2); - } else argv[opt++] = argv[i]; +static char *nfs_strerror(int status) +{ + int i; + static char buf[256]; + + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == status) + return strerror(nfs_errtbl[i].errnum); } - argc = opt; + sprintf(buf, "unknown nfs status return value: %d", status); + return buf; +} - // Parse remaining options +static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) +{ + if (!xdr_opaque(xdrs, objp, FHSIZE)) + return FALSE; + return TRUE; +} - while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) { - switch (opt) { - case 'o': - append_mount_options(&cmdopts, optarg); - break; - case 't': - fstype = optarg; - break; - case 'r': - append_mount_options(&cmdopts, "ro"); - break; - case 'w': - append_mount_options(&cmdopts, "rw"); - break; - case 'a': - all = TRUE; - break; - case 'n': - USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;) - break; - case 'f': - USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;) - break; - case 'v': - break; // ignore -v - default: - bb_show_usage(); - } +static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp) +{ + if (!xdr_u_int(xdrs, &objp->fhs_status)) + return FALSE; + switch (objp->fhs_status) { + case 0: + if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) + return FALSE; + break; + default: + break; } + return TRUE; +} - // Three or more non-option arguments? Die with a usage message. +static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) +{ + if (!xdr_string(xdrs, objp, MNTPATHLEN)) + return FALSE; + return TRUE; +} - if (optind-argc>2) bb_show_usage(); +static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) +{ + if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) + return FALSE; + return TRUE; +} - // If we have no arguments, show currently mounted filesystems +static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) +{ + if (!xdr_fhandle3(xdrs, &objp->fhandle)) + return FALSE; + if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0, + sizeof (int), (xdrproc_t) xdr_int)) + return FALSE; + return TRUE; +} - if (optind == argc) { - if (!all) { - FILE *mountTable = setmntent(bb_path_mtab_file, "r"); +static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} - if(!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file); +static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) +{ + if (!xdr_mountstat3(xdrs, &objp->fhs_status)) + return FALSE; + switch (objp->fhs_status) { + case MNT_OK: + if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo)) + return FALSE; + break; + default: + break; + } + return TRUE; +} - while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1, - sizeof(bb_common_bufsiz1))) - { - // Don't show rootfs. - if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue; +#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) - if (!fstype || !strcmp(mtpair->mnt_type, fstype)) - printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, - mtpair->mnt_dir, mtpair->mnt_type, - mtpair->mnt_opts); - } - if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable); - return EXIT_SUCCESS; - } - } else storage_path = bb_simplify_path(argv[optind]); +/* + * nfs_mount_version according to the sources seen at compile time. + */ +static int nfs_mount_version; +static int kernel_version; - // When we have two arguments, the second is the directory and we can - // skip looking at fstab entirely. We can always abspath() the directory - // argument when we get it. +/* + * Unfortunately, the kernel prints annoying console messages + * in case of an unexpected nfs mount version (instead of + * just returning some error). Therefore we'll have to try + * and figure out what version the kernel expects. + * + * Variables: + * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time + * NFS_MOUNT_VERSION: these nfsmount sources at compile time + * nfs_mount_version: version this source and running kernel can handle + */ +static void +find_kernel_nfs_mount_version(void) +{ + if (kernel_version) + return; - if (optind+2 == argc) { - mtpair->mnt_fsname = argv[optind]; - mtpair->mnt_dir = argv[optind+1]; - mtpair->mnt_type = fstype; - mtpair->mnt_opts = cmdopts; - rc = singlemount(mtpair, 0); - goto clean_up; - } + nfs_mount_version = 4; /* default */ - // If we have a shared subtree flag, don't worry about fstab or mtab. - i = parse_mount_options(cmdopts,0); - if (ENABLE_FEATURE_MOUNT_FLAGS && - (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE ))) - { - rc = mount("", argv[optind], "", i, ""); - if (rc) bb_perror_msg_and_die("%s", argv[optind]); - goto clean_up; + kernel_version = get_linux_version_code(); + if (kernel_version) { + if (kernel_version < KERNEL_VERSION(2,1,32)) + nfs_mount_version = 1; + else if (kernel_version < KERNEL_VERSION(2,2,18) || + (kernel_version >= KERNEL_VERSION(2,3,0) && + kernel_version < KERNEL_VERSION(2,3,99))) + nfs_mount_version = 3; + /* else v4 since 2.3.99pre4 */ } - - // Open either fstab or mtab - - if (parse_mount_options(cmdopts,0) & MS_REMOUNT) - fstabname = bb_path_mtab_file; - else fstabname="/etc/fstab"; - - if (!(fstab=setmntent(fstabname,"r"))) - bb_perror_msg_and_die("cannot read %s",fstabname); - - // Loop through entries until we find what we're looking for. - - memset(mtpair,0,sizeof(mtpair)); - for (;;) { - struct mntent *mtnext = mtpair + (mtcur==mtpair ? 1 : 0); - - // Get next fstab entry - - if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1 - + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0), - sizeof(bb_common_bufsiz1)/2)) - { - // Were we looking for something specific? - - if (optind != argc) { - - // If we didn't find anything, complain. - - if (!mtnext->mnt_fsname) - bb_error_msg_and_die("can't find %s in %s", - argv[optind], fstabname); - - // Mount the last thing we found. - - mtcur = mtnext; - mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); - append_mount_options(&(mtcur->mnt_opts),cmdopts); - rc = singlemount(mtcur, 0); - free(mtcur->mnt_opts); - } - goto clean_up; - } +} - /* If we're trying to mount something specific and this isn't it, - * skip it. Note we must match both the exact text in fstab (ala - * "proc") or a full path from root */ +static struct pmap * +get_mountport(struct sockaddr_in *server_addr, + long unsigned prog, + long unsigned version, + long unsigned proto, + long unsigned port) +{ + struct pmaplist *pmap; + static struct pmap p = {0, 0, 0, 0}; - if (optind != argc) { + server_addr->sin_port = PMAPPORT; + pmap = pmap_getmaps(server_addr); - // Is this what we're looking for? + if (version > MAX_NFSPROT) + version = MAX_NFSPROT; + if (!prog) + prog = MOUNTPROG; + p.pm_prog = prog; + p.pm_vers = version; + p.pm_prot = proto; + p.pm_port = port; + + while (pmap) { + if (pmap->pml_map.pm_prog != prog) + goto next; + if (!version && p.pm_vers > pmap->pml_map.pm_vers) + goto next; + if (version > 2 && pmap->pml_map.pm_vers != version) + goto next; + if (version && version <= 2 && pmap->pml_map.pm_vers > 2) + goto next; + if (pmap->pml_map.pm_vers > MAX_NFSPROT || + (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || + (port && pmap->pml_map.pm_port != port)) + goto next; + memcpy(&p, &pmap->pml_map, sizeof(p)); +next: + pmap = pmap->pml_next; + } + if (!p.pm_vers) + p.pm_vers = MOUNTVERS; + if (!p.pm_port) + p.pm_port = MOUNTPORT; + if (!p.pm_prot) + p.pm_prot = IPPROTO_TCP; + return &p; +} - if(strcmp(argv[optind],mtcur->mnt_fsname) && - strcmp(storage_path,mtcur->mnt_fsname) && - strcmp(argv[optind],mtcur->mnt_dir) && - strcmp(storage_path,mtcur->mnt_dir)) continue; +static int daemonize(void) +{ + int fd; + int pid = fork(); + if (pid < 0) /* error */ + return -errno; + if (pid > 0) /* parent */ + return 0; + /* child */ + fd = xopen(bb_dev_null, O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) close(fd); + setsid(); + openlog(bb_applet_name, LOG_PID, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; + return 1; +} - // Remember this entry. Something later may have overmounted - // it, and we want the _last_ match. +// TODO +static inline int we_saw_this_host_before(const char *hostname) +{ + return 0; +} - mtcur = mtnext; +/* RPC strerror analogs are terminally idiotic: + * *mandatory* prefix and \n at end. + * This hopefully helps. Usage: + * error_msg_rpc(clnt_*error*(" ")) */ +static void error_msg_rpc(const char *msg) +{ + size_t len; + while (msg[0] == ' ' || msg[0] == ':') msg++; + len = strlen(msg); + while (len && msg[len-1] == '\n') len--; + bb_error_msg("%.*s", len, msg); +} - // If we're mounting all. +static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) +{ + CLIENT *mclient; + char *hostname; + char *pathname; + char *mounthost; + struct nfs_mount_data data; + char *opt; + struct hostent *hp; + struct sockaddr_in server_addr; + struct sockaddr_in mount_server_addr; + int msock, fsock; + union { + struct fhstatus nfsv2; + struct mountres3 nfsv3; + } status; + int daemonized; + char *s; + int port; + int mountport; + int proto; + int bg; + int soft; + int intr; + int posix; + int nocto; + int noac; + int nolock; + int retry; + int tcp; + int mountprog; + int mountvers; + int nfsprog; + int nfsvers; + int retval; - } else { + find_kernel_nfs_mount_version(); - // Do we need to match a filesystem type? - if (fstype && strcmp(mtcur->mnt_type,fstype)) continue; + daemonized = 0; + mounthost = NULL; + retval = ETIMEDOUT; + msock = fsock = -1; + mclient = NULL; - // Skip noauto and swap anyway. + /* NB: hostname, mounthost, filteropts must be free()d prior to return */ - if (parse_mount_options(mtcur->mnt_opts,0) - & (MOUNT_NOAUTO | MOUNT_SWAP)) continue; + filteropts = xstrdup(filteropts); /* going to trash it later... */ - // Mount this thing. + hostname = xstrdup(mp->mnt_fsname); + /* mount_main() guarantees that ':' is there */ + s = strchr(hostname, ':'); + pathname = s + 1; + *s = '\0'; + /* Ignore all but first hostname in replicated mounts + until they can be fully supported. (mack@sgi.com) */ + s = strchr(hostname, ','); + if (s) { + *s = '\0'; + bb_error_msg("warning: multiple hostnames not supported"); + } - if (singlemount(mtcur, 1)) { - /* Count number of failed mounts */ - rc++; - } + server_addr.sin_family = AF_INET; + if (!inet_aton(hostname, &server_addr.sin_addr)) { + hp = gethostbyname(hostname); + if (hp == NULL) { + bb_herror_msg("%s", hostname); + goto fail; } + if (hp->h_length > sizeof(struct in_addr)) { + bb_error_msg("got bad hp->h_length"); + hp->h_length = sizeof(struct in_addr); + } + memcpy(&server_addr.sin_addr, + hp->h_addr, hp->h_length); } - if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab); -clean_up: + memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); - if (ENABLE_FEATURE_CLEAN_UP) { - free(storage_path); - free(cmdopts); - } + /* add IP address to mtab options for use when unmounting */ - return rc; -} + if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ + mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); + } else { + char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, + mp->mnt_opts[0] ? "," : "", + inet_ntoa(server_addr.sin_addr)); + free(mp->mnt_opts); + mp->mnt_opts = tmp; + } + /* Set default options. + * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to + * let the kernel decide. + * timeo is filled in after we know whether it'll be TCP or UDP. */ + memset(&data, 0, sizeof(data)); + data.retrans = 3; + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; + data.namlen = NAME_MAX; -#if ENABLE_FEATURE_MOUNT_NFS + bg = 0; + soft = 0; + intr = 0; + posix = 0; + nocto = 0; + nolock = 0; + noac = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ + tcp = 0; -/* - * Linux NFS mount - * Copyright (C) 1993 Rick Sladkey - * - * Licensed under GPLv2, see file LICENSE in this tarball for details. - * - * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port - * numbers to be specified on the command line. - * - * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler : - * Omit the call to connect() for Linux version 1.3.11 or later. - * - * Wed Oct 1 23:55:28 1997: Dick Streefland - * Implemented the "bg", "fg" and "retry" mount options for NFS. - * - * 1999-02-22 Arkadiusz Mi¶kiewicz - * - added Native Language Support - * - * Modified by Olaf Kirch and Trond Myklebust for new NFS code, - * plus NFSv3 stuff. - */ - -#include -#include -#undef TRUE -#undef FALSE -#include -#include -#include - -/* This is just a warning of a common mistake. Possibly this should be a - * uclibc faq entry rather than in busybox... */ -#if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) -#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support." -#endif - -#define MOUNTPORT 635 -#define MNTPATHLEN 1024 -#define MNTNAMLEN 255 -#define FHSIZE 32 -#define FHSIZE3 64 - -typedef char fhandle[FHSIZE]; - -typedef struct { - unsigned int fhandle3_len; - char *fhandle3_val; -} fhandle3; - -enum mountstat3 { - MNT_OK = 0, - MNT3ERR_PERM = 1, - MNT3ERR_NOENT = 2, - MNT3ERR_IO = 5, - MNT3ERR_ACCES = 13, - MNT3ERR_NOTDIR = 20, - MNT3ERR_INVAL = 22, - MNT3ERR_NAMETOOLONG = 63, - MNT3ERR_NOTSUPP = 10004, - MNT3ERR_SERVERFAULT = 10006, -}; -typedef enum mountstat3 mountstat3; - -struct fhstatus { - unsigned int fhs_status; - union { - fhandle fhs_fhandle; - } fhstatus_u; -}; -typedef struct fhstatus fhstatus; - -struct mountres3_ok { - fhandle3 fhandle; - struct { - unsigned int auth_flavours_len; - char *auth_flavours_val; - } auth_flavours; -}; -typedef struct mountres3_ok mountres3_ok; - -struct mountres3 { - mountstat3 fhs_status; - union { - mountres3_ok mountinfo; - } mountres3_u; -}; -typedef struct mountres3 mountres3; - -typedef char *dirpath; - -typedef char *name; - -typedef struct mountbody *mountlist; - -struct mountbody { - name ml_hostname; - dirpath ml_directory; - mountlist ml_next; -}; -typedef struct mountbody mountbody; - -typedef struct groupnode *groups; - -struct groupnode { - name gr_name; - groups gr_next; -}; -typedef struct groupnode groupnode; - -typedef struct exportnode *exports; - -struct exportnode { - dirpath ex_dir; - groups ex_groups; - exports ex_next; -}; -typedef struct exportnode exportnode; - -struct ppathcnf { - int pc_link_max; - short pc_max_canon; - short pc_max_input; - short pc_name_max; - short pc_path_max; - short pc_pipe_buf; - u_char pc_vdisable; - char pc_xxx; - short pc_mask[2]; -}; -typedef struct ppathcnf ppathcnf; - -#define MOUNTPROG 100005 -#define MOUNTVERS 1 - -#define MOUNTPROC_NULL 0 -#define MOUNTPROC_MNT 1 -#define MOUNTPROC_DUMP 2 -#define MOUNTPROC_UMNT 3 -#define MOUNTPROC_UMNTALL 4 -#define MOUNTPROC_EXPORT 5 -#define MOUNTPROC_EXPORTALL 6 + mountprog = MOUNTPROG; + mountvers = 0; + port = 0; + mountport = 0; + nfsprog = 100003; + nfsvers = 0; -#define MOUNTVERS_POSIX 2 + /* parse options */ -#define MOUNTPROC_PATHCONF 7 + for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { + char *opteq = strchr(opt, '='); + if (opteq) { + int val = atoi(opteq + 1); + *opteq = '\0'; + if (!strcmp(opt, "rsize")) + data.rsize = val; + else if (!strcmp(opt, "wsize")) + data.wsize = val; + else if (!strcmp(opt, "timeo")) + data.timeo = val; + else if (!strcmp(opt, "retrans")) + data.retrans = val; + else if (!strcmp(opt, "acregmin")) + data.acregmin = val; + else if (!strcmp(opt, "acregmax")) + data.acregmax = val; + else if (!strcmp(opt, "acdirmin")) + data.acdirmin = val; + else if (!strcmp(opt, "acdirmax")) + data.acdirmax = val; + else if (!strcmp(opt, "actimeo")) { + data.acregmin = val; + data.acregmax = val; + data.acdirmin = val; + data.acdirmax = val; + } + else if (!strcmp(opt, "retry")) + retry = val; + else if (!strcmp(opt, "port")) + port = val; + else if (!strcmp(opt, "mountport")) + mountport = val; + else if (!strcmp(opt, "mounthost")) + mounthost = xstrndup(opteq+1, + strcspn(opteq+1," \t\n\r,")); + else if (!strcmp(opt, "mountprog")) + mountprog = val; + else if (!strcmp(opt, "mountvers")) + mountvers = val; + else if (!strcmp(opt, "nfsprog")) + nfsprog = val; + else if (!strcmp(opt, "nfsvers") || + !strcmp(opt, "vers")) + nfsvers = val; + else if (!strcmp(opt, "proto")) { + if (!strncmp(opteq+1, "tcp", 3)) + tcp = 1; + else if (!strncmp(opteq+1, "udp", 3)) + tcp = 0; + else + bb_error_msg("warning: unrecognized proto= option"); + } else if (!strcmp(opt, "namlen")) { + if (nfs_mount_version >= 2) + data.namlen = val; + else + bb_error_msg("warning: option namlen is not supported\n"); + } else if (!strcmp(opt, "addr")) + /* ignore */; + else { + bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); + goto fail; + } + } + else { + int val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + if (!strcmp(opt, "bg")) + bg = val; + else if (!strcmp(opt, "fg")) + bg = !val; + else if (!strcmp(opt, "soft")) + soft = val; + else if (!strcmp(opt, "hard")) + soft = !val; + else if (!strcmp(opt, "intr")) + intr = val; + else if (!strcmp(opt, "posix")) + posix = val; + else if (!strcmp(opt, "cto")) + nocto = !val; + else if (!strcmp(opt, "ac")) + noac = !val; + else if (!strcmp(opt, "tcp")) + tcp = val; + else if (!strcmp(opt, "udp")) + tcp = !val; + else if (!strcmp(opt, "lock")) { + if (nfs_mount_version >= 3) + nolock = !val; + else + bb_error_msg("warning: option nolock is not supported"); + } else { + bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); + goto fail; + } + } + } + proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; -#define MOUNT_V3 3 + data.flags = (soft ? NFS_MOUNT_SOFT : 0) + | (intr ? NFS_MOUNT_INTR : 0) + | (posix ? NFS_MOUNT_POSIX : 0) + | (nocto ? NFS_MOUNT_NOCTO : 0) + | (noac ? NFS_MOUNT_NOAC : 0); + if (nfs_mount_version >= 2) + data.flags |= (tcp ? NFS_MOUNT_TCP : 0); + if (nfs_mount_version >= 3) + data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); + if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { + bb_error_msg("NFSv%d not supported", nfsvers); + goto fail; + } + if (nfsvers && !mountvers) + mountvers = (nfsvers < 3) ? 1 : nfsvers; + if (nfsvers && nfsvers < mountvers) { + mountvers = nfsvers; + } -#define MOUNTPROC3_NULL 0 -#define MOUNTPROC3_MNT 1 -#define MOUNTPROC3_DUMP 2 -#define MOUNTPROC3_UMNT 3 -#define MOUNTPROC3_UMNTALL 4 -#define MOUNTPROC3_EXPORT 5 + /* Adjust options if none specified */ + if (!data.timeo) + data.timeo = tcp ? 70 : 7; -enum { -#ifndef NFS_FHSIZE - NFS_FHSIZE = 32, -#endif -#ifndef NFS_PORT - NFS_PORT = 2049 -#endif -}; + data.version = nfs_mount_version; + if (vfsflags & MS_REMOUNT) + goto do_mount; -/* - * We want to be able to compile mount on old kernels in such a way - * that the binary will work well on more recent kernels. - * Thus, if necessary we teach nfsmount.c the structure of new fields - * that will come later. - * - * Moreover, the new kernel includes conflict with glibc includes - * so it is easiest to ignore the kernel altogether (at compile time). - */ + /* + * If the previous mount operation on the same host was + * backgrounded, and the "bg" for this mount is also set, + * give up immediately, to avoid the initial timeout. + */ + if (bg && we_saw_this_host_before(hostname)) { + daemonized = daemonize(); /* parent or error */ + if (daemonized <= 0) { /* parent or error */ + retval = -daemonized; + goto ret; + } + } -struct nfs2_fh { - char data[32]; -}; -struct nfs3_fh { - unsigned short size; - unsigned char data[64]; -}; + /* create mount daemon client */ + /* See if the nfs host = mount host. */ + if (mounthost) { + if (mounthost[0] >= '0' && mounthost[0] <= '9') { + mount_server_addr.sin_family = AF_INET; + mount_server_addr.sin_addr.s_addr = inet_addr(hostname); + } else { + hp = gethostbyname(mounthost); + if (hp == NULL) { + bb_herror_msg("%s", mounthost); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + bb_error_msg("got bad hp->h_length?"); + hp->h_length = sizeof(struct in_addr); + } + mount_server_addr.sin_family = AF_INET; + memcpy(&mount_server_addr.sin_addr, + hp->h_addr, hp->h_length); + } + } + } -struct nfs_mount_data { - int version; /* 1 */ - int fd; /* 1 */ - struct nfs2_fh old_root; /* 1 */ - int flags; /* 1 */ - int rsize; /* 1 */ - int wsize; /* 1 */ - int timeo; /* 1 */ - int retrans; /* 1 */ - int acregmin; /* 1 */ - int acregmax; /* 1 */ - int acdirmin; /* 1 */ - int acdirmax; /* 1 */ - struct sockaddr_in addr; /* 1 */ - char hostname[256]; /* 1 */ - int namlen; /* 2 */ - unsigned int bsize; /* 3 */ - struct nfs3_fh root; /* 4 */ -}; + /* + * The following loop implements the mount retries. When the mount + * times out, and the "bg" option is set, we background ourself + * and continue trying. + * + * The case where the mount point is not present and the "bg" + * option is set, is treated as a timeout. This is done to + * support nested mounts. + * + * The "retry" count specified by the user is the number of + * minutes to retry before giving up. + */ + { + struct timeval total_timeout; + struct timeval retry_timeout; + struct pmap* pm_mnt; + time_t t; + time_t prevt; + time_t timeout; -/* bits in the flags field */ -enum { - NFS_MOUNT_SOFT = 0x0001, /* 1 */ - NFS_MOUNT_INTR = 0x0002, /* 1 */ - NFS_MOUNT_SECURE = 0x0004, /* 1 */ - NFS_MOUNT_POSIX = 0x0008, /* 1 */ - NFS_MOUNT_NOCTO = 0x0010, /* 1 */ - NFS_MOUNT_NOAC = 0x0020, /* 1 */ - NFS_MOUNT_TCP = 0x0040, /* 2 */ - NFS_MOUNT_VER3 = 0x0080, /* 3 */ - NFS_MOUNT_KERBEROS = 0x0100, /* 3 */ - NFS_MOUNT_NONLM = 0x0200 /* 3 */ -}; + retry_timeout.tv_sec = 3; + retry_timeout.tv_usec = 0; + total_timeout.tv_sec = 20; + total_timeout.tv_usec = 0; + timeout = time(NULL) + 60 * retry; + prevt = 0; + t = 30; +retry: + /* be careful not to use too many CPU cycles */ + if (t - prevt < 30) + sleep(30); + pm_mnt = get_mountport(&mount_server_addr, + mountprog, + mountvers, + proto, + mountport); + nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; -/* - * We need to translate between nfs status return values and - * the local errno values which may not be the same. - * - * Andreas Schwab : change errno: - * "after #include the symbol errno is reserved for any use, - * it cannot even be used as a struct tag or field name". - */ + /* contact the mount daemon via TCP */ + mount_server_addr.sin_port = htons(pm_mnt->pm_port); + msock = RPC_ANYSOCK; -#ifndef EDQUOT -#define EDQUOT ENOSPC -#endif + switch (pm_mnt->pm_prot) { + case IPPROTO_UDP: + mclient = clntudp_create(&mount_server_addr, + pm_mnt->pm_prog, + pm_mnt->pm_vers, + retry_timeout, + &msock); + if (mclient) + break; + mount_server_addr.sin_port = htons(pm_mnt->pm_port); + msock = RPC_ANYSOCK; + case IPPROTO_TCP: + mclient = clnttcp_create(&mount_server_addr, + pm_mnt->pm_prog, + pm_mnt->pm_vers, + &msock, 0, 0); + break; + default: + mclient = 0; + } + if (!mclient) { + if (!daemonized && prevt == 0) + error_msg_rpc(clnt_spcreateerror(" ")); + } else { + enum clnt_stat clnt_stat; + /* try to mount hostname:pathname */ + mclient->cl_auth = authunix_create_default(); -// Convert each NFSERR_BLAH into EBLAH + /* make pointers in xdr_mountres3 NULL so + * that xdr_array allocates memory for us + */ + memset(&status, 0, sizeof(status)); -static const struct { - int stat; - int errnum; -} nfs_errtbl[] = { - {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST}, - {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG}, - {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT}, - {70,ESTALE}, {71,EREMOTE}, {-1,EIO} -}; + if (pm_mnt->pm_vers == 3) + clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, + (xdrproc_t) xdr_dirpath, + (caddr_t) &pathname, + (xdrproc_t) xdr_mountres3, + (caddr_t) &status, + total_timeout); + else + clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, + (xdrproc_t) xdr_dirpath, + (caddr_t) &pathname, + (xdrproc_t) xdr_fhstatus, + (caddr_t) &status, + total_timeout); -static char *nfs_strerror(int status) -{ - int i; - static char buf[256]; + if (clnt_stat == RPC_SUCCESS) + goto prepare_kernel_data; /* we're done */ + if (errno != ECONNREFUSED) { + error_msg_rpc(clnt_sperror(mclient, " ")); + goto fail; /* don't retry */ + } + /* Connection refused */ + if (!daemonized && prevt == 0) /* print just once */ + error_msg_rpc(clnt_sperror(mclient, " ")); + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + mclient = 0; + close(msock); + } - for (i = 0; nfs_errtbl[i].stat != -1; i++) { - if (nfs_errtbl[i].stat == status) - return strerror(nfs_errtbl[i].errnum); - } - sprintf(buf, "unknown nfs status return value: %d", status); - return buf; -} + /* Timeout. We are going to retry... maybe */ -static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) -{ - if (!xdr_opaque(xdrs, objp, FHSIZE)) - return FALSE; - return TRUE; -} + if (!bg) + goto fail; + if (!daemonized) { + daemonized = daemonize(); + if (daemonized <= 0) { /* parent or error */ + retval = -daemonized; + goto ret; + } + } + prevt = t; + t = time(NULL); + if (t >= timeout) + /* TODO error message */ + goto fail; -static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp) -{ - if (!xdr_u_int(xdrs, &objp->fhs_status)) - return FALSE; - switch (objp->fhs_status) { - case 0: - if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) - return FALSE; - break; - default: - break; + goto retry; } - return TRUE; -} -static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) -{ - if (!xdr_string(xdrs, objp, MNTPATHLEN)) - return FALSE; - return TRUE; -} +prepare_kernel_data: -static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) -{ - if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) - return FALSE; - return TRUE; -} + if (nfsvers == 2) { + if (status.nfsv2.fhs_status != 0) { + bb_error_msg("%s:%s failed, reason given by server: %s", + hostname, pathname, + nfs_strerror(status.nfsv2.fhs_status)); + goto fail; + } + memcpy(data.root.data, + (char *) status.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); + data.root.size = NFS_FHSIZE; + memcpy(data.old_root.data, + (char *) status.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); + } else { + fhandle3 *my_fhandle; + if (status.nfsv3.fhs_status != 0) { + bb_error_msg("%s:%s failed, reason given by server: %s", + hostname, pathname, + nfs_strerror(status.nfsv3.fhs_status)); + goto fail; + } + my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; + memset(data.old_root.data, 0, NFS_FHSIZE); + memset(&data.root, 0, sizeof(data.root)); + data.root.size = my_fhandle->fhandle3_len; + memcpy(data.root.data, + (char *) my_fhandle->fhandle3_val, + my_fhandle->fhandle3_len); -static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) -{ - if (!xdr_fhandle3(xdrs, &objp->fhandle)) - return FALSE; - if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0, - sizeof (int), (xdrproc_t) xdr_int)) - return FALSE; - return TRUE; -} + data.flags |= NFS_MOUNT_VER3; + } -static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp) -{ - if (!xdr_enum(xdrs, (enum_t *) objp)) - return FALSE; - return TRUE; -} + /* create nfs socket for kernel */ -static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) -{ - if (!xdr_mountstat3(xdrs, &objp->fhs_status)) - return FALSE; - switch (objp->fhs_status) { - case MNT_OK: - if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo)) - return FALSE; - break; - default: - break; + if (tcp) { + if (nfs_mount_version < 3) { + bb_error_msg("NFS over TCP is not supported"); + goto fail; + } + fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + } else + fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fsock < 0) { + bb_perror_msg("nfs socket"); + goto fail; } - return TRUE; -} + if (bindresvport(fsock, 0) < 0) { + bb_perror_msg("nfs bindresvport"); + goto fail; + } + if (port == 0) { + server_addr.sin_port = PMAPPORT; + port = pmap_getport(&server_addr, nfsprog, nfsvers, + tcp ? IPPROTO_TCP : IPPROTO_UDP); + if (port == 0) + port = NFS_PORT; + } + server_addr.sin_port = htons(port); -#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) + /* prepare data structure for kernel */ -/* - * nfs_mount_version according to the sources seen at compile time. - */ -static int nfs_mount_version; -static int kernel_version; + data.fd = fsock; + memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); + strncpy(data.hostname, hostname, sizeof(data.hostname)); -/* - * Unfortunately, the kernel prints annoying console messages - * in case of an unexpected nfs mount version (instead of - * just returning some error). Therefore we'll have to try - * and figure out what version the kernel expects. - * - * Variables: - * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time - * NFS_MOUNT_VERSION: these nfsmount sources at compile time - * nfs_mount_version: version this source and running kernel can handle - */ -static void -find_kernel_nfs_mount_version(void) -{ - if (kernel_version) - return; + /* clean up */ - nfs_mount_version = 4; /* default */ + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + close(msock); - kernel_version = get_linux_version_code(); - if (kernel_version) { - if (kernel_version < KERNEL_VERSION(2,1,32)) - nfs_mount_version = 1; - else if (kernel_version < KERNEL_VERSION(2,2,18) || - (kernel_version >= KERNEL_VERSION(2,3,0) && - kernel_version < KERNEL_VERSION(2,3,99))) - nfs_mount_version = 3; - /* else v4 since 2.3.99pre4 */ + if (bg) { + /* We must wait until mount directory is available */ + struct stat statbuf; + int delay = 1; + while (stat(mp->mnt_dir, &statbuf) == -1) { + if (!daemonized) { + daemonized = daemonize(); + if (daemonized <= 0) { /* parent or error */ + retval = -daemonized; + goto ret; + } + } + sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */ + delay *= 2; + if (delay > 30) + delay = 30; + } } -} -static struct pmap * -get_mountport(struct sockaddr_in *server_addr, - long unsigned prog, - long unsigned version, - long unsigned proto, - long unsigned port) -{ - struct pmaplist *pmap; - static struct pmap p = {0, 0, 0, 0}; +do_mount: /* perform actual mount */ - server_addr->sin_port = PMAPPORT; - pmap = pmap_getmaps(server_addr); + mp->mnt_type = "nfs"; + retval = mount_it_now(mp, vfsflags, (char*)&data); + goto ret; - if (version > MAX_NFSPROT) - version = MAX_NFSPROT; - if (!prog) - prog = MOUNTPROG; - p.pm_prog = prog; - p.pm_vers = version; - p.pm_prot = proto; - p.pm_port = port; - - while (pmap) { - if (pmap->pml_map.pm_prog != prog) - goto next; - if (!version && p.pm_vers > pmap->pml_map.pm_vers) - goto next; - if (version > 2 && pmap->pml_map.pm_vers != version) - goto next; - if (version && version <= 2 && pmap->pml_map.pm_vers > 2) - goto next; - if (pmap->pml_map.pm_vers > MAX_NFSPROT || - (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || - (port && pmap->pml_map.pm_port != port)) - goto next; - memcpy(&p, &pmap->pml_map, sizeof(p)); -next: - pmap = pmap->pml_next; +fail: /* abort */ + + if (msock != -1) { + if (mclient) { + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + } + close(msock); } - if (!p.pm_vers) - p.pm_vers = MOUNTVERS; - if (!p.pm_port) - p.pm_port = MOUNTPORT; - if (!p.pm_prot) - p.pm_prot = IPPROTO_TCP; - return &p; -} + if (fsock != -1) + close(fsock); -static int daemonize(void) -{ - int fd; - int pid = fork(); - if (pid < 0) /* error */ - return -errno; - if (pid > 0) /* parent */ - return 0; - /* child */ - fd = xopen(bb_dev_null, O_RDWR); - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) close(fd); - setsid(); - openlog(bb_applet_name, LOG_PID, LOG_DAEMON); - logmode = LOGMODE_SYSLOG; - return 1; +ret: + free(hostname); + free(mounthost); + free(filteropts); + return retval; } -// TODO -static inline int we_saw_this_host_before(const char *hostname) -{ - return 0; -} +#else /* !ENABLE_FEATURE_MOUNT_NFS */ -/* RPC strerror analogs are terminally idiotic: - * *mandatory* prefix and \n at end. - * This hopefully helps. Usage: - * error_msg_rpc(clnt_*error*(" ")) */ -static void error_msg_rpc(const char *msg) -{ - size_t len; - while (msg[0] == ' ' || msg[0] == ':') msg++; - len = strlen(msg); - while (len && msg[len-1] == '\n') len--; - bb_error_msg("%.*s", len, msg); -} +/* Never called. Call should be optimized out. */ +int nfsmount(struct mntent *mp, int vfsflags, char *filteropts); -static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) +#endif /* !ENABLE_FEATURE_MOUNT_NFS */ + +// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem +// type detection. Returns 0 for success, nonzero for failure. + +static int singlemount(struct mntent *mp, int ignore_busy) { - CLIENT *mclient; - char *hostname; - char *pathname; - char *mounthost; - struct nfs_mount_data data; - char *opt; - struct hostent *hp; - struct sockaddr_in server_addr; - struct sockaddr_in mount_server_addr; - int msock, fsock; - union { - struct fhstatus nfsv2; - struct mountres3 nfsv3; - } status; - int daemonized; - char *s; - int port; - int mountport; - int proto; - int bg; - int soft; - int intr; - int posix; - int nocto; - int noac; - int nolock; - int retry; - int tcp; - int mountprog; - int mountvers; - int nfsprog; - int nfsvers; - int retval; + int rc = -1, vfsflags; + char *loopFile = 0, *filteropts = 0; + llist_t *fl = 0; + struct stat st; - find_kernel_nfs_mount_version(); + vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); - daemonized = 0; - mounthost = NULL; - retval = ETIMEDOUT; - msock = fsock = -1; - mclient = NULL; + // Treat fstype "auto" as unspecified. - /* NB: hostname, mounthost, filteropts must be free()d prior to return */ + if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0; - filteropts = xstrdup(filteropts); /* going to trash it later... */ + // Might this be an CIFS filesystem? - hostname = xstrdup(mp->mnt_fsname); - /* mount_main() guarantees that ':' is there */ - s = strchr(hostname, ':'); - pathname = s + 1; - *s = '\0'; - /* Ignore all but first hostname in replicated mounts - until they can be fully supported. (mack@sgi.com) */ - s = strchr(hostname, ','); - if (s) { - *s = '\0'; - bb_error_msg("warning: multiple hostnames not supported"); - } + if(ENABLE_FEATURE_MOUNT_CIFS && + (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) && + (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\'))) + { + struct hostent *he; + char ip[32], *s; - server_addr.sin_family = AF_INET; - if (!inet_aton(hostname, &server_addr.sin_addr)) { - hp = gethostbyname(hostname); - if (hp == NULL) { - bb_herror_msg("%s", hostname); - goto fail; - } - if (hp->h_length > sizeof(struct in_addr)) { - bb_error_msg("got bad hp->h_length"); - hp->h_length = sizeof(struct in_addr); - } - memcpy(&server_addr.sin_addr, - hp->h_addr, hp->h_length); - } + rc = 1; + // Replace '/' with '\' and verify that unc points to "//server/share". - memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); + for (s = mp->mnt_fsname; *s; ++s) + if (*s == '/') *s = '\\'; - /* add IP address to mtab options for use when unmounting */ + // get server IP - if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ - mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); - } else { - char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, - mp->mnt_opts[0] ? "," : "", - inet_ntoa(server_addr.sin_addr)); - free(mp->mnt_opts); - mp->mnt_opts = tmp; + s = strrchr(mp->mnt_fsname, '\\'); + if (s == mp->mnt_fsname+1) goto report_error; + *s = 0; + he = gethostbyname(mp->mnt_fsname+2); + *s = '\\'; + if (!he) goto report_error; + + // Insert ip=... option into string flags. (NOTE: Add IPv6 support.) + + sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1], + he->h_addr[2], he->h_addr[3]); + parse_mount_options(ip, &filteropts); + + // compose new unc '\\server-ip\share' + + s = xasprintf("\\\\%s%s",ip+3,strchr(mp->mnt_fsname+2,'\\')); + if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname); + mp->mnt_fsname = s; + + // lock is required + vfsflags |= MS_MANDLOCK; + + mp->mnt_type = "cifs"; + rc = mount_it_now(mp, vfsflags, filteropts); + goto report_error; } - /* Set default options. - * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to - * let the kernel decide. - * timeo is filled in after we know whether it'll be TCP or UDP. */ - memset(&data, 0, sizeof(data)); - data.retrans = 3; - data.acregmin = 3; - data.acregmax = 60; - data.acdirmin = 30; - data.acdirmax = 60; - data.namlen = NAME_MAX; + // Might this be an NFS filesystem? - bg = 0; - soft = 0; - intr = 0; - posix = 0; - nocto = 0; - nolock = 0; - noac = 0; - retry = 10000; /* 10000 minutes ~ 1 week */ - tcp = 0; + if (ENABLE_FEATURE_MOUNT_NFS && + (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) && + strchr(mp->mnt_fsname, ':') != NULL) + { + rc = nfsmount(mp, vfsflags, filteropts); + goto report_error; + } - mountprog = MOUNTPROG; - mountvers = 0; - port = 0; - mountport = 0; - nfsprog = 100003; - nfsvers = 0; + // Look at the file. (Not found isn't a failure for remount, or for + // a synthetic filesystem like proc or sysfs.) - /* parse options */ + if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) + { + // Do we need to allocate a loopback device for it? - for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { - char *opteq = strchr(opt, '='); - if (opteq) { - int val = atoi(opteq + 1); - *opteq = '\0'; - if (!strcmp(opt, "rsize")) - data.rsize = val; - else if (!strcmp(opt, "wsize")) - data.wsize = val; - else if (!strcmp(opt, "timeo")) - data.timeo = val; - else if (!strcmp(opt, "retrans")) - data.retrans = val; - else if (!strcmp(opt, "acregmin")) - data.acregmin = val; - else if (!strcmp(opt, "acregmax")) - data.acregmax = val; - else if (!strcmp(opt, "acdirmin")) - data.acdirmin = val; - else if (!strcmp(opt, "acdirmax")) - data.acdirmax = val; - else if (!strcmp(opt, "actimeo")) { - data.acregmin = val; - data.acregmax = val; - data.acdirmin = val; - data.acdirmax = val; - } - else if (!strcmp(opt, "retry")) - retry = val; - else if (!strcmp(opt, "port")) - port = val; - else if (!strcmp(opt, "mountport")) - mountport = val; - else if (!strcmp(opt, "mounthost")) - mounthost = xstrndup(opteq+1, - strcspn(opteq+1," \t\n\r,")); - else if (!strcmp(opt, "mountprog")) - mountprog = val; - else if (!strcmp(opt, "mountvers")) - mountvers = val; - else if (!strcmp(opt, "nfsprog")) - nfsprog = val; - else if (!strcmp(opt, "nfsvers") || - !strcmp(opt, "vers")) - nfsvers = val; - else if (!strcmp(opt, "proto")) { - if (!strncmp(opteq+1, "tcp", 3)) - tcp = 1; - else if (!strncmp(opteq+1, "udp", 3)) - tcp = 0; - else - bb_error_msg("warning: unrecognized proto= option"); - } else if (!strcmp(opt, "namlen")) { - if (nfs_mount_version >= 2) - data.namlen = val; - else - bb_error_msg("warning: option namlen is not supported\n"); - } else if (!strcmp(opt, "addr")) - /* ignore */; - else { - bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); - goto fail; + if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { + loopFile = bb_simplify_path(mp->mnt_fsname); + mp->mnt_fsname = 0; + switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) { + case 0: + case 1: + break; + default: + bb_error_msg( errno == EPERM || errno == EACCES + ? bb_msg_perm_denied_are_you_root + : "cannot setup loop device"); + return errno; } + + // Autodetect bind mounts + + } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) + vfsflags |= MS_BIND; + } + + /* If we know the fstype (or don't need to), jump straight + * to the actual mount. */ + + if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) + rc = mount_it_now(mp, vfsflags, filteropts); + + // Loop through filesystem types until mount succeeds or we run out + + else { + + /* Initialize list of block backed filesystems. This has to be + * done here so that during "mount -a", mounts after /proc shows up + * can autodetect. */ + + if (!fslist) { + fslist = get_block_backed_filesystems(); + if (ENABLE_FEATURE_CLEAN_UP && fslist) + atexit(delete_block_backed_filesystems); } - else { - int val = 1; - if (!strncmp(opt, "no", 2)) { - val = 0; - opt += 2; - } - if (!strcmp(opt, "bg")) - bg = val; - else if (!strcmp(opt, "fg")) - bg = !val; - else if (!strcmp(opt, "soft")) - soft = val; - else if (!strcmp(opt, "hard")) - soft = !val; - else if (!strcmp(opt, "intr")) - intr = val; - else if (!strcmp(opt, "posix")) - posix = val; - else if (!strcmp(opt, "cto")) - nocto = !val; - else if (!strcmp(opt, "ac")) - noac = !val; - else if (!strcmp(opt, "tcp")) - tcp = val; - else if (!strcmp(opt, "udp")) - tcp = !val; - else if (!strcmp(opt, "lock")) { - if (nfs_mount_version >= 3) - nolock = !val; - else - bb_error_msg("warning: option nolock is not supported"); - } else { - bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); - goto fail; - } + + for (fl = fslist; fl; fl = fl->link) { + mp->mnt_type = fl->data; + + if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break; + + mp->mnt_type = 0; } } - proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; - data.flags = (soft ? NFS_MOUNT_SOFT : 0) - | (intr ? NFS_MOUNT_INTR : 0) - | (posix ? NFS_MOUNT_POSIX : 0) - | (nocto ? NFS_MOUNT_NOCTO : 0) - | (noac ? NFS_MOUNT_NOAC : 0); - if (nfs_mount_version >= 2) - data.flags |= (tcp ? NFS_MOUNT_TCP : 0); - if (nfs_mount_version >= 3) - data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); - if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { - bb_error_msg("NFSv%d not supported", nfsvers); - goto fail; - } - if (nfsvers && !mountvers) - mountvers = (nfsvers < 3) ? 1 : nfsvers; - if (nfsvers && nfsvers < mountvers) { - mountvers = nfsvers; + // If mount failed, clean up loop file (if any). + + if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { + del_loop(mp->mnt_fsname); + if (ENABLE_FEATURE_CLEAN_UP) { + free(loopFile); + free(mp->mnt_fsname); + } } - /* Adjust options if none specified */ - if (!data.timeo) - data.timeo = tcp ? 70 : 7; +report_error: + if (ENABLE_FEATURE_CLEAN_UP) free(filteropts); -#ifdef NFS_MOUNT_DEBUG - printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", - data.rsize, data.wsize, data.timeo, data.retrans); - printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", - data.acregmin, data.acregmax, data.acdirmin, data.acdirmax); - printf("port = %d, bg = %d, retry = %d, flags = %.8x\n", - port, bg, retry, data.flags); - printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n", - mountprog, mountvers, nfsprog, nfsvers); - printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", - (data.flags & NFS_MOUNT_SOFT) != 0, - (data.flags & NFS_MOUNT_INTR) != 0, - (data.flags & NFS_MOUNT_POSIX) != 0, - (data.flags & NFS_MOUNT_NOCTO) != 0, - (data.flags & NFS_MOUNT_NOAC) != 0); - printf("tcp = %d\n", - (data.flags & NFS_MOUNT_TCP) != 0); -#endif + if (rc && errno == EBUSY && ignore_busy) rc = 0; + if (rc < 0) + /* perror here sometimes says "mounting ... on ... failed: Success" */ + bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); - data.version = nfs_mount_version; + return rc; +} - if (vfsflags & MS_REMOUNT) - goto do_mount; +// Parse options, if necessary parse fstab/mtab, and call singlemount for +// each directory to be mounted. - /* - * If the previous mount operation on the same host was - * backgrounded, and the "bg" for this mount is also set, - * give up immediately, to avoid the initial timeout. - */ - if (bg && we_saw_this_host_before(hostname)) { - daemonized = daemonize(); /* parent or error */ - if (daemonized <= 0) { /* parent or error */ - retval = -daemonized; - goto ret; +int mount_main(int argc, char **argv) +{ + char *cmdopts = xstrdup(""), *fstabname, *fstype=0, *storage_path=0; + FILE *fstab; + int i, opt, all = FALSE, rc = 0; + struct mntent mtpair[2], *mtcur = mtpair; + + /* parse long options, like --bind and --move. Note that -o option + * and --option are synonymous. Yes, this means --remount,rw works. */ + + for (i = opt = 0; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '-') { + append_mount_options(&cmdopts,argv[i]+2); + } else argv[opt++] = argv[i]; + } + argc = opt; + + // Parse remaining options + + while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) { + switch (opt) { + case 'o': + append_mount_options(&cmdopts, optarg); + break; + case 't': + fstype = optarg; + break; + case 'r': + append_mount_options(&cmdopts, "ro"); + break; + case 'w': + append_mount_options(&cmdopts, "rw"); + break; + case 'a': + all = TRUE; + break; + case 'n': + USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;) + break; + case 'f': + USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;) + break; + case 'v': + break; // ignore -v + default: + bb_show_usage(); } } - /* create mount daemon client */ - /* See if the nfs host = mount host. */ - if (mounthost) { - if (mounthost[0] >= '0' && mounthost[0] <= '9') { - mount_server_addr.sin_family = AF_INET; - mount_server_addr.sin_addr.s_addr = inet_addr(hostname); - } else { - hp = gethostbyname(mounthost); - if (hp == NULL) { - bb_herror_msg("%s", mounthost); - goto fail; - } else { - if (hp->h_length > sizeof(struct in_addr)) { - bb_error_msg("got bad hp->h_length?"); - hp->h_length = sizeof(struct in_addr); - } - mount_server_addr.sin_family = AF_INET; - memcpy(&mount_server_addr.sin_addr, - hp->h_addr, hp->h_length); + // Three or more non-option arguments? Die with a usage message. + + if (optind-argc>2) bb_show_usage(); + + // If we have no arguments, show currently mounted filesystems + + if (optind == argc) { + if (!all) { + FILE *mountTable = setmntent(bb_path_mtab_file, "r"); + + if(!mountTable) bb_error_msg_and_die("no %s",bb_path_mtab_file); + + while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1, + sizeof(bb_common_bufsiz1))) + { + // Don't show rootfs. + if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue; + + if (!fstype || !strcmp(mtpair->mnt_type, fstype)) + printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, + mtpair->mnt_dir, mtpair->mnt_type, + mtpair->mnt_opts); } + if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable); + return EXIT_SUCCESS; } + } else storage_path = bb_simplify_path(argv[optind]); + + // When we have two arguments, the second is the directory and we can + // skip looking at fstab entirely. We can always abspath() the directory + // argument when we get it. + + if (optind+2 == argc) { + mtpair->mnt_fsname = argv[optind]; + mtpair->mnt_dir = argv[optind+1]; + mtpair->mnt_type = fstype; + mtpair->mnt_opts = cmdopts; + rc = singlemount(mtpair, 0); + goto clean_up; } - /* - * The following loop implements the mount retries. When the mount - * times out, and the "bg" option is set, we background ourself - * and continue trying. - * - * The case where the mount point is not present and the "bg" - * option is set, is treated as a timeout. This is done to - * support nested mounts. - * - * The "retry" count specified by the user is the number of - * minutes to retry before giving up. - */ + // If we have a shared subtree flag, don't worry about fstab or mtab. + i = parse_mount_options(cmdopts,0); + if (ENABLE_FEATURE_MOUNT_FLAGS && + (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE ))) { - struct timeval total_timeout; - struct timeval retry_timeout; - struct pmap* pm_mnt; - time_t t; - time_t prevt; - time_t timeout; + rc = mount("", argv[optind], "", i, ""); + if (rc) bb_perror_msg_and_die("%s", argv[optind]); + goto clean_up; + } + + // Open either fstab or mtab + + if (parse_mount_options(cmdopts,0) & MS_REMOUNT) + fstabname = bb_path_mtab_file; + else fstabname="/etc/fstab"; - retry_timeout.tv_sec = 3; - retry_timeout.tv_usec = 0; - total_timeout.tv_sec = 20; - total_timeout.tv_usec = 0; - timeout = time(NULL) + 60 * retry; - prevt = 0; - t = 30; -retry: - /* be careful not to use too many CPU cycles */ - if (t - prevt < 30) - sleep(30); + if (!(fstab=setmntent(fstabname,"r"))) + bb_perror_msg_and_die("cannot read %s",fstabname); - pm_mnt = get_mountport(&mount_server_addr, - mountprog, - mountvers, - proto, - mountport); - nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; + // Loop through entries until we find what we're looking for. - /* contact the mount daemon via TCP */ - mount_server_addr.sin_port = htons(pm_mnt->pm_port); - msock = RPC_ANYSOCK; + memset(mtpair,0,sizeof(mtpair)); + for (;;) { + struct mntent *mtnext = mtpair + (mtcur==mtpair ? 1 : 0); - switch (pm_mnt->pm_prot) { - case IPPROTO_UDP: - mclient = clntudp_create(&mount_server_addr, - pm_mnt->pm_prog, - pm_mnt->pm_vers, - retry_timeout, - &msock); - if (mclient) - break; - mount_server_addr.sin_port = htons(pm_mnt->pm_port); - msock = RPC_ANYSOCK; - case IPPROTO_TCP: - mclient = clnttcp_create(&mount_server_addr, - pm_mnt->pm_prog, - pm_mnt->pm_vers, - &msock, 0, 0); - break; - default: - mclient = 0; - } - if (!mclient) { - if (!daemonized && prevt == 0) - error_msg_rpc(clnt_spcreateerror(" ")); - } else { - enum clnt_stat clnt_stat; - /* try to mount hostname:pathname */ - mclient->cl_auth = authunix_create_default(); + // Get next fstab entry - /* make pointers in xdr_mountres3 NULL so - * that xdr_array allocates memory for us - */ - memset(&status, 0, sizeof(status)); + if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1 + + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0), + sizeof(bb_common_bufsiz1)/2)) + { + // Were we looking for something specific? - if (pm_mnt->pm_vers == 3) - clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, - (xdrproc_t) xdr_dirpath, - (caddr_t) &pathname, - (xdrproc_t) xdr_mountres3, - (caddr_t) &status, - total_timeout); - else - clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, - (xdrproc_t) xdr_dirpath, - (caddr_t) &pathname, - (xdrproc_t) xdr_fhstatus, - (caddr_t) &status, - total_timeout); + if (optind != argc) { - if (clnt_stat == RPC_SUCCESS) - goto prepare_kernel_data; /* we're done */ - if (errno != ECONNREFUSED) { - error_msg_rpc(clnt_sperror(mclient, " ")); - goto fail; /* don't retry */ - } - /* Connection refused */ - if (!daemonized && prevt == 0) /* print just once */ - error_msg_rpc(clnt_sperror(mclient, " ")); - auth_destroy(mclient->cl_auth); - clnt_destroy(mclient); - mclient = 0; - close(msock); - } + // If we didn't find anything, complain. - /* Timeout. We are going to retry... maybe */ + if (!mtnext->mnt_fsname) + bb_error_msg_and_die("can't find %s in %s", + argv[optind], fstabname); - if (!bg) - goto fail; - if (!daemonized) { - daemonized = daemonize(); - if (daemonized <= 0) { /* parent or error */ - retval = -daemonized; - goto ret; + // Mount the last thing we found. + + mtcur = mtnext; + mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); + append_mount_options(&(mtcur->mnt_opts),cmdopts); + rc = singlemount(mtcur, 0); + free(mtcur->mnt_opts); } + goto clean_up; } - prevt = t; - t = time(NULL); - if (t >= timeout) - /* TODO error message */ - goto fail; - goto retry; - } + /* If we're trying to mount something specific and this isn't it, + * skip it. Note we must match both the exact text in fstab (ala + * "proc") or a full path from root */ -prepare_kernel_data: + if (optind != argc) { - if (nfsvers == 2) { - if (status.nfsv2.fhs_status != 0) { - bb_error_msg("%s:%s failed, reason given by server: %s", - hostname, pathname, - nfs_strerror(status.nfsv2.fhs_status)); - goto fail; - } - memcpy(data.root.data, - (char *) status.nfsv2.fhstatus_u.fhs_fhandle, - NFS_FHSIZE); - data.root.size = NFS_FHSIZE; - memcpy(data.old_root.data, - (char *) status.nfsv2.fhstatus_u.fhs_fhandle, - NFS_FHSIZE); - } else { - fhandle3 *my_fhandle; - if (status.nfsv3.fhs_status != 0) { - bb_error_msg("%s:%s failed, reason given by server: %s", - hostname, pathname, - nfs_strerror(status.nfsv3.fhs_status)); - goto fail; - } - my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; - memset(data.old_root.data, 0, NFS_FHSIZE); - memset(&data.root, 0, sizeof(data.root)); - data.root.size = my_fhandle->fhandle3_len; - memcpy(data.root.data, - (char *) my_fhandle->fhandle3_val, - my_fhandle->fhandle3_len); + // Is this what we're looking for? - data.flags |= NFS_MOUNT_VER3; - } + if(strcmp(argv[optind],mtcur->mnt_fsname) && + strcmp(storage_path,mtcur->mnt_fsname) && + strcmp(argv[optind],mtcur->mnt_dir) && + strcmp(storage_path,mtcur->mnt_dir)) continue; - /* create nfs socket for kernel */ + // Remember this entry. Something later may have overmounted + // it, and we want the _last_ match. - if (tcp) { - if (nfs_mount_version < 3) { - bb_error_msg("NFS over TCP is not supported"); - goto fail; - } - fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - } else - fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fsock < 0) { - bb_perror_msg("nfs socket"); - goto fail; - } - if (bindresvport(fsock, 0) < 0) { - bb_perror_msg("nfs bindresvport"); - goto fail; - } - if (port == 0) { - server_addr.sin_port = PMAPPORT; - port = pmap_getport(&server_addr, nfsprog, nfsvers, - tcp ? IPPROTO_TCP : IPPROTO_UDP); - if (port == 0) - port = NFS_PORT; -#ifdef NFS_MOUNT_DEBUG - else - printf("used portmapper to find NFS port\n"); -#endif - } -#ifdef NFS_MOUNT_DEBUG - printf("using port %d for nfs daemon\n", port); -#endif - server_addr.sin_port = htons(port); + mtcur = mtnext; - /* prepare data structure for kernel */ + // If we're mounting all. - data.fd = fsock; - memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); - strncpy(data.hostname, hostname, sizeof(data.hostname)); + } else { - /* clean up */ + // Do we need to match a filesystem type? + if (fstype && strcmp(mtcur->mnt_type,fstype)) continue; - auth_destroy(mclient->cl_auth); - clnt_destroy(mclient); - close(msock); + // Skip noauto and swap anyway. - if (bg) { - /* We must wait until mount directory is available */ - struct stat statbuf; - int delay = 1; - while (stat(mp->mnt_dir, &statbuf) == -1) { - if (!daemonized) { - daemonized = daemonize(); - if (daemonized <= 0) { /* parent or error */ - retval = -daemonized; - goto ret; - } + if (parse_mount_options(mtcur->mnt_opts,0) + & (MOUNT_NOAUTO | MOUNT_SWAP)) continue; + + // Mount this thing. + + if (singlemount(mtcur, 1)) { + /* Count number of failed mounts */ + rc++; } - sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */ - delay *= 2; - if (delay > 30) - delay = 30; } } + if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab); -do_mount: /* perform actual mount */ - - mp->mnt_type = "nfs"; - retval = mount_it_now(mp, vfsflags, (char*)&data); - goto ret; - -fail: /* abort */ +clean_up: - if (msock != -1) { - if (mclient) { - auth_destroy(mclient->cl_auth); - clnt_destroy(mclient); - } - close(msock); + if (ENABLE_FEATURE_CLEAN_UP) { + free(storage_path); + free(cmdopts); } - if (fsock != -1) - close(fsock); -ret: - free(hostname); - free(mounthost); - free(filteropts); - return retval; + return rc; } - -#endif /* ENABLE_FEATURE_MOUNT_NFS */ -- cgit v1.1