1/* 2 * Copyright (c) 1997-2006 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 * 40 * File: am-utils/amd/amfs_nfsx.c 41 * 42 */ 43 44/* 45 * NFS hierarchical mounts 46 * 47 * TODO: Re-implement. 48 */ 49 50#ifdef HAVE_CONFIG_H 51# include <config.h> 52#endif /* HAVE_CONFIG_H */ 53#include <am_defs.h> 54#include <amd.h> 55 56/* 57 * The rfs field contains a list of mounts to be done from 58 * the remote host. 59 */ 60typedef struct amfs_nfsx_mnt { 61 mntfs *n_mnt; 62 int n_error; 63} amfs_nfsx_mnt; 64 65struct amfs_nfsx { 66 int nx_c; /* Number of elements in nx_v */ 67 amfs_nfsx_mnt *nx_v; /* Underlying mounts */ 68 amfs_nfsx_mnt *nx_try; 69 am_node *nx_mp; 70}; 71 72/* forward definitions */ 73static char *amfs_nfsx_match(am_opts *fo); 74static int amfs_nfsx_mount(am_node *am, mntfs *mf); 75static int amfs_nfsx_umount(am_node *am, mntfs *mf); 76static int amfs_nfsx_init(mntfs *mf); 77 78/* 79 * Ops structure 80 */ 81am_ops amfs_nfsx_ops = 82{ 83 "nfsx", 84 amfs_nfsx_match, 85 amfs_nfsx_init, 86 amfs_nfsx_mount, 87 amfs_nfsx_umount, 88 amfs_error_lookup_child, 89 amfs_error_mount_child, 90 amfs_error_readdir, 91 0, /* amfs_nfsx_readlink */ 92 0, /* amfs_nfsx_mounted */ 93 0, /* amfs_nfsx_umounted */ 94 find_nfs_srvr, /* XXX */ 95 0, /* amfs_nfsx_get_wchan */ 96 /* FS_UBACKGROUND| */ FS_AMQINFO, /* nfs_fs_flags */ 97#ifdef HAVE_FS_AUTOFS 98 AUTOFS_NFSX_FS_FLAGS, 99#endif /* HAVE_FS_AUTOFS */ 100}; 101 102 103static char * 104amfs_nfsx_match(am_opts *fo) 105{ 106 char *xmtab; 107 char *ptr; 108 int len; 109 110 if (!fo->opt_rfs) { 111 plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified"); 112 return FALSE; 113 } 114 115 if (!fo->opt_rhost) { 116 plog(XLOG_USER, "amfs_nfsx: no remote host specified"); 117 return FALSE; 118 } 119 120 /* set default sublink */ 121 if (fo->opt_sublink == 0) { 122 ptr = strchr(fo->opt_rfs, ','); 123 if (ptr && ptr > (fo->opt_rfs + 1)) 124 fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1); 125 } 126 127 /* 128 * Remove trailing ",..." from ${fs} 129 * After deslashifying, overwrite the end of ${fs} with "/" 130 * to make sure it is unique. 131 */ 132 if ((ptr = strchr(fo->opt_fs, ','))) 133 *ptr = '\0'; 134 deslashify(fo->opt_fs); 135 136 /* 137 * Bump string length to allow trailing / 138 */ 139 len = strlen(fo->opt_fs); 140 fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1); 141 ptr = fo->opt_fs + len; 142 143 /* 144 * Make unique... 145 */ 146 *ptr++ = '/'; 147 *ptr = '\0'; 148 149 /* 150 * Determine magic cookie to put in mtab 151 */ 152 xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs); 153 dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 154 fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 155 156 return xmtab; 157} 158 159 160static void 161amfs_nfsx_prfree(opaque_t 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 /* nothing */; 217 218 nx = ALLOC(struct amfs_nfsx); 219 mf->mf_private = (opaque_t) nx; 220 mf->mf_prfree = amfs_nfsx_prfree; 221 222 nx->nx_c = i - 1; /* i-1 because we don't want the prefix */ 223 nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt)); 224 nx->nx_mp = 0; 225 { 226 char *mp = 0; 227 char *xinfo = 0; 228 char *fs = mf->mf_fo->opt_fs; 229 char *rfs = 0; 230 for (i = 0; i < nx->nx_c; i++) { 231 char *path = ivec[i + 1]; 232 rfs = str3cat(rfs, pref, "/", path); 233 /* 234 * Determine the mount point. 235 * If this is the root, then don't remove 236 * the trailing slash to avoid mntfs name clashes. 237 */ 238 mp = str3cat(mp, fs, "/", rfs); 239 normalize_slash(mp); 240 deslashify(mp); 241 /* 242 * Determine the mount info 243 */ 244 xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path); 245 normalize_slash(xinfo); 246 if (pref[1] != '\0') 247 deslashify(xinfo); 248 dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp); 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 /* propagate the on_autofs flag */ 252 nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS; 253 } 254 if (rfs) 255 XFREE(rfs); 256 if (mp) 257 XFREE(mp); 258 if (xinfo) 259 XFREE(xinfo); 260 } 261 262 XFREE(ivec); 263 errexit: 264 if (info) 265 XFREE(info); 266 if (error) 267 return error; 268 } 269 270 /* 271 * Iterate through the mntfs's and call 272 * the underlying init routine on each 273 */ 274 glob_error = 0; 275 276 for (i = 0; i < nx->nx_c; i++) { 277 amfs_nfsx_mnt *n = &nx->nx_v[i]; 278 mntfs *m = n->n_mnt; 279 int error = 0; 280 if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART)) 281 error = m->mf_ops->fs_init(m); 282 /* 283 * if you just "return error" here, you will have made a failure 284 * in any submounts to fail the whole group. There was old unused code 285 * here before. 286 */ 287 if (error > 0) 288 n->n_error = error; 289 290 else if (error < 0) { 291 glob_error = -1; 292 if (!asked_for_wakeup) { 293 asked_for_wakeup = 1; 294 sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m)); 295 } 296 } 297 } 298 299 return glob_error; 300} 301 302 303static void 304amfs_nfsx_cont(int rc, int term, opaque_t arg) 305{ 306 mntfs *mf = (mntfs *) arg; 307 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 308 am_node *mp = nx->nx_mp; 309 amfs_nfsx_mnt *n = nx->nx_try; 310 311 n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING); 312 mf->mf_flags &= ~MFF_ERROR; 313 314 /* 315 * Wakeup anything waiting for this mount 316 */ 317 wakeup(get_mntfs_wchan(n->n_mnt)); 318 319 if (rc || term) { 320 if (term) { 321 /* 322 * Not sure what to do for an error code. 323 */ 324 plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term); 325 n->n_error = EIO; 326 } else { 327 /* 328 * Check for exit status 329 */ 330 errno = rc; /* XXX */ 331 plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount); 332 n->n_error = rc; 333 } 334 free_mntfs(n->n_mnt); 335 n->n_mnt = new_mntfs(); 336 n->n_mnt->mf_error = n->n_error; 337 n->n_mnt->mf_flags |= MFF_ERROR; 338 } else { 339 /* 340 * The mount worked. 341 */ 342 mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */ 343 n->n_error = 0; 344 } 345 346 /* 347 * Do the remaining bits 348 */ 349 if (amfs_nfsx_mount(mp, mf) >= 0) 350 wakeup(get_mntfs_wchan(mf)); 351} 352 353 354static int 355try_amfs_nfsx_mount(opaque_t mv) 356{ 357 mntfs *mf = (mntfs *) mv; 358 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 359 am_node *mp = nx->nx_mp; 360 int error; 361 362 error = mf->mf_ops->mount_fs(mp, mf); 363 364 return error; 365} 366 367 368static int 369amfs_nfsx_remount(am_node *am, mntfs *mf, int fg) 370{ 371 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 372 amfs_nfsx_mnt *n; 373 int glob_error = -1; 374 375 /* Save the am_node pointer for later use */ 376 nx->nx_mp = am; 377 378 /* 379 * Iterate through the mntfs's and mount each filesystem 380 * which is not yet mounted. 381 */ 382 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { 383 mntfs *m = n->n_mnt; 384 385 if (m->mf_flags & MFF_MOUNTING) 386 break; 387 388 if (m->mf_flags & MFF_MOUNTED) { 389 mf_mounted(m, FALSE); /* FALSE => don't free the m->am_opts */ 390 n->n_error = glob_error = 0; 391 continue; 392 } 393 394 if (n->n_error < 0) { 395 /* Create the mountpoint, if and as required */ 396 if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) { 397 if (!mkdirs(m->mf_mount, 0555)) 398 m->mf_flags |= MFF_MKMNT; 399 } 400 401 dlog("calling underlying mount on %s", m->mf_mount); 402 if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) { 403 m->mf_flags |= MFF_MOUNTING; 404 dlog("backgrounding mount of \"%s\"", m->mf_info); 405 nx->nx_try = n; 406 run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf); 407 n->n_error = -1; 408 return -1; 409 } else { 410 dlog("foreground mount of \"%s\" ...", mf->mf_info); 411 n->n_error = m->mf_ops->mount_fs(am, m); 412 } 413 414 if (n->n_error > 0) 415 dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error)); 416 417 if (n->n_error == 0) { 418 glob_error = 0; 419 } else if (glob_error < 0) { 420 glob_error = n->n_error; 421 } 422 } 423 } 424 425 return glob_error < 0 ? 0 : glob_error; 426} 427 428 429static int 430amfs_nfsx_mount(am_node *am, mntfs *mf) 431{ 432 return amfs_nfsx_remount(am, mf, FALSE); 433} 434 435 436/* 437 * Unmount an NFS hierarchy. 438 * Note that this is called in the foreground 439 * and so may hang under extremely rare conditions. 440 */ 441static int 442amfs_nfsx_umount(am_node *am, mntfs *mf) 443{ 444 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; 445 amfs_nfsx_mnt *n; 446 int glob_error = 0; 447 448 /* 449 * Iterate in reverse through the mntfs's and unmount each filesystem 450 * which is mounted. 451 */ 452 for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) { 453 mntfs *m = n->n_mnt; 454 /* 455 * If this node has not been messed with 456 * and there has been no error so far 457 * then try and unmount. 458 * If an error had occurred then zero 459 * the error code so that the remount 460 * only tries to unmount those nodes 461 * which had been successfully unmounted. 462 */ 463 if (n->n_error == 0) { 464 dlog("calling underlying fumount on %s", m->mf_mount); 465 n->n_error = m->mf_ops->umount_fs(am, m); 466 if (n->n_error) { 467 glob_error = n->n_error; 468 n->n_error = 0; 469 } else { 470 /* 471 * Make sure remount gets this node 472 */ 473 n->n_error = -1; 474 } 475 } 476 } 477 478 /* 479 * If any unmounts failed then remount the 480 * whole lot... 481 */ 482 if (glob_error) { 483 glob_error = amfs_nfsx_remount(am, mf, TRUE); 484 if (glob_error) { 485 errno = glob_error; /* XXX */ 486 plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount); 487 } 488 glob_error = EBUSY; 489 } else { 490 /* 491 * Remove all the mount points 492 */ 493 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { 494 mntfs *m = n->n_mnt; 495 dlog("calling underlying umounted on %s", m->mf_mount); 496 if (m->mf_ops->umounted) 497 m->mf_ops->umounted(m); 498 499 if (n->n_error < 0) { 500 if (m->mf_fsflags & FS_MKMNT) { 501 (void) rmdirs(m->mf_mount); 502 m->mf_flags &= ~MFF_MKMNT; 503 } 504 } 505 free_mntfs(m); 506 n->n_mnt = 0; 507 n->n_error = -1; 508 } 509 } 510 511 return glob_error; 512} 513