4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/amfs_nfsx.c
47 * NFS hierarchical mounts
54 #endif /* HAVE_CONFIG_H */
59 * The rfs field contains a list of mounts to be done from
62 typedef struct amfs_nfsx_mnt
{
68 int nx_c
; /* Number of elements in nx_v */
69 amfs_nfsx_mnt
*nx_v
; /* Underlying mounts */
70 amfs_nfsx_mnt
*nx_try
;
74 /* forward definitions */
75 static char *amfs_nfsx_match(am_opts
*fo
);
76 static int amfs_nfsx_mount(am_node
*am
, mntfs
*mf
);
77 static int amfs_nfsx_umount(am_node
*am
, mntfs
*mf
);
78 static int amfs_nfsx_init(mntfs
*mf
);
83 am_ops amfs_nfsx_ops
=
90 amfs_error_lookup_child
,
91 amfs_error_mount_child
,
93 0, /* amfs_nfsx_readlink */
94 0, /* amfs_nfsx_mounted */
95 0, /* amfs_nfsx_umounted */
96 find_nfs_srvr
, /* XXX */
97 0, /* amfs_nfsx_get_wchan */
98 /* FS_UBACKGROUND| */ FS_AMQINFO
, /* nfs_fs_flags */
100 AUTOFS_NFSX_FS_FLAGS
,
101 #endif /* HAVE_FS_AUTOFS */
106 amfs_nfsx_match(am_opts
*fo
)
113 plog(XLOG_USER
, "amfs_nfsx: no remote filesystem specified");
117 if (!fo
->opt_rhost
) {
118 plog(XLOG_USER
, "amfs_nfsx: no remote host specified");
122 /* set default sublink */
123 if (fo
->opt_sublink
== NULL
|| fo
->opt_sublink
[0] == '\0') {
124 ptr
= strchr(fo
->opt_rfs
, ',');
125 if (ptr
&& ptr
> (fo
->opt_rfs
+ 1))
126 fo
->opt_sublink
= strnsave(fo
->opt_rfs
+ 1, ptr
- fo
->opt_rfs
- 1);
130 * Remove trailing ",..." from ${fs}
131 * After deslashifying, overwrite the end of ${fs} with "/"
132 * to make sure it is unique.
134 if ((ptr
= strchr(fo
->opt_fs
, ',')))
136 deslashify(fo
->opt_fs
);
139 * Bump string length to allow trailing /
141 len
= strlen(fo
->opt_fs
);
142 fo
->opt_fs
= xrealloc(fo
->opt_fs
, len
+ 1 + 1);
143 ptr
= fo
->opt_fs
+ len
;
152 * Determine magic cookie to put in mtab
154 xmtab
= str3cat((char *) NULL
, fo
->opt_rhost
, ":", fo
->opt_rfs
);
155 dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
156 fo
->opt_rhost
, fo
->opt_rfs
, fo
->opt_fs
);
163 amfs_nfsx_prfree(opaque_t vp
)
165 struct amfs_nfsx
*nx
= (struct amfs_nfsx
*) vp
;
168 for (i
= 0; i
< nx
->nx_c
; i
++) {
169 mntfs
*m
= nx
->nx_v
[i
].n_mnt
;
180 amfs_nfsx_init(mntfs
*mf
)
183 * mf_info has the form:
184 * host:/prefix/path,sub,sub,sub
188 struct amfs_nfsx
*nx
;
189 int asked_for_wakeup
= 0;
191 nx
= (struct amfs_nfsx
*) mf
->mf_private
;
200 info
= strdup(mf
->mf_info
);
201 host
= strchr(info
, ':');
210 * Split the prefix off from the suffices
212 ivec
= strsplit(pref
, ',', '\'');
217 for (i
= 0; ivec
[i
]; i
++)
220 nx
= ALLOC(struct amfs_nfsx
);
221 mf
->mf_private
= (opaque_t
) nx
;
222 mf
->mf_prfree
= amfs_nfsx_prfree
;
224 nx
->nx_c
= i
- 1; /* i-1 because we don't want the prefix */
225 nx
->nx_v
= (amfs_nfsx_mnt
*) xmalloc(nx
->nx_c
* sizeof(amfs_nfsx_mnt
));
230 char *fs
= mf
->mf_fo
->opt_fs
;
232 for (i
= 0; i
< nx
->nx_c
; i
++) {
233 char *path
= ivec
[i
+ 1];
234 rfs
= str3cat(rfs
, pref
, "/", path
);
236 * Determine the mount point.
237 * If this is the root, then don't remove
238 * the trailing slash to avoid mntfs name clashes.
240 mp
= str3cat(mp
, fs
, "/", rfs
);
244 * Determine the mount info
246 xinfo
= str3cat(xinfo
, host
, *path
== '/' ? "" : "/", path
);
247 normalize_slash(xinfo
);
250 dlog("amfs_nfsx: init mount for %s on %s", xinfo
, mp
);
251 nx
->nx_v
[i
].n_error
= -1;
252 nx
->nx_v
[i
].n_mnt
= find_mntfs(&nfs_ops
, mf
->mf_fo
, mp
, xinfo
, "", mf
->mf_mopts
, mf
->mf_remopts
);
253 /* propagate the on_autofs flag */
254 nx
->nx_v
[i
].n_mnt
->mf_flags
|= mf
->mf_flags
& MFF_ON_AUTOFS
;
273 * Iterate through the mntfs's and call
274 * the underlying init routine on each
278 for (i
= 0; i
< nx
->nx_c
; i
++) {
279 amfs_nfsx_mnt
*n
= &nx
->nx_v
[i
];
282 if (m
->mf_ops
->fs_init
&& !(mf
->mf_flags
& MFF_RESTART
))
283 error
= m
->mf_ops
->fs_init(m
);
285 * if you just "return error" here, you will have made a failure
286 * in any submounts to fail the whole group. There was old unused code
292 else if (error
< 0) {
294 if (!asked_for_wakeup
) {
295 asked_for_wakeup
= 1;
296 sched_task(wakeup_task
, (opaque_t
) mf
, get_mntfs_wchan(m
));
306 amfs_nfsx_cont(int rc
, int term
, opaque_t arg
)
308 mntfs
*mf
= (mntfs
*) arg
;
309 struct amfs_nfsx
*nx
= (struct amfs_nfsx
*) mf
->mf_private
;
310 am_node
*mp
= nx
->nx_mp
;
311 amfs_nfsx_mnt
*n
= nx
->nx_try
;
313 n
->n_mnt
->mf_flags
&= ~(MFF_ERROR
| MFF_MOUNTING
);
314 mf
->mf_flags
&= ~MFF_ERROR
;
317 * Wakeup anything waiting for this mount
319 wakeup(get_mntfs_wchan(n
->n_mnt
));
324 * Not sure what to do for an error code.
326 plog(XLOG_ERROR
, "mount for %s got signal %d", n
->n_mnt
->mf_mount
, term
);
330 * Check for exit status
332 errno
= rc
; /* XXX */
333 plog(XLOG_ERROR
, "%s: mount (amfs_nfsx_cont): %m", n
->n_mnt
->mf_mount
);
336 free_mntfs(n
->n_mnt
);
337 n
->n_mnt
= new_mntfs();
338 n
->n_mnt
->mf_error
= n
->n_error
;
339 n
->n_mnt
->mf_flags
|= MFF_ERROR
;
344 mf_mounted(n
->n_mnt
, FALSE
); /* FALSE => don't free the n_mnt->am_opts */
349 * Do the remaining bits
351 if (amfs_nfsx_mount(mp
, mf
) >= 0)
352 wakeup(get_mntfs_wchan(mf
));
357 try_amfs_nfsx_mount(opaque_t mv
)
359 mntfs
*mf
= (mntfs
*) mv
;
360 struct amfs_nfsx
*nx
= (struct amfs_nfsx
*) mf
->mf_private
;
361 am_node
*mp
= nx
->nx_mp
;
364 error
= mf
->mf_ops
->mount_fs(mp
, mf
);
371 amfs_nfsx_remount(am_node
*am
, mntfs
*mf
, int fg
)
373 struct amfs_nfsx
*nx
= (struct amfs_nfsx
*) mf
->mf_private
;
377 /* Save the am_node pointer for later use */
381 * Iterate through the mntfs's and mount each filesystem
382 * which is not yet mounted.
384 for (n
= nx
->nx_v
; n
< nx
->nx_v
+ nx
->nx_c
; n
++) {
387 if (m
->mf_flags
& MFF_MOUNTING
)
390 if (m
->mf_flags
& MFF_MOUNTED
) {
391 mf_mounted(m
, FALSE
); /* FALSE => don't free the m->am_opts */
392 n
->n_error
= glob_error
= 0;
396 if (n
->n_error
< 0) {
397 /* Create the mountpoint, if and as required */
398 if (!(m
->mf_flags
& MFF_MKMNT
) && m
->mf_fsflags
& FS_MKMNT
) {
399 if (!mkdirs(m
->mf_mount
, 0555))
400 m
->mf_flags
|= MFF_MKMNT
;
403 dlog("calling underlying mount on %s", m
->mf_mount
);
404 if (!fg
&& foreground
&& (m
->mf_fsflags
& FS_MBACKGROUND
)) {
405 m
->mf_flags
|= MFF_MOUNTING
;
406 dlog("backgrounding mount of \"%s\"", m
->mf_info
);
408 run_task(try_amfs_nfsx_mount
, (opaque_t
) m
, amfs_nfsx_cont
, (opaque_t
) mf
);
412 dlog("foreground mount of \"%s\" ...", mf
->mf_info
);
413 n
->n_error
= m
->mf_ops
->mount_fs(am
, m
);
417 dlog("underlying fmount of %s failed: %s", m
->mf_mount
, strerror(n
->n_error
));
419 if (n
->n_error
== 0) {
421 } else if (glob_error
< 0) {
422 glob_error
= n
->n_error
;
427 return glob_error
< 0 ? 0 : glob_error
;
432 amfs_nfsx_mount(am_node
*am
, mntfs
*mf
)
434 return amfs_nfsx_remount(am
, mf
, FALSE
);
439 * Unmount an NFS hierarchy.
440 * Note that this is called in the foreground
441 * and so may hang under extremely rare conditions.
444 amfs_nfsx_umount(am_node
*am
, mntfs
*mf
)
446 struct amfs_nfsx
*nx
= (struct amfs_nfsx
*) mf
->mf_private
;
451 * Iterate in reverse through the mntfs's and unmount each filesystem
454 for (n
= nx
->nx_v
+ nx
->nx_c
- 1; n
>= nx
->nx_v
; --n
) {
457 * If this node has not been messed with
458 * and there has been no error so far
459 * then try and unmount.
460 * If an error had occurred then zero
461 * the error code so that the remount
462 * only tries to unmount those nodes
463 * which had been successfully unmounted.
465 if (n
->n_error
== 0) {
466 dlog("calling underlying fumount on %s", m
->mf_mount
);
467 n
->n_error
= m
->mf_ops
->umount_fs(am
, m
);
469 glob_error
= n
->n_error
;
473 * Make sure remount gets this node
481 * If any unmounts failed then remount the
485 glob_error
= amfs_nfsx_remount(am
, mf
, TRUE
);
487 errno
= glob_error
; /* XXX */
488 plog(XLOG_USER
, "amfs_nfsx: remount of %s failed: %m", mf
->mf_mount
);
493 * Remove all the mount points
495 for (n
= nx
->nx_v
; n
< nx
->nx_v
+ nx
->nx_c
; n
++) {
497 dlog("calling underlying umounted on %s", m
->mf_mount
);
498 if (m
->mf_ops
->umounted
)
499 m
->mf_ops
->umounted(m
);
501 if (n
->n_error
< 0) {
502 if (m
->mf_fsflags
& FS_MKMNT
) {
503 (void) rmdirs(m
->mf_mount
);
504 m
->mf_flags
&= ~MFF_MKMNT
;