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