1 /*
2 * Copyright (c) 1990 Jan-Simon Pendry
3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. 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 8.1 (Berkeley) 06/06/93
13 *
14 * $Id: host_ops.c,v 5.2.2.2 1992/05/31 16:36:08 jsp Exp $
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));
host_match(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
host_init(mf)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
xdr_pri_free(xdr_args,args_ptr)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));
do_mount(fhp,dir,fs_name,opts,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));
sortfun(a,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));
fetch_fhandle(client,dir,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));
already_mounted(mlist,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));
host_fmount(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));
directory_prefix(pref,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));
host_fumount(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 /*
482 * Tell mountd we're done.
483 * This is not quite right, because we may still
484 * have other filesystems mounted, but the existing
485 * mountd protocol is badly broken anyway.
486 */
host_umounted(mp)487 static void host_umounted(mp)
488 am_node *mp;
489 {
490 #ifdef INFORM_MOUNTD
491 mntfs *mf = mp->am_mnt;
492 char *host;
493 CLIENT *client;
494 enum clnt_stat clnt_stat;
495 struct sockaddr_in sin;
496 int sock = RPC_ANYSOCK;
497 struct timeval tv;
498 tv.tv_sec = 10; tv.tv_usec = 0;
499
500 if (mf->mf_error || mf->mf_refc > 1 || ! mf->mf_server)
501 return;
502
503 host = mf->mf_server->fs_host;
504 sin = *mf->mf_server->fs_ip;
505
506 /*
507 * Zero out the port - make sure we recompute
508 */
509 sin.sin_port = 0;
510 /*
511 * Make a client end-point.
512 * Try TCP first
513 */
514 if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL &&
515 (client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) {
516 plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
517 goto out;
518 }
519
520 if (!nfs_auth) {
521 if (make_nfs_auth())
522 goto out;
523 }
524
525 client->cl_auth = nfs_auth;
526
527 #ifdef DEBUG
528 dlog("Unmounting all from %s", host);
529 #endif /* DEBUG */
530
531 clnt_stat = clnt_call(client, MOUNTPROC_UMNTALL, xdr_void, 0, xdr_void, 0, tv);
532 if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
533 /* RPC_SYSTEMERROR seems to be returned for no good reason ...*/
534 extern char *clnt_sperrno();
535 char *msg = clnt_sperrno(clnt_stat);
536 plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg, clnt_stat);
537 goto out;
538 }
539
540 out:
541 if (client)
542 clnt_destroy(client);
543
544 #endif /* INFORM_MOUNTD */
545 }
546
547
548 #else /* HOST_EXEC */
549
550 static int host_exec P((char*op, char*host, char*fs, char*opts));
host_exec(op,host,fs,opts)551 static int host_exec(op, host, fs, opts)
552 char *op;
553 char *host;
554 char *fs;
555 char *opts;
556 {
557 int error;
558 char *argv[7];
559
560 /*
561 * Build arg vector
562 */
563 argv[0] = host_helper;
564 argv[1] = host_helper;
565 argv[2] = op;
566 argv[3] = host;
567 argv[4] = fs;
568 argv[5] = opts && *opts ? opts : "rw,default";
569 argv[6] = 0;
570
571 /*
572 * Put stdout to stderr
573 */
574 (void) fclose(stdout);
575 (void) dup(fileno(logfp));
576 if (fileno(logfp) != fileno(stderr)) {
577 (void) fclose(stderr);
578 (void) dup(fileno(logfp));
579 }
580 /*
581 * Try the exec
582 */
583 #ifdef DEBUG
584 Debug(D_FULL) {
585 char **cp = argv;
586 plog(XLOG_DEBUG, "executing (un)mount command...");
587 while (*cp) {
588 plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp);
589 cp++;
590 }
591 }
592 #endif /* DEBUG */
593 if (argv[0] == 0 || argv[1] == 0) {
594 errno = EINVAL;
595 plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
596 } else {
597 (void) execv(argv[0], argv+1);
598 }
599 /*
600 * Save error number
601 */
602 error = errno;
603 plog(XLOG_ERROR, "exec %s failed: %m", argv[0]);
604
605 /*
606 * Return error
607 */
608 return error;
609 }
610
611 static int host_mount P((am_node *mp));
host_mount(mp)612 static int host_mount(mp)
613 am_node *mp;
614 {
615 mntfs *mf = mp->am_mnt;
616
617 return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts);
618 }
619
620 static int host_umount P((am_node *mp));
host_umount(mp)621 static int host_umount(mp)
622 am_node *mp;
623 {
624 mntfs *mf = mp->am_mnt;
625
626 return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
627 }
628
629 #endif /* HOST_EXEC */
630
631 /*
632 * Ops structure
633 */
634 am_ops host_ops = {
635 "host",
636 host_match,
637 host_init,
638 auto_fmount,
639 host_fmount,
640 auto_fumount,
641 host_fumount,
642 efs_lookuppn,
643 efs_readdir,
644 0, /* host_readlink */
645 0, /* host_mounted */
646 #ifdef HOST_EXEC
647 0, /* host_umounted */
648 #else
649 host_umounted,
650 #endif
651 find_nfs_srvr,
652 FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
653 };
654
655 #endif /* HAS_HOST */
656