amfs_host.c revision 131702
1118611Snjl/* 2118611Snjl * Copyright (c) 1997-2004 Erez Zadok 3118611Snjl * Copyright (c) 1990 Jan-Simon Pendry 4118611Snjl * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5118611Snjl * Copyright (c) 1990 The Regents of the University of California. 6118611Snjl * All rights reserved. 7118611Snjl * 8118611Snjl * This code is derived from software contributed to Berkeley by 9118611Snjl * Jan-Simon Pendry at Imperial College, London. 10118611Snjl * 11118611Snjl * Redistribution and use in source and binary forms, with or without 12118611Snjl * modification, are permitted provided that the following conditions 13118611Snjl * are met: 14118611Snjl * 1. Redistributions of source code must retain the above copyright 15118611Snjl * notice, this list of conditions and the following disclaimer. 16118611Snjl * 2. Redistributions in binary form must reproduce the above copyright 17118611Snjl * notice, this list of conditions and the following disclaimer in the 18118611Snjl * documentation and/or other materials provided with the distribution. 19118611Snjl * 3. All advertising materials mentioning features or use of this software 20118611Snjl * must display the following acknowledgment: 21118611Snjl * This product includes software developed by the University of 22118611Snjl * California, Berkeley and its contributors. 23118611Snjl * 4. Neither the name of the University nor the names of its contributors 24118611Snjl * may be used to endorse or promote products derived from this software 25118611Snjl * without specific prior written permission. 26118611Snjl * 27118611Snjl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28118611Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29118611Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30118611Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32118611Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34118611Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36118611Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37118611Snjl * SUCH DAMAGE. 38118611Snjl * 39118611Snjl * %W% (Berkeley) %G% 40118611Snjl * 41118611Snjl * $Id: amfs_host.c,v 1.4.2.7 2004/01/06 03:15:16 ezk Exp $ 42118611Snjl * 43118611Snjl */ 44118611Snjl 45118611Snjl/* 46118611Snjl * NFS host file system. 47118611Snjl * Mounts all exported filesystems from a given host. 48118611Snjl * This has now degenerated into a mess but will not 49118611Snjl * be rewritten. Amd 6 will support the abstractions 50118611Snjl * needed to make this work correctly. 51118611Snjl */ 52118611Snjl 53118611Snjl#ifdef HAVE_CONFIG_H 54118611Snjl# include <config.h> 55118611Snjl#endif /* HAVE_CONFIG_H */ 56118611Snjl#include <am_defs.h> 57118611Snjl#include <amd.h> 58118611Snjl 59118611Snjlstatic char *amfs_host_match(am_opts *fo); 60118611Snjlstatic int amfs_host_fmount(mntfs *mf); 61118611Snjlstatic int amfs_host_fumount(mntfs *mf); 62118611Snjlstatic int amfs_host_init(mntfs *mf); 63118611Snjlstatic void amfs_host_umounted(am_node *mp); 64118611Snjl 65118611Snjl/* 66118611Snjl * Ops structure 67118611Snjl */ 68118611Snjlam_ops amfs_host_ops = 69118611Snjl{ 70118611Snjl "host", 71118611Snjl amfs_host_match, 72118611Snjl amfs_host_init, 73118611Snjl amfs_auto_fmount, 74118611Snjl amfs_host_fmount, 75118611Snjl amfs_auto_fumount, 76118611Snjl amfs_host_fumount, 77118611Snjl amfs_error_lookuppn, 78118611Snjl amfs_error_readdir, 79118611Snjl 0, /* amfs_host_readlink */ 80118611Snjl 0, /* amfs_host_mounted */ 81118611Snjl amfs_host_umounted, 82118611Snjl find_nfs_srvr, 83118611Snjl FS_MKMNT | FS_BACKGROUND | FS_AMQINFO 84118611Snjl}; 85118611Snjl 86118611Snjl 87118611Snjl/* 88118611Snjl * Determine the mount point: 89118611Snjl * 90118611Snjl * The next change we put in to better handle PCs. This is a bit 91118611Snjl * disgusting, so you'd better sit down. We change the make_mntpt function 92118611Snjl * to look for exported file systems without a leading '/'. If they don't 93118611Snjl * have a leading '/', we add one. If the export is 'a:' through 'z:' 94118611Snjl * (without a leading slash), we change it to 'a%' (or b% or z%). This 95118611Snjl * allows the entire PC disk to be mounted. 96118611Snjl */ 97118611Snjlstatic void 98118611Snjlmake_mntpt(char *mntpt, const exports ex, const mntfs *mf) 99118611Snjl{ 100118611Snjl if (ex->ex_dir[0] == '/') { 101118611Snjl if (ex->ex_dir[1] == 0) 102118611Snjl strcpy(mntpt, (mf)->mf_mount); 103118611Snjl else 104118611Snjl sprintf(mntpt, "%s%s", mf->mf_mount, ex->ex_dir); 105118611Snjl } else if (ex->ex_dir[0] >= 'a' && 106118611Snjl ex->ex_dir[0] <= 'z' && 107118611Snjl ex->ex_dir[1] == ':' && 108118611Snjl ex->ex_dir[2] == '/' && 109118611Snjl ex->ex_dir[3] == 0) 110118611Snjl sprintf(mntpt, "%s/%c%%", mf->mf_mount, ex->ex_dir[0]); 111118611Snjl else 112118611Snjl sprintf(mntpt, "%s/%s", mf->mf_mount, ex->ex_dir); 113118611Snjl} 114118611Snjl 115118611Snjl 116118611Snjl/* 117118611Snjl * Execute needs the same as NFS plus a helper command 118118611Snjl */ 119118611Snjlstatic char * 120118611Snjlamfs_host_match(am_opts *fo) 121118611Snjl{ 122118611Snjl extern am_ops nfs_ops; 123118611Snjl 124118611Snjl /* 125118611Snjl * Make sure rfs is specified to keep nfs_match happy... 126118611Snjl */ 127118611Snjl if (!fo->opt_rfs) 128118611Snjl fo->opt_rfs = "/"; 129118611Snjl 130118611Snjl return (*nfs_ops.fs_match) (fo); 131118611Snjl} 132118611Snjl 133118611Snjl 134118611Snjlstatic int 135118611Snjlamfs_host_init(mntfs *mf) 136118611Snjl{ 137118611Snjl fserver *fs; 138118611Snjl u_short port; 139118611Snjl 140118611Snjl if (strchr(mf->mf_info, ':') == 0) 141118611Snjl return ENOENT; 142118611Snjl 143118611Snjl /* 144118611Snjl * This is primarily to schedule a wakeup so that as soon 145118611Snjl * as our fileserver is ready, we can continue setting up 146118611Snjl * the host filesystem. If we don't do this, the standard 147118611Snjl * amfs_auto code will set up a fileserver structure, but it will 148118611Snjl * have to wait for another nfs request from the client to come 149118611Snjl * in before finishing. Our way is faster since we don't have 150118611Snjl * to wait for the client to resend its request (which could 151118611Snjl * take a second or two). 152118611Snjl */ 153118611Snjl /* 154118611Snjl * First, we find the fileserver for this mntfs and then call 155118611Snjl * nfs_srvr_port with our mntfs passed as the wait channel. 156118611Snjl * nfs_srvr_port will check some things and then schedule 157118611Snjl * it so that when the fileserver is ready, a wakeup is done 158118611Snjl * on this mntfs. amfs_auto_cont() is already sleeping on this mntfs 159118611Snjl * so as soon as that wakeup happens amfs_auto_cont() is called and 160118611Snjl * this mount is retried. 161118611Snjl */ 162118611Snjl if ((fs = mf->mf_server)) 163118611Snjl /* 164118611Snjl * We don't really care if there's an error returned. 165118611Snjl * Since this is just to help speed things along, the 166118611Snjl * error will get handled properly elsewhere. 167118611Snjl */ 168118611Snjl (void) nfs_srvr_port(fs, &port, (voidp) mf); 169118611Snjl 170118611Snjl return 0; 171118611Snjl} 172118611Snjl 173118611Snjl 174118611Snjlstatic int 175118611Snjldo_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf) 176118611Snjl{ 177118611Snjl struct stat stb; 178118611Snjl 179118611Snjl#ifdef DEBUG 180118611Snjl dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir); 181118611Snjl#endif /* DEBUG */ 182118611Snjl 183118611Snjl (void) mkdirs(dir, 0555); 184118611Snjl if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { 185118611Snjl plog(XLOG_ERROR, "No mount point for %s - skipping", dir); 186118611Snjl return ENOENT; 187118611Snjl } 188118611Snjl 189118611Snjl return mount_nfs_fh(fhp, dir, fs_name, opts, mf); 190118611Snjl} 191118611Snjl 192118611Snjl 193118611Snjlstatic int 194118611Snjlsortfun(const voidp x, const voidp y) 195118611Snjl{ 196118611Snjl exports *a = (exports *) x; 197118611Snjl exports *b = (exports *) y; 198118611Snjl 199118611Snjl return strcmp((*a)->ex_dir, (*b)->ex_dir); 200118611Snjl} 201118611Snjl 202118611Snjl 203118611Snjl/* 204118611Snjl * Get filehandle 205118611Snjl */ 206118611Snjlstatic int 207118611Snjlfetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version) 208118611Snjl{ 209118611Snjl struct timeval tv; 210118611Snjl enum clnt_stat clnt_stat; 211118611Snjl 212118611Snjl /* 213118611Snjl * Pick a number, any number... 214118611Snjl */ 215118611Snjl tv.tv_sec = 20; 216118611Snjl tv.tv_usec = 0; 217118611Snjl 218118611Snjl#ifdef DEBUG 219118611Snjl dlog("Fetching fhandle for %s", dir); 220118611Snjl#endif /* DEBUG */ 221118611Snjl 222118611Snjl /* 223118611Snjl * Call the mount daemon on the remote host to 224118611Snjl * get the filehandle. Use NFS version specific call. 225118611Snjl */ 226118611Snjl 227118611Snjl plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version); 228118611Snjl#ifdef HAVE_FS_NFS3 229118611Snjl if (nfs_version == NFS_VERSION3) { 230118611Snjl memset((char *) &fhp->v3, 0, sizeof(fhp->v3)); 231118611Snjl clnt_stat = clnt_call(client, 232118611Snjl MOUNTPROC_MNT, 233118611Snjl (XDRPROC_T_TYPE) xdr_dirpath, 234118611Snjl (SVC_IN_ARG_TYPE) &dir, 235118611Snjl (XDRPROC_T_TYPE) xdr_mountres3, 236118611Snjl (SVC_IN_ARG_TYPE) &fhp->v3, 237118611Snjl tv); 238118611Snjl if (clnt_stat != RPC_SUCCESS) { 239118611Snjl plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 240118611Snjl return EIO; 241118611Snjl } 242118611Snjl /* Check the status of the filehandle */ 243118611Snjl if ((errno = fhp->v3.fhs_status)) { 244118611Snjl#ifdef DEBUG 245118611Snjl dlog("fhandle fetch for mount version 3 failed: %m"); 246118611Snjl#endif /* DEBUG */ 247118611Snjl return errno; 248118611Snjl } 249118611Snjl } else { /* not NFS_VERSION3 mount */ 250118611Snjl#endif /* HAVE_FS_NFS3 */ 251118611Snjl clnt_stat = clnt_call(client, 252118611Snjl MOUNTPROC_MNT, 253118611Snjl (XDRPROC_T_TYPE) xdr_dirpath, 254118611Snjl (SVC_IN_ARG_TYPE) &dir, 255118611Snjl (XDRPROC_T_TYPE) xdr_fhstatus, 256118611Snjl (SVC_IN_ARG_TYPE) &fhp->v2, 257118611Snjl tv); 258118611Snjl if (clnt_stat != RPC_SUCCESS) { 259118611Snjl plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 260118611Snjl return EIO; 261118611Snjl } 262118611Snjl /* Check status of filehandle */ 263118611Snjl if (fhp->v2.fhs_status) { 264118611Snjl errno = fhp->v2.fhs_status; 265118611Snjl#ifdef DEBUG 266118611Snjl dlog("fhandle fetch for mount version 1 failed: %m"); 267118611Snjl#endif /* DEBUG */ 268118611Snjl return errno; 269118611Snjl } 270118611Snjl#ifdef HAVE_FS_NFS3 271118611Snjl } /* end of "if (nfs_version == NFS_VERSION3)" statement */ 272118611Snjl#endif /* HAVE_FS_NFS3 */ 273118611Snjl 274118611Snjl /* all is well */ 275118611Snjl return 0; 276118611Snjl} 277118611Snjl 278118611Snjl 279118611Snjl/* 280118611Snjl * Scan mount table to see if something already mounted 281118611Snjl */ 282118611Snjlstatic int 283118611Snjlalready_mounted(mntlist *mlist, char *dir) 284118611Snjl{ 285118611Snjl mntlist *ml; 286118611Snjl 287118611Snjl for (ml = mlist; ml; ml = ml->mnext) 288118611Snjl if (STREQ(ml->mnt->mnt_dir, dir)) 289118611Snjl return 1; 290118611Snjl return 0; 291118611Snjl} 292118611Snjl 293118611Snjl 294118611Snjl/* 295118611Snjl * Mount the export tree from a host 296118611Snjl */ 297118611Snjlstatic int 298118611Snjlamfs_host_fmount(mntfs *mf) 299118611Snjl{ 300118611Snjl struct timeval tv2; 301118611Snjl CLIENT *client; 302118611Snjl enum clnt_stat clnt_stat; 303118611Snjl int n_export; 304118611Snjl int j, k; 305118611Snjl exports exlist = 0, ex; 306118611Snjl exports *ep = 0; 307118611Snjl am_nfs_handle_t *fp = 0; 308118611Snjl char *host; 309118611Snjl int error = 0; 310118611Snjl struct sockaddr_in sin; 311118611Snjl int sock = RPC_ANYSOCK; 312118611Snjl int ok = FALSE; 313118611Snjl mntlist *mlist; 314118611Snjl char fs_name[MAXPATHLEN], *rfs_dir; 315118611Snjl char mntpt[MAXPATHLEN]; 316118611Snjl struct timeval tv; 317118611Snjl u_long mnt_version; 318118611Snjl 319118611Snjl /* 320118611Snjl * Read the mount list 321118611Snjl */ 322118611Snjl mlist = read_mtab(mf->mf_mount, mnttab_file_name); 323118611Snjl 324118611Snjl#ifdef MOUNT_TABLE_ON_FILE 325118611Snjl /* 326118611Snjl * Unlock the mount list 327118611Snjl */ 328118611Snjl unlock_mntlist(); 329118611Snjl#endif /* MOUNT_TABLE_ON_FILE */ 330118611Snjl 331118611Snjl /* 332118611Snjl * Take a copy of the server hostname, address, and nfs version 333118611Snjl * to mount version conversion. 334118611Snjl */ 335118611Snjl host = mf->mf_server->fs_host; 336118611Snjl sin = *mf->mf_server->fs_ip; 337118611Snjl plog(XLOG_INFO, "amfs_host_fmount: NFS version %d", (int) mf->mf_server->fs_version); 338118611Snjl#ifdef HAVE_FS_NFS3 339118611Snjl if (mf->mf_server->fs_version == NFS_VERSION3) 340118611Snjl mnt_version = MOUNTVERS3; 341118611Snjl else 342118611Snjl#endif /* HAVE_FS_NFS3 */ 343118611Snjl mnt_version = MOUNTVERS; 344118611Snjl 345118611Snjl /* 346118611Snjl * The original 10 second per try timeout is WAY too large, especially 347118611Snjl * if we're only waiting 10 or 20 seconds max for the response. 348118611Snjl * That would mean we'd try only once in 10 seconds, and we could 349118611Snjl * lose the transmit or receive packet, and never try again. 350118611Snjl * A 2-second per try timeout here is much more reasonable. 351118611Snjl * 09/28/92 Mike Mitchell, mcm@unx.sas.com 352118611Snjl */ 353118611Snjl tv.tv_sec = 2; 354118611Snjl tv.tv_usec = 0; 355118611Snjl 356118611Snjl /* 357118611Snjl * Create a client attached to mountd 358118611Snjl */ 359118611Snjl client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 360118611Snjl if (client == NULL) { 361118611Snjl#ifdef HAVE_CLNT_SPCREATEERROR 362118611Snjl plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 363118611Snjl host, clnt_spcreateerror("")); 364118611Snjl#else /* not HAVE_CLNT_SPCREATEERROR */ 365118611Snjl plog(XLOG_ERROR, "get_mount_client failed for %s", host); 366118611Snjl#endif /* not HAVE_CLNT_SPCREATEERROR */ 367118611Snjl error = EIO; 368118611Snjl goto out; 369118611Snjl } 370118611Snjl if (!nfs_auth) { 371118611Snjl error = make_nfs_auth(); 372118611Snjl if (error) 373118611Snjl goto out; 374118611Snjl } 375118611Snjl client->cl_auth = nfs_auth; 376118611Snjl 377118611Snjl#ifdef DEBUG 378118611Snjl dlog("Fetching export list from %s", host); 379118611Snjl#endif /* DEBUG */ 380118611Snjl 381118611Snjl /* 382118611Snjl * Fetch the export list 383118611Snjl */ 384118611Snjl tv2.tv_sec = 10; 385118611Snjl tv2.tv_usec = 0; 386118611Snjl clnt_stat = clnt_call(client, 387118611Snjl MOUNTPROC_EXPORT, 388118611Snjl (XDRPROC_T_TYPE) xdr_void, 389118611Snjl 0, 390118611Snjl (XDRPROC_T_TYPE) xdr_exports, 391118611Snjl (SVC_IN_ARG_TYPE) & exlist, 392118611Snjl tv2); 393118611Snjl if (clnt_stat != RPC_SUCCESS) { 394118611Snjl const char *msg = clnt_sperrno(clnt_stat); 395118611Snjl plog(XLOG_ERROR, "host_fmount rpc failed: %s", msg); 396118611Snjl /* clnt_perror(client, "rpc"); */ 397118611Snjl error = EIO; 398118611Snjl goto out; 399118611Snjl } 400118611Snjl 401118611Snjl /* 402118611Snjl * Figure out how many exports were returned 403118611Snjl */ 404118611Snjl for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 405118611Snjl n_export++; 406118611Snjl } 407118611Snjl 408118611Snjl /* 409118611Snjl * Allocate an array of pointers into the list 410118611Snjl * so that they can be sorted. If the filesystem 411118611Snjl * is already mounted then ignore it. 412118611Snjl */ 413118611Snjl ep = (exports *) xmalloc(n_export * sizeof(exports)); 414118611Snjl for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 415118611Snjl make_mntpt(mntpt, ex, mf); 416118611Snjl if (already_mounted(mlist, mntpt)) 417118611Snjl /* we have at least one mounted f/s, so don't fail the mount */ 418118611Snjl ok = TRUE; 419118611Snjl else 420118611Snjl ep[j++] = ex; 421118611Snjl } 422118611Snjl n_export = j; 423118611Snjl 424118611Snjl /* 425118611Snjl * Sort into order. 426118611Snjl * This way the mounts are done in order down the tree, 427118611Snjl * instead of any random order returned by the mount 428118611Snjl * daemon (the protocol doesn't specify...). 429118611Snjl */ 430118611Snjl qsort(ep, n_export, sizeof(exports), sortfun); 431118611Snjl 432118611Snjl /* 433118611Snjl * Allocate an array of filehandles 434118611Snjl */ 435118611Snjl fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); 436118611Snjl 437118611Snjl /* 438118611Snjl * Try to obtain filehandles for each directory. 439118611Snjl * If a fetch fails then just zero out the array 440118611Snjl * reference but discard the error. 441118611Snjl */ 442118611Snjl for (j = k = 0; j < n_export; j++) { 443118611Snjl /* Check and avoid a duplicated export entry */ 444118611Snjl if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { 445118611Snjl#ifdef DEBUG 446118611Snjl dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 447118611Snjl#endif /* DEBUG */ 448118611Snjl ep[j] = 0; 449118611Snjl } else { 450118611Snjl k = j; 451118611Snjl error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], 452118611Snjl mf->mf_server->fs_version); 453118611Snjl if (error) 454118611Snjl ep[j] = 0; 455118611Snjl } 456118611Snjl } 457118611Snjl 458118611Snjl /* 459118611Snjl * Mount each filesystem for which we have a filehandle. 460118611Snjl * If any of the mounts succeed then mark "ok" and return 461118611Snjl * error code 0 at the end. If they all fail then return 462118611Snjl * the last error code. 463118611Snjl */ 464118611Snjl strncpy(fs_name, mf->mf_info, sizeof(fs_name)); 465118611Snjl if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { 466118611Snjl plog(XLOG_FATAL, "amfs_host_fmount: mf_info has no colon"); 467118611Snjl error = EINVAL; 468118611Snjl goto out; 469118611Snjl } 470118611Snjl ++rfs_dir; 471118611Snjl for (j = 0; j < n_export; j++) { 472118611Snjl ex = ep[j]; 473118611Snjl if (ex) { 474118611Snjl strcpy(rfs_dir, ex->ex_dir); 475118611Snjl make_mntpt(mntpt, ex, mf); 476118611Snjl if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0) 477118611Snjl ok = TRUE; 478118611Snjl } 479118611Snjl } 480118611Snjl 481118611Snjl /* 482118611Snjl * Clean up and exit 483118611Snjl */ 484118611Snjlout: 485118611Snjl discard_mntlist(mlist); 486118611Snjl if (ep) 487118611Snjl XFREE(ep); 488118611Snjl if (fp) 489118611Snjl XFREE(fp); 490118611Snjl if (sock != RPC_ANYSOCK) 491118611Snjl (void) amu_close(sock); 492118611Snjl if (client) 493118611Snjl clnt_destroy(client); 494118611Snjl if (exlist) 495118611Snjl xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); 496118611Snjl if (ok) 497118611Snjl return 0; 498118611Snjl return error; 499118611Snjl} 500118611Snjl 501118611Snjl 502118611Snjl/* 503118611Snjl * Return true if pref is a directory prefix of dir. 504118611Snjl * 505118611Snjl * XXX TODO: 506118611Snjl * Does not work if pref is "/". 507118611Snjl */ 508118611Snjlstatic int 509118611Snjldirectory_prefix(char *pref, char *dir) 510118611Snjl{ 511118611Snjl int len = strlen(pref); 512118611Snjl 513118611Snjl if (!NSTREQ(pref, dir, len)) 514118611Snjl return FALSE; 515118611Snjl if (dir[len] == '/' || dir[len] == '\0') 516118611Snjl return TRUE; 517118611Snjl return FALSE; 518118611Snjl} 519118611Snjl 520118611Snjl 521118611Snjl/* 522118611Snjl * Unmount a mount tree 523118611Snjl */ 524118611Snjlstatic int 525118611Snjlamfs_host_fumount(mntfs *mf) 526118611Snjl{ 527118611Snjl mntlist *ml, *mprev; 528118611Snjl int xerror = 0; 529118611Snjl 530118611Snjl /* 531118611Snjl * Read the mount list 532118611Snjl */ 533118611Snjl mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); 534118611Snjl 535118611Snjl#ifdef MOUNT_TABLE_ON_FILE 536118611Snjl /* 537118611Snjl * Unlock the mount list 538118611Snjl */ 539118611Snjl unlock_mntlist(); 540118611Snjl#endif /* MOUNT_TABLE_ON_FILE */ 541118611Snjl 542118611Snjl /* 543118611Snjl * Reverse list... 544118611Snjl */ 545118611Snjl ml = mlist; 546118611Snjl mprev = 0; 547118611Snjl while (ml) { 548118611Snjl mntlist *ml2 = ml->mnext; 549118611Snjl ml->mnext = mprev; 550118611Snjl mprev = ml; 551118611Snjl ml = ml2; 552118611Snjl } 553118611Snjl mlist = mprev; 554118611Snjl 555118611Snjl /* 556118611Snjl * Unmount all filesystems... 557118611Snjl */ 558118611Snjl for (ml = mlist; ml && !xerror; ml = ml->mnext) { 559118611Snjl char *dir = ml->mnt->mnt_dir; 560118611Snjl if (directory_prefix(mf->mf_mount, dir)) { 561118611Snjl int error; 562118611Snjl#ifdef DEBUG 563118611Snjl dlog("amfs_host: unmounts %s", dir); 564118611Snjl#endif /* DEBUG */ 565118611Snjl /* 566118611Snjl * Unmount "dir" 567118611Snjl */ 568118611Snjl error = UMOUNT_FS(dir, mnttab_file_name); 569118611Snjl /* 570118611Snjl * Keep track of errors 571118611Snjl */ 572118611Snjl if (error) { 573118611Snjl if (!xerror) 574118611Snjl xerror = error; 575118611Snjl if (error != EBUSY) { 576118611Snjl errno = error; 577118611Snjl plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 578118611Snjl } 579118611Snjl } else { 580118611Snjl (void) rmdirs(dir); 581118611Snjl } 582118611Snjl } 583118611Snjl } 584118611Snjl 585118611Snjl /* 586118611Snjl * Throw away mount list 587118611Snjl */ 588118611Snjl discard_mntlist(mlist); 589118611Snjl 590118611Snjl /* 591118611Snjl * Try to remount, except when we are shutting down. 592118611Snjl */ 593118611Snjl if (xerror && amd_state != Finishing) { 594118611Snjl xerror = amfs_host_fmount(mf); 595118611Snjl if (!xerror) { 596118611Snjl /* 597118611Snjl * Don't log this - it's usually too verbose 598118611Snjl plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 599118611Snjl */ 600118611Snjl xerror = EBUSY; 601118611Snjl } 602118611Snjl } 603118611Snjl return xerror; 604118611Snjl} 605118611Snjl 606118611Snjl 607118611Snjl/* 608118611Snjl * Tell mountd we're done. 609118611Snjl * This is not quite right, because we may still 610118611Snjl * have other filesystems mounted, but the existing 611118611Snjl * mountd protocol is badly broken anyway. 612118611Snjl */ 613118611Snjlstatic void 614118611Snjlamfs_host_umounted(am_node *mp) 615118611Snjl{ 616118611Snjl mntfs *mf = mp->am_mnt; 617118611Snjl char *host; 618118611Snjl CLIENT *client; 619118611Snjl enum clnt_stat clnt_stat; 620118611Snjl struct sockaddr_in sin; 621118611Snjl int sock = RPC_ANYSOCK; 622118611Snjl struct timeval tv; 623118611Snjl u_long mnt_version; 624118611Snjl 625118611Snjl if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) 626118611Snjl return; 627118611Snjl 628118611Snjl /* 629118611Snjl * Take a copy of the server hostname, address, and NFS version 630118611Snjl * to mount version conversion. 631118611Snjl */ 632118611Snjl host = mf->mf_server->fs_host; 633118611Snjl sin = *mf->mf_server->fs_ip; 634118611Snjl plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); 635118611Snjl#ifdef HAVE_FS_NFS3 636118611Snjl if (mf->mf_server->fs_version == NFS_VERSION3) 637118611Snjl mnt_version = MOUNTVERS3; 638118611Snjl else 639118611Snjl#endif /* HAVE_FS_NFS3 */ 640118611Snjl mnt_version = MOUNTVERS; 641118611Snjl 642118611Snjl /* 643118611Snjl * Create a client attached to mountd 644118611Snjl */ 645118611Snjl tv.tv_sec = 10; 646118611Snjl tv.tv_usec = 0; 647118611Snjl client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 648118611Snjl if (client == NULL) { 649118611Snjl#ifdef HAVE_CLNT_SPCREATEERROR 650118611Snjl plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 651118611Snjl host, clnt_spcreateerror("")); 652118611Snjl#else /* not HAVE_CLNT_SPCREATEERROR */ 653118611Snjl plog(XLOG_ERROR, "get_mount_client failed for %s", host); 654118611Snjl#endif /* not HAVE_CLNT_SPCREATEERROR */ 655118611Snjl goto out; 656118611Snjl } 657118611Snjl 658118611Snjl if (!nfs_auth) { 659118611Snjl if (make_nfs_auth()) 660118611Snjl goto out; 661118611Snjl } 662118611Snjl client->cl_auth = nfs_auth; 663118611Snjl 664118611Snjl#ifdef DEBUG 665118611Snjl dlog("Unmounting all from %s", host); 666118611Snjl#endif /* DEBUG */ 667118611Snjl 668118611Snjl clnt_stat = clnt_call(client, 669118611Snjl MOUNTPROC_UMNTALL, 670118611Snjl (XDRPROC_T_TYPE) xdr_void, 671118611Snjl 0, 672118611Snjl (XDRPROC_T_TYPE) xdr_void, 673118611Snjl 0, 674118611Snjl tv); 675118611Snjl if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) { 676118611Snjl /* RPC_SYSTEMERROR seems to be returned for no good reason ... */ 677118611Snjl const char *msg = clnt_sperrno(clnt_stat); 678118611Snjl plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg); 679118611Snjl goto out; 680118611Snjl } 681118611Snjl 682118611Snjlout: 683118611Snjl if (sock != RPC_ANYSOCK) 684118611Snjl (void) amu_close(sock); 685118611Snjl if (client) 686118611Snjl clnt_destroy(client); 687118611Snjl} 688118611Snjl