amfs_nfsx.c revision 310490
1/*
2 * Copyright (c) 1997-2014 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. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *
36 * File: am-utils/amd/amfs_nfsx.c
37 *
38 */
39
40/*
41 * NFS hierarchical mounts
42 *
43 * TODO: Re-implement.
44 */
45
46#ifdef HAVE_CONFIG_H
47# include <config.h>
48#endif /* HAVE_CONFIG_H */
49#include <am_defs.h>
50#include <amd.h>
51
52/*
53 * The rfs field contains a list of mounts to be done from
54 * the remote host.
55 */
56typedef struct amfs_nfsx_mnt {
57  mntfs *n_mnt;
58  int n_error;
59} amfs_nfsx_mnt;
60
61struct amfs_nfsx {
62  int nx_c;			/* Number of elements in nx_v */
63  amfs_nfsx_mnt *nx_v;		/* Underlying mounts */
64  amfs_nfsx_mnt *nx_try;
65  am_node *nx_mp;
66};
67
68/* forward definitions */
69static char *amfs_nfsx_match(am_opts *fo);
70static int amfs_nfsx_mount(am_node *am, mntfs *mf);
71static int amfs_nfsx_umount(am_node *am, mntfs *mf);
72static int amfs_nfsx_init(mntfs *mf);
73
74/*
75 * Ops structure
76 */
77am_ops amfs_nfsx_ops =
78{
79  "nfsx",
80  amfs_nfsx_match,
81  amfs_nfsx_init,
82  amfs_nfsx_mount,
83  amfs_nfsx_umount,
84  amfs_error_lookup_child,
85  amfs_error_mount_child,
86  amfs_error_readdir,
87  0,				/* amfs_nfsx_readlink */
88  0,				/* amfs_nfsx_mounted */
89  0,				/* amfs_nfsx_umounted */
90  find_nfs_srvr,		/* XXX */
91  0,				/* amfs_nfsx_get_wchan */
92  /* FS_UBACKGROUND| */ FS_AMQINFO,	/* nfs_fs_flags */
93#ifdef HAVE_FS_AUTOFS
94  AUTOFS_NFSX_FS_FLAGS,
95#endif /* HAVE_FS_AUTOFS */
96};
97
98
99static char *
100amfs_nfsx_match(am_opts *fo)
101{
102  char *xmtab;
103  char *ptr;
104  int len;
105
106  if (!fo->opt_rfs) {
107    plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
108    return FALSE;
109  }
110
111  if (!fo->opt_rhost) {
112    plog(XLOG_USER, "amfs_nfsx: no remote host specified");
113    return FALSE;
114  }
115
116  /* set default sublink */
117  if (fo->opt_sublink == NULL || fo->opt_sublink[0] == '\0') {
118    ptr = strchr(fo->opt_rfs, ',');
119    if (ptr && ptr > (fo->opt_rfs + 1))
120      fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
121  }
122
123  /*
124   * Remove trailing ",..." from ${fs}
125   * After deslashifying, overwrite the end of ${fs} with "/"
126   * to make sure it is unique.
127   */
128  if ((ptr = strchr(fo->opt_fs, ',')))
129    *ptr = '\0';
130  deslashify(fo->opt_fs);
131
132  /*
133   * Bump string length to allow trailing /
134   */
135  len = strlen(fo->opt_fs);
136  fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
137  ptr = fo->opt_fs + len;
138
139  /*
140   * Make unique...
141   */
142  *ptr++ = '/';
143  *ptr = '\0';
144
145  /*
146   * Determine magic cookie to put in mtab
147   */
148  xmtab = str3cat((char *) NULL, fo->opt_rhost, ":", fo->opt_rfs);
149  dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
150       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
151
152  return xmtab;
153}
154
155
156static void
157amfs_nfsx_prfree(opaque_t vp)
158{
159  struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
160  int i;
161
162  for (i = 0; i < nx->nx_c; i++) {
163    mntfs *m = nx->nx_v[i].n_mnt;
164    if (m)
165      free_mntfs(m);
166  }
167
168  XFREE(nx->nx_v);
169  XFREE(nx);
170}
171
172
173static int
174amfs_nfsx_init(mntfs *mf)
175{
176  /*
177   * mf_info has the form:
178   *   host:/prefix/path,sub,sub,sub
179   */
180  int i;
181  int glob_error;
182  struct amfs_nfsx *nx;
183  int asked_for_wakeup = 0;
184
185  nx = (struct amfs_nfsx *) mf->mf_private;
186
187  if (nx == 0) {
188    char **ivec;
189    char *info = NULL;
190    char *host;
191    char *pref;
192    int error = 0;
193
194    info = xstrdup(mf->mf_info);
195    if (info == NULL)
196      return errno;
197
198    host = strchr(info, ':');
199    if (!host) {
200      error = EINVAL;
201      goto errexit;
202    }
203    pref = host + 1;
204    host = info;
205
206    /*
207     * Split the prefix off from the suffices
208     */
209    ivec = strsplit(pref, ',', '\'');
210
211    /*
212     * Count array size
213     */
214    for (i = 0; ivec[i]; i++)
215      /* nothing */;
216
217    nx = ALLOC(struct amfs_nfsx);
218    mf->mf_private = (opaque_t) 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    nx->nx_mp = NULL;
224    {
225      char *mp = NULL;
226      char *xinfo = NULL;
227      char *fs = mf->mf_fo->opt_fs;
228      char *rfs = NULL;
229      for (i = 0; i < nx->nx_c; i++) {
230	char *path = ivec[i + 1];
231	rfs = str3cat(rfs, pref, "/", path);
232	/*
233	 * Determine the mount point.
234	 * If this is the root, then don't remove
235	 * the trailing slash to avoid mntfs name clashes.
236	 */
237	mp = str3cat(mp, fs, "/", rfs);
238	normalize_slash(mp);
239	deslashify(mp);
240	/*
241	 * Determine the mount info
242	 */
243	xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
244	normalize_slash(xinfo);
245	if (pref[1] != '\0')
246	  deslashify(xinfo);
247	dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
248	nx->nx_v[i].n_error = -1;
249	nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
250	/* propagate the on_autofs flag */
251	nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS;
252      }
253      XFREE(rfs);
254      XFREE(mp);
255      XFREE(xinfo);
256    }
257
258    XFREE(ivec);
259  errexit:
260    XFREE(info);
261    if (error)
262      return error;
263  }
264
265  /*
266   * Iterate through the mntfs's and call
267   * the underlying init routine on each
268   */
269  glob_error = 0;
270
271  for (i = 0; i < nx->nx_c; i++) {
272    amfs_nfsx_mnt *n = &nx->nx_v[i];
273    mntfs *m = n->n_mnt;
274    int error = 0;
275    if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART))
276      error = m->mf_ops->fs_init(m);
277    /*
278     * if you just "return error" here, you will have made a failure
279     * in any submounts to fail the whole group.  There was old unused code
280     * here before.
281     */
282    if (error > 0)
283      n->n_error = error;
284
285    else if (error < 0) {
286      glob_error = -1;
287      if (!asked_for_wakeup) {
288	asked_for_wakeup = 1;
289	sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m));
290      }
291    }
292  }
293
294  return glob_error;
295}
296
297
298static void
299amfs_nfsx_cont(int rc, int term, opaque_t arg)
300{
301  mntfs *mf = (mntfs *) arg;
302  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
303  am_node *mp = nx->nx_mp;
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(get_mntfs_wchan(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, FALSE); /* FALSE => don't free the n_mnt->am_opts */
338    n->n_error = 0;
339  }
340
341  /*
342   * Do the remaining bits
343   */
344  if (amfs_nfsx_mount(mp, mf) >= 0)
345    wakeup(get_mntfs_wchan(mf));
346}
347
348
349static int
350try_amfs_nfsx_mount(opaque_t mv)
351{
352  mntfs *mf = (mntfs *) mv;
353  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
354  am_node *mp = nx->nx_mp;
355  int error;
356
357  error = mf->mf_ops->mount_fs(mp, mf);
358
359  return error;
360}
361
362
363static int
364amfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
365{
366  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
367  amfs_nfsx_mnt *n;
368  int glob_error = -1;
369
370  /* Save the am_node pointer for later use */
371  nx->nx_mp = am;
372
373  /*
374   * Iterate through the mntfs's and mount each filesystem
375   * which is not yet mounted.
376   */
377  for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
378    mntfs *m = n->n_mnt;
379
380    if (m->mf_flags & MFF_MOUNTING)
381      break;
382
383    if (m->mf_flags & MFF_MOUNTED) {
384      mf_mounted(m, FALSE);	/* FALSE => don't free the m->am_opts */
385      n->n_error = glob_error = 0;
386      continue;
387    }
388
389    if (n->n_error < 0) {
390      /* Create the mountpoint, if and as required */
391      if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) {
392	if (!mkdirs(m->mf_mount, 0555))
393	  m->mf_flags |= MFF_MKMNT;
394      }
395
396      dlog("calling underlying mount on %s", m->mf_mount);
397      if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) {
398	m->mf_flags |= MFF_MOUNTING;
399	dlog("backgrounding mount of \"%s\"", m->mf_info);
400	nx->nx_try = n;
401	run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf);
402	n->n_error = -1;
403	return -1;
404      } else {
405	dlog("foreground mount of \"%s\" ...", mf->mf_info);
406	n->n_error = m->mf_ops->mount_fs(am, m);
407      }
408
409      if (n->n_error > 0)
410	dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error));
411
412      if (n->n_error == 0) {
413	glob_error = 0;
414      } else if (glob_error < 0) {
415	glob_error = n->n_error;
416      }
417    }
418  }
419
420  return glob_error < 0 ? 0 : glob_error;
421}
422
423
424static int
425amfs_nfsx_mount(am_node *am, mntfs *mf)
426{
427  return amfs_nfsx_remount(am, mf, FALSE);
428}
429
430
431/*
432 * Unmount an NFS hierarchy.
433 * Note that this is called in the foreground
434 * and so may hang under extremely rare conditions.
435 */
436static int
437amfs_nfsx_umount(am_node *am, mntfs *mf)
438{
439  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
440  amfs_nfsx_mnt *n;
441  int glob_error = 0;
442
443  /*
444   * Iterate in reverse through the mntfs's and unmount each filesystem
445   * which is mounted.
446   */
447  for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
448    mntfs *m = n->n_mnt;
449    /*
450     * If this node has not been messed with
451     * and there has been no error so far
452     * then try and unmount.
453     * If an error had occurred then zero
454     * the error code so that the remount
455     * only tries to unmount those nodes
456     * which had been successfully unmounted.
457     */
458    if (n->n_error == 0) {
459      dlog("calling underlying fumount on %s", m->mf_mount);
460      n->n_error = m->mf_ops->umount_fs(am, m);
461      if (n->n_error) {
462	glob_error = n->n_error;
463	n->n_error = 0;
464      } else {
465	/*
466	 * Make sure remount gets this node
467	 */
468	n->n_error = -1;
469      }
470    }
471  }
472
473  /*
474   * If any unmounts failed then remount the
475   * whole lot...
476   */
477  if (glob_error) {
478    glob_error = amfs_nfsx_remount(am, mf, TRUE);
479    if (glob_error) {
480      errno = glob_error;	/* XXX */
481      plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
482    }
483    glob_error = EBUSY;
484  } else {
485    /*
486     * Remove all the mount points
487     */
488    for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
489      mntfs *m = n->n_mnt;
490      dlog("calling underlying umounted on %s", m->mf_mount);
491      if (m->mf_ops->umounted)
492	m->mf_ops->umounted(m);
493
494      if (n->n_error < 0) {
495	if (m->mf_fsflags & FS_MKMNT) {
496	  (void) rmdirs(m->mf_mount);
497	  m->mf_flags &= ~MFF_MKMNT;
498	}
499      }
500      free_mntfs(m);
501      n->n_mnt = NULL;
502      n->n_error = -1;
503    }
504  }
505
506  return glob_error;
507}
508