MFC r1.27:
[dragonfly.git] / contrib / amd / amd / amfs_nfsx.c
bloba253e5060989b5de0218c2fa9571584ace7815be
1 /*
2 * Copyright (c) 1997-1999 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 * %W% (Berkeley) %G%
41 * $Id: amfs_nfsx.c,v 1.2 1999/01/10 21:53:42 ezk Exp $
46 * NFS hierarchical mounts
48 * TODO: Re-implement.
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif /* HAVE_CONFIG_H */
54 #include <am_defs.h>
55 #include <amd.h>
58 * The rfs field contains a list of mounts to be done from
59 * the remote host.
61 typedef struct amfs_nfsx_mnt {
62 mntfs *n_mnt;
63 int n_error;
64 } amfs_nfsx_mnt;
66 struct amfs_nfsx {
67 int nx_c; /* Number of elements in nx_v */
68 amfs_nfsx_mnt *nx_v; /* Underlying mounts */
69 amfs_nfsx_mnt *nx_try;
72 /* forward definitions */
73 static char *amfs_nfsx_match(am_opts *fo);
74 static int amfs_nfsx_fmount (mntfs *);
75 static int amfs_nfsx_fmount(mntfs *mf);
76 static int amfs_nfsx_fumount(mntfs *mf);
77 static int amfs_nfsx_init(mntfs *mf);
80 * Ops structure
82 am_ops amfs_nfsx_ops =
84 "nfsx",
85 amfs_nfsx_match,
86 amfs_nfsx_init,
87 amfs_auto_fmount,
88 amfs_nfsx_fmount,
89 amfs_auto_fumount,
90 amfs_nfsx_fumount,
91 amfs_error_lookuppn,
92 amfs_error_readdir,
93 0, /* amfs_nfsx_readlink */
94 0, /* amfs_nfsx_mounted */
95 0, /* amfs_nfsx_umounted */
96 find_nfs_srvr, /* XXX */
97 /* FS_UBACKGROUND| */ FS_AMQINFO
101 static char *
102 amfs_nfsx_match(am_opts *fo)
104 char *xmtab;
105 char *ptr;
106 int len;
108 if (!fo->opt_rfs) {
109 plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
110 return FALSE;
113 if (!fo->opt_rhost) {
114 plog(XLOG_USER, "amfs_nfsx: no remote host specified");
115 return FALSE;
118 /* set default sublink */
119 if (fo->opt_sublink == 0) {
120 ptr = strchr(fo->opt_rfs, ',');
121 if (ptr && ptr != (fo->opt_rfs + 1))
122 fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
126 * Remove trailing ",..." from ${fs}
127 * After deslashifying, overwrite the end of ${fs} with "/"
128 * to make sure it is unique.
130 if ((ptr = strchr(fo->opt_fs, ',')))
131 *ptr = '\0';
132 deslashify(fo->opt_fs);
135 * Bump string length to allow trailing /
137 len = strlen(fo->opt_fs);
138 fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
139 ptr = fo->opt_fs + len;
142 * Make unique...
144 *ptr++ = '/';
145 *ptr = '\0';
148 * Determine magic cookie to put in mtab
150 xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
151 #ifdef DEBUG
152 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
153 fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
154 #endif /* DEBUG */
156 return xmtab;
160 static void
161 amfs_nfsx_prfree(voidp vp)
163 struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
164 int i;
166 for (i = 0; i < nx->nx_c; i++) {
167 mntfs *m = nx->nx_v[i].n_mnt;
168 if (m)
169 free_mntfs(m);
172 XFREE(nx->nx_v);
173 XFREE(nx);
177 static int
178 amfs_nfsx_init(mntfs *mf)
181 * mf_info has the form:
182 * host:/prefix/path,sub,sub,sub
184 int i;
185 int glob_error;
186 struct amfs_nfsx *nx;
187 int asked_for_wakeup = 0;
189 nx = (struct amfs_nfsx *) mf->mf_private;
191 if (nx == 0) {
192 char **ivec;
193 char *info = 0;
194 char *host;
195 char *pref;
196 int error = 0;
198 info = strdup(mf->mf_info);
199 host = strchr(info, ':');
200 if (!host) {
201 error = EINVAL;
202 goto errexit;
204 pref = host +1;
205 host = info;
208 * Split the prefix off from the suffices
210 ivec = strsplit(pref, ',', '\'');
213 * Count array size
215 for (i = 0; ivec[i]; i++) ;
217 nx = ALLOC(struct amfs_nfsx);
218 mf->mf_private = (voidp) nx;
219 mf->mf_prfree = amfs_nfsx_prfree;
221 nx->nx_c = i - 1; /* i-1 because we don't want the prefix */
222 nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt));
224 char *mp = 0;
225 char *xinfo = 0;
226 char *fs = mf->mf_fo->opt_fs;
227 char *rfs = 0;
228 for (i = 0; i < nx->nx_c; i++) {
229 char *path = ivec[i + 1];
230 rfs = str3cat(rfs, pref, "/", path);
232 * Determine the mount point.
233 * If this is the root, then don't remove
234 * the trailing slash to avoid mntfs name clashes.
236 mp = str3cat(mp, fs, "/", rfs);
237 normalize_slash(mp);
238 deslashify(mp);
240 * Determine the mount info
242 xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
243 normalize_slash(xinfo);
244 if (pref[1] != '\0')
245 deslashify(xinfo);
246 #ifdef DEBUG
247 dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
248 #endif /* DEBUG */
249 nx->nx_v[i].n_error = -1;
250 nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
252 if (rfs)
253 XFREE(rfs);
254 if (mp)
255 XFREE(mp);
256 if (xinfo)
257 XFREE(xinfo);
260 XFREE(ivec);
261 errexit:
262 if (info)
263 XFREE(info);
264 if (error)
265 return error;
269 * Iterate through the mntfs's and call
270 * the underlying init routine on each
272 glob_error = 0;
274 for (i = 0; i < nx->nx_c; i++) {
275 amfs_nfsx_mnt *n = &nx->nx_v[i];
276 mntfs *m = n->n_mnt;
277 int error = (*m->mf_ops->fs_init) (m);
279 * if you just "return error" here, you will have made a failure
280 * in any submounts to fail the whole group. There was old unused code
281 * here before.
283 if (error > 0)
284 n->n_error = error;
286 else if (error < 0) {
287 glob_error = -1;
288 if (!asked_for_wakeup) {
289 asked_for_wakeup = 1;
290 sched_task(wakeup_task, (voidp) mf, (voidp) m);
295 return glob_error;
299 static void
300 amfs_nfsx_cont(int rc, int term, voidp closure)
302 mntfs *mf = (mntfs *) closure;
303 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
304 amfs_nfsx_mnt *n = nx->nx_try;
306 n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
307 mf->mf_flags &= ~MFF_ERROR;
310 * Wakeup anything waiting for this mount
312 wakeup((voidp) n->n_mnt);
314 if (rc || term) {
315 if (term) {
317 * Not sure what to do for an error code.
319 plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
320 n->n_error = EIO;
321 } else {
323 * Check for exit status
325 errno = rc; /* XXX */
326 plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
327 n->n_error = rc;
329 free_mntfs(n->n_mnt);
330 n->n_mnt = new_mntfs();
331 n->n_mnt->mf_error = n->n_error;
332 n->n_mnt->mf_flags |= MFF_ERROR;
333 } else {
335 * The mount worked.
337 mf_mounted(n->n_mnt);
338 n->n_error = 0;
342 * Do the remaining bits
344 if (amfs_nfsx_fmount(mf) >= 0) {
345 wakeup((voidp) mf);
346 mf->mf_flags &= ~MFF_MOUNTING;
347 mf_mounted(mf);
352 static int
353 try_amfs_nfsx_mount(voidp mv)
355 mntfs *mf = (mntfs *) mv;
356 int error;
358 mf->mf_flags |= MFF_MOUNTING;
359 error = (*mf->mf_ops->fmount_fs) (mf);
360 mf->mf_flags &= ~MFF_MOUNTING;
362 return error;
366 static int
367 amfs_nfsx_remount(mntfs *mf, int fg)
369 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
370 amfs_nfsx_mnt *n;
371 int glob_error = -1;
373 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
374 mntfs *m = n->n_mnt;
375 if (n->n_error < 0) {
376 if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
377 int error = mkdirs(m->mf_mount, 0555);
378 if (!error)
379 m->mf_flags |= MFF_MKMNT;
385 * Iterate through the mntfs's and mount each filesystem
386 * which is not yet mounted.
388 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
389 mntfs *m = n->n_mnt;
390 if (n->n_error < 0) {
392 * Check fmount entry pt. exists
393 * and then mount...
395 if (!m->mf_ops->fmount_fs) {
396 n->n_error = EINVAL;
397 } else {
398 #ifdef DEBUG
399 dlog("calling underlying fmount on %s", m->mf_mount);
400 #endif /* DEBUG */
401 if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
402 m->mf_flags |= MFF_MOUNTING; /* XXX */
403 #ifdef DEBUG
404 dlog("backgrounding mount of \"%s\"", m->mf_info);
405 #endif /* DEBUG */
406 nx->nx_try = n;
407 run_task(try_amfs_nfsx_mount, (voidp) m, amfs_nfsx_cont, (voidp) mf);
408 n->n_error = -1;
409 return -1;
410 } else {
411 #ifdef DEBUG
412 dlog("foreground mount of \"%s\" ...", mf->mf_info);
413 #endif /* DEBUG */
414 n->n_error = (*m->mf_ops->fmount_fs) (m);
418 #ifdef DEBUG
419 if (n->n_error > 0) {
420 errno = n->n_error; /* XXX */
421 dlog("underlying fmount of %s failed: %m", m->mf_mount);
423 #endif /* DEBUG */
425 if (n->n_error == 0) {
426 glob_error = 0;
427 } else if (glob_error < 0) {
428 glob_error = n->n_error;
433 return glob_error < 0 ? 0 : glob_error;
437 static int
438 amfs_nfsx_fmount(mntfs *mf)
440 return amfs_nfsx_remount(mf, FALSE);
445 * Unmount an NFS hierarchy.
446 * Note that this is called in the foreground
447 * and so may hang under extremely rare conditions.
449 static int
450 amfs_nfsx_fumount(mntfs *mf)
452 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
453 amfs_nfsx_mnt *n;
454 int glob_error = 0;
457 * Iterate in reverse through the mntfs's and unmount each filesystem
458 * which is mounted.
460 for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
461 mntfs *m = n->n_mnt;
463 * If this node has not been messed with
464 * and there has been no error so far
465 * then try and unmount.
466 * If an error had occurred then zero
467 * the error code so that the remount
468 * only tries to unmount those nodes
469 * which had been successfully unmounted.
471 if (n->n_error == 0) {
472 #ifdef DEBUG
473 dlog("calling underlying fumount on %s", m->mf_mount);
474 #endif /* DEBUG */
475 n->n_error = (*m->mf_ops->fumount_fs) (m);
476 if (n->n_error) {
477 glob_error = n->n_error;
478 n->n_error = 0;
479 } else {
481 * Make sure remount gets this node
483 n->n_error = -1;
489 * If any unmounts failed then remount the
490 * whole lot...
492 if (glob_error) {
493 glob_error = amfs_nfsx_remount(mf, TRUE);
494 if (glob_error) {
495 errno = glob_error; /* XXX */
496 plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
498 glob_error = EBUSY;
499 } else {
501 * Remove all the mount points
503 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
504 mntfs *m = n->n_mnt;
505 am_node am;
508 * XXX: all the umounted handler needs is a
509 * mntfs pointer, so pass an am_node with the right
510 * pointer in it.
512 memset((voidp) &am, 0, sizeof(am));
513 am.am_mnt = m;
514 #ifdef DEBUG
515 dlog("calling underlying umounted on %s", m->mf_mount);
516 #endif /* DEBUG */
517 (*m->mf_ops->umounted) (&am);
519 if (n->n_error < 0) {
520 if (m->mf_ops->fs_flags & FS_MKMNT) {
521 (void) rmdirs(m->mf_mount);
522 m->mf_flags &= ~MFF_MKMNT;
525 free_mntfs(m);
526 n->n_mnt = 0;
527 n->n_error = -1;
531 return glob_error;