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