amfs_nfsx.c revision 119679
1/*
2 * Copyright (c) 1997-2003 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.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
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.
26 *
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.
38 *
39 *      %W% (Berkeley) %G%
40 *
41 * $Id: amfs_nfsx.c,v 1.3.2.4 2002/12/27 22:44:32 ezk Exp $
42 *
43 */
44
45/*
46 * NFS hierarchical mounts
47 *
48 * TODO: Re-implement.
49 */
50
51#ifdef HAVE_CONFIG_H
52# include <config.h>
53#endif /* HAVE_CONFIG_H */
54#include <am_defs.h>
55#include <amd.h>
56
57/*
58 * The rfs field contains a list of mounts to be done from
59 * the remote host.
60 */
61typedef struct amfs_nfsx_mnt {
62  mntfs *n_mnt;
63  int n_error;
64} amfs_nfsx_mnt;
65
66struct 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;
70};
71
72/* forward definitions */
73static char *amfs_nfsx_match(am_opts *fo);
74static int amfs_nfsx_fmount (mntfs *);
75static int amfs_nfsx_fmount(mntfs *mf);
76static int amfs_nfsx_fumount(mntfs *mf);
77static int amfs_nfsx_init(mntfs *mf);
78
79/*
80 * Ops structure
81 */
82am_ops amfs_nfsx_ops =
83{
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
98};
99
100
101static char *
102amfs_nfsx_match(am_opts *fo)
103{
104  char *xmtab;
105  char *ptr;
106  int len;
107
108  if (!fo->opt_rfs) {
109    plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
110    return FALSE;
111  }
112
113  if (!fo->opt_rhost) {
114    plog(XLOG_USER, "amfs_nfsx: no remote host specified");
115    return FALSE;
116  }
117
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);
123  }
124
125  /*
126   * Remove trailing ",..." from ${fs}
127   * After deslashifying, overwrite the end of ${fs} with "/"
128   * to make sure it is unique.
129   */
130  if ((ptr = strchr(fo->opt_fs, ',')))
131    *ptr = '\0';
132  deslashify(fo->opt_fs);
133
134  /*
135   * Bump string length to allow trailing /
136   */
137  len = strlen(fo->opt_fs);
138  fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
139  ptr = fo->opt_fs + len;
140
141  /*
142   * Make unique...
143   */
144  *ptr++ = '/';
145  *ptr = '\0';
146
147  /*
148   * Determine magic cookie to put in mtab
149   */
150  xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
151#ifdef DEBUG
152  dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
153       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
154#endif /* DEBUG */
155
156  return xmtab;
157}
158
159
160static void
161amfs_nfsx_prfree(voidp vp)
162{
163  struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
164  int i;
165
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);
170  }
171
172  XFREE(nx->nx_v);
173  XFREE(nx);
174}
175
176
177static int
178amfs_nfsx_init(mntfs *mf)
179{
180  /*
181   * mf_info has the form:
182   *   host:/prefix/path,sub,sub,sub
183   */
184  int i;
185  int glob_error;
186  struct amfs_nfsx *nx;
187  int asked_for_wakeup = 0;
188
189  nx = (struct amfs_nfsx *) mf->mf_private;
190
191  if (nx == 0) {
192    char **ivec;
193    char *info = 0;
194    char *host;
195    char *pref;
196    int error = 0;
197
198    info = strdup(mf->mf_info);
199    host = strchr(info, ':');
200    if (!host) {
201      error = EINVAL;
202      goto errexit;
203    }
204    pref = host +1;
205    host = info;
206
207    /*
208     * Split the prefix off from the suffices
209     */
210    ivec = strsplit(pref, ',', '\'');
211
212    /*
213     * Count array size
214     */
215    for (i = 0; ivec[i]; i++) ;
216
217    nx = ALLOC(struct amfs_nfsx);
218    mf->mf_private = (voidp) nx;
219    mf->mf_prfree = amfs_nfsx_prfree;
220
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));
223    {
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);
231	/*
232	 * Determine the mount point.
233	 * If this is the root, then don't remove
234	 * the trailing slash to avoid mntfs name clashes.
235	 */
236	mp = str3cat(mp, fs, "/", rfs);
237	normalize_slash(mp);
238	deslashify(mp);
239	/*
240	 * Determine the mount info
241	 */
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);
251      }
252      if (rfs)
253	XFREE(rfs);
254      if (mp)
255	XFREE(mp);
256      if (xinfo)
257	XFREE(xinfo);
258    }
259
260    XFREE(ivec);
261  errexit:
262    if (info)
263      XFREE(info);
264    if (error)
265      return error;
266  }
267
268  /*
269   * Iterate through the mntfs's and call
270   * the underlying init routine on each
271   */
272  glob_error = 0;
273
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);
278    /*
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.
282     */
283    if (error > 0)
284      n->n_error = error;
285
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);
291      }
292    }
293  }
294
295  return glob_error;
296}
297
298
299static void
300amfs_nfsx_cont(int rc, int term, voidp closure)
301{
302  mntfs *mf = (mntfs *) closure;
303  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
304  amfs_nfsx_mnt *n = nx->nx_try;
305
306  n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
307  mf->mf_flags &= ~MFF_ERROR;
308
309  /*
310   * Wakeup anything waiting for this mount
311   */
312  wakeup((voidp) n->n_mnt);
313
314  if (rc || term) {
315    if (term) {
316      /*
317       * Not sure what to do for an error code.
318       */
319      plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
320      n->n_error = EIO;
321    } else {
322      /*
323       * Check for exit status
324       */
325      errno = rc;		/* XXX */
326      plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
327      n->n_error = rc;
328    }
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 {
334    /*
335     * The mount worked.
336     */
337    mf_mounted(n->n_mnt);
338    n->n_error = 0;
339  }
340
341  /*
342   * Do the remaining bits
343   */
344  if (amfs_nfsx_fmount(mf) >= 0) {
345    wakeup((voidp) mf);
346    mf->mf_flags &= ~MFF_MOUNTING;
347    mf_mounted(mf);
348  }
349}
350
351
352static int
353try_amfs_nfsx_mount(voidp mv)
354{
355  mntfs *mf = (mntfs *) mv;
356  int error;
357
358  mf->mf_flags |= MFF_MOUNTING;
359  error = (*mf->mf_ops->fmount_fs) (mf);
360  mf->mf_flags &= ~MFF_MOUNTING;
361
362  return error;
363}
364
365
366static int
367amfs_nfsx_remount(mntfs *mf, int fg)
368{
369  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
370  amfs_nfsx_mnt *n;
371  int glob_error = -1;
372
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;
380      }
381    }
382  }
383
384  /*
385   * Iterate through the mntfs's and mount each filesystem
386   * which is not yet mounted.
387   */
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) {
391      /*
392       * Check fmount entry pt. exists
393       * and then mount...
394       */
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);
415	}
416      }
417
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);
422      }
423#endif /* DEBUG */
424
425      if (n->n_error == 0) {
426	glob_error = 0;
427      } else if (glob_error < 0) {
428	glob_error = n->n_error;
429      }
430    }
431  }
432
433  return glob_error < 0 ? 0 : glob_error;
434}
435
436
437static int
438amfs_nfsx_fmount(mntfs *mf)
439{
440  return amfs_nfsx_remount(mf, FALSE);
441}
442
443
444/*
445 * Unmount an NFS hierarchy.
446 * Note that this is called in the foreground
447 * and so may hang under extremely rare conditions.
448 */
449static int
450amfs_nfsx_fumount(mntfs *mf)
451{
452  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
453  amfs_nfsx_mnt *n;
454  int glob_error = 0;
455
456  /*
457   * Iterate in reverse through the mntfs's and unmount each filesystem
458   * which is mounted.
459   */
460  for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
461    mntfs *m = n->n_mnt;
462    /*
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.
470     */
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 {
480	/*
481	 * Make sure remount gets this node
482	 */
483	n->n_error = -1;
484      }
485    }
486  }
487
488  /*
489   * If any unmounts failed then remount the
490   * whole lot...
491   */
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);
497    }
498    glob_error = EBUSY;
499  } else {
500    /*
501     * Remove all the mount points
502     */
503    for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
504      mntfs *m = n->n_mnt;
505      am_node am;
506
507      /*
508       * XXX: all the umounted handler needs is a
509       * mntfs pointer, so pass an am_node with the right
510       * pointer in it.
511       */
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);
518
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;
523	}
524      }
525      free_mntfs(m);
526      n->n_mnt = 0;
527      n->n_error = -1;
528    }
529  }
530
531  return glob_error;
532}
533