GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/amd/amd/host_ops.c Lines: 0 116 0.0 %
Date: 2017-11-07 Branches: 0 82 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: host_ops.c,v 1.19 2015/12/05 21:15:01 mmcc 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 *
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
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
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
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
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
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
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
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[MAXPATHLEN], *rfs_dir;
230
	char mntpt[MAXPATHLEN];
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
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
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
 */
480
static void host_umounted(am_node *mp)
481
{
482
}
483
484
485
#else /* HOST_EXEC */
486
487
static int
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
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
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 */