amfs_nfsx.c revision 119679
1/* 2 * Copyright (c) 1997-2003 Erez Zadok 3 * Copyright (c) 1990 Jan-Simon Pendry 4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * %W% (Berkeley) %G% 40 * 41 * $Id: amfs_nfsx.c,v 1.3.2.4 2002/12/27 22:44:32 ezk Exp $ 42 * 43 */ 44 45/* 46 * NFS hierarchical mounts 47 * 48 * TODO: Re-implement. 49 */ 50 51#ifdef HAVE_CONFIG_H 52# include <config.h> 53#endif /* HAVE_CONFIG_H */ 54#include <am_defs.h> 55#include <amd.h> 56 57/* 58 * The rfs field contains a list of mounts to be done from 59 * the remote host. 60 */ 61typedef struct amfs_nfsx_mnt { 62 mntfs *n_mnt; 63 int n_error; 64} amfs_nfsx_mnt; 65 66struct amfs_nfsx { 67 int nx_c; /* Number of elements in nx_v */ 68 amfs_nfsx_mnt *nx_v; /* Underlying mounts */ 69 amfs_nfsx_mnt *nx_try; 70}; 71 72/* forward definitions */ 73static char *amfs_nfsx_match(am_opts *fo); 74static int amfs_nfsx_fmount (mntfs *); 75static int amfs_nfsx_fmount(mntfs *mf); 76static int amfs_nfsx_fumount(mntfs *mf); 77static int amfs_nfsx_init(mntfs *mf); 78 79/* 80 * Ops structure 81 */ 82am_ops amfs_nfsx_ops = 83{ 84 "nfsx", 85 amfs_nfsx_match, 86 amfs_nfsx_init, 87 amfs_auto_fmount, 88 amfs_nfsx_fmount, 89 amfs_auto_fumount, 90 amfs_nfsx_fumount, 91 amfs_error_lookuppn, 92 amfs_error_readdir, 93 0, /* amfs_nfsx_readlink */ 94 0, /* amfs_nfsx_mounted */ 95 0, /* amfs_nfsx_umounted */ 96 find_nfs_srvr, /* XXX */ 97 /* FS_UBACKGROUND| */ FS_AMQINFO 98}; 99 100 101static char * 102amfs_nfsx_match(am_opts *fo) 103{ 104 char *xmtab; 105 char *ptr; 106 int len; 107 108 if (!fo->opt_rfs) { 109 plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified"); 110 return FALSE; 111 } 112 113 if (!fo->opt_rhost) { 114 plog(XLOG_USER, "amfs_nfsx: no remote host specified"); 115 return FALSE; 116 } 117 118 /* set default sublink */ 119 if (fo->opt_sublink == 0) { 120 ptr = strchr(fo->opt_rfs, ','); 121 if (ptr && ptr != (fo->opt_rfs + 1)) 122 fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1); 123 } 124 125 /* 126 * Remove trailing ",..." from ${fs} 127 * After deslashifying, overwrite the end of ${fs} with "/" 128 * to make sure it is unique. 129 */ 130 if ((ptr = strchr(fo->opt_fs, ','))) 131 *ptr = '\0'; 132 deslashify(fo->opt_fs); 133 134 /* 135 * Bump string length to allow trailing / 136 */ 137 len = strlen(fo->opt_fs); 138 fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1); 139 ptr = fo->opt_fs + len; 140 141 /* 142 * Make unique... 143 */ 144 *ptr++ = '/'; 145 *ptr = '\0'; 146 147 /* 148 * Determine magic cookie to put in mtab 149 */ 150 xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs); 151#ifdef DEBUG 152 dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 153 fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 154#endif /* DEBUG */ 155 156 return xmtab; 157} 158 159 160static void 161amfs_nfsx_prfree(voidp vp) 162{ 163 struct amfs_nfsx *nx = (struct amfs_nfsx *) vp; 164 int i; 165 166 for (i = 0; i < nx->nx_c; i++) { 167 mntfs *m = nx->nx_v[i].n_mnt; 168 if (m) 169 free_mntfs(m); 170 } 171 172 XFREE(nx->nx_v); 173 XFREE(nx); 174} 175 176 177static int 178amfs_nfsx_init(mntfs *mf) 179{ 180 /* 181 * mf_info has the form: 182 * host:/prefix/path,sub,sub,sub 183 */ 184 int i; 185 int glob_error; 186 struct amfs_nfsx *nx; 187 int asked_for_wakeup = 0; 188 189 nx = (struct amfs_nfsx *) mf->mf_private; 190 191 if (nx == 0) { 192 char **ivec; 193 char *info = 0; 194 char *host; 195 char *pref; 196 int error = 0; 197 198 info = strdup(mf->mf_info); 199 host = strchr(info, ':'); 200 if (!host) { 201 error = EINVAL; 202 goto errexit; 203 } 204 pref = host +1; 205 host = info; 206 207 /* 208 * Split the prefix off from the suffices 209 */ 210 ivec = strsplit(pref, ',', '\''); 211 212 /* 213 * Count array size 214 */ 215 for (i = 0; ivec[i]; i++) ; 216 217 nx = ALLOC(struct amfs_nfsx); 218 mf->mf_private = (voidp) nx; 219 mf->mf_prfree = amfs_nfsx_prfree; 220 221 nx->nx_c = i - 1; /* i-1 because we don't want the prefix */ 222 nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt)); 223 { 224 char *mp = 0; 225 char *xinfo = 0; 226 char *fs = mf->mf_fo->opt_fs; 227 char *rfs = 0; 228 for (i = 0; i < nx->nx_c; i++) { 229 char *path = ivec[i + 1]; 230 rfs = str3cat(rfs, pref, "/", path); 231 /* 232 * Determine the mount point. 233 * If this is the root, then don't remove 234 * the trailing slash to avoid mntfs name clashes. 235 */ 236 mp = str3cat(mp, fs, "/", rfs); 237 normalize_slash(mp); 238 deslashify(mp); 239 /* 240 * Determine the mount info 241 */ 242 xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path); 243 normalize_slash(xinfo); 244 if (pref[1] != '\0') 245 deslashify(xinfo); 246#ifdef DEBUG 247 dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp); 248#endif /* DEBUG */ 249 nx->nx_v[i].n_error = -1; 250 nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts); 251 } 252 if (rfs) 253 XFREE(rfs); 254 if (mp) 255 XFREE(mp); 256 if (xinfo) 257 XFREE(xinfo); 258 } 259 260 XFREE(ivec); 261 errexit: 262 if (info) 263 XFREE(info); 264 if (error) 265 return error; 266 } 267 268 /* 269 * Iterate through the mntfs's and call 270 * the underlying init routine on each 271 */ 272 glob_error = 0; 273 274 for (i = 0; i < nx->nx_c; i++) { 275 amfs_nfsx_mnt *n = &nx->nx_v[i]; 276 mntfs *m = n->n_mnt; 277 int error = (*m->mf_ops->fs_init) (m); 278 /* 279 * if you just "return error" here, you will have made a failure 280 * in any submounts to fail the whole group. There was old unused code 281 * here before. 282 */ 283 if (error > 0) 284 n->n_error = error; 285 286 else if (error < 0) { 287 glob_error = -1; 288 if (!asked_for_wakeup) { 289 asked_for_wakeup = 1; 290 sched_task(wakeup_task, (voidp) mf, (voidp) m); 291 } 292 } 293 } 294 295 return glob_error; 296} 297 298 299static void 300amfs_nfsx_cont(int rc, int term, voidp closure) 301{ 302 mntfs *mf = (mntfs *) closure; 303 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 304 amfs_nfsx_mnt *n = nx->nx_try; 305 306 n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING); 307 mf->mf_flags &= ~MFF_ERROR; 308 309 /* 310 * Wakeup anything waiting for this mount 311 */ 312 wakeup((voidp) n->n_mnt); 313 314 if (rc || term) { 315 if (term) { 316 /* 317 * Not sure what to do for an error code. 318 */ 319 plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term); 320 n->n_error = EIO; 321 } else { 322 /* 323 * Check for exit status 324 */ 325 errno = rc; /* XXX */ 326 plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount); 327 n->n_error = rc; 328 } 329 free_mntfs(n->n_mnt); 330 n->n_mnt = new_mntfs(); 331 n->n_mnt->mf_error = n->n_error; 332 n->n_mnt->mf_flags |= MFF_ERROR; 333 } else { 334 /* 335 * The mount worked. 336 */ 337 mf_mounted(n->n_mnt); 338 n->n_error = 0; 339 } 340 341 /* 342 * Do the remaining bits 343 */ 344 if (amfs_nfsx_fmount(mf) >= 0) { 345 wakeup((voidp) mf); 346 mf->mf_flags &= ~MFF_MOUNTING; 347 mf_mounted(mf); 348 } 349} 350 351 352static int 353try_amfs_nfsx_mount(voidp mv) 354{ 355 mntfs *mf = (mntfs *) mv; 356 int error; 357 358 mf->mf_flags |= MFF_MOUNTING; 359 error = (*mf->mf_ops->fmount_fs) (mf); 360 mf->mf_flags &= ~MFF_MOUNTING; 361 362 return error; 363} 364 365 366static int 367amfs_nfsx_remount(mntfs *mf, int fg) 368{ 369 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 370 amfs_nfsx_mnt *n; 371 int glob_error = -1; 372 373 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { 374 mntfs *m = n->n_mnt; 375 if (n->n_error < 0) { 376 if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) { 377 int error = mkdirs(m->mf_mount, 0555); 378 if (!error) 379 m->mf_flags |= MFF_MKMNT; 380 } 381 } 382 } 383 384 /* 385 * Iterate through the mntfs's and mount each filesystem 386 * which is not yet mounted. 387 */ 388 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { 389 mntfs *m = n->n_mnt; 390 if (n->n_error < 0) { 391 /* 392 * Check fmount entry pt. exists 393 * and then mount... 394 */ 395 if (!m->mf_ops->fmount_fs) { 396 n->n_error = EINVAL; 397 } else { 398#ifdef DEBUG 399 dlog("calling underlying fmount on %s", m->mf_mount); 400#endif /* DEBUG */ 401 if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) { 402 m->mf_flags |= MFF_MOUNTING; /* XXX */ 403#ifdef DEBUG 404 dlog("backgrounding mount of \"%s\"", m->mf_info); 405#endif /* DEBUG */ 406 nx->nx_try = n; 407 run_task(try_amfs_nfsx_mount, (voidp) m, amfs_nfsx_cont, (voidp) mf); 408 n->n_error = -1; 409 return -1; 410 } else { 411#ifdef DEBUG 412 dlog("foreground mount of \"%s\" ...", mf->mf_info); 413#endif /* DEBUG */ 414 n->n_error = (*m->mf_ops->fmount_fs) (m); 415 } 416 } 417 418#ifdef DEBUG 419 if (n->n_error > 0) { 420 errno = n->n_error; /* XXX */ 421 dlog("underlying fmount of %s failed: %m", m->mf_mount); 422 } 423#endif /* DEBUG */ 424 425 if (n->n_error == 0) { 426 glob_error = 0; 427 } else if (glob_error < 0) { 428 glob_error = n->n_error; 429 } 430 } 431 } 432 433 return glob_error < 0 ? 0 : glob_error; 434} 435 436 437static int 438amfs_nfsx_fmount(mntfs *mf) 439{ 440 return amfs_nfsx_remount(mf, FALSE); 441} 442 443 444/* 445 * Unmount an NFS hierarchy. 446 * Note that this is called in the foreground 447 * and so may hang under extremely rare conditions. 448 */ 449static int 450amfs_nfsx_fumount(mntfs *mf) 451{ 452 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 453 amfs_nfsx_mnt *n; 454 int glob_error = 0; 455 456 /* 457 * Iterate in reverse through the mntfs's and unmount each filesystem 458 * which is mounted. 459 */ 460 for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) { 461 mntfs *m = n->n_mnt; 462 /* 463 * If this node has not been messed with 464 * and there has been no error so far 465 * then try and unmount. 466 * If an error had occurred then zero 467 * the error code so that the remount 468 * only tries to unmount those nodes 469 * which had been successfully unmounted. 470 */ 471 if (n->n_error == 0) { 472#ifdef DEBUG 473 dlog("calling underlying fumount on %s", m->mf_mount); 474#endif /* DEBUG */ 475 n->n_error = (*m->mf_ops->fumount_fs) (m); 476 if (n->n_error) { 477 glob_error = n->n_error; 478 n->n_error = 0; 479 } else { 480 /* 481 * Make sure remount gets this node 482 */ 483 n->n_error = -1; 484 } 485 } 486 } 487 488 /* 489 * If any unmounts failed then remount the 490 * whole lot... 491 */ 492 if (glob_error) { 493 glob_error = amfs_nfsx_remount(mf, TRUE); 494 if (glob_error) { 495 errno = glob_error; /* XXX */ 496 plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount); 497 } 498 glob_error = EBUSY; 499 } else { 500 /* 501 * Remove all the mount points 502 */ 503 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { 504 mntfs *m = n->n_mnt; 505 am_node am; 506 507 /* 508 * XXX: all the umounted handler needs is a 509 * mntfs pointer, so pass an am_node with the right 510 * pointer in it. 511 */ 512 memset((voidp) &am, 0, sizeof(am)); 513 am.am_mnt = m; 514#ifdef DEBUG 515 dlog("calling underlying umounted on %s", m->mf_mount); 516#endif /* DEBUG */ 517 (*m->mf_ops->umounted) (&am); 518 519 if (n->n_error < 0) { 520 if (m->mf_ops->fs_flags & FS_MKMNT) { 521 (void) rmdirs(m->mf_mount); 522 m->mf_flags &= ~MFF_MKMNT; 523 } 524 } 525 free_mntfs(m); 526 n->n_mnt = 0; 527 n->n_error = -1; 528 } 529 } 530 531 return glob_error; 532} 533