1 1.1 christos /* $NetBSD: amfs_host.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1.1.3 christos * Copyright (c) 1997-2014 Erez Zadok 5 1.1 christos * Copyright (c) 1990 Jan-Simon Pendry 6 1.1 christos * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 1.1 christos * Copyright (c) 1990 The Regents of the University of California. 8 1.1 christos * All rights reserved. 9 1.1 christos * 10 1.1 christos * This code is derived from software contributed to Berkeley by 11 1.1 christos * Jan-Simon Pendry at Imperial College, London. 12 1.1 christos * 13 1.1 christos * Redistribution and use in source and binary forms, with or without 14 1.1 christos * modification, are permitted provided that the following conditions 15 1.1 christos * are met: 16 1.1 christos * 1. Redistributions of source code must retain the above copyright 17 1.1 christos * notice, this list of conditions and the following disclaimer. 18 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 19 1.1 christos * notice, this list of conditions and the following disclaimer in the 20 1.1 christos * documentation and/or other materials provided with the distribution. 21 1.1.1.3 christos * 3. Neither the name of the University nor the names of its contributors 22 1.1 christos * may be used to endorse or promote products derived from this software 23 1.1 christos * without specific prior written permission. 24 1.1 christos * 25 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 1.1 christos * SUCH DAMAGE. 36 1.1 christos * 37 1.1 christos * 38 1.1 christos * File: am-utils/amd/amfs_host.c 39 1.1 christos * 40 1.1 christos */ 41 1.1 christos 42 1.1 christos /* 43 1.1 christos * NFS host file system. 44 1.1 christos * Mounts all exported filesystems from a given host. 45 1.1 christos * This has now degenerated into a mess but will not 46 1.1 christos * be rewritten. Amd 6 will support the abstractions 47 1.1 christos * needed to make this work correctly. 48 1.1 christos */ 49 1.1 christos 50 1.1 christos #ifdef HAVE_CONFIG_H 51 1.1 christos # include <config.h> 52 1.1 christos #endif /* HAVE_CONFIG_H */ 53 1.1 christos #include <am_defs.h> 54 1.1 christos #include <amd.h> 55 1.1 christos 56 1.1 christos static char *amfs_host_match(am_opts *fo); 57 1.1 christos static int amfs_host_init(mntfs *mf); 58 1.1 christos static int amfs_host_mount(am_node *am, mntfs *mf); 59 1.1 christos static int amfs_host_umount(am_node *am, mntfs *mf); 60 1.1 christos static void amfs_host_umounted(mntfs *mf); 61 1.1 christos 62 1.1 christos /* 63 1.1 christos * Ops structure 64 1.1 christos */ 65 1.1 christos am_ops amfs_host_ops = 66 1.1 christos { 67 1.1 christos "host", 68 1.1 christos amfs_host_match, 69 1.1 christos amfs_host_init, 70 1.1 christos amfs_host_mount, 71 1.1 christos amfs_host_umount, 72 1.1 christos amfs_error_lookup_child, 73 1.1 christos amfs_error_mount_child, 74 1.1 christos amfs_error_readdir, 75 1.1 christos 0, /* amfs_host_readlink */ 76 1.1 christos 0, /* amfs_host_mounted */ 77 1.1 christos amfs_host_umounted, 78 1.1 christos find_nfs_srvr, 79 1.1 christos 0, /* amfs_host_get_wchan */ 80 1.1 christos FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, 81 1.1 christos #ifdef HAVE_FS_AUTOFS 82 1.1 christos AUTOFS_HOST_FS_FLAGS, 83 1.1 christos #endif /* HAVE_FS_AUTOFS */ 84 1.1 christos }; 85 1.1 christos 86 1.1 christos 87 1.1 christos /* 88 1.1 christos * Determine the mount point: 89 1.1 christos * 90 1.1 christos * The next change we put in to better handle PCs. This is a bit 91 1.1 christos * disgusting, so you'd better sit down. We change the make_mntpt function 92 1.1 christos * to look for exported file systems without a leading '/'. If they don't 93 1.1 christos * have a leading '/', we add one. If the export is 'a:' through 'z:' 94 1.1 christos * (without a leading slash), we change it to 'a%' (or b% or z%). This 95 1.1 christos * allows the entire PC disk to be mounted. 96 1.1 christos */ 97 1.1 christos static void 98 1.1 christos make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount) 99 1.1 christos { 100 1.1 christos if (ex->ex_dir[0] == '/') { 101 1.1 christos if (ex->ex_dir[1] == 0) 102 1.1 christos xstrlcpy(mntpt, mf_mount, l); 103 1.1 christos else 104 1.1 christos xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir); 105 1.1 christos } else if (ex->ex_dir[0] >= 'a' && 106 1.1 christos ex->ex_dir[0] <= 'z' && 107 1.1 christos ex->ex_dir[1] == ':' && 108 1.1 christos ex->ex_dir[2] == '/' && 109 1.1 christos ex->ex_dir[3] == 0) 110 1.1 christos xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]); 111 1.1 christos else 112 1.1 christos xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir); 113 1.1 christos } 114 1.1 christos 115 1.1 christos 116 1.1 christos /* 117 1.1 christos * Execute needs the same as NFS plus a helper command 118 1.1 christos */ 119 1.1 christos static char * 120 1.1 christos amfs_host_match(am_opts *fo) 121 1.1 christos { 122 1.1 christos extern am_ops nfs_ops; 123 1.1 christos 124 1.1 christos /* 125 1.1 christos * Make sure rfs is specified to keep nfs_match happy... 126 1.1 christos */ 127 1.1 christos if (!fo->opt_rfs) 128 1.1 christos fo->opt_rfs = "/"; 129 1.1 christos 130 1.1 christos return (*nfs_ops.fs_match) (fo); 131 1.1 christos } 132 1.1 christos 133 1.1 christos 134 1.1 christos static int 135 1.1 christos amfs_host_init(mntfs *mf) 136 1.1 christos { 137 1.1 christos u_short mountd_port; 138 1.1 christos 139 1.1 christos if (strchr(mf->mf_info, ':') == 0) 140 1.1 christos return ENOENT; 141 1.1 christos 142 1.1 christos /* 143 1.1 christos * This is primarily to schedule a wakeup so that as soon 144 1.1 christos * as our fileserver is ready, we can continue setting up 145 1.1 christos * the host filesystem. If we don't do this, the standard 146 1.1 christos * amfs_auto code will set up a fileserver structure, but it will 147 1.1 christos * have to wait for another nfs request from the client to come 148 1.1 christos * in before finishing. Our way is faster since we don't have 149 1.1 christos * to wait for the client to resend its request (which could 150 1.1 christos * take a second or two). 151 1.1 christos */ 152 1.1 christos /* 153 1.1 christos * First, we find the fileserver for this mntfs and then call 154 1.1 christos * get_mountd_port with our mntfs passed as the wait channel. 155 1.1 christos * get_mountd_port will check some things and then schedule 156 1.1 christos * it so that when the fileserver is ready, a wakeup is done 157 1.1 christos * on this mntfs. amfs_cont() is already sleeping on this mntfs 158 1.1 christos * so as soon as that wakeup happens amfs_cont() is called and 159 1.1 christos * this mount is retried. 160 1.1 christos */ 161 1.1 christos if (mf->mf_server) 162 1.1 christos /* 163 1.1 christos * We don't really care if there's an error returned. 164 1.1 christos * Since this is just to help speed things along, the 165 1.1 christos * error will get handled properly elsewhere. 166 1.1 christos */ 167 1.1 christos get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf)); 168 1.1 christos 169 1.1 christos return 0; 170 1.1 christos } 171 1.1 christos 172 1.1 christos 173 1.1 christos static int 174 1.1 christos do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) 175 1.1 christos { 176 1.1 christos struct stat stb; 177 1.1 christos 178 1.1 christos dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir); 179 1.1 christos 180 1.1 christos (void) mkdirs(mntdir, 0555); 181 1.1 christos if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { 182 1.1 christos plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir); 183 1.1 christos return ENOENT; 184 1.1 christos } 185 1.1 christos 186 1.1 christos return mount_nfs_fh(fhp, mntdir, fs_name, mf); 187 1.1 christos } 188 1.1 christos 189 1.1 christos 190 1.1 christos static int 191 1.1 christos sortfun(const voidp x, const voidp y) 192 1.1 christos { 193 1.1 christos exports *a = (exports *) x; 194 1.1 christos exports *b = (exports *) y; 195 1.1 christos 196 1.1 christos return strcmp((*a)->ex_dir, (*b)->ex_dir); 197 1.1 christos } 198 1.1 christos 199 1.1 christos 200 1.1 christos /* 201 1.1 christos * Get filehandle 202 1.1 christos */ 203 1.1 christos static int 204 1.1 christos fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version) 205 1.1 christos { 206 1.1 christos struct timeval tv; 207 1.1 christos enum clnt_stat clnt_stat; 208 1.1 christos struct fhstatus res; 209 1.1 christos #ifdef HAVE_FS_NFS3 210 1.1 christos struct am_mountres3 res3; 211 1.1 christos #endif /* HAVE_FS_NFS3 */ 212 1.1 christos 213 1.1 christos /* 214 1.1 christos * Pick a number, any number... 215 1.1 christos */ 216 1.1 christos tv.tv_sec = 20; 217 1.1 christos tv.tv_usec = 0; 218 1.1 christos 219 1.1 christos dlog("Fetching fhandle for %s", dir); 220 1.1 christos 221 1.1 christos /* 222 1.1 christos * Call the mount daemon on the remote host to 223 1.1 christos * get the filehandle. Use NFS version specific call. 224 1.1 christos */ 225 1.1 christos 226 1.1 christos plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version); 227 1.1 christos #ifdef HAVE_FS_NFS3 228 1.1.1.3 christos if (nfs_version == NFS_VERSION3 229 1.1.1.3 christos #ifdef HAVE_FS_NFS4 230 1.1.1.3 christos #ifndef NO_FALLBACK 231 1.1.1.3 christos || nfs_version == NFS_VERSION4 232 1.1.1.3 christos #endif /* NO_FALLBACK */ 233 1.1.1.3 christos #endif /* HAVE_FS_NFS4 */ 234 1.1.1.3 christos ) { 235 1.1.1.3 christos 236 1.1 christos memset((char *) &res3, 0, sizeof(res3)); 237 1.1 christos clnt_stat = clnt_call(client, 238 1.1 christos MOUNTPROC_MNT, 239 1.1 christos (XDRPROC_T_TYPE) xdr_dirpath, 240 1.1 christos (SVC_IN_ARG_TYPE) &dir, 241 1.1 christos (XDRPROC_T_TYPE) xdr_am_mountres3, 242 1.1 christos (SVC_IN_ARG_TYPE) &res3, 243 1.1 christos tv); 244 1.1 christos if (clnt_stat != RPC_SUCCESS) { 245 1.1 christos plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 246 1.1 christos return EIO; 247 1.1 christos } 248 1.1 christos /* Check the status of the filehandle */ 249 1.1 christos if ((errno = res3.fhs_status)) { 250 1.1 christos dlog("fhandle fetch for mount version 3 failed: %m"); 251 1.1 christos return errno; 252 1.1 christos } 253 1.1 christos memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3)); 254 1.1 christos fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; 255 1.1 christos memmove(fhp->v3.am_fh3_data, 256 1.1 christos res3.mountres3_u.mountinfo.fhandle.fhandle3_val, 257 1.1 christos fhp->v3.am_fh3_length); 258 1.1 christos } else { /* not NFS_VERSION3 mount */ 259 1.1 christos #endif /* HAVE_FS_NFS3 */ 260 1.1 christos clnt_stat = clnt_call(client, 261 1.1 christos MOUNTPROC_MNT, 262 1.1 christos (XDRPROC_T_TYPE) xdr_dirpath, 263 1.1 christos (SVC_IN_ARG_TYPE) &dir, 264 1.1 christos (XDRPROC_T_TYPE) xdr_fhstatus, 265 1.1 christos (SVC_IN_ARG_TYPE) &res, 266 1.1 christos tv); 267 1.1 christos if (clnt_stat != RPC_SUCCESS) { 268 1.1 christos plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 269 1.1 christos return EIO; 270 1.1 christos } 271 1.1 christos /* Check status of filehandle */ 272 1.1 christos if (res.fhs_status) { 273 1.1 christos errno = res.fhs_status; 274 1.1 christos dlog("fhandle fetch for mount version 1 failed: %m"); 275 1.1 christos return errno; 276 1.1 christos } 277 1.1 christos memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE); 278 1.1 christos #ifdef HAVE_FS_NFS3 279 1.1 christos } /* end of "if (nfs_version == NFS_VERSION3)" statement */ 280 1.1 christos #endif /* HAVE_FS_NFS3 */ 281 1.1 christos 282 1.1 christos /* all is well */ 283 1.1 christos return 0; 284 1.1 christos } 285 1.1 christos 286 1.1 christos 287 1.1 christos /* 288 1.1 christos * Scan mount table to see if something already mounted 289 1.1 christos */ 290 1.1 christos static int 291 1.1 christos already_mounted(mntlist *mlist, char *dir) 292 1.1 christos { 293 1.1 christos mntlist *ml; 294 1.1 christos 295 1.1 christos for (ml = mlist; ml; ml = ml->mnext) 296 1.1 christos if (STREQ(ml->mnt->mnt_dir, dir)) 297 1.1 christos return 1; 298 1.1 christos return 0; 299 1.1 christos } 300 1.1 christos 301 1.1 christos 302 1.1 christos static int 303 1.1 christos amfs_host_mount(am_node *am, mntfs *mf) 304 1.1 christos { 305 1.1 christos struct timeval tv2; 306 1.1 christos CLIENT *client; 307 1.1 christos enum clnt_stat clnt_stat; 308 1.1 christos int n_export; 309 1.1 christos int j, k; 310 1.1 christos exports exlist = 0, ex; 311 1.1 christos exports *ep = NULL; 312 1.1 christos am_nfs_handle_t *fp = NULL; 313 1.1 christos char *host; 314 1.1 christos int error = 0; 315 1.1 christos struct sockaddr_in sin; 316 1.1 christos int sock = RPC_ANYSOCK; 317 1.1 christos int ok = FALSE; 318 1.1 christos mntlist *mlist; 319 1.1 christos char fs_name[MAXPATHLEN], *rfs_dir; 320 1.1 christos char mntpt[MAXPATHLEN]; 321 1.1 christos struct timeval tv; 322 1.1 christos u_long mnt_version; 323 1.1 christos 324 1.1 christos /* 325 1.1 christos * WebNFS servers don't necessarily run mountd. 326 1.1 christos */ 327 1.1 christos if (mf->mf_flags & MFF_WEBNFS) { 328 1.1 christos plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS"); 329 1.1 christos return EIO; 330 1.1 christos } 331 1.1 christos 332 1.1 christos /* 333 1.1 christos * Read the mount list 334 1.1 christos */ 335 1.1 christos mlist = read_mtab(mf->mf_mount, mnttab_file_name); 336 1.1 christos 337 1.1 christos #ifdef MOUNT_TABLE_ON_FILE 338 1.1 christos /* 339 1.1 christos * Unlock the mount list 340 1.1 christos */ 341 1.1 christos unlock_mntlist(); 342 1.1 christos #endif /* MOUNT_TABLE_ON_FILE */ 343 1.1 christos 344 1.1 christos /* 345 1.1 christos * Take a copy of the server hostname, address, and nfs version 346 1.1 christos * to mount version conversion. 347 1.1 christos */ 348 1.1 christos host = mf->mf_server->fs_host; 349 1.1 christos sin = *mf->mf_server->fs_ip; 350 1.1 christos plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version); 351 1.1 christos #ifdef HAVE_FS_NFS3 352 1.1 christos if (mf->mf_server->fs_version == NFS_VERSION3) 353 1.1 christos mnt_version = AM_MOUNTVERS3; 354 1.1 christos else 355 1.1 christos #endif /* HAVE_FS_NFS3 */ 356 1.1 christos mnt_version = MOUNTVERS; 357 1.1 christos 358 1.1 christos /* 359 1.1 christos * The original 10 second per try timeout is WAY too large, especially 360 1.1 christos * if we're only waiting 10 or 20 seconds max for the response. 361 1.1 christos * That would mean we'd try only once in 10 seconds, and we could 362 1.1 christos * lose the transmit or receive packet, and never try again. 363 1.1 christos * A 2-second per try timeout here is much more reasonable. 364 1.1 christos * 09/28/92 Mike Mitchell, mcm (at) unx.sas.com 365 1.1 christos */ 366 1.1 christos tv.tv_sec = 2; 367 1.1 christos tv.tv_usec = 0; 368 1.1 christos 369 1.1 christos /* 370 1.1 christos * Create a client attached to mountd 371 1.1 christos */ 372 1.1 christos client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 373 1.1 christos if (client == NULL) { 374 1.1 christos #ifdef HAVE_CLNT_SPCREATEERROR 375 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 376 1.1 christos host, clnt_spcreateerror("")); 377 1.1 christos #else /* not HAVE_CLNT_SPCREATEERROR */ 378 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s", host); 379 1.1 christos #endif /* not HAVE_CLNT_SPCREATEERROR */ 380 1.1 christos error = EIO; 381 1.1 christos goto out; 382 1.1 christos } 383 1.1 christos if (!nfs_auth) { 384 1.1 christos error = make_nfs_auth(); 385 1.1 christos if (error) 386 1.1 christos goto out; 387 1.1 christos } 388 1.1 christos client->cl_auth = nfs_auth; 389 1.1 christos 390 1.1 christos dlog("Fetching export list from %s", host); 391 1.1 christos 392 1.1 christos /* 393 1.1 christos * Fetch the export list 394 1.1 christos */ 395 1.1 christos tv2.tv_sec = 10; 396 1.1 christos tv2.tv_usec = 0; 397 1.1 christos clnt_stat = clnt_call(client, 398 1.1 christos MOUNTPROC_EXPORT, 399 1.1 christos (XDRPROC_T_TYPE) xdr_void, 400 1.1 christos 0, 401 1.1 christos (XDRPROC_T_TYPE) xdr_exports, 402 1.1 christos (SVC_IN_ARG_TYPE) & exlist, 403 1.1 christos tv2); 404 1.1 christos if (clnt_stat != RPC_SUCCESS) { 405 1.1 christos const char *msg = clnt_sperrno(clnt_stat); 406 1.1 christos plog(XLOG_ERROR, "host_mount rpc failed: %s", msg); 407 1.1 christos /* clnt_perror(client, "rpc"); */ 408 1.1 christos error = EIO; 409 1.1 christos goto out; 410 1.1 christos } 411 1.1 christos 412 1.1 christos /* 413 1.1 christos * Figure out how many exports were returned 414 1.1 christos */ 415 1.1 christos for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 416 1.1 christos n_export++; 417 1.1 christos } 418 1.1 christos 419 1.1 christos /* 420 1.1 christos * Allocate an array of pointers into the list 421 1.1 christos * so that they can be sorted. If the filesystem 422 1.1 christos * is already mounted then ignore it. 423 1.1 christos */ 424 1.1 christos ep = (exports *) xmalloc(n_export * sizeof(exports)); 425 1.1 christos for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 426 1.1 christos make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); 427 1.1 christos if (already_mounted(mlist, mntpt)) 428 1.1 christos /* we have at least one mounted f/s, so don't fail the mount */ 429 1.1 christos ok = TRUE; 430 1.1 christos else 431 1.1 christos ep[j++] = ex; 432 1.1 christos } 433 1.1 christos n_export = j; 434 1.1 christos 435 1.1 christos /* 436 1.1 christos * Sort into order. 437 1.1 christos * This way the mounts are done in order down the tree, 438 1.1 christos * instead of any random order returned by the mount 439 1.1 christos * daemon (the protocol doesn't specify...). 440 1.1 christos */ 441 1.1 christos qsort(ep, n_export, sizeof(exports), sortfun); 442 1.1 christos 443 1.1 christos /* 444 1.1 christos * Allocate an array of filehandles 445 1.1 christos */ 446 1.1 christos fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); 447 1.1 christos 448 1.1 christos /* 449 1.1 christos * Try to obtain filehandles for each directory. 450 1.1 christos * If a fetch fails then just zero out the array 451 1.1 christos * reference but discard the error. 452 1.1 christos */ 453 1.1 christos for (j = k = 0; j < n_export; j++) { 454 1.1 christos /* Check and avoid a duplicated export entry */ 455 1.1 christos if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { 456 1.1 christos dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 457 1.1 christos ep[j] = NULL; 458 1.1 christos } else { 459 1.1 christos k = j; 460 1.1 christos error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], 461 1.1 christos mf->mf_server->fs_version); 462 1.1 christos if (error) 463 1.1 christos ep[j] = NULL; 464 1.1 christos } 465 1.1 christos } 466 1.1 christos 467 1.1 christos /* 468 1.1 christos * Mount each filesystem for which we have a filehandle. 469 1.1 christos * If any of the mounts succeed then mark "ok" and return 470 1.1 christos * error code 0 at the end. If they all fail then return 471 1.1 christos * the last error code. 472 1.1 christos */ 473 1.1 christos xstrlcpy(fs_name, mf->mf_info, sizeof(fs_name)); 474 1.1 christos if ((rfs_dir = strchr(fs_name, ':')) == (char *) NULL) { 475 1.1 christos plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon"); 476 1.1 christos error = EINVAL; 477 1.1 christos goto out; 478 1.1 christos } 479 1.1 christos ++rfs_dir; 480 1.1 christos for (j = 0; j < n_export; j++) { 481 1.1 christos ex = ep[j]; 482 1.1 christos if (ex) { 483 1.1 christos /* 484 1.1 christos * Note: the sizeof space left in rfs_dir is what's left in fs_name 485 1.1 christos * after strchr() above returned a pointer _inside_ fs_name. The 486 1.1 christos * calculation below also takes into account that rfs_dir was 487 1.1 christos * incremented by the ++ above. 488 1.1 christos */ 489 1.1 christos xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name)); 490 1.1 christos make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); 491 1.1 christos if (do_mount(&fp[j], mntpt, fs_name, mf) == 0) 492 1.1 christos ok = TRUE; 493 1.1 christos } 494 1.1 christos } 495 1.1 christos 496 1.1 christos /* 497 1.1 christos * Clean up and exit 498 1.1 christos */ 499 1.1 christos out: 500 1.1 christos discard_mntlist(mlist); 501 1.1.1.3 christos XFREE(ep); 502 1.1.1.3 christos XFREE(fp); 503 1.1 christos if (sock != RPC_ANYSOCK) 504 1.1 christos (void) amu_close(sock); 505 1.1 christos if (client) 506 1.1 christos clnt_destroy(client); 507 1.1 christos if (exlist) 508 1.1 christos xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); 509 1.1 christos if (ok) 510 1.1 christos return 0; 511 1.1 christos return error; 512 1.1 christos } 513 1.1 christos 514 1.1 christos 515 1.1 christos /* 516 1.1 christos * Return true if pref is a directory prefix of dir. 517 1.1 christos * 518 1.1 christos * XXX TODO: 519 1.1 christos * Does not work if pref is "/". 520 1.1 christos */ 521 1.1 christos static int 522 1.1 christos directory_prefix(char *pref, char *dir) 523 1.1 christos { 524 1.1 christos int len = strlen(pref); 525 1.1 christos 526 1.1 christos if (!NSTREQ(pref, dir, len)) 527 1.1 christos return FALSE; 528 1.1 christos if (dir[len] == '/' || dir[len] == '\0') 529 1.1 christos return TRUE; 530 1.1 christos return FALSE; 531 1.1 christos } 532 1.1 christos 533 1.1 christos 534 1.1 christos /* 535 1.1 christos * Unmount a mount tree 536 1.1 christos */ 537 1.1 christos static int 538 1.1 christos amfs_host_umount(am_node *am, mntfs *mf) 539 1.1 christos { 540 1.1 christos mntlist *ml, *mprev; 541 1.1 christos int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 542 1.1 christos int xerror = 0; 543 1.1 christos 544 1.1 christos /* 545 1.1 christos * Read the mount list 546 1.1 christos */ 547 1.1 christos mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); 548 1.1 christos 549 1.1 christos #ifdef MOUNT_TABLE_ON_FILE 550 1.1 christos /* 551 1.1 christos * Unlock the mount list 552 1.1 christos */ 553 1.1 christos unlock_mntlist(); 554 1.1 christos #endif /* MOUNT_TABLE_ON_FILE */ 555 1.1 christos 556 1.1 christos /* 557 1.1 christos * Reverse list... 558 1.1 christos */ 559 1.1 christos ml = mlist; 560 1.1 christos mprev = NULL; 561 1.1 christos while (ml) { 562 1.1 christos mntlist *ml2 = ml->mnext; 563 1.1 christos ml->mnext = mprev; 564 1.1 christos mprev = ml; 565 1.1 christos ml = ml2; 566 1.1 christos } 567 1.1 christos mlist = mprev; 568 1.1 christos 569 1.1 christos /* 570 1.1 christos * Unmount all filesystems... 571 1.1 christos */ 572 1.1 christos for (ml = mlist; ml && !xerror; ml = ml->mnext) { 573 1.1 christos char *dir = ml->mnt->mnt_dir; 574 1.1 christos if (directory_prefix(mf->mf_mount, dir)) { 575 1.1 christos int error; 576 1.1 christos dlog("amfs_host: unmounts %s", dir); 577 1.1 christos /* 578 1.1 christos * Unmount "dir" 579 1.1 christos */ 580 1.1 christos error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); 581 1.1 christos /* 582 1.1 christos * Keep track of errors 583 1.1 christos */ 584 1.1 christos if (error) { 585 1.1 christos /* 586 1.1 christos * If we have not already set xerror and error is not ENOENT, 587 1.1 christos * then set xerror equal to error and log it. 588 1.1 christos * 'xerror' is the return value for this function. 589 1.1 christos * 590 1.1 christos * We do not want to pass ENOENT as an error because if the 591 1.1 christos * directory does not exists our work is done anyway. 592 1.1 christos */ 593 1.1 christos if (!xerror && error != ENOENT) 594 1.1 christos xerror = error; 595 1.1 christos if (error != EBUSY) { 596 1.1 christos errno = error; 597 1.1 christos plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 598 1.1 christos } 599 1.1 christos } else { 600 1.1 christos (void) rmdirs(dir); 601 1.1 christos } 602 1.1 christos } 603 1.1 christos } 604 1.1 christos 605 1.1 christos /* 606 1.1 christos * Throw away mount list 607 1.1 christos */ 608 1.1 christos discard_mntlist(mlist); 609 1.1 christos 610 1.1 christos /* 611 1.1 christos * Try to remount, except when we are shutting down. 612 1.1 christos */ 613 1.1 christos if (xerror && amd_state != Finishing) { 614 1.1 christos xerror = amfs_host_mount(am, mf); 615 1.1 christos if (!xerror) { 616 1.1 christos /* 617 1.1 christos * Don't log this - it's usually too verbose 618 1.1 christos plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 619 1.1 christos */ 620 1.1 christos xerror = EBUSY; 621 1.1 christos } 622 1.1 christos } 623 1.1 christos return xerror; 624 1.1 christos } 625 1.1 christos 626 1.1 christos 627 1.1 christos /* 628 1.1 christos * Tell mountd we're done. 629 1.1 christos * This is not quite right, because we may still 630 1.1 christos * have other filesystems mounted, but the existing 631 1.1 christos * mountd protocol is badly broken anyway. 632 1.1 christos */ 633 1.1 christos static void 634 1.1 christos amfs_host_umounted(mntfs *mf) 635 1.1 christos { 636 1.1 christos char *host; 637 1.1 christos CLIENT *client; 638 1.1 christos enum clnt_stat clnt_stat; 639 1.1 christos struct sockaddr_in sin; 640 1.1 christos int sock = RPC_ANYSOCK; 641 1.1 christos struct timeval tv; 642 1.1 christos u_long mnt_version; 643 1.1 christos 644 1.1 christos if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) 645 1.1 christos return; 646 1.1 christos 647 1.1 christos /* 648 1.1 christos * WebNFS servers shouldn't ever get here. 649 1.1 christos */ 650 1.1 christos if (mf->mf_flags & MFF_WEBNFS) { 651 1.1 christos plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS"); 652 1.1 christos return; 653 1.1 christos } 654 1.1 christos 655 1.1 christos /* 656 1.1 christos * Take a copy of the server hostname, address, and NFS version 657 1.1 christos * to mount version conversion. 658 1.1 christos */ 659 1.1 christos host = mf->mf_server->fs_host; 660 1.1 christos sin = *mf->mf_server->fs_ip; 661 1.1 christos plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); 662 1.1 christos #ifdef HAVE_FS_NFS3 663 1.1 christos if (mf->mf_server->fs_version == NFS_VERSION3) 664 1.1 christos mnt_version = AM_MOUNTVERS3; 665 1.1 christos else 666 1.1 christos #endif /* HAVE_FS_NFS3 */ 667 1.1 christos mnt_version = MOUNTVERS; 668 1.1 christos 669 1.1 christos /* 670 1.1 christos * Create a client attached to mountd 671 1.1 christos */ 672 1.1 christos tv.tv_sec = 10; 673 1.1 christos tv.tv_usec = 0; 674 1.1 christos client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 675 1.1 christos if (client == NULL) { 676 1.1 christos #ifdef HAVE_CLNT_SPCREATEERROR 677 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 678 1.1 christos host, clnt_spcreateerror("")); 679 1.1 christos #else /* not HAVE_CLNT_SPCREATEERROR */ 680 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s", host); 681 1.1 christos #endif /* not HAVE_CLNT_SPCREATEERROR */ 682 1.1 christos goto out; 683 1.1 christos } 684 1.1 christos 685 1.1 christos if (!nfs_auth) { 686 1.1 christos if (make_nfs_auth()) 687 1.1 christos goto out; 688 1.1 christos } 689 1.1 christos client->cl_auth = nfs_auth; 690 1.1 christos 691 1.1 christos dlog("Unmounting all from %s", host); 692 1.1 christos 693 1.1 christos clnt_stat = clnt_call(client, 694 1.1 christos MOUNTPROC_UMNTALL, 695 1.1 christos (XDRPROC_T_TYPE) xdr_void, 696 1.1 christos 0, 697 1.1 christos (XDRPROC_T_TYPE) xdr_void, 698 1.1 christos 0, 699 1.1 christos tv); 700 1.1 christos if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) { 701 1.1 christos /* RPC_SYSTEMERROR seems to be returned for no good reason ... */ 702 1.1 christos const char *msg = clnt_sperrno(clnt_stat); 703 1.1 christos plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg); 704 1.1 christos goto out; 705 1.1 christos } 706 1.1 christos 707 1.1 christos out: 708 1.1 christos if (sock != RPC_ANYSOCK) 709 1.1 christos (void) amu_close(sock); 710 1.1 christos if (client) 711 1.1 christos clnt_destroy(client); 712 1.1 christos } 713