1 /* $OpenBSD: host_ops.c,v 1.20 2021/10/21 10:55:56 deraadt Exp $ */
2
3 /*
4 * Copyright (c) 1990 Jan-Simon Pendry
5 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6 * Copyright (c) 1990, 1993
7 * The Regents of the University of California. 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 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: @(#)host_ops.c 8.1 (Berkeley) 6/6/93
37 */
38
39 #include "am.h"
40
41 #ifdef HAS_HOST
42
43 #include "mount.h"
44 #include <sys/stat.h>
45
46 /*
47 * NFS host file system.
48 * Mounts all exported filesystems from a given host.
49 * This has now degenerated into a mess but will not
50 * be rewritten. Amd 6 will support the abstractions
51 * needed to make this work correctly.
52 */
53
54 /*
55 * Define HOST_RPC_UDP to use dgram instead of stream RPC.
56 * Datagrams are generally much faster.
57 */
58 /*#define HOST_RPC_UDP*/
59
60 /*
61 * Define HOST_MKDIRS to make Amd automatically try
62 * to create the mount points.
63 */
64 #define HOST_MKDIRS
65
66 /*
67 * Determine the mount point
68 */
69 #define MAKE_MNTPT(mntpt, ex, mf) { \
70 if (strcmp((ex)->ex_dir, "/") == 0) \
71 strlcpy((mntpt), (mf)->mf_mount, sizeof((mntpt))); \
72 else \
73 snprintf((mntpt), sizeof(mntpt), "%s%s", (mf)->mf_mount, (ex)->ex_dir); \
74 }
75
76 /*
77 * Execute needs the same as NFS plus a helper command
78 */
79 static char *
host_match(am_opts * fo)80 host_match(am_opts *fo)
81 {
82 #ifdef HOST_EXEC
83 if (!host_helper) {
84 plog(XLOG_USER, "No host helper command given");
85 return FALSE;
86 }
87 #endif /* HOST_EXEC */
88
89 /*
90 * Make sure rfs is specified to keep nfs_match happy...
91 */
92 if (!fo->opt_rfs)
93 fo->opt_rfs = "/";
94
95 return (*nfs_ops.fs_match)(fo);
96 }
97
98 static int
host_init(mntfs * mf)99 host_init(mntfs *mf)
100 {
101 if (strchr(mf->mf_info, ':') == 0)
102 return ENOENT;
103 return 0;
104 }
105
106 /*
107 * Two implementations:
108 * HOST_EXEC gets you the external version. The program specified with
109 * the -h option is called. The external program is not published...
110 * roll your own.
111 *
112 * Otherwise you get the native version. Faster but makes the program
113 * bigger.
114 */
115
116 #ifndef HOST_EXEC
117
118 static bool_t
xdr_pri_free(xdrproc_t xdr_args,void * args_ptr)119 xdr_pri_free(xdrproc_t xdr_args, void *args_ptr)
120 {
121 XDR xdr;
122
123 xdr.x_op = XDR_FREE;
124 return ((*xdr_args)(&xdr, args_ptr));
125 }
126
127 static int
do_mount(fhstatus * fhp,char * dir,char * fs_name,char * opts,mntfs * mf)128 do_mount(fhstatus *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
129 {
130 struct stat stb;
131
132 #ifdef DEBUG
133 dlog("host: mounting fs %s on %s", fs_name, dir);
134 #endif /* DEBUG */
135 #ifdef HOST_MKDIRS
136 (void) mkdirs(dir, 0555);
137 #endif /* HOST_MKDIRS */
138 if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
139 plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
140 return ENOENT;
141 }
142
143 return mount_nfs_fh(fhp, dir, fs_name, opts, mf);
144 }
145
146 static int
sortfun(const void * arg1,const void * arg2)147 sortfun(const void *arg1, const void *arg2)
148 {
149 const exports *a = arg1, *b = arg2;
150 return strcmp((*a)->ex_dir, (*b)->ex_dir);
151 }
152
153 /*
154 * Get filehandle
155 */
156 static int
fetch_fhandle(CLIENT * client,char * dir,fhstatus * fhp)157 fetch_fhandle(CLIENT *client, char *dir, fhstatus *fhp)
158 {
159 struct timeval tv;
160 enum clnt_stat clnt_stat;
161
162 /*
163 * Pick a number, any number...
164 */
165 tv.tv_sec = 20;
166 tv.tv_usec = 0;
167
168 #ifdef DEBUG
169 dlog("Fetching fhandle for %s", dir);
170 #endif /* DEBUG */
171 /*
172 * Call the mount daemon on the remote host to
173 * get the filehandle.
174 */
175 fhp->fhs_vers = MOUNTVERS;
176 clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_fhstatus, fhp, tv);
177 if (clnt_stat != RPC_SUCCESS) {
178 char *msg = clnt_sperrno(clnt_stat);
179 plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
180 return EIO;
181 }
182 /*
183 * Check status of filehandle
184 */
185 if (fhp->fhs_stat) {
186 #ifdef DEBUG
187 errno = fhp->fhs_stat;
188 dlog("fhandle fetch failed: %m");
189 #endif /* DEBUG */
190 return fhp->fhs_stat;
191 }
192 return 0;
193 }
194
195 /*
196 * Scan mount table to see if something already mounted
197 */
198 static int
already_mounted(mntlist * mlist,char * dir)199 already_mounted(mntlist *mlist, char *dir)
200 {
201 mntlist *ml;
202
203 for (ml = mlist; ml; ml = ml->mnext)
204 if (strcmp(ml->mnt->mnt_dir, dir) == 0)
205 return 1;
206 return 0;
207 }
208
209 /*
210 * Mount the export tree from a host
211 */
212 static int
host_fmount(mntfs * mf)213 host_fmount(mntfs *mf)
214 {
215 struct timeval tv2;
216 CLIENT *client;
217 enum clnt_stat clnt_stat;
218 int n_export;
219 int j, k;
220 exports exlist = 0, ex;
221 exports *ep = 0;
222 fhstatus *fp = 0;
223 char *host = mf->mf_server->fs_host;
224 int error = 0;
225 struct sockaddr_in sin;
226 int sock = RPC_ANYSOCK;
227 int ok = FALSE;
228 mntlist *mlist;
229 char fs_name[PATH_MAX], *rfs_dir;
230 char mntpt[PATH_MAX];
231 struct timeval tv;
232 tv.tv_sec = 10; tv.tv_usec = 0;
233
234 /*
235 * Read the mount list
236 */
237 mlist = read_mtab(mf->mf_mount);
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 * Try TCP first
251 */
252 if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL &&
253 (client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) {
254 plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
255 error = EIO;
256 goto out;
257 }
258
259 if (!nfs_auth) {
260 error = make_nfs_auth();
261 if (error)
262 goto out;
263 }
264
265 client->cl_auth = nfs_auth;
266
267 #ifdef DEBUG
268 dlog("Fetching export list from %s", host);
269 #endif /* DEBUG */
270
271 /*
272 * Fetch the export list
273 */
274 tv2.tv_sec = 10; tv2.tv_usec = 0;
275 clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2);
276 if (clnt_stat != RPC_SUCCESS) {
277 /*clnt_perror(client, "rpc");*/
278 error = EIO;
279 goto out;
280 }
281
282 /*
283 * Figure out how many exports were returned
284 */
285 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
286 /*printf("export %s\n", ex->ex_dir);*/
287 n_export++;
288 }
289 #ifdef DEBUG
290 /*dlog("%d exports returned", n_export);*/
291 #endif /* DEBUG */
292
293 /*
294 * Allocate an array of pointers into the list
295 * so that they can be sorted. If the filesystem
296 * is already mounted then ignore it.
297 */
298 ep = xreallocarray(NULL, n_export, sizeof *ep);
299 for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
300 MAKE_MNTPT(mntpt, ex, mf);
301 if (!already_mounted(mlist, mntpt))
302 ep[j++] = ex;
303 }
304 n_export = j;
305
306 /*
307 * Sort into order.
308 * This way the mounts are done in order down the tree,
309 * instead of any random order returned by the mount
310 * daemon (the protocol doesn't specify...).
311 */
312 qsort(ep, n_export, sizeof(exports), sortfun);
313
314 /*
315 * Allocate an array of filehandles
316 */
317 fp = xreallocarray(NULL, n_export, sizeof *fp);
318
319 /*
320 * Try to obtain filehandles for each directory.
321 * If a fetch fails then just zero out the array
322 * reference but discard the error.
323 */
324 for (j = k = 0; j < n_export; j++) {
325 /* Check and avoid a duplicated export entry */
326 if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) {
327 #ifdef DEBUG
328 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
329 #endif
330 ep[j] = 0;
331 } else {
332 k = j;
333 if ((error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j])))
334 ep[j] = 0;
335 }
336 }
337
338 /*
339 * Mount each filesystem for which we have a filehandle.
340 * If any of the mounts succeed then mark "ok" and return
341 * error code 0 at the end. If they all fail then return
342 * the last error code.
343 */
344 strlcpy(fs_name, mf->mf_info, sizeof(fs_name));
345 if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
346 plog(XLOG_FATAL, "host_fmount: mf_info has no colon");
347 error = EINVAL;
348 goto out;
349 }
350 ++rfs_dir;
351 for (j = 0; j < n_export; j++) {
352 ex = ep[j];
353 if (ex) {
354 strlcpy(rfs_dir, ex->ex_dir, fs_name + sizeof fs_name - rfs_dir);
355 MAKE_MNTPT(mntpt, ex, mf);
356 if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0)
357 ok = TRUE;
358 }
359 }
360
361 /*
362 * Clean up and exit
363 */
364 out:
365 discard_mntlist(mlist);
366 free(ep);
367 free(fp);
368 if (client)
369 clnt_destroy(client);
370 if (exlist)
371 xdr_pri_free(xdr_exports, &exlist);
372 if (ok)
373 return 0;
374 return error;
375 }
376
377 /*
378 * Return true if pref is a directory prefix of dir.
379 *
380 * TODO:
381 * Does not work if pref is "/".
382 */
383 static int
directory_prefix(char * pref,char * dir)384 directory_prefix(char *pref, char *dir)
385 {
386 int len = strlen(pref);
387 if (strncmp(pref, dir, len) != 0)
388 return FALSE;
389 if (dir[len] == '/' || dir[len] == '\0')
390 return TRUE;
391 return FALSE;
392 }
393
394 /*
395 * Unmount a mount tree
396 */
397 static int
host_fumount(mntfs * mf)398 host_fumount(mntfs *mf)
399 {
400 mntlist *ml, *mprev;
401 int xerror = 0;
402
403 /*
404 * Read the mount list
405 */
406 mntlist *mlist = read_mtab(mf->mf_mount);
407
408 /*
409 * Reverse list...
410 */
411 ml = mlist;
412 mprev = 0;
413 while (ml) {
414 mntlist *ml2 = ml->mnext;
415 ml->mnext = mprev;
416 mprev = ml;
417 ml = ml2;
418 }
419 mlist = mprev;
420
421 /*
422 * Unmount all filesystems...
423 */
424 for (ml = mlist; ml && !xerror; ml = ml->mnext) {
425 char *dir = ml->mnt->mnt_dir;
426 if (directory_prefix(mf->mf_mount, dir)) {
427 int error;
428 #ifdef DEBUG
429 dlog("host: unmounts %s", dir);
430 #endif /* DEBUG */
431 /*
432 * Unmount "dir"
433 */
434 error = umount_fs(dir);
435 /*
436 * Keep track of errors
437 */
438 if (error) {
439 if (!xerror)
440 xerror = error;
441 if (error != EBUSY) {
442 errno = error;
443 plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
444 }
445 } else {
446 #ifdef HOST_MKDIRS
447 (void) rmdirs(dir);
448 #endif /* HOST_MKDIRS */
449 }
450 }
451 }
452
453 /*
454 * Throw away mount list
455 */
456 discard_mntlist(mlist);
457
458 /*
459 * Try to remount, except when we are shutting down.
460 */
461 if (xerror && amd_state != Finishing) {
462 xerror = host_fmount(mf);
463 if (!xerror) {
464 /*
465 * Don't log this - it's usually too verbose
466 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
467 */
468 xerror = EBUSY;
469 }
470 }
471 return xerror;
472 }
473
474 /*
475 * Tell mountd we're done.
476 * This is not quite right, because we may still
477 * have other filesystems mounted, but the existing
478 * mountd protocol is badly broken anyway.
479 */
host_umounted(am_node * mp)480 static void host_umounted(am_node *mp)
481 {
482 }
483
484
485 #else /* HOST_EXEC */
486
487 static int
host_exec(char * op,char * host,char * fs,char * opts)488 host_exec(char *op, char *host, char *fs, 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(am_node * mp)545 host_mount(am_node *mp)
546 {
547 mntfs *mf = mp->am_mnt;
548
549 return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts);
550 }
551
552 static int
host_umount(am_node * mp)553 host_umount(am_node *mp)
554 {
555 mntfs *mf = mp->am_mnt;
556
557 return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
558 }
559
560 #endif /* HOST_EXEC */
561
562 /*
563 * Ops structure
564 */
565 am_ops host_ops = {
566 "host",
567 host_match,
568 host_init,
569 auto_fmount,
570 host_fmount,
571 auto_fumount,
572 host_fumount,
573 efs_lookuppn,
574 efs_readdir,
575 0, /* host_readlink */
576 0, /* host_mounted */
577 #ifdef HOST_EXEC
578 0, /* host_umounted */
579 #else
580 host_umounted,
581 #endif
582 find_nfs_srvr,
583 FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
584 };
585
586 #endif /* HAS_HOST */
587