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