138494Sobrien/* 2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok 338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1990 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 1938494Sobrien * 3. All advertising materials mentioning features or use of this software 2042629Sobrien * must display the following acknowledgment: 2138494Sobrien * This product includes software developed by the University of 2238494Sobrien * California, Berkeley and its contributors. 2338494Sobrien * 4. Neither the name of the University nor the names of its contributors 2438494Sobrien * may be used to endorse or promote products derived from this software 2538494Sobrien * without specific prior written permission. 2638494Sobrien * 2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3038494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3738494Sobrien * SUCH DAMAGE. 3838494Sobrien * 3938494Sobrien * 40174294Sobrien * File: am-utils/amd/amfs_host.c 4138494Sobrien * 4238494Sobrien */ 4338494Sobrien 4438494Sobrien/* 4538494Sobrien * NFS host file system. 4638494Sobrien * Mounts all exported filesystems from a given host. 4738494Sobrien * This has now degenerated into a mess but will not 4838494Sobrien * be rewritten. Amd 6 will support the abstractions 4938494Sobrien * needed to make this work correctly. 5038494Sobrien */ 5138494Sobrien 5238494Sobrien#ifdef HAVE_CONFIG_H 5338494Sobrien# include <config.h> 5438494Sobrien#endif /* HAVE_CONFIG_H */ 5538494Sobrien#include <am_defs.h> 5638494Sobrien#include <amd.h> 5738494Sobrien 5838494Sobrienstatic char *amfs_host_match(am_opts *fo); 5938494Sobrienstatic int amfs_host_init(mntfs *mf); 60174294Sobrienstatic int amfs_host_mount(am_node *am, mntfs *mf); 61174294Sobrienstatic int amfs_host_umount(am_node *am, mntfs *mf); 62174294Sobrienstatic void amfs_host_umounted(mntfs *mf); 6338494Sobrien 6438494Sobrien/* 6538494Sobrien * Ops structure 6638494Sobrien */ 6738494Sobrienam_ops amfs_host_ops = 6838494Sobrien{ 6938494Sobrien "host", 7038494Sobrien amfs_host_match, 7138494Sobrien amfs_host_init, 72174294Sobrien amfs_host_mount, 73174294Sobrien amfs_host_umount, 74174294Sobrien amfs_error_lookup_child, 75174294Sobrien amfs_error_mount_child, 7638494Sobrien amfs_error_readdir, 7738494Sobrien 0, /* amfs_host_readlink */ 7838494Sobrien 0, /* amfs_host_mounted */ 7938494Sobrien amfs_host_umounted, 8038494Sobrien find_nfs_srvr, 81174294Sobrien 0, /* amfs_host_get_wchan */ 82174294Sobrien FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, 83174294Sobrien#ifdef HAVE_FS_AUTOFS 84174294Sobrien AUTOFS_HOST_FS_FLAGS, 85174294Sobrien#endif /* HAVE_FS_AUTOFS */ 8638494Sobrien}; 8738494Sobrien 8838494Sobrien 8938494Sobrien/* 9038494Sobrien * Determine the mount point: 9138494Sobrien * 9238494Sobrien * The next change we put in to better handle PCs. This is a bit 9338494Sobrien * disgusting, so you'd better sit down. We change the make_mntpt function 9438494Sobrien * to look for exported file systems without a leading '/'. If they don't 9538494Sobrien * have a leading '/', we add one. If the export is 'a:' through 'z:' 9638494Sobrien * (without a leading slash), we change it to 'a%' (or b% or z%). This 9738494Sobrien * allows the entire PC disk to be mounted. 9838494Sobrien */ 9938494Sobrienstatic void 100174294Sobrienmake_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount) 10138494Sobrien{ 10238494Sobrien if (ex->ex_dir[0] == '/') { 10338494Sobrien if (ex->ex_dir[1] == 0) 104174294Sobrien xstrlcpy(mntpt, mf_mount, l); 10538494Sobrien else 106174294Sobrien xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir); 10738494Sobrien } else if (ex->ex_dir[0] >= 'a' && 10838494Sobrien ex->ex_dir[0] <= 'z' && 10938494Sobrien ex->ex_dir[1] == ':' && 11038494Sobrien ex->ex_dir[2] == '/' && 11138494Sobrien ex->ex_dir[3] == 0) 112174294Sobrien xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]); 11338494Sobrien else 114174294Sobrien xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir); 11538494Sobrien} 11638494Sobrien 11738494Sobrien 11838494Sobrien/* 11938494Sobrien * Execute needs the same as NFS plus a helper command 12038494Sobrien */ 12138494Sobrienstatic char * 12238494Sobrienamfs_host_match(am_opts *fo) 12338494Sobrien{ 12438494Sobrien extern am_ops nfs_ops; 12538494Sobrien 12638494Sobrien /* 12738494Sobrien * Make sure rfs is specified to keep nfs_match happy... 12838494Sobrien */ 12938494Sobrien if (!fo->opt_rfs) 13038494Sobrien fo->opt_rfs = "/"; 13138494Sobrien 13238494Sobrien return (*nfs_ops.fs_match) (fo); 13338494Sobrien} 13438494Sobrien 13538494Sobrien 13638494Sobrienstatic int 13738494Sobrienamfs_host_init(mntfs *mf) 13838494Sobrien{ 139174294Sobrien u_short mountd_port; 14038494Sobrien 14138494Sobrien if (strchr(mf->mf_info, ':') == 0) 14238494Sobrien return ENOENT; 14338494Sobrien 14438494Sobrien /* 14538494Sobrien * This is primarily to schedule a wakeup so that as soon 14638494Sobrien * as our fileserver is ready, we can continue setting up 14738494Sobrien * the host filesystem. If we don't do this, the standard 14838494Sobrien * amfs_auto code will set up a fileserver structure, but it will 14938494Sobrien * have to wait for another nfs request from the client to come 15038494Sobrien * in before finishing. Our way is faster since we don't have 15138494Sobrien * to wait for the client to resend its request (which could 15238494Sobrien * take a second or two). 15338494Sobrien */ 15438494Sobrien /* 15538494Sobrien * First, we find the fileserver for this mntfs and then call 156174294Sobrien * get_mountd_port with our mntfs passed as the wait channel. 157174294Sobrien * get_mountd_port will check some things and then schedule 15838494Sobrien * it so that when the fileserver is ready, a wakeup is done 159174294Sobrien * on this mntfs. amfs_cont() is already sleeping on this mntfs 160174294Sobrien * so as soon as that wakeup happens amfs_cont() is called and 16138494Sobrien * this mount is retried. 16238494Sobrien */ 163174294Sobrien if (mf->mf_server) 16438494Sobrien /* 16538494Sobrien * We don't really care if there's an error returned. 16638494Sobrien * Since this is just to help speed things along, the 16738494Sobrien * error will get handled properly elsewhere. 16838494Sobrien */ 169174294Sobrien get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf)); 17038494Sobrien 17138494Sobrien return 0; 17238494Sobrien} 17338494Sobrien 17438494Sobrien 17538494Sobrienstatic int 176174294Sobriendo_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) 17738494Sobrien{ 17838494Sobrien struct stat stb; 17938494Sobrien 180174294Sobrien dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir); 18138494Sobrien 182174294Sobrien (void) mkdirs(mntdir, 0555); 183174294Sobrien if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { 184174294Sobrien plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir); 18538494Sobrien return ENOENT; 18638494Sobrien } 18738494Sobrien 188174294Sobrien return mount_nfs_fh(fhp, mntdir, fs_name, mf); 18938494Sobrien} 19038494Sobrien 19138494Sobrien 19238494Sobrienstatic int 19338494Sobriensortfun(const voidp x, const voidp y) 19438494Sobrien{ 19538494Sobrien exports *a = (exports *) x; 19638494Sobrien exports *b = (exports *) y; 19738494Sobrien 19838494Sobrien return strcmp((*a)->ex_dir, (*b)->ex_dir); 19938494Sobrien} 20038494Sobrien 20138494Sobrien 20238494Sobrien/* 20338494Sobrien * Get filehandle 20438494Sobrien */ 20538494Sobrienstatic int 20682794Sobrienfetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version) 20738494Sobrien{ 20838494Sobrien struct timeval tv; 20938494Sobrien enum clnt_stat clnt_stat; 210174294Sobrien struct fhstatus res; 211174294Sobrien#ifdef HAVE_FS_NFS3 212174294Sobrien struct am_mountres3 res3; 213174294Sobrien#endif /* HAVE_FS_NFS3 */ 21438494Sobrien 21538494Sobrien /* 21638494Sobrien * Pick a number, any number... 21738494Sobrien */ 21838494Sobrien tv.tv_sec = 20; 21938494Sobrien tv.tv_usec = 0; 22038494Sobrien 22138494Sobrien dlog("Fetching fhandle for %s", dir); 22238494Sobrien 22338494Sobrien /* 22438494Sobrien * Call the mount daemon on the remote host to 22538494Sobrien * get the filehandle. Use NFS version specific call. 22638494Sobrien */ 22738494Sobrien 22851292Sobrien plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version); 22938494Sobrien#ifdef HAVE_FS_NFS3 23038494Sobrien if (nfs_version == NFS_VERSION3) { 231174294Sobrien memset((char *) &res3, 0, sizeof(res3)); 23238494Sobrien clnt_stat = clnt_call(client, 23338494Sobrien MOUNTPROC_MNT, 23438494Sobrien (XDRPROC_T_TYPE) xdr_dirpath, 23538494Sobrien (SVC_IN_ARG_TYPE) &dir, 236174294Sobrien (XDRPROC_T_TYPE) xdr_am_mountres3, 237174294Sobrien (SVC_IN_ARG_TYPE) &res3, 23838494Sobrien tv); 23938494Sobrien if (clnt_stat != RPC_SUCCESS) { 24038494Sobrien plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 24138494Sobrien return EIO; 24238494Sobrien } 24338494Sobrien /* Check the status of the filehandle */ 244174294Sobrien if ((errno = res3.fhs_status)) { 24538494Sobrien dlog("fhandle fetch for mount version 3 failed: %m"); 24638494Sobrien return errno; 24738494Sobrien } 248174294Sobrien memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3)); 249174294Sobrien fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; 250174294Sobrien memmove(fhp->v3.am_fh3_data, 251174294Sobrien res3.mountres3_u.mountinfo.fhandle.fhandle3_val, 252174294Sobrien fhp->v3.am_fh3_length); 25338494Sobrien } else { /* not NFS_VERSION3 mount */ 25438494Sobrien#endif /* HAVE_FS_NFS3 */ 25538494Sobrien clnt_stat = clnt_call(client, 25638494Sobrien MOUNTPROC_MNT, 25738494Sobrien (XDRPROC_T_TYPE) xdr_dirpath, 25838494Sobrien (SVC_IN_ARG_TYPE) &dir, 25938494Sobrien (XDRPROC_T_TYPE) xdr_fhstatus, 260174294Sobrien (SVC_IN_ARG_TYPE) &res, 26138494Sobrien tv); 26238494Sobrien if (clnt_stat != RPC_SUCCESS) { 26382794Sobrien plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 26438494Sobrien return EIO; 26538494Sobrien } 26638494Sobrien /* Check status of filehandle */ 267174294Sobrien if (res.fhs_status) { 268174294Sobrien errno = res.fhs_status; 26938494Sobrien dlog("fhandle fetch for mount version 1 failed: %m"); 27038494Sobrien return errno; 27138494Sobrien } 272174294Sobrien memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE); 27338494Sobrien#ifdef HAVE_FS_NFS3 27438494Sobrien } /* end of "if (nfs_version == NFS_VERSION3)" statement */ 27538494Sobrien#endif /* HAVE_FS_NFS3 */ 27638494Sobrien 27738494Sobrien /* all is well */ 27838494Sobrien return 0; 27938494Sobrien} 28038494Sobrien 28138494Sobrien 28238494Sobrien/* 28338494Sobrien * Scan mount table to see if something already mounted 28438494Sobrien */ 28538494Sobrienstatic int 28638494Sobrienalready_mounted(mntlist *mlist, char *dir) 28738494Sobrien{ 28838494Sobrien mntlist *ml; 28938494Sobrien 29038494Sobrien for (ml = mlist; ml; ml = ml->mnext) 29138494Sobrien if (STREQ(ml->mnt->mnt_dir, dir)) 29238494Sobrien return 1; 29338494Sobrien return 0; 29438494Sobrien} 29538494Sobrien 29638494Sobrien 29738494Sobrienstatic int 298174294Sobrienamfs_host_mount(am_node *am, mntfs *mf) 29938494Sobrien{ 30038494Sobrien struct timeval tv2; 30138494Sobrien CLIENT *client; 30238494Sobrien enum clnt_stat clnt_stat; 30338494Sobrien int n_export; 30438494Sobrien int j, k; 30538494Sobrien exports exlist = 0, ex; 30638494Sobrien exports *ep = 0; 30738494Sobrien am_nfs_handle_t *fp = 0; 30838494Sobrien char *host; 30938494Sobrien int error = 0; 31038494Sobrien struct sockaddr_in sin; 31138494Sobrien int sock = RPC_ANYSOCK; 31238494Sobrien int ok = FALSE; 31338494Sobrien mntlist *mlist; 31438494Sobrien char fs_name[MAXPATHLEN], *rfs_dir; 31538494Sobrien char mntpt[MAXPATHLEN]; 31638494Sobrien struct timeval tv; 31738494Sobrien u_long mnt_version; 31838494Sobrien 31938494Sobrien /* 320174294Sobrien * WebNFS servers don't necessarily run mountd. 321174294Sobrien */ 322174294Sobrien if (mf->mf_flags & MFF_WEBNFS) { 323174294Sobrien plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS"); 324174294Sobrien return EIO; 325174294Sobrien } 326174294Sobrien 327174294Sobrien /* 32838494Sobrien * Read the mount list 32938494Sobrien */ 33038494Sobrien mlist = read_mtab(mf->mf_mount, mnttab_file_name); 33138494Sobrien 33238494Sobrien#ifdef MOUNT_TABLE_ON_FILE 33338494Sobrien /* 33438494Sobrien * Unlock the mount list 33538494Sobrien */ 33638494Sobrien unlock_mntlist(); 33738494Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 33838494Sobrien 33938494Sobrien /* 34038494Sobrien * Take a copy of the server hostname, address, and nfs version 34138494Sobrien * to mount version conversion. 34238494Sobrien */ 34338494Sobrien host = mf->mf_server->fs_host; 34438494Sobrien sin = *mf->mf_server->fs_ip; 345174294Sobrien plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version); 34638494Sobrien#ifdef HAVE_FS_NFS3 34738494Sobrien if (mf->mf_server->fs_version == NFS_VERSION3) 348174294Sobrien mnt_version = AM_MOUNTVERS3; 34938494Sobrien else 35038494Sobrien#endif /* HAVE_FS_NFS3 */ 35138494Sobrien mnt_version = MOUNTVERS; 35238494Sobrien 35338494Sobrien /* 35438494Sobrien * The original 10 second per try timeout is WAY too large, especially 35538494Sobrien * if we're only waiting 10 or 20 seconds max for the response. 35638494Sobrien * That would mean we'd try only once in 10 seconds, and we could 35742629Sobrien * lose the transmit or receive packet, and never try again. 35838494Sobrien * A 2-second per try timeout here is much more reasonable. 35938494Sobrien * 09/28/92 Mike Mitchell, mcm@unx.sas.com 36038494Sobrien */ 36138494Sobrien tv.tv_sec = 2; 36238494Sobrien tv.tv_usec = 0; 36338494Sobrien 36438494Sobrien /* 36538494Sobrien * Create a client attached to mountd 36638494Sobrien */ 36738494Sobrien client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 36838494Sobrien if (client == NULL) { 36938494Sobrien#ifdef HAVE_CLNT_SPCREATEERROR 37038494Sobrien plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 37138494Sobrien host, clnt_spcreateerror("")); 37238494Sobrien#else /* not HAVE_CLNT_SPCREATEERROR */ 37338494Sobrien plog(XLOG_ERROR, "get_mount_client failed for %s", host); 37438494Sobrien#endif /* not HAVE_CLNT_SPCREATEERROR */ 37538494Sobrien error = EIO; 37638494Sobrien goto out; 37738494Sobrien } 37838494Sobrien if (!nfs_auth) { 37938494Sobrien error = make_nfs_auth(); 38038494Sobrien if (error) 38138494Sobrien goto out; 38238494Sobrien } 38338494Sobrien client->cl_auth = nfs_auth; 38438494Sobrien 38538494Sobrien dlog("Fetching export list from %s", host); 38638494Sobrien 38738494Sobrien /* 38838494Sobrien * Fetch the export list 38938494Sobrien */ 39038494Sobrien tv2.tv_sec = 10; 39138494Sobrien tv2.tv_usec = 0; 39238494Sobrien clnt_stat = clnt_call(client, 39338494Sobrien MOUNTPROC_EXPORT, 39438494Sobrien (XDRPROC_T_TYPE) xdr_void, 39538494Sobrien 0, 39638494Sobrien (XDRPROC_T_TYPE) xdr_exports, 39738494Sobrien (SVC_IN_ARG_TYPE) & exlist, 39838494Sobrien tv2); 39938494Sobrien if (clnt_stat != RPC_SUCCESS) { 40041142Sobrien const char *msg = clnt_sperrno(clnt_stat); 401174294Sobrien plog(XLOG_ERROR, "host_mount rpc failed: %s", msg); 40238494Sobrien /* clnt_perror(client, "rpc"); */ 40338494Sobrien error = EIO; 40438494Sobrien goto out; 40538494Sobrien } 40638494Sobrien 40738494Sobrien /* 40838494Sobrien * Figure out how many exports were returned 40938494Sobrien */ 41038494Sobrien for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 41138494Sobrien n_export++; 41238494Sobrien } 41338494Sobrien 41438494Sobrien /* 41538494Sobrien * Allocate an array of pointers into the list 41638494Sobrien * so that they can be sorted. If the filesystem 41738494Sobrien * is already mounted then ignore it. 41838494Sobrien */ 41938494Sobrien ep = (exports *) xmalloc(n_export * sizeof(exports)); 42038494Sobrien for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 421174294Sobrien make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); 42282794Sobrien if (already_mounted(mlist, mntpt)) 42382794Sobrien /* we have at least one mounted f/s, so don't fail the mount */ 42482794Sobrien ok = TRUE; 42582794Sobrien else 42638494Sobrien ep[j++] = ex; 42738494Sobrien } 42838494Sobrien n_export = j; 42938494Sobrien 43038494Sobrien /* 43138494Sobrien * Sort into order. 43238494Sobrien * This way the mounts are done in order down the tree, 43338494Sobrien * instead of any random order returned by the mount 43438494Sobrien * daemon (the protocol doesn't specify...). 43538494Sobrien */ 43638494Sobrien qsort(ep, n_export, sizeof(exports), sortfun); 43738494Sobrien 43838494Sobrien /* 43938494Sobrien * Allocate an array of filehandles 44038494Sobrien */ 44138494Sobrien fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); 44238494Sobrien 44338494Sobrien /* 44438494Sobrien * Try to obtain filehandles for each directory. 44538494Sobrien * If a fetch fails then just zero out the array 44638494Sobrien * reference but discard the error. 44738494Sobrien */ 44838494Sobrien for (j = k = 0; j < n_export; j++) { 44938494Sobrien /* Check and avoid a duplicated export entry */ 45038494Sobrien if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { 45138494Sobrien dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 45238494Sobrien ep[j] = 0; 45338494Sobrien } else { 45438494Sobrien k = j; 45538494Sobrien error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], 45638494Sobrien mf->mf_server->fs_version); 45738494Sobrien if (error) 45838494Sobrien ep[j] = 0; 45938494Sobrien } 46038494Sobrien } 46138494Sobrien 46238494Sobrien /* 46338494Sobrien * Mount each filesystem for which we have a filehandle. 46438494Sobrien * If any of the mounts succeed then mark "ok" and return 46538494Sobrien * error code 0 at the end. If they all fail then return 46638494Sobrien * the last error code. 46738494Sobrien */ 468174294Sobrien xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN); 46938494Sobrien if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { 470174294Sobrien plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon"); 47138494Sobrien error = EINVAL; 47238494Sobrien goto out; 47338494Sobrien } 47438494Sobrien ++rfs_dir; 47538494Sobrien for (j = 0; j < n_export; j++) { 47638494Sobrien ex = ep[j]; 47738494Sobrien if (ex) { 478174294Sobrien /* 479174294Sobrien * Note: the sizeof space left in rfs_dir is what's left in fs_name 480174294Sobrien * after strchr() above returned a pointer _inside_ fs_name. The 481174294Sobrien * calculation below also takes into account that rfs_dir was 482174294Sobrien * incremented by the ++ above. 483174294Sobrien */ 484174294Sobrien xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name)); 485174294Sobrien make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); 486174294Sobrien if (do_mount(&fp[j], mntpt, fs_name, mf) == 0) 48738494Sobrien ok = TRUE; 48838494Sobrien } 48938494Sobrien } 49038494Sobrien 49138494Sobrien /* 49238494Sobrien * Clean up and exit 49338494Sobrien */ 49438494Sobrienout: 49538494Sobrien discard_mntlist(mlist); 49638494Sobrien if (ep) 49738494Sobrien XFREE(ep); 49838494Sobrien if (fp) 49938494Sobrien XFREE(fp); 50038494Sobrien if (sock != RPC_ANYSOCK) 50138494Sobrien (void) amu_close(sock); 50238494Sobrien if (client) 50338494Sobrien clnt_destroy(client); 50438494Sobrien if (exlist) 50538494Sobrien xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); 50638494Sobrien if (ok) 50738494Sobrien return 0; 50838494Sobrien return error; 50938494Sobrien} 51038494Sobrien 51138494Sobrien 51238494Sobrien/* 51338494Sobrien * Return true if pref is a directory prefix of dir. 51438494Sobrien * 51538494Sobrien * XXX TODO: 51638494Sobrien * Does not work if pref is "/". 51738494Sobrien */ 51838494Sobrienstatic int 51938494Sobriendirectory_prefix(char *pref, char *dir) 52038494Sobrien{ 52138494Sobrien int len = strlen(pref); 52238494Sobrien 52338494Sobrien if (!NSTREQ(pref, dir, len)) 52438494Sobrien return FALSE; 52538494Sobrien if (dir[len] == '/' || dir[len] == '\0') 52638494Sobrien return TRUE; 52738494Sobrien return FALSE; 52838494Sobrien} 52938494Sobrien 53038494Sobrien 53138494Sobrien/* 53238494Sobrien * Unmount a mount tree 53338494Sobrien */ 53438494Sobrienstatic int 535174294Sobrienamfs_host_umount(am_node *am, mntfs *mf) 53638494Sobrien{ 53738494Sobrien mntlist *ml, *mprev; 538174294Sobrien int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 53938494Sobrien int xerror = 0; 54038494Sobrien 54138494Sobrien /* 54238494Sobrien * Read the mount list 54338494Sobrien */ 54438494Sobrien mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); 54538494Sobrien 54638494Sobrien#ifdef MOUNT_TABLE_ON_FILE 54738494Sobrien /* 54838494Sobrien * Unlock the mount list 54938494Sobrien */ 55038494Sobrien unlock_mntlist(); 55138494Sobrien#endif /* MOUNT_TABLE_ON_FILE */ 55238494Sobrien 55338494Sobrien /* 55438494Sobrien * Reverse list... 55538494Sobrien */ 55638494Sobrien ml = mlist; 55738494Sobrien mprev = 0; 55838494Sobrien while (ml) { 55938494Sobrien mntlist *ml2 = ml->mnext; 56038494Sobrien ml->mnext = mprev; 56138494Sobrien mprev = ml; 56238494Sobrien ml = ml2; 56338494Sobrien } 56438494Sobrien mlist = mprev; 56538494Sobrien 56638494Sobrien /* 56738494Sobrien * Unmount all filesystems... 56838494Sobrien */ 56938494Sobrien for (ml = mlist; ml && !xerror; ml = ml->mnext) { 57038494Sobrien char *dir = ml->mnt->mnt_dir; 57138494Sobrien if (directory_prefix(mf->mf_mount, dir)) { 57238494Sobrien int error; 57338494Sobrien dlog("amfs_host: unmounts %s", dir); 57438494Sobrien /* 57538494Sobrien * Unmount "dir" 57638494Sobrien */ 577174294Sobrien error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); 57838494Sobrien /* 57938494Sobrien * Keep track of errors 58038494Sobrien */ 58138494Sobrien if (error) { 582174294Sobrien /* 583174294Sobrien * If we have not already set xerror and error is not ENOENT, 584174294Sobrien * then set xerror equal to error and log it. 585174294Sobrien * 'xerror' is the return value for this function. 586174294Sobrien * 587174294Sobrien * We do not want to pass ENOENT as an error because if the 588174294Sobrien * directory does not exists our work is done anyway. 589174294Sobrien */ 590174294Sobrien if (!xerror && error != ENOENT) 59138494Sobrien xerror = error; 59238494Sobrien if (error != EBUSY) { 59338494Sobrien errno = error; 59438494Sobrien plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 59538494Sobrien } 59638494Sobrien } else { 59738494Sobrien (void) rmdirs(dir); 59838494Sobrien } 59938494Sobrien } 60038494Sobrien } 60138494Sobrien 60238494Sobrien /* 60338494Sobrien * Throw away mount list 60438494Sobrien */ 60538494Sobrien discard_mntlist(mlist); 60638494Sobrien 60738494Sobrien /* 60838494Sobrien * Try to remount, except when we are shutting down. 60938494Sobrien */ 61038494Sobrien if (xerror && amd_state != Finishing) { 611174294Sobrien xerror = amfs_host_mount(am, mf); 61238494Sobrien if (!xerror) { 61338494Sobrien /* 61438494Sobrien * Don't log this - it's usually too verbose 61538494Sobrien plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 61638494Sobrien */ 61738494Sobrien xerror = EBUSY; 61838494Sobrien } 61938494Sobrien } 62038494Sobrien return xerror; 62138494Sobrien} 62238494Sobrien 62338494Sobrien 62438494Sobrien/* 62538494Sobrien * Tell mountd we're done. 62638494Sobrien * This is not quite right, because we may still 62738494Sobrien * have other filesystems mounted, but the existing 62838494Sobrien * mountd protocol is badly broken anyway. 62938494Sobrien */ 63038494Sobrienstatic void 631174294Sobrienamfs_host_umounted(mntfs *mf) 63238494Sobrien{ 63338494Sobrien char *host; 63438494Sobrien CLIENT *client; 63538494Sobrien enum clnt_stat clnt_stat; 63638494Sobrien struct sockaddr_in sin; 63738494Sobrien int sock = RPC_ANYSOCK; 63838494Sobrien struct timeval tv; 63938494Sobrien u_long mnt_version; 64038494Sobrien 64138494Sobrien if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) 64238494Sobrien return; 64338494Sobrien 64438494Sobrien /* 645174294Sobrien * WebNFS servers shouldn't ever get here. 646174294Sobrien */ 647174294Sobrien if (mf->mf_flags & MFF_WEBNFS) { 648174294Sobrien plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS"); 649174294Sobrien return; 650174294Sobrien } 651174294Sobrien 652174294Sobrien /* 65338494Sobrien * Take a copy of the server hostname, address, and NFS version 65438494Sobrien * to mount version conversion. 65538494Sobrien */ 65638494Sobrien host = mf->mf_server->fs_host; 65738494Sobrien sin = *mf->mf_server->fs_ip; 65851292Sobrien plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); 65938494Sobrien#ifdef HAVE_FS_NFS3 66038494Sobrien if (mf->mf_server->fs_version == NFS_VERSION3) 661174294Sobrien mnt_version = AM_MOUNTVERS3; 66238494Sobrien else 66338494Sobrien#endif /* HAVE_FS_NFS3 */ 66438494Sobrien mnt_version = MOUNTVERS; 66538494Sobrien 66638494Sobrien /* 66738494Sobrien * Create a client attached to mountd 66838494Sobrien */ 66938494Sobrien tv.tv_sec = 10; 67038494Sobrien tv.tv_usec = 0; 67138494Sobrien client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 67238494Sobrien if (client == NULL) { 67338494Sobrien#ifdef HAVE_CLNT_SPCREATEERROR 67438494Sobrien plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 67538494Sobrien host, clnt_spcreateerror("")); 67638494Sobrien#else /* not HAVE_CLNT_SPCREATEERROR */ 67738494Sobrien plog(XLOG_ERROR, "get_mount_client failed for %s", host); 67838494Sobrien#endif /* not HAVE_CLNT_SPCREATEERROR */ 67938494Sobrien goto out; 68038494Sobrien } 68138494Sobrien 68238494Sobrien if (!nfs_auth) { 68338494Sobrien if (make_nfs_auth()) 68438494Sobrien goto out; 68538494Sobrien } 68638494Sobrien client->cl_auth = nfs_auth; 68738494Sobrien 68838494Sobrien dlog("Unmounting all from %s", host); 68938494Sobrien 69038494Sobrien clnt_stat = clnt_call(client, 69138494Sobrien MOUNTPROC_UMNTALL, 69238494Sobrien (XDRPROC_T_TYPE) xdr_void, 69338494Sobrien 0, 69438494Sobrien (XDRPROC_T_TYPE) xdr_void, 69538494Sobrien 0, 69638494Sobrien tv); 69738494Sobrien if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) { 69838494Sobrien /* RPC_SYSTEMERROR seems to be returned for no good reason ... */ 69941142Sobrien const char *msg = clnt_sperrno(clnt_stat); 70051292Sobrien plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg); 70138494Sobrien goto out; 70238494Sobrien } 70338494Sobrien 70438494Sobrienout: 70538494Sobrien if (sock != RPC_ANYSOCK) 70638494Sobrien (void) amu_close(sock); 70738494Sobrien if (client) 70838494Sobrien clnt_destroy(client); 70938494Sobrien} 710