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 * @(#)host_ops.c 5.3 (Berkeley) 05/12/91 13 * 14 * $Id: host_ops.c,v 5.2.1.6 91/05/07 22:17:53 jsp Alpha $ 15 * 16 */ 17 18 #include "am.h" 19 20 #ifdef HAS_HOST 21 22 #include "mount.h" 23 #include <sys/stat.h> 24 25 /* 26 * NFS host file system. 27 * Mounts all exported filesystems from a given host. 28 * This has now degenerated into a mess but will not 29 * be rewritten. Amd 6 will support the abstractions 30 * needed to make this work correctly. 31 */ 32 33 /* 34 * Define HOST_RPC_UDP to use dgram instead of stream RPC. 35 * Datagrams are generally much faster. 36 */ 37 #define HOST_RPC_UDP 38 39 /* 40 * Define HOST_MKDIRS to make Amd automatically try 41 * to create the mount points. 42 */ 43 #define HOST_MKDIRS 44 45 /* 46 * Determine the mount point 47 */ 48 #define MAKE_MNTPT(mntpt, ex, mf) { \ 49 if (strcmp((ex)->ex_dir, "/") == 0) \ 50 strcpy((mntpt), (mf)->mf_mount); \ 51 else \ 52 sprintf((mntpt), "%s%s", (mf)->mf_mount, (ex)->ex_dir); \ 53 } 54 55 /* 56 * Execute needs the same as NFS plus a helper command 57 */ 58 static char *host_match P((am_opts *fo)); 59 static char *host_match(fo) 60 am_opts *fo; 61 { 62 #ifdef HOST_EXEC 63 if (!host_helper) { 64 plog(XLOG_USER, "No host helper command given"); 65 return FALSE; 66 } 67 #endif /* HOST_EXEC */ 68 69 /* 70 * Make sure rfs is specified to keep nfs_match happy... 71 */ 72 if (!fo->opt_rfs) 73 fo->opt_rfs = "/"; 74 75 76 return (*nfs_ops.fs_match)(fo); 77 } 78 79 static int host_init(mf) 80 mntfs *mf; 81 { 82 if (strchr(mf->mf_info, ':') == 0) 83 return ENOENT; 84 return 0; 85 } 86 87 /* 88 * Two implementations: 89 * HOST_EXEC gets you the external version. The program specified with 90 * the -h option is called. The external program is not published... 91 * roll your own. 92 * 93 * Otherwise you get the native version. Faster but makes the program 94 * bigger. 95 */ 96 97 #ifndef HOST_EXEC 98 99 static bool_t 100 xdr_pri_free(xdr_args, args_ptr) 101 xdrproc_t xdr_args; 102 caddr_t args_ptr; 103 { 104 XDR xdr; 105 xdr.x_op = XDR_FREE; 106 return ((*xdr_args)(&xdr, args_ptr)); 107 } 108 109 static int do_mount P((fhstatus *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)); 110 static int do_mount(fhp, dir, fs_name, opts, mf) 111 fhstatus *fhp; 112 char *dir; 113 char *fs_name; 114 char *opts; 115 mntfs *mf; 116 { 117 struct stat stb; 118 #ifdef DEBUG 119 dlog("host: mounting fs %s on %s\n", fs_name, dir); 120 #endif /* DEBUG */ 121 #ifdef HOST_MKDIRS 122 (void) mkdirs(dir, 0555); 123 #endif /* HOST_MKDIRS */ 124 if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { 125 plog(XLOG_ERROR, "No mount point for %s - skipping", dir); 126 return ENOENT; 127 } 128 129 return mount_nfs_fh(fhp, dir, fs_name, opts, mf); 130 } 131 132 static int sortfun P((exports *a, exports *b)); 133 static int sortfun(a, b) 134 exports *a,*b; 135 { 136 return strcmp((*a)->ex_dir, (*b)->ex_dir); 137 } 138 139 /* 140 * Get filehandle 141 */ 142 static int fetch_fhandle P((CLIENT *client, char *dir, fhstatus *fhp)); 143 static int fetch_fhandle(client, dir, fhp) 144 CLIENT *client; 145 char *dir; 146 fhstatus *fhp; 147 { 148 struct timeval tv; 149 enum clnt_stat clnt_stat; 150 151 /* 152 * Pick a number, any number... 153 */ 154 tv.tv_sec = 20; 155 tv.tv_usec = 0; 156 157 #ifdef DEBUG 158 dlog("Fetching fhandle for %s", dir); 159 #endif /* DEBUG */ 160 /* 161 * Call the mount daemon on the remote host to 162 * get the filehandle. 163 */ 164 clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_fhstatus, fhp, tv); 165 if (clnt_stat != RPC_SUCCESS) { 166 extern char *clnt_sperrno(); 167 char *msg = clnt_sperrno(clnt_stat); 168 plog(XLOG_ERROR, "mountd rpc failed: %s", msg); 169 return EIO; 170 } 171 /* 172 * Check status of filehandle 173 */ 174 if (fhp->fhs_status) { 175 #ifdef DEBUG 176 errno = fhp->fhs_status; 177 dlog("fhandle fetch failed: %m"); 178 #endif /* DEBUG */ 179 return fhp->fhs_status; 180 } 181 return 0; 182 } 183 184 /* 185 * Scan mount table to see if something already mounted 186 */ 187 static int already_mounted P((mntlist *mlist, char*dir)); 188 static int already_mounted(mlist, dir) 189 mntlist *mlist; 190 char *dir; 191 { 192 mntlist *ml; 193 194 for (ml = mlist; ml; ml = ml->mnext) 195 if (strcmp(ml->mnt->mnt_dir, dir) == 0) 196 return 1; 197 return 0; 198 } 199 200 /* 201 * Mount the export tree from a host 202 */ 203 static int host_fmount P((mntfs *mf)); 204 static int host_fmount(mf) 205 mntfs *mf; 206 { 207 struct timeval tv2; 208 CLIENT *client; 209 enum clnt_stat clnt_stat; 210 int n_export; 211 int j, k; 212 exports exlist = 0, ex; 213 exports *ep = 0; 214 fhstatus *fp = 0; 215 char *host = mf->mf_server->fs_host; 216 int error = 0; 217 struct sockaddr_in sin; 218 int sock = RPC_ANYSOCK; 219 int ok = FALSE; 220 mntlist *mlist; 221 char fs_name[MAXPATHLEN], *rfs_dir; 222 char mntpt[MAXPATHLEN]; 223 224 #ifdef HOST_RPC_UDP 225 struct timeval tv; 226 tv.tv_sec = 10; tv.tv_usec = 0; 227 #endif /* HOST_RPC_UDP */ 228 229 /* 230 * Read the mount list 231 */ 232 mlist = read_mtab(mf->mf_mount); 233 234 /* 235 * Unlock the mount list 236 */ 237 unlock_mntlist(); 238 239 /* 240 * Take a copy of the server address 241 */ 242 sin = *mf->mf_server->fs_ip; 243 244 /* 245 * Zero out the port - make sure we recompute 246 */ 247 sin.sin_port = 0; 248 /* 249 * Make a client end-point 250 */ 251 #ifdef HOST_RPC_UDP 252 if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) 253 #else 254 if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL) 255 #endif /* HOST_RPC_UDP */ 256 { 257 plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host); 258 error = EIO; 259 goto out; 260 } 261 262 if (!nfs_auth) { 263 error = make_nfs_auth(); 264 if (error) 265 goto out; 266 } 267 268 client->cl_auth = nfs_auth; 269 270 #ifdef DEBUG 271 dlog("Fetching export list from %s", host); 272 #endif /* DEBUG */ 273 274 /* 275 * Fetch the export list 276 */ 277 tv2.tv_sec = 10; tv2.tv_usec = 0; 278 clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2); 279 if (clnt_stat != RPC_SUCCESS) { 280 /*clnt_perror(client, "rpc");*/ 281 error = EIO; 282 goto out; 283 } 284 285 /* 286 * Figure out how many exports were returned 287 */ 288 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 289 /*printf("export %s\n", ex->ex_dir);*/ 290 n_export++; 291 } 292 #ifdef DEBUG 293 /*dlog("%d exports returned\n", n_export);*/ 294 #endif /* DEBUG */ 295 296 /* 297 * Allocate an array of pointers into the list 298 * so that they can be sorted. If the filesystem 299 * is already mounted then ignore it. 300 */ 301 ep = (exports *) xmalloc(n_export * sizeof(exports)); 302 for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 303 MAKE_MNTPT(mntpt, ex, mf); 304 if (!already_mounted(mlist, mntpt)) 305 ep[j++] = ex; 306 } 307 n_export = j; 308 309 /* 310 * Sort into order. 311 * This way the mounts are done in order down the tree, 312 * instead of any random order returned by the mount 313 * daemon (the protocol doesn't specify...). 314 */ 315 qsort(ep, n_export, sizeof(exports), sortfun); 316 317 /* 318 * Allocate an array of filehandles 319 */ 320 fp = (fhstatus *) xmalloc(n_export * sizeof(fhstatus)); 321 322 /* 323 * Try to obtain filehandles for each directory. 324 * If a fetch fails then just zero out the array 325 * reference but discard the error. 326 */ 327 for (j = k = 0; j < n_export; j++) { 328 /* Check and avoid a duplicated export entry */ 329 if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) { 330 #ifdef DEBUG 331 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 332 #endif 333 ep[j] = 0; 334 } else { 335 k = j; 336 if (error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j])) 337 ep[j] = 0; 338 } 339 } 340 341 /* 342 * Mount each filesystem for which we have a filehandle. 343 * If any of the mounts succeed then mark "ok" and return 344 * error code 0 at the end. If they all fail then return 345 * the last error code. 346 */ 347 strncpy(fs_name, mf->mf_info, sizeof(fs_name)); 348 if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { 349 plog(XLOG_FATAL, "host_fmount: mf_info has no colon"); 350 error = EINVAL; 351 goto out; 352 } 353 ++rfs_dir; 354 for (j = 0; j < n_export; j++) { 355 ex = ep[j]; 356 if (ex) { 357 strcpy(rfs_dir, ex->ex_dir); 358 MAKE_MNTPT(mntpt, ex, mf); 359 if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0) 360 ok = TRUE; 361 } 362 } 363 364 /* 365 * Clean up and exit 366 */ 367 out: 368 discard_mntlist(mlist); 369 if (ep) 370 free(ep); 371 if (fp) 372 free(fp); 373 if (client) 374 clnt_destroy(client); 375 if (exlist) 376 xdr_pri_free(xdr_exports, &exlist); 377 if (ok) 378 return 0; 379 return error; 380 } 381 382 /* 383 * Return true if pref is a directory prefix of dir. 384 * 385 * TODO: 386 * Does not work if pref is "/". 387 */ 388 static int directory_prefix P((char *pref, char *dir)); 389 static int directory_prefix(pref, dir) 390 char *pref; 391 char *dir; 392 { 393 int len = strlen(pref); 394 if (strncmp(pref, dir, len) != 0) 395 return FALSE; 396 if (dir[len] == '/' || dir[len] == '\0') 397 return TRUE; 398 return FALSE; 399 } 400 401 /* 402 * Unmount a mount tree 403 */ 404 static int host_fumount P((mntfs *mf)); 405 static int host_fumount(mf) 406 mntfs *mf; 407 { 408 mntlist *ml, *mprev; 409 int xerror = 0; 410 411 /* 412 * Read the mount list 413 */ 414 mntlist *mlist = read_mtab(mf->mf_mount); 415 416 /* 417 * Unlock the mount list 418 */ 419 unlock_mntlist(); 420 421 /* 422 * Reverse list... 423 */ 424 ml = mlist; 425 mprev = 0; 426 while (ml) { 427 mntlist *ml2 = ml->mnext; 428 ml->mnext = mprev; 429 mprev = ml; 430 ml = ml2; 431 } 432 mlist = mprev; 433 434 /* 435 * Unmount all filesystems... 436 */ 437 for (ml = mlist; ml && !xerror; ml = ml->mnext) { 438 char *dir = ml->mnt->mnt_dir; 439 if (directory_prefix(mf->mf_mount, dir)) { 440 int error; 441 #ifdef DEBUG 442 dlog("host: unmounts %s", dir); 443 #endif /* DEBUG */ 444 /* 445 * Unmount "dir" 446 */ 447 error = UMOUNT_FS(dir); 448 /* 449 * Keep track of errors 450 */ 451 if (error) { 452 if (!xerror) 453 xerror = error; 454 if (error != EBUSY) { 455 errno = error; 456 plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 457 } 458 } else { 459 #ifdef HOST_MKDIRS 460 (void) rmdirs(dir); 461 #endif /* HOST_MKDIRS */ 462 } 463 } 464 } 465 466 /* 467 * Throw away mount list 468 */ 469 discard_mntlist(mlist); 470 471 /* 472 * Try to remount, except when we are shutting down. 473 */ 474 if (xerror && amd_state != Finishing) { 475 xerror = host_fmount(mf); 476 if (!xerror) { 477 /* 478 * Don't log this - it's usually too verbose 479 plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 480 */ 481 xerror = EBUSY; 482 } 483 } 484 return xerror; 485 } 486 487 #else /* HOST_EXEC */ 488 489 static int host_exec P((char*op, char*host, char*fs, char*opts)); 490 static int host_exec(op, host, fs, opts) 491 char *op; 492 char *host; 493 char *fs; 494 char *opts; 495 { 496 int error; 497 char *argv[7]; 498 499 /* 500 * Build arg vector 501 */ 502 argv[0] = host_helper; 503 argv[1] = host_helper; 504 argv[2] = op; 505 argv[3] = host; 506 argv[4] = fs; 507 argv[5] = opts && *opts ? opts : "rw,default"; 508 argv[6] = 0; 509 510 /* 511 * Put stdout to stderr 512 */ 513 (void) fclose(stdout); 514 (void) dup(fileno(logfp)); 515 if (fileno(logfp) != fileno(stderr)) { 516 (void) fclose(stderr); 517 (void) dup(fileno(logfp)); 518 } 519 /* 520 * Try the exec 521 */ 522 #ifdef DEBUG 523 Debug(D_FULL) { 524 char **cp = argv; 525 plog(XLOG_DEBUG, "executing (un)mount command..."); 526 while (*cp) { 527 plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp); 528 cp++; 529 } 530 } 531 #endif /* DEBUG */ 532 if (argv[0] == 0 || argv[1] == 0) { 533 errno = EINVAL; 534 plog(XLOG_USER, "1st/2nd args missing to (un)mount program"); 535 } else { 536 (void) execv(argv[0], argv+1); 537 } 538 /* 539 * Save error number 540 */ 541 error = errno; 542 plog(XLOG_ERROR, "exec %s failed: %m", argv[0]); 543 544 /* 545 * Return error 546 */ 547 return error; 548 } 549 550 static int host_mount P((am_node *mp)); 551 static int host_mount(mp) 552 am_node *mp; 553 { 554 mntfs *mf = mp->am_mnt; 555 556 return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts); 557 } 558 559 static int host_umount P((am_node *mp)); 560 static int host_umount(mp) 561 am_node *mp; 562 { 563 mntfs *mf = mp->am_mnt; 564 565 return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx"); 566 } 567 568 #endif /* HOST_EXEC */ 569 570 /* 571 * Ops structure 572 */ 573 am_ops host_ops = { 574 "host", 575 host_match, 576 host_init, 577 auto_fmount, 578 host_fmount, 579 auto_fumount, 580 host_fumount, 581 efs_lookuppn, 582 efs_readdir, 583 0, /* host_readlink */ 584 0, /* host_mounted */ 585 0, /* host_umounted */ 586 find_nfs_srvr, 587 FS_MKMNT|FS_BACKGROUND|FS_AMQINFO 588 }; 589 590 #endif /* HAS_HOST */ 591