amfs_host.c revision 267654
1181834Sroberto/* 2285612Sdelphij * Copyright (c) 1997-2006 Erez Zadok 3181834Sroberto * Copyright (c) 1990 Jan-Simon Pendry 4285612Sdelphij * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5285612Sdelphij * Copyright (c) 1990 The Regents of the University of California. 6181834Sroberto * All rights reserved. 7181834Sroberto * 8181834Sroberto * This code is derived from software contributed to Berkeley by 9181834Sroberto * Jan-Simon Pendry at Imperial College, London. 10181834Sroberto * 11181834Sroberto * Redistribution and use in source and binary forms, with or without 12285612Sdelphij * modification, are permitted provided that the following conditions 13181834Sroberto * are met: 14285612Sdelphij * 1. Redistributions of source code must retain the above copyright 15285612Sdelphij * notice, this list of conditions and the following disclaimer. 16285612Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 17285612Sdelphij * notice, this list of conditions and the following disclaimer in the 18285612Sdelphij * documentation and/or other materials provided with the distribution. 19285612Sdelphij * 3. All advertising materials mentioning features or use of this software 20285612Sdelphij * must display the following acknowledgment: 21285612Sdelphij * This product includes software developed by the University of 22285612Sdelphij * California, Berkeley and its contributors. 23285612Sdelphij * 4. Neither the name of the University nor the names of its contributors 24285612Sdelphij * may be used to endorse or promote products derived from this software 25285612Sdelphij * without specific prior written permission. 26285612Sdelphij * 27285612Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28285612Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29285612Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30285612Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31285612Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32285612Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33181834Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34181834Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35285612Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36285612Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37285612Sdelphij * SUCH DAMAGE. 38285612Sdelphij * 39285612Sdelphij * 40285612Sdelphij * File: am-utils/amd/amfs_host.c 41181834Sroberto * 42285612Sdelphij */ 43181834Sroberto 44285612Sdelphij/* 45285612Sdelphij * NFS host file system. 46285612Sdelphij * Mounts all exported filesystems from a given host. 47285612Sdelphij * This has now degenerated into a mess but will not 48285612Sdelphij * be rewritten. Amd 6 will support the abstractions 49285612Sdelphij * needed to make this work correctly. 50285612Sdelphij */ 51285612Sdelphij 52285612Sdelphij#ifdef HAVE_CONFIG_H 53285612Sdelphij# include <config.h> 54181834Sroberto#endif /* HAVE_CONFIG_H */ 55285612Sdelphij#include <am_defs.h> 56285612Sdelphij#include <amd.h> 57285612Sdelphij 58285612Sdelphijstatic char *amfs_host_match(am_opts *fo); 59285612Sdelphijstatic int amfs_host_init(mntfs *mf); 60181834Srobertostatic int amfs_host_mount(am_node *am, mntfs *mf); 61285612Sdelphijstatic int amfs_host_umount(am_node *am, mntfs *mf); 62285612Sdelphijstatic void amfs_host_umounted(mntfs *mf); 63285612Sdelphij 64181834Sroberto/* 65285612Sdelphij * Ops structure 66285612Sdelphij */ 67285612Sdelphijam_ops amfs_host_ops = 68285612Sdelphij{ 69285612Sdelphij "host", 70181834Sroberto amfs_host_match, 71285612Sdelphij amfs_host_init, 72285612Sdelphij amfs_host_mount, 73285612Sdelphij amfs_host_umount, 74285612Sdelphij amfs_error_lookup_child, 75285612Sdelphij amfs_error_mount_child, 76285612Sdelphij amfs_error_readdir, 77285612Sdelphij 0, /* amfs_host_readlink */ 78285612Sdelphij 0, /* amfs_host_mounted */ 79285612Sdelphij amfs_host_umounted, 80285612Sdelphij find_nfs_srvr, 81285612Sdelphij 0, /* amfs_host_get_wchan */ 82285612Sdelphij FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, 83285612Sdelphij#ifdef HAVE_FS_AUTOFS 84285612Sdelphij AUTOFS_HOST_FS_FLAGS, 85285612Sdelphij#endif /* HAVE_FS_AUTOFS */ 86285612Sdelphij}; 87285612Sdelphij 88285612Sdelphij 89285612Sdelphij/* 90285612Sdelphij * Determine the mount point: 91285612Sdelphij * 92285612Sdelphij * The next change we put in to better handle PCs. This is a bit 93285612Sdelphij * disgusting, so you'd better sit down. We change the make_mntpt function 94181834Sroberto * to look for exported file systems without a leading '/'. If they don't 95181834Sroberto * have a leading '/', we add one. If the export is 'a:' through 'z:' 96181834Sroberto * (without a leading slash), we change it to 'a%' (or b% or z%). This 97181834Sroberto * allows the entire PC disk to be mounted. 98181834Sroberto */ 99181834Srobertostatic void 100181834Srobertomake_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount) 101285612Sdelphij{ 102181834Sroberto if (ex->ex_dir[0] == '/') { 103285612Sdelphij if (ex->ex_dir[1] == 0) 104285612Sdelphij xstrlcpy(mntpt, mf_mount, l); 105181834Sroberto else 106181834Sroberto xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir); 107181834Sroberto } else if (ex->ex_dir[0] >= 'a' && 108285612Sdelphij ex->ex_dir[0] <= 'z' && 109285612Sdelphij ex->ex_dir[1] == ':' && 110181834Sroberto ex->ex_dir[2] == '/' && 111285612Sdelphij ex->ex_dir[3] == 0) 112285612Sdelphij xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]); 113285612Sdelphij else 114285612Sdelphij xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir); 115285612Sdelphij} 116285612Sdelphij 117285612Sdelphij 118285612Sdelphij/* 119285612Sdelphij * Execute needs the same as NFS plus a helper command 120285612Sdelphij */ 121285612Sdelphijstatic char * 122285612Sdelphijamfs_host_match(am_opts *fo) 123285612Sdelphij{ 124181834Sroberto extern am_ops nfs_ops; 125285612Sdelphij 126285612Sdelphij /* 127285612Sdelphij * Make sure rfs is specified to keep nfs_match happy... 128285612Sdelphij */ 129285612Sdelphij if (!fo->opt_rfs) 130181834Sroberto fo->opt_rfs = "/"; 131285612Sdelphij 132285612Sdelphij return (*nfs_ops.fs_match) (fo); 133285612Sdelphij} 134285612Sdelphij 135285612Sdelphij 136285612Sdelphijstatic int 137285612Sdelphijamfs_host_init(mntfs *mf) 138285612Sdelphij{ 139285612Sdelphij u_short mountd_port; 140285612Sdelphij 141285612Sdelphij if (strchr(mf->mf_info, ':') == 0) 142285612Sdelphij return ENOENT; 143285612Sdelphij 144181834Sroberto /* 145181834Sroberto * This is primarily to schedule a wakeup so that as soon 146285612Sdelphij * as our fileserver is ready, we can continue setting up 147285612Sdelphij * the host filesystem. If we don't do this, the standard 148285612Sdelphij * amfs_auto code will set up a fileserver structure, but it will 149181834Sroberto * have to wait for another nfs request from the client to come 150285612Sdelphij * in before finishing. Our way is faster since we don't have 151285612Sdelphij * to wait for the client to resend its request (which could 152181834Sroberto * take a second or two). 153285612Sdelphij */ 154285612Sdelphij /* 155285612Sdelphij * First, we find the fileserver for this mntfs and then call 156285612Sdelphij * get_mountd_port with our mntfs passed as the wait channel. 157285612Sdelphij * get_mountd_port will check some things and then schedule 158285612Sdelphij * it so that when the fileserver is ready, a wakeup is done 159181834Sroberto * on this mntfs. amfs_cont() is already sleeping on this mntfs 160181834Sroberto * so as soon as that wakeup happens amfs_cont() is called and 161181834Sroberto * this mount is retried. 162285612Sdelphij */ 163285612Sdelphij if (mf->mf_server) 164181834Sroberto /* 165181834Sroberto * We don't really care if there's an error returned. 166285612Sdelphij * Since this is just to help speed things along, the 167285612Sdelphij * error will get handled properly elsewhere. 168285612Sdelphij */ 169285612Sdelphij get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf)); 170285612Sdelphij 171285612Sdelphij return 0; 172285612Sdelphij} 173285612Sdelphij 174285612Sdelphij 175181834Srobertostatic int 176285612Sdelphijdo_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) 177285612Sdelphij{ 178285612Sdelphij struct stat stb; 179285612Sdelphij 180285612Sdelphij dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir); 181285612Sdelphij 182285612Sdelphij (void) mkdirs(mntdir, 0555); 183181834Sroberto if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { 184285612Sdelphij plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir); 185285612Sdelphij return ENOENT; 186285612Sdelphij } 187285612Sdelphij 188285612Sdelphij return mount_nfs_fh(fhp, mntdir, fs_name, mf); 189285612Sdelphij} 190285612Sdelphij 191285612Sdelphij 192285612Sdelphijstatic int 193285612Sdelphijsortfun(const voidp x, const voidp y) 194285612Sdelphij{ 195181834Sroberto exports *a = (exports *) x; 196285612Sdelphij exports *b = (exports *) y; 197285612Sdelphij 198285612Sdelphij return strcmp((*a)->ex_dir, (*b)->ex_dir); 199285612Sdelphij} 200285612Sdelphij 201181834Sroberto 202285612Sdelphij/* 203285612Sdelphij * Get filehandle 204285612Sdelphij */ 205285612Sdelphijstatic int 206181834Srobertofetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version) 207285612Sdelphij{ 208285612Sdelphij struct timeval tv; 209181834Sroberto enum clnt_stat clnt_stat; 210285612Sdelphij struct fhstatus res; 211285612Sdelphij#ifdef HAVE_FS_NFS3 212285612Sdelphij struct am_mountres3 res3; 213285612Sdelphij#endif /* HAVE_FS_NFS3 */ 214285612Sdelphij 215285612Sdelphij /* 216285612Sdelphij * Pick a number, any number... 217285612Sdelphij */ 218285612Sdelphij tv.tv_sec = 20; 219285612Sdelphij tv.tv_usec = 0; 220285612Sdelphij 221285612Sdelphij dlog("Fetching fhandle for %s", dir); 222285612Sdelphij 223285612Sdelphij /* 224285612Sdelphij * Call the mount daemon on the remote host to 225285612Sdelphij * get the filehandle. Use NFS version specific call. 226285612Sdelphij */ 227285612Sdelphij 228285612Sdelphij plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version); 229285612Sdelphij#ifdef HAVE_FS_NFS3 230285612Sdelphij if (nfs_version == NFS_VERSION3) { 231285612Sdelphij memset((char *) &res3, 0, sizeof(res3)); 232285612Sdelphij clnt_stat = clnt_call(client, 233285612Sdelphij MOUNTPROC_MNT, 234285612Sdelphij (XDRPROC_T_TYPE) xdr_dirpath, 235285612Sdelphij (SVC_IN_ARG_TYPE) &dir, 236285612Sdelphij (XDRPROC_T_TYPE) xdr_am_mountres3, 237285612Sdelphij (SVC_IN_ARG_TYPE) &res3, 238285612Sdelphij tv); 239285612Sdelphij if (clnt_stat != RPC_SUCCESS) { 240285612Sdelphij plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 241285612Sdelphij return EIO; 242285612Sdelphij } 243285612Sdelphij /* Check the status of the filehandle */ 244285612Sdelphij if ((errno = res3.fhs_status)) { 245285612Sdelphij dlog("fhandle fetch for mount version 3 failed: %m"); 246285612Sdelphij return errno; 247285612Sdelphij } 248285612Sdelphij memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3)); 249285612Sdelphij fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; 250285612Sdelphij memmove(fhp->v3.am_fh3_data, 251285612Sdelphij res3.mountres3_u.mountinfo.fhandle.fhandle3_val, 252285612Sdelphij fhp->v3.am_fh3_length); 253285612Sdelphij } else { /* not NFS_VERSION3 mount */ 254285612Sdelphij#endif /* HAVE_FS_NFS3 */ 255285612Sdelphij clnt_stat = clnt_call(client, 256285612Sdelphij MOUNTPROC_MNT, 257285612Sdelphij (XDRPROC_T_TYPE) xdr_dirpath, 258285612Sdelphij (SVC_IN_ARG_TYPE) &dir, 259285612Sdelphij (XDRPROC_T_TYPE) xdr_fhstatus, 260285612Sdelphij (SVC_IN_ARG_TYPE) &res, 261285612Sdelphij tv); 262285612Sdelphij if (clnt_stat != RPC_SUCCESS) { 263285612Sdelphij plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 264285612Sdelphij return EIO; 265285612Sdelphij } 266285612Sdelphij /* Check status of filehandle */ 267285612Sdelphij if (res.fhs_status) { 268285612Sdelphij errno = res.fhs_status; 269285612Sdelphij dlog("fhandle fetch for mount version 1 failed: %m"); 270285612Sdelphij return errno; 271285612Sdelphij } 272285612Sdelphij memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE); 273285612Sdelphij#ifdef HAVE_FS_NFS3 274285612Sdelphij } /* end of "if (nfs_version == NFS_VERSION3)" statement */ 275285612Sdelphij#endif /* HAVE_FS_NFS3 */ 276285612Sdelphij 277285612Sdelphij /* all is well */ 278285612Sdelphij return 0; 279285612Sdelphij} 280285612Sdelphij 281285612Sdelphij 282285612Sdelphij/* 283285612Sdelphij * Scan mount table to see if something already mounted 284285612Sdelphij */ 285285612Sdelphijstatic int 286285612Sdelphijalready_mounted(mntlist *mlist, char *dir) 287285612Sdelphij{ 288285612Sdelphij mntlist *ml; 289285612Sdelphij 290285612Sdelphij for (ml = mlist; ml; ml = ml->mnext) 291285612Sdelphij if (STREQ(ml->mnt->mnt_dir, dir)) 292285612Sdelphij return 1; 293285612Sdelphij return 0; 294285612Sdelphij} 295285612Sdelphij 296285612Sdelphij 297285612Sdelphijstatic int 298285612Sdelphijamfs_host_mount(am_node *am, mntfs *mf) 299285612Sdelphij{ 300285612Sdelphij struct timeval tv2; 301285612Sdelphij CLIENT *client; 302285612Sdelphij enum clnt_stat clnt_stat; 303285612Sdelphij int n_export; 304285612Sdelphij int j, k; 305285612Sdelphij exports exlist = 0, ex; 306285612Sdelphij exports *ep = 0; 307285612Sdelphij am_nfs_handle_t *fp = 0; 308285612Sdelphij char *host; 309285612Sdelphij int error = 0; 310285612Sdelphij struct sockaddr_in sin; 311181834Sroberto int sock = RPC_ANYSOCK; 312181834Sroberto int ok = FALSE; 313181834Sroberto mntlist *mlist; 314181834Sroberto char fs_name[MAXPATHLEN], *rfs_dir; 315181834Sroberto char mntpt[MAXPATHLEN]; 316285612Sdelphij struct timeval tv; 317181834Sroberto u_long mnt_version; 318285612Sdelphij 319285612Sdelphij /* 320285612Sdelphij * WebNFS servers don't necessarily run mountd. 321285612Sdelphij */ 322285612Sdelphij if (mf->mf_flags & MFF_WEBNFS) { 323285612Sdelphij plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS"); 324285612Sdelphij return EIO; 325181834Sroberto } 326285612Sdelphij 327285612Sdelphij /* 328181834Sroberto * Read the mount list 329181834Sroberto */ 330181834Sroberto mlist = read_mtab(mf->mf_mount, mnttab_file_name); 331181834Sroberto 332181834Sroberto#ifdef MOUNT_TABLE_ON_FILE 333181834Sroberto /* 334181834Sroberto * Unlock the mount list 335285612Sdelphij */ 336181834Sroberto unlock_mntlist(); 337285612Sdelphij#endif /* MOUNT_TABLE_ON_FILE */ 338181834Sroberto 339181834Sroberto /* 340285612Sdelphij * Take a copy of the server hostname, address, and nfs version 341285612Sdelphij * to mount version conversion. 342181834Sroberto */ 343285612Sdelphij host = mf->mf_server->fs_host; 344285612Sdelphij sin = *mf->mf_server->fs_ip; 345285612Sdelphij plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version); 346285612Sdelphij#ifdef HAVE_FS_NFS3 347285612Sdelphij if (mf->mf_server->fs_version == NFS_VERSION3) 348285612Sdelphij mnt_version = AM_MOUNTVERS3; 349285612Sdelphij else 350285612Sdelphij#endif /* HAVE_FS_NFS3 */ 351285612Sdelphij mnt_version = MOUNTVERS; 352285612Sdelphij 353285612Sdelphij /* 354285612Sdelphij * The original 10 second per try timeout is WAY too large, especially 355285612Sdelphij * if we're only waiting 10 or 20 seconds max for the response. 356285612Sdelphij * That would mean we'd try only once in 10 seconds, and we could 357285612Sdelphij * lose the transmit or receive packet, and never try again. 358285612Sdelphij * A 2-second per try timeout here is much more reasonable. 359285612Sdelphij * 09/28/92 Mike Mitchell, mcm@unx.sas.com 360285612Sdelphij */ 361285612Sdelphij tv.tv_sec = 2; 362285612Sdelphij tv.tv_usec = 0; 363285612Sdelphij 364285612Sdelphij /* 365285612Sdelphij * Create a client attached to mountd 366285612Sdelphij */ 367285612Sdelphij client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 368285612Sdelphij if (client == NULL) { 369285612Sdelphij#ifdef HAVE_CLNT_SPCREATEERROR 370285612Sdelphij plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 371285612Sdelphij host, clnt_spcreateerror("")); 372285612Sdelphij#else /* not HAVE_CLNT_SPCREATEERROR */ 373285612Sdelphij plog(XLOG_ERROR, "get_mount_client failed for %s", host); 374285612Sdelphij#endif /* not HAVE_CLNT_SPCREATEERROR */ 375285612Sdelphij error = EIO; 376285612Sdelphij goto out; 377285612Sdelphij } 378285612Sdelphij if (!nfs_auth) { 379285612Sdelphij error = make_nfs_auth(); 380285612Sdelphij if (error) 381285612Sdelphij goto out; 382285612Sdelphij } 383285612Sdelphij client->cl_auth = nfs_auth; 384285612Sdelphij 385285612Sdelphij dlog("Fetching export list from %s", host); 386285612Sdelphij 387285612Sdelphij /* 388285612Sdelphij * Fetch the export list 389181834Sroberto */ 390285612Sdelphij tv2.tv_sec = 10; 391285612Sdelphij tv2.tv_usec = 0; 392285612Sdelphij clnt_stat = clnt_call(client, 393285612Sdelphij MOUNTPROC_EXPORT, 394285612Sdelphij (XDRPROC_T_TYPE) xdr_void, 395285612Sdelphij 0, 396285612Sdelphij (XDRPROC_T_TYPE) xdr_exports, 397285612Sdelphij (SVC_IN_ARG_TYPE) & exlist, 398285612Sdelphij tv2); 399285612Sdelphij if (clnt_stat != RPC_SUCCESS) { 400285612Sdelphij const char *msg = clnt_sperrno(clnt_stat); 401285612Sdelphij plog(XLOG_ERROR, "host_mount rpc failed: %s", msg); 402285612Sdelphij /* clnt_perror(client, "rpc"); */ 403285612Sdelphij error = EIO; 404285612Sdelphij goto out; 405181834Sroberto } 406181834Sroberto 407181834Sroberto /* 408285612Sdelphij * Figure out how many exports were returned 409285612Sdelphij */ 410285612Sdelphij for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 411181834Sroberto n_export++; 412285612Sdelphij } 413285612Sdelphij 414285612Sdelphij /* 415285612Sdelphij * Allocate an array of pointers into the list 416285612Sdelphij * so that they can be sorted. If the filesystem 417285612Sdelphij * is already mounted then ignore it. 418181834Sroberto */ 419285612Sdelphij ep = (exports *) xmalloc(n_export * sizeof(exports)); 420285612Sdelphij for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 421285612Sdelphij make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); 422285612Sdelphij if (already_mounted(mlist, mntpt)) 423285612Sdelphij /* we have at least one mounted f/s, so don't fail the mount */ 424285612Sdelphij ok = TRUE; 425285612Sdelphij else 426285612Sdelphij ep[j++] = ex; 427285612Sdelphij } 428285612Sdelphij n_export = j; 429285612Sdelphij 430285612Sdelphij /* 431285612Sdelphij * Sort into order. 432285612Sdelphij * This way the mounts are done in order down the tree, 433285612Sdelphij * instead of any random order returned by the mount 434181834Sroberto * daemon (the protocol doesn't specify...). 435285612Sdelphij */ 436285612Sdelphij qsort(ep, n_export, sizeof(exports), sortfun); 437285612Sdelphij 438181834Sroberto /* 439181834Sroberto * Allocate an array of filehandles 440285612Sdelphij */ 441181834Sroberto fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); 442181834Sroberto 443181834Sroberto /* 444285612Sdelphij * Try to obtain filehandles for each directory. 445181834Sroberto * If a fetch fails then just zero out the array 446285612Sdelphij * reference but discard the error. 447285612Sdelphij */ 448285612Sdelphij for (j = k = 0; j < n_export; j++) { 449285612Sdelphij /* Check and avoid a duplicated export entry */ 450285612Sdelphij if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { 451285612Sdelphij dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 452181834Sroberto ep[j] = 0; 453181834Sroberto } else { 454181834Sroberto k = j; 455285612Sdelphij error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], 456285612Sdelphij mf->mf_server->fs_version); 457181834Sroberto if (error) 458285612Sdelphij ep[j] = 0; 459285612Sdelphij } 460285612Sdelphij } 461285612Sdelphij 462285612Sdelphij /* 463285612Sdelphij * Mount each filesystem for which we have a filehandle. 464285612Sdelphij * If any of the mounts succeed then mark "ok" and return 465181834Sroberto * error code 0 at the end. If they all fail then return 466181834Sroberto * the last error code. 467181834Sroberto */ 468285612Sdelphij xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN); 469181834Sroberto if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { 470285612Sdelphij plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon"); 471285612Sdelphij error = EINVAL; 472285612Sdelphij goto out; 473181834Sroberto } 474285612Sdelphij ++rfs_dir; 475181834Sroberto for (j = 0; j < n_export; j++) { 476285612Sdelphij ex = ep[j]; 477285612Sdelphij if (ex) { 478285612Sdelphij /* 479285612Sdelphij * Note: the sizeof space left in rfs_dir is what's left in fs_name 480181834Sroberto * after strchr() above returned a pointer _inside_ fs_name. The 481285612Sdelphij * calculation below also takes into account that rfs_dir was 482285612Sdelphij * incremented by the ++ above. 483285612Sdelphij */ 484285612Sdelphij xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name)); 485285612Sdelphij make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); 486285612Sdelphij if (do_mount(&fp[j], mntpt, fs_name, mf) == 0) 487285612Sdelphij ok = TRUE; 488285612Sdelphij } 489285612Sdelphij } 490285612Sdelphij 491181834Sroberto /* 492285612Sdelphij * Clean up and exit 493285612Sdelphij */ 494285612Sdelphijout: 495285612Sdelphij discard_mntlist(mlist); 496181834Sroberto if (ep) 497285612Sdelphij XFREE(ep); 498285612Sdelphij if (fp) 499285612Sdelphij XFREE(fp); 500285612Sdelphij if (sock != RPC_ANYSOCK) 501285612Sdelphij (void) amu_close(sock); 502285612Sdelphij if (client) 503285612Sdelphij clnt_destroy(client); 504285612Sdelphij if (exlist) 505285612Sdelphij xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); 506285612Sdelphij if (ok) 507285612Sdelphij return 0; 508285612Sdelphij return error; 509285612Sdelphij} 510285612Sdelphij 511285612Sdelphij 512285612Sdelphij/* 513181834Sroberto * Return true if pref is a directory prefix of dir. 514285612Sdelphij * 515181834Sroberto * XXX TODO: 516181834Sroberto * Does not work if pref is "/". 517181834Sroberto */ 518285612Sdelphijstatic int 519285612Sdelphijdirectory_prefix(char *pref, char *dir) 520285612Sdelphij{ 521285612Sdelphij int len = strlen(pref); 522285612Sdelphij 523285612Sdelphij if (!NSTREQ(pref, dir, len)) 524285612Sdelphij return FALSE; 525285612Sdelphij if (dir[len] == '/' || dir[len] == '\0') 526285612Sdelphij return TRUE; 527181834Sroberto return FALSE; 528285612Sdelphij} 529285612Sdelphij 530285612Sdelphij 531285612Sdelphij/* 532285612Sdelphij * Unmount a mount tree 533285612Sdelphij */ 534285612Sdelphijstatic int 535285612Sdelphijamfs_host_umount(am_node *am, mntfs *mf) 536285612Sdelphij{ 537181834Sroberto mntlist *ml, *mprev; 538285612Sdelphij int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 539285612Sdelphij int xerror = 0; 540285612Sdelphij 541285612Sdelphij /* 542285612Sdelphij * Read the mount list 543285612Sdelphij */ 544285612Sdelphij mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); 545285612Sdelphij 546285612Sdelphij#ifdef MOUNT_TABLE_ON_FILE 547285612Sdelphij /* 548181834Sroberto * Unlock the mount list 549285612Sdelphij */ 550285612Sdelphij unlock_mntlist(); 551285612Sdelphij#endif /* MOUNT_TABLE_ON_FILE */ 552181834Sroberto 553285612Sdelphij /* 554285612Sdelphij * Reverse list... 555285612Sdelphij */ 556285612Sdelphij ml = mlist; 557285612Sdelphij mprev = 0; 558285612Sdelphij while (ml) { 559285612Sdelphij mntlist *ml2 = ml->mnext; 560285612Sdelphij ml->mnext = mprev; 561285612Sdelphij mprev = ml; 562285612Sdelphij ml = ml2; 563285612Sdelphij } 564285612Sdelphij mlist = mprev; 565285612Sdelphij 566285612Sdelphij /* 567285612Sdelphij * Unmount all filesystems... 568285612Sdelphij */ 569285612Sdelphij for (ml = mlist; ml && !xerror; ml = ml->mnext) { 570285612Sdelphij char *dir = ml->mnt->mnt_dir; 571285612Sdelphij if (directory_prefix(mf->mf_mount, dir)) { 572285612Sdelphij int error; 573285612Sdelphij dlog("amfs_host: unmounts %s", dir); 574285612Sdelphij /* 575285612Sdelphij * Unmount "dir" 576181834Sroberto */ 577181834Sroberto error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); 578285612Sdelphij /* 579181834Sroberto * Keep track of errors 580285612Sdelphij */ 581181834Sroberto if (error) { 582285612Sdelphij /* 583285612Sdelphij * If we have not already set xerror and error is not ENOENT, 584285612Sdelphij * then set xerror equal to error and log it. 585285612Sdelphij * 'xerror' is the return value for this function. 586285612Sdelphij * 587285612Sdelphij * We do not want to pass ENOENT as an error because if the 588285612Sdelphij * directory does not exists our work is done anyway. 589285612Sdelphij */ 590181834Sroberto if (!xerror && error != ENOENT) 591285612Sdelphij xerror = error; 592181834Sroberto if (error != EBUSY) { 593181834Sroberto errno = error; 594181834Sroberto plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 595181834Sroberto } 596285612Sdelphij } else { 597285612Sdelphij (void) rmdirs(dir); 598285612Sdelphij } 599285612Sdelphij } 600285612Sdelphij } 601285612Sdelphij 602181834Sroberto /* 603285612Sdelphij * Throw away mount list 604285612Sdelphij */ 605285612Sdelphij discard_mntlist(mlist); 606285612Sdelphij 607285612Sdelphij /* 608285612Sdelphij * Try to remount, except when we are shutting down. 609181834Sroberto */ 610285612Sdelphij if (xerror && amd_state != Finishing) { 611285612Sdelphij xerror = amfs_host_mount(am, mf); 612285612Sdelphij if (!xerror) { 613285612Sdelphij /* 614285612Sdelphij * Don't log this - it's usually too verbose 615285612Sdelphij plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 616285612Sdelphij */ 617285612Sdelphij xerror = EBUSY; 618285612Sdelphij } 619285612Sdelphij } 620285612Sdelphij return xerror; 621285612Sdelphij} 622285612Sdelphij 623285612Sdelphij 624285612Sdelphij/* 625285612Sdelphij * Tell mountd we're done. 626285612Sdelphij * This is not quite right, because we may still 627285612Sdelphij * have other filesystems mounted, but the existing 628285612Sdelphij * mountd protocol is badly broken anyway. 629285612Sdelphij */ 630285612Sdelphijstatic void 631285612Sdelphijamfs_host_umounted(mntfs *mf) 632285612Sdelphij{ 633285612Sdelphij char *host; 634285612Sdelphij CLIENT *client; 635285612Sdelphij enum clnt_stat clnt_stat; 636285612Sdelphij struct sockaddr_in sin; 637181834Sroberto int sock = RPC_ANYSOCK; 638285612Sdelphij struct timeval tv; 639285612Sdelphij u_long mnt_version; 640285612Sdelphij 641285612Sdelphij if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) 642181834Sroberto return; 643285612Sdelphij 644285612Sdelphij /* 645285612Sdelphij * WebNFS servers shouldn't ever get here. 646285612Sdelphij */ 647285612Sdelphij if (mf->mf_flags & MFF_WEBNFS) { 648181834Sroberto plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS"); 649285612Sdelphij return; 650285612Sdelphij } 651285612Sdelphij 652285612Sdelphij /* 653285612Sdelphij * Take a copy of the server hostname, address, and NFS version 654285612Sdelphij * to mount version conversion. 655285612Sdelphij */ 656285612Sdelphij host = mf->mf_server->fs_host; 657285612Sdelphij sin = *mf->mf_server->fs_ip; 658285612Sdelphij plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); 659285612Sdelphij#ifdef HAVE_FS_NFS3 660285612Sdelphij if (mf->mf_server->fs_version == NFS_VERSION3) 661285612Sdelphij mnt_version = AM_MOUNTVERS3; 662285612Sdelphij else 663285612Sdelphij#endif /* HAVE_FS_NFS3 */ 664285612Sdelphij mnt_version = MOUNTVERS; 665285612Sdelphij 666285612Sdelphij /* 667285612Sdelphij * Create a client attached to mountd 668285612Sdelphij */ 669181834Sroberto tv.tv_sec = 10; 670181834Sroberto tv.tv_usec = 0; 671181834Sroberto client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 672285612Sdelphij if (client == NULL) { 673285612Sdelphij#ifdef HAVE_CLNT_SPCREATEERROR 674285612Sdelphij plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 675285612Sdelphij host, clnt_spcreateerror("")); 676285612Sdelphij#else /* not HAVE_CLNT_SPCREATEERROR */ 677285612Sdelphij plog(XLOG_ERROR, "get_mount_client failed for %s", host); 678285612Sdelphij#endif /* not HAVE_CLNT_SPCREATEERROR */ 679285612Sdelphij goto out; 680285612Sdelphij } 681285612Sdelphij 682285612Sdelphij if (!nfs_auth) { 683285612Sdelphij if (make_nfs_auth()) 684285612Sdelphij goto out; 685285612Sdelphij } 686285612Sdelphij client->cl_auth = nfs_auth; 687285612Sdelphij 688285612Sdelphij dlog("Unmounting all from %s", host); 689285612Sdelphij 690285612Sdelphij clnt_stat = clnt_call(client, 691285612Sdelphij MOUNTPROC_UMNTALL, 692285612Sdelphij (XDRPROC_T_TYPE) xdr_void, 693285612Sdelphij 0, 694285612Sdelphij (XDRPROC_T_TYPE) xdr_void, 695285612Sdelphij 0, 696285612Sdelphij tv); 697181834Sroberto if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) { 698181834Sroberto /* RPC_SYSTEMERROR seems to be returned for no good reason ... */ 699181834Sroberto const char *msg = clnt_sperrno(clnt_stat); 700285612Sdelphij plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg); 701285612Sdelphij goto out; 702181834Sroberto } 703181834Sroberto 704181834Srobertoout: 705181834Sroberto if (sock != RPC_ANYSOCK) 706181834Sroberto (void) amu_close(sock); 707181834Sroberto if (client) 708181834Sroberto clnt_destroy(client); 709181834Sroberto} 710181834Sroberto