xref: /openbsd/usr.sbin/amd/amd/nfsx_ops.c (revision 3cab2bb3)
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  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	from: @(#)nfsx_ops.c	8.1 (Berkeley) 6/6/93
35  *	$Id: nfsx_ops.c,v 1.10 2015/12/05 21:15:01 mmcc Exp $
36  */
37 
38 #include "am.h"
39 
40 #ifdef HAS_NFSX
41 
42 /*
43  * NFS hierarchical mounts
44  *
45  * TODO: Re-implement.
46  */
47 
48 /*
49  * The rfs field contains a list of mounts to be done from
50  * the remote host.
51  */
52 typedef struct nfsx_mnt {
53 	mntfs *n_mnt;
54 	int n_error;
55 } nfsx_mnt;
56 
57 struct nfsx {
58 	int nx_c;		/* Number of elements in nx_v */
59 	nfsx_mnt *nx_v;		/* Underlying mounts */
60 	nfsx_mnt *nx_try;
61 };
62 
63 static int nfsx_fmount(mntfs *);
64 
65 static char *
66 nfsx_match(am_opts *fo)
67 {
68 	char *xmtab;
69 	char *ptr;
70 	int len;
71 
72 	if (!fo->opt_rfs) {
73 		plog(XLOG_USER, "nfsx: no remote filesystem specified");
74 		return FALSE;
75 	}
76 	if (!fo->opt_rhost) {
77 		plog(XLOG_USER, "nfsx: no remote host specified");
78 		return FALSE;
79 	}
80 
81 #ifdef notdef
82 	/* fiddle sublink, must be last... */
83 	if (fo->opt_sublink) {
84 		plog(XLOG_WARNING, "nfsx: sublink %s ignored", fo->opt_sublink);
85 		free(fo->opt_sublink);
86 		fo->opt_sublink = 0;
87 	}
88 #endif
89 
90 	/* set default sublink */
91 	if (fo->opt_sublink == 0) {
92 		ptr = strchr(fo->opt_rfs, ',');
93 		if (ptr && ptr != (fo->opt_rfs + 1))
94 			fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
95 	}
96 
97 	/*
98 	 * Remove trailing ",..." from ${fs}
99 	 * After deslashifying, overwrite the end of ${fs} with "/"
100 	 * to make sure it is unique.
101 	 */
102 	if ((ptr = strchr(fo->opt_fs, ',')))
103 		*ptr = '\0';
104 	deslashify(fo->opt_fs);
105 	/*
106 	 * Bump string length to allow trailing /
107 	 */
108 	len = strlen(fo->opt_fs);
109 	if (len > SIZE_MAX - 2)
110 		 xmallocfailure();
111 	fo->opt_fs = xreallocarray(fo->opt_fs, len + 1 + 1, 1);
112 	ptr = fo->opt_fs + len;
113 	/*
114 	 * Make unique...
115 	 */
116 	*ptr++ = '/';
117 	*ptr = '\0';
118 
119 	/*
120 	 * Determine magic cookie to put in mtab
121 	 */
122 	xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
123 #ifdef DEBUG
124 	dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
125 		fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
126 #endif /* DEBUG */
127 
128 	return xmtab;
129 }
130 
131 static void
132 nfsx_prfree(void *vp)
133 {
134 	struct nfsx *nx = (struct nfsx *) vp;
135 	int i;
136 
137 	for (i = 0; i < nx->nx_c; i++) {
138 		mntfs *m = nx->nx_v[i].n_mnt;
139 		if (m)
140 			free_mntfs(m);
141 	}
142 
143 	free(nx->nx_v);
144 	free(nx);
145 }
146 
147 static int
148 nfsx_init(mntfs *mf)
149 {
150 	/*
151 	 * mf_info has the form:
152 	 *   host:/prefix/path,sub,sub,sub
153 	 */
154 	int i;
155 	int glob_error;
156 	struct nfsx *nx;
157 	int asked_for_wakeup = 0;
158 
159 	nx = (struct nfsx *) mf->mf_private;
160 
161 	if (nx == 0) {
162 		char **ivec;
163 		char *info = 0;
164 		char *host;
165 		char *pref;
166 		int error = 0;
167 
168 		info = strdup(mf->mf_info);
169 		host = strchr(info, ':');
170 		if (!host) {
171 			error = EINVAL;
172 			goto errexit;
173 		}
174 
175 		pref = host+1;
176 		host = info;
177 
178 		/*
179 		 * Split the prefix off from the suffices
180 		 */
181 		ivec = strsplit(pref, ',', '\'');
182 
183 		/*
184 		 * Count array size
185 		 */
186 		for (i = 0; ivec[i]; i++)
187 			;
188 
189 		nx = ALLOC(nfsx);
190 		mf->mf_private = nx;
191 		mf->mf_prfree = nfsx_prfree;
192 
193 		nx->nx_c = i - 1;	/* i-1 because we don't want the prefix */
194 		nx->nx_v = xreallocarray(NULL, nx->nx_c, sizeof *nx->nx_v);
195 		{ char *mp = 0;
196 		  char *xinfo = 0;
197 		  char *fs = mf->mf_fo->opt_fs;
198 		  char *rfs = 0;
199 		  for (i = 0; i < nx->nx_c; i++) {
200 			char *path = ivec[i+1];
201 			rfs = str3cat(rfs, pref, "/", path);
202 			/*
203 			 * Determine the mount point.
204 			 * If this is the root, then don't remove
205 			 * the trailing slash to avoid mntfs name clashes.
206 			 */
207 			mp = str3cat(mp, fs, "/", rfs);
208 			normalize_slash(mp);
209 			deslashify(mp);
210 			/*
211 			 * Determine the mount info
212 			 */
213 			xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
214 			normalize_slash(xinfo);
215 			if (pref[1] != '\0')
216 				deslashify(xinfo);
217 #ifdef DEBUG
218 			dlog("nfsx: init mount for %s on %s", xinfo, mp);
219 #endif
220 			nx->nx_v[i].n_error = -1;
221 			nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
222 		  }
223 		  free(rfs);
224 		  free(mp);
225 		  free(xinfo);
226 		}
227 
228 		free(ivec);
229 errexit:
230 		free(info);
231 		if (error)
232 			return error;
233 	}
234 
235 	/*
236 	 * Iterate through the mntfs's and call
237 	 * the underlying init routine on each
238 	 */
239 	glob_error = 0;
240 	for (i = 0; i < nx->nx_c; i++) {
241 		nfsx_mnt *n = &nx->nx_v[i];
242 		mntfs *m = n->n_mnt;
243 		int error = (*m->mf_ops->fs_init)(m);
244 		/*
245 		 * If HARD_NFSX_ERRORS is defined, make any
246 		 * initialisation failure a hard error and
247 		 * fail the entire group.  Otherwise only fail
248 		 * if none of the group is mountable (see nfsx_fmount).
249 		 */
250 #ifdef HARD_NFSX_ERRORS
251 		if (error > 0)
252 			return error;
253 #else
254 		if (error > 0)
255 			n->n_error = error;
256 #endif
257 		else if (error < 0) {
258 			glob_error = -1;
259 			if (!asked_for_wakeup) {
260 				asked_for_wakeup = 1;
261 				sched_task(wakeup_task, mf, m);
262 			}
263 		}
264 	}
265 
266 	return glob_error;
267 }
268 
269 static void
270 nfsx_cont(int rc, int term, void *closure)
271 {
272 	mntfs *mf = (mntfs *) closure;
273 	struct nfsx *nx = (struct nfsx *) mf->mf_private;
274 	nfsx_mnt *n = nx->nx_try;
275 
276 	n->n_mnt->mf_flags &= ~(MFF_ERROR|MFF_MOUNTING);
277 	mf->mf_flags &= ~MFF_ERROR;
278 
279 	/*
280 	 * Wakeup anything waiting for this mount
281 	 */
282 	wakeup(n->n_mnt);
283 
284 	if (rc || term) {
285 		if (term) {
286 			/*
287 			 * Not sure what to do for an error code.
288 			 */
289 			plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
290 			n->n_error = EIO;
291 		} else {
292 			/*
293 			 * Check for exit status
294 			 */
295 			errno = rc;	/* XXX */
296 			plog(XLOG_ERROR, "%s: mount (nfsx_cont): %m", n->n_mnt->mf_mount);
297 			n->n_error = rc;
298 		}
299 		free_mntfs(n->n_mnt);
300 		n->n_mnt = new_mntfs();
301 		n->n_mnt->mf_error = n->n_error;
302 		n->n_mnt->mf_flags |= MFF_ERROR;
303 	} else {
304 		/*
305 		 * The mount worked.
306 		 */
307 		mf_mounted(n->n_mnt);
308 		n->n_error = 0;
309 	}
310 
311 	/*
312 	 * Do the remaining bits
313 	 */
314 	if (nfsx_fmount(mf) >= 0) {
315 		wakeup(mf);
316 		mf->mf_flags &= ~MFF_MOUNTING;
317 		mf_mounted(mf);
318 	}
319 }
320 
321 static int
322 try_nfsx_mount(void *mv)
323 {
324 	mntfs *mf = (mntfs *) mv;
325 	int error;
326 
327 	mf->mf_flags |= MFF_MOUNTING;
328 	error = (*mf->mf_ops->fmount_fs)(mf);
329 	mf->mf_flags &= ~MFF_MOUNTING;
330 	return error;
331 }
332 
333 static int
334 nfsx_remount(mntfs *mf, int fg)
335 {
336 	struct nfsx *nx = (struct nfsx *) mf->mf_private;
337 	nfsx_mnt *n;
338 	int glob_error = -1;
339 
340 	for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
341 		mntfs *m = n->n_mnt;
342 		if (n->n_error < 0) {
343 			if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
344 				int error = mkdirs(m->mf_mount, 0555);
345 				if (!error)
346 					m->mf_flags |= MFF_MKMNT;
347 			}
348 		}
349 	}
350 
351 	/*
352 	 * Iterate through the mntfs's and mount each filesystem
353 	 * which is not yet mounted.
354 	 */
355 	for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
356 		mntfs *m = n->n_mnt;
357 		if (n->n_error < 0) {
358 			/*
359 			 * Check fmount entry pt. exists
360 			 * and then mount...
361 			 */
362 			if (!m->mf_ops->fmount_fs) {
363 				n->n_error = EINVAL;
364 			} else {
365 #ifdef DEBUG
366 				dlog("calling underlying fmount on %s", m->mf_mount);
367 #endif
368 				if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
369 					m->mf_flags |= MFF_MOUNTING;	/* XXX */
370 #ifdef DEBUG
371 					dlog("backgrounding mount of \"%s\"", m->mf_info);
372 #endif
373 					nx->nx_try = n;
374 					run_task(try_nfsx_mount, m,
375 					    nfsx_cont, mf);
376 					n->n_error = -1;
377 					return -1;
378 				} else {
379 #ifdef DEBUG
380 					dlog("foreground mount of \"%s\" ...", mf->mf_info);
381 #endif
382 					n->n_error = (*m->mf_ops->fmount_fs)(m);
383 				}
384 			}
385 #ifdef DEBUG
386 			if (n->n_error > 0) {
387 				errno = n->n_error;	/* XXX */
388 				dlog("underlying fmount of %s failed: %m", m->mf_mount);
389 			}
390 #endif
391 			if (n->n_error == 0) {
392 				glob_error = 0;
393 			} else if (glob_error < 0) {
394 				glob_error = n->n_error;
395 			}
396 		}
397 	}
398 
399 	return glob_error < 0 ? 0 : glob_error;
400 }
401 
402 static int
403 nfsx_fmount(mntfs *mf)
404 {
405 	return nfsx_remount(mf, FALSE);
406 }
407 
408 /*
409  * Unmount an NFS hierarchy.
410  * Note that this is called in the foreground
411  * and so may hang under extremely rare conditions.
412  */
413 static int
414 nfsx_fumount(mntfs *mf)
415 {
416 	struct nfsx *nx = (struct nfsx *) mf->mf_private;
417 	nfsx_mnt *n;
418 	int glob_error = 0;
419 
420 	/*
421 	 * Iterate in reverse through the mntfs's and unmount each filesystem
422 	 * which is mounted.
423 	 */
424 	for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
425 		mntfs *m = n->n_mnt;
426 		/*
427 		 * If this node has not been messed with
428 		 * and there has been no error so far
429 		 * then try and unmount.
430 		 * If an error had occured then zero
431 		 * the error code so that the remount
432 		 * only tries to unmount those nodes
433 		 * which had been successfully unmounted.
434 		 */
435 		if (n->n_error == 0) {
436 #ifdef DEBUG
437 			dlog("calling underlying fumount on %s", m->mf_mount);
438 #endif
439 			n->n_error = (*m->mf_ops->fumount_fs)(m);
440 			if (n->n_error) {
441 				glob_error = n->n_error;
442 				n->n_error = 0;
443 			} else {
444 				/*
445 				 * Make sure remount gets this node
446 				 */
447 				n->n_error = -1;
448 			}
449 		}
450 	}
451 
452 	/*
453 	 * If any unmounts failed then remount the
454 	 * whole lot...
455 	 */
456 	if (glob_error) {
457 		glob_error = nfsx_remount(mf, TRUE);
458 		if (glob_error) {
459 			errno = glob_error; /* XXX */
460 			plog(XLOG_USER, "nfsx: remount of %s failed: %m", mf->mf_mount);
461 		}
462 		glob_error = EBUSY;
463 	} else {
464 		/*
465 		 * Remove all the mount points
466 		 */
467 		for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
468 			mntfs *m = n->n_mnt;
469 			if (n->n_error < 0) {
470 				if (m->mf_ops->fs_flags & FS_MKMNT) {
471 					(void) rmdirs(m->mf_mount);
472 					m->mf_flags &= ~MFF_MKMNT;
473 				}
474 			}
475 			free_mntfs(m);
476 			n->n_mnt = 0;
477 			n->n_error = -1;
478 		}
479 	}
480 
481 	return glob_error;
482 }
483 
484 /*
485  * Ops structure
486  */
487 am_ops nfsx_ops = {
488 	"nfsx",
489 	nfsx_match,
490 	nfsx_init,
491 	auto_fmount,
492 	nfsx_fmount,
493 	auto_fumount,
494 	nfsx_fumount,
495 	efs_lookuppn,
496 	efs_readdir,
497 	0, /* nfsx_readlink */
498 	0, /* nfsx_mounted */
499 	0, /* nfsx_umounted */
500 	find_nfs_srvr,			/* XXX */
501 	/*FS_UBACKGROUND|*/FS_AMQINFO
502 };
503 
504 #endif /* HAS_NFSX */
505