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.4 (Berkeley) 02/09/92 13 * 14 * $Id: host_ops.c,v 5.2.2.1 1992/02/09 15:08:24 jsp beta $ 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 struct timeval tv; 224 tv.tv_sec = 10; tv.tv_usec = 0; 225 226 /* 227 * Read the mount list 228 */ 229 mlist = read_mtab(mf->mf_mount); 230 231 /* 232 * Unlock the mount list 233 */ 234 unlock_mntlist(); 235 236 /* 237 * Take a copy of the server address 238 */ 239 sin = *mf->mf_server->fs_ip; 240 241 /* 242 * Zero out the port - make sure we recompute 243 */ 244 sin.sin_port = 0; 245 /* 246 * Make a client end-point. 247 * Try TCP first 248 */ 249 if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL && 250 (client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) { 251 plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host); 252 error = EIO; 253 goto out; 254 } 255 256 if (!nfs_auth) { 257 error = make_nfs_auth(); 258 if (error) 259 goto out; 260 } 261 262 client->cl_auth = nfs_auth; 263 264 #ifdef DEBUG 265 dlog("Fetching export list from %s", host); 266 #endif /* DEBUG */ 267 268 /* 269 * Fetch the export list 270 */ 271 tv2.tv_sec = 10; tv2.tv_usec = 0; 272 clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2); 273 if (clnt_stat != RPC_SUCCESS) { 274 /*clnt_perror(client, "rpc");*/ 275 error = EIO; 276 goto out; 277 } 278 279 /* 280 * Figure out how many exports were returned 281 */ 282 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 283 /*printf("export %s\n", ex->ex_dir);*/ 284 n_export++; 285 } 286 #ifdef DEBUG 287 /*dlog("%d exports returned\n", n_export);*/ 288 #endif /* DEBUG */ 289 290 /* 291 * Allocate an array of pointers into the list 292 * so that they can be sorted. If the filesystem 293 * is already mounted then ignore it. 294 */ 295 ep = (exports *) xmalloc(n_export * sizeof(exports)); 296 for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 297 MAKE_MNTPT(mntpt, ex, mf); 298 if (!already_mounted(mlist, mntpt)) 299 ep[j++] = ex; 300 } 301 n_export = j; 302 303 /* 304 * Sort into order. 305 * This way the mounts are done in order down the tree, 306 * instead of any random order returned by the mount 307 * daemon (the protocol doesn't specify...). 308 */ 309 qsort(ep, n_export, sizeof(exports), sortfun); 310 311 /* 312 * Allocate an array of filehandles 313 */ 314 fp = (fhstatus *) xmalloc(n_export * sizeof(fhstatus)); 315 316 /* 317 * Try to obtain filehandles for each directory. 318 * If a fetch fails then just zero out the array 319 * reference but discard the error. 320 */ 321 for (j = k = 0; j < n_export; j++) { 322 /* Check and avoid a duplicated export entry */ 323 if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) { 324 #ifdef DEBUG 325 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 326 #endif 327 ep[j] = 0; 328 } else { 329 k = j; 330 if (error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j])) 331 ep[j] = 0; 332 } 333 } 334 335 /* 336 * Mount each filesystem for which we have a filehandle. 337 * If any of the mounts succeed then mark "ok" and return 338 * error code 0 at the end. If they all fail then return 339 * the last error code. 340 */ 341 strncpy(fs_name, mf->mf_info, sizeof(fs_name)); 342 if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { 343 plog(XLOG_FATAL, "host_fmount: mf_info has no colon"); 344 error = EINVAL; 345 goto out; 346 } 347 ++rfs_dir; 348 for (j = 0; j < n_export; j++) { 349 ex = ep[j]; 350 if (ex) { 351 strcpy(rfs_dir, ex->ex_dir); 352 MAKE_MNTPT(mntpt, ex, mf); 353 if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0) 354 ok = TRUE; 355 } 356 } 357 358 /* 359 * Clean up and exit 360 */ 361 out: 362 discard_mntlist(mlist); 363 if (ep) 364 free(ep); 365 if (fp) 366 free(fp); 367 if (client) 368 clnt_destroy(client); 369 if (exlist) 370 xdr_pri_free(xdr_exports, &exlist); 371 if (ok) 372 return 0; 373 return error; 374 } 375 376 /* 377 * Return true if pref is a directory prefix of dir. 378 * 379 * TODO: 380 * Does not work if pref is "/". 381 */ 382 static int directory_prefix P((char *pref, char *dir)); 383 static int directory_prefix(pref, dir) 384 char *pref; 385 char *dir; 386 { 387 int len = strlen(pref); 388 if (strncmp(pref, dir, len) != 0) 389 return FALSE; 390 if (dir[len] == '/' || dir[len] == '\0') 391 return TRUE; 392 return FALSE; 393 } 394 395 /* 396 * Unmount a mount tree 397 */ 398 static int host_fumount P((mntfs *mf)); 399 static int host_fumount(mf) 400 mntfs *mf; 401 { 402 mntlist *ml, *mprev; 403 int xerror = 0; 404 405 /* 406 * Read the mount list 407 */ 408 mntlist *mlist = read_mtab(mf->mf_mount); 409 410 /* 411 * Unlock the mount list 412 */ 413 unlock_mntlist(); 414 415 /* 416 * Reverse list... 417 */ 418 ml = mlist; 419 mprev = 0; 420 while (ml) { 421 mntlist *ml2 = ml->mnext; 422 ml->mnext = mprev; 423 mprev = ml; 424 ml = ml2; 425 } 426 mlist = mprev; 427 428 /* 429 * Unmount all filesystems... 430 */ 431 for (ml = mlist; ml && !xerror; ml = ml->mnext) { 432 char *dir = ml->mnt->mnt_dir; 433 if (directory_prefix(mf->mf_mount, dir)) { 434 int error; 435 #ifdef DEBUG 436 dlog("host: unmounts %s", dir); 437 #endif /* DEBUG */ 438 /* 439 * Unmount "dir" 440 */ 441 error = UMOUNT_FS(dir); 442 /* 443 * Keep track of errors 444 */ 445 if (error) { 446 if (!xerror) 447 xerror = error; 448 if (error != EBUSY) { 449 errno = error; 450 plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 451 } 452 } else { 453 #ifdef HOST_MKDIRS 454 (void) rmdirs(dir); 455 #endif /* HOST_MKDIRS */ 456 } 457 } 458 } 459 460 /* 461 * Throw away mount list 462 */ 463 discard_mntlist(mlist); 464 465 /* 466 * Try to remount, except when we are shutting down. 467 */ 468 if (xerror && amd_state != Finishing) { 469 xerror = host_fmount(mf); 470 if (!xerror) { 471 /* 472 * Don't log this - it's usually too verbose 473 plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 474 */ 475 xerror = EBUSY; 476 } 477 } 478 return xerror; 479 } 480 481 #else /* HOST_EXEC */ 482 483 static int host_exec P((char*op, char*host, char*fs, char*opts)); 484 static int host_exec(op, host, fs, opts) 485 char *op; 486 char *host; 487 char *fs; 488 char *opts; 489 { 490 int error; 491 char *argv[7]; 492 493 /* 494 * Build arg vector 495 */ 496 argv[0] = host_helper; 497 argv[1] = host_helper; 498 argv[2] = op; 499 argv[3] = host; 500 argv[4] = fs; 501 argv[5] = opts && *opts ? opts : "rw,default"; 502 argv[6] = 0; 503 504 /* 505 * Put stdout to stderr 506 */ 507 (void) fclose(stdout); 508 (void) dup(fileno(logfp)); 509 if (fileno(logfp) != fileno(stderr)) { 510 (void) fclose(stderr); 511 (void) dup(fileno(logfp)); 512 } 513 /* 514 * Try the exec 515 */ 516 #ifdef DEBUG 517 Debug(D_FULL) { 518 char **cp = argv; 519 plog(XLOG_DEBUG, "executing (un)mount command..."); 520 while (*cp) { 521 plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp); 522 cp++; 523 } 524 } 525 #endif /* DEBUG */ 526 if (argv[0] == 0 || argv[1] == 0) { 527 errno = EINVAL; 528 plog(XLOG_USER, "1st/2nd args missing to (un)mount program"); 529 } else { 530 (void) execv(argv[0], argv+1); 531 } 532 /* 533 * Save error number 534 */ 535 error = errno; 536 plog(XLOG_ERROR, "exec %s failed: %m", argv[0]); 537 538 /* 539 * Return error 540 */ 541 return error; 542 } 543 544 static int host_mount P((am_node *mp)); 545 static int host_mount(mp) 546 am_node *mp; 547 { 548 mntfs *mf = mp->am_mnt; 549 550 return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts); 551 } 552 553 static int host_umount P((am_node *mp)); 554 static int host_umount(mp) 555 am_node *mp; 556 { 557 mntfs *mf = mp->am_mnt; 558 559 return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx"); 560 } 561 562 #endif /* HOST_EXEC */ 563 564 /* 565 * Ops structure 566 */ 567 am_ops host_ops = { 568 "host", 569 host_match, 570 host_init, 571 auto_fmount, 572 host_fmount, 573 auto_fumount, 574 host_fumount, 575 efs_lookuppn, 576 efs_readdir, 577 0, /* host_readlink */ 578 0, /* host_mounted */ 579 0, /* host_umounted */ 580 find_nfs_srvr, 581 FS_MKMNT|FS_BACKGROUND|FS_AMQINFO 582 }; 583 584 #endif /* HAS_HOST */ 585