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