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