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.
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
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.
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
36 * File: am-utils/amd/amfs_nfsx.c
41 * NFS hierarchical mounts
48 #endif /* HAVE_CONFIG_H */
53 * The rfs field contains a list of mounts to be done from
56 typedef struct amfs_nfsx_mnt {
62 int nx_c; /* Number of elements in nx_v */
63 amfs_nfsx_mnt *nx_v; /* Underlying mounts */
64 amfs_nfsx_mnt *nx_try;
68 /* forward definitions */
69 static char *amfs_nfsx_match(am_opts *fo);
70 static int amfs_nfsx_mount(am_node *am, mntfs *mf);
71 static int amfs_nfsx_umount(am_node *am, mntfs *mf);
72 static int amfs_nfsx_init(mntfs *mf);
77 am_ops amfs_nfsx_ops =
84 amfs_error_lookup_child,
85 amfs_error_mount_child,
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 */
95 #endif /* HAVE_FS_AUTOFS */
100 amfs_nfsx_match(am_opts *fo)
107 plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
111 if (!fo->opt_rhost) {
112 plog(XLOG_USER, "amfs_nfsx: no remote host specified");
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);
124 * Remove trailing ",..." from ${fs}
125 * After deslashifying, overwrite the end of ${fs} with "/"
126 * to make sure it is unique.
128 if ((ptr = strchr(fo->opt_fs, ',')))
130 deslashify(fo->opt_fs);
133 * Bump string length to allow trailing /
135 len = strlen(fo->opt_fs);
136 fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
137 ptr = fo->opt_fs + len;
146 * Determine magic cookie to put in mtab
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);
157 amfs_nfsx_prfree(opaque_t vp)
159 struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
162 for (i = 0; i < nx->nx_c; i++) {
163 mntfs *m = nx->nx_v[i].n_mnt;
174 amfs_nfsx_init(mntfs *mf)
177 * mf_info has the form:
178 * host:/prefix/path,sub,sub,sub
182 struct amfs_nfsx *nx;
183 int asked_for_wakeup = 0;
185 nx = (struct amfs_nfsx *) mf->mf_private;
194 info = xstrdup(mf->mf_info);
198 host = strchr(info, ':');
207 * Split the prefix off from the suffices
209 ivec = strsplit(pref, ',', '\'');
214 for (i = 0; ivec[i]; i++)
217 nx = ALLOC(struct amfs_nfsx);
218 mf->mf_private = (opaque_t) 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));
227 char *fs = mf->mf_fo->opt_fs;
229 for (i = 0; i < nx->nx_c; i++) {
230 char *path = ivec[i + 1];
231 rfs = str3cat(rfs, pref, "/", path);
233 * Determine the mount point.
234 * If this is the root, then don't remove
235 * the trailing slash to avoid mntfs name clashes.
237 mp = str3cat(mp, fs, "/", rfs);
241 * Determine the mount info
243 xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
244 normalize_slash(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;
266 * Iterate through the mntfs's and call
267 * the underlying init routine on each
271 for (i = 0; i < nx->nx_c; i++) {
272 amfs_nfsx_mnt *n = &nx->nx_v[i];
275 if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART))
276 error = m->mf_ops->fs_init(m);
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
285 else if (error < 0) {
287 if (!asked_for_wakeup) {
288 asked_for_wakeup = 1;
289 sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m));
299 amfs_nfsx_cont(int rc, int term, opaque_t arg)
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;
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(get_mntfs_wchan(n->n_mnt));
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);
323 * Check for exit status
325 errno = rc; /* XXX */
326 plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
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;
337 mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */
342 * Do the remaining bits
344 if (amfs_nfsx_mount(mp, mf) >= 0)
345 wakeup(get_mntfs_wchan(mf));
350 try_amfs_nfsx_mount(opaque_t mv)
352 mntfs *mf = (mntfs *) mv;
353 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
354 am_node *mp = nx->nx_mp;
357 error = mf->mf_ops->mount_fs(mp, mf);
364 amfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
366 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
370 /* Save the am_node pointer for later use */
374 * Iterate through the mntfs's and mount each filesystem
375 * which is not yet mounted.
377 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
380 if (m->mf_flags & MFF_MOUNTING)
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;
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;
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);
401 run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf);
405 dlog("foreground mount of \"%s\" ...", mf->mf_info);
406 n->n_error = m->mf_ops->mount_fs(am, m);
410 dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error));
412 if (n->n_error == 0) {
414 } else if (glob_error < 0) {
415 glob_error = n->n_error;
420 return glob_error < 0 ? 0 : glob_error;
425 amfs_nfsx_mount(am_node *am, mntfs *mf)
427 return amfs_nfsx_remount(am, mf, FALSE);
432 * Unmount an NFS hierarchy.
433 * Note that this is called in the foreground
434 * and so may hang under extremely rare conditions.
437 amfs_nfsx_umount(am_node *am, mntfs *mf)
439 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
444 * Iterate in reverse through the mntfs's and unmount each filesystem
447 for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
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.
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);
462 glob_error = n->n_error;
466 * Make sure remount gets this node
474 * If any unmounts failed then remount the
478 glob_error = amfs_nfsx_remount(am, mf, TRUE);
480 errno = glob_error; /* XXX */
481 plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
486 * Remove all the mount points
488 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
490 dlog("calling underlying umounted on %s", m->mf_mount);
491 if (m->mf_ops->umounted)
492 m->mf_ops->umounted(m);
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;