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