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