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