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