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