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