 |
|
|
| |
1 /*
2 * Copyright (c) 1997-2005 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 *
40 * File: am-utils/amd/amfs_nfsx.c
41 *
42 */
43
44 /*
45 * NFS hierarchical mounts
46 *
47 * TODO: Re-implement.
48 */
49
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amd.h>
55
56 /*
57 * The rfs field contains a list of mounts to be done from
58 * the remote host.
59 */
60 typedef struct amfs_nfsx_mnt {
61 mntfs *n_mnt;
62 int n_error;
63 } amfs_nfsx_mnt;
64
65 struct amfs_nfsx {
66 int nx_c; /* Number of elements in nx_v */
67 amfs_nfsx_mnt *nx_v; /* Underlying mounts */
68 amfs_nfsx_mnt *nx_try;
69 am_node *nx_mp;
70 };
71
72 /* forward definitions */
73 static char *amfs_nfsx_match(am_opts *fo);
74 static int amfs_nfsx_mount(am_node *am, mntfs *mf);
75 static int amfs_nfsx_umount(am_node *am, mntfs *mf);
76 static int amfs_nfsx_init(mntfs *mf);
77
78 /*
79 * Ops structure
80 */
81 am_ops amfs_nfsx_ops =
82 {
83 "nfsx",
84 amfs_nfsx_match,
85 amfs_nfsx_init,
86 amfs_nfsx_mount,
87 amfs_nfsx_umount,
88 amfs_error_lookup_child,
89 amfs_error_mount_child,
90 amfs_error_readdir,
91 0, /* amfs_nfsx_readlink */
92 0, /* amfs_nfsx_mounted */
93 0, /* amfs_nfsx_umounted */
94 find_nfs_srvr, /* XXX */
95 0, /* amfs_nfsx_get_wchan */
96 /* FS_UBACKGROUND| */ FS_AMQINFO, /* nfs_fs_flags */
97 #ifdef HAVE_FS_AUTOFS
98 AUTOFS_NFSX_FS_FLAGS,
99 #endif /* HAVE_FS_AUTOFS */
100 };
101
102
103 static char *
104 amfs_nfsx_match(am_opts *fo)
105 {
106 char *xmtab;
107 char *ptr;
108 int len;
109
110 if (!fo->opt_rfs) {
111 plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
112 return FALSE;
113 }
114
115 if (!fo->opt_rhost) {
116 plog(XLOG_USER, "amfs_nfsx: no remote host specified");
117 return FALSE;
118 }
119
120 /* set default sublink */
121 if (fo->opt_sublink == 0) {
122 ptr = strchr(fo->opt_rfs, ',');
123 if (ptr && ptr > (fo->opt_rfs + 1))
124 fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
125 }
126
127 /*
128 * Remove trailing ",..." from ${fs}
129 * After deslashifying, overwrite the end of ${fs} with "/"
130 * to make sure it is unique.
131 */
132 if ((ptr = strchr(fo->opt_fs, ',')))
133 *ptr = '\0';
134 deslashify(fo->opt_fs);
135
136 /*
137 * Bump string length to allow trailing /
138 */
139 len = strlen(fo->opt_fs);
140 fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
141 ptr = fo->opt_fs + len;
142
143 /*
144 * Make unique...
145 */
146 *ptr++ = '/';
147 *ptr = '\0';
148
149 /*
150 * Determine magic cookie to put in mtab
151 */
152 xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
153 dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
154 fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
155
156 return xmtab;
157 }
158
159
160 static void
161 amfs_nfsx_prfree(opaque_t 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
177 static int
178 amfs_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 /* nothing */;
217
218 nx = ALLOC(struct amfs_nfsx);
219 mf->mf_private = (opaque_t) nx;
220 mf->mf_prfree = amfs_nfsx_prfree;
221
222 nx->nx_c = i - 1; /* i-1 because we don't want the prefix */
223 nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt));
224 nx->nx_mp = 0;
225 {
226 char *mp = 0;
227 char *xinfo = 0;
228 char *fs = mf->mf_fo->opt_fs;
229 char *rfs = 0;
230 for (i = 0; i < nx->nx_c; i++) {
231 char *path = ivec[i + 1];
232 rfs = str3cat(rfs, pref, "/", path);
233 /*
234 * Determine the mount point.
235 * If this is the root, then don't remove
236 * the trailing slash to avoid mntfs name clashes.
237 */
238 mp = str3cat(mp, fs, "/", rfs);
239 normalize_slash(mp);
240 deslashify(mp);
241 /*
242 * Determine the mount info
243 */
244 xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
245 normalize_slash(xinfo);
246 if (pref[1] != '\0')
247 deslashify(xinfo);
248 dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
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 /* propagate the on_autofs flag */
252 nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS;
253 }
254 if (rfs)
255 XFREE(rfs);
256 if (mp)
257 XFREE(mp);
258 if (xinfo)
259 XFREE(xinfo);
260 }
261
262 XFREE(ivec);
263 errexit:
264 if (info)
265 XFREE(info);
266 if (error)
267 return error;
268 }
269
270 /*
271 * Iterate through the mntfs's and call
272 * the underlying init routine on each
273 */
274 glob_error = 0;
275
276 for (i = 0; i < nx->nx_c; i++) {
277 amfs_nfsx_mnt *n = &nx->nx_v[i];
278 mntfs *m = n->n_mnt;
279 int error = 0;
280 if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART))
281 error = m->mf_ops->fs_init(m);
282 /*
283 * if you just "return error" here, you will have made a failure
284 * in any submounts to fail the whole group. There was old unused code
285 * here before.
286 */
287 if (error > 0)
288 n->n_error = error;
289
290 else if (error < 0) {
291 glob_error = -1;
292 if (!asked_for_wakeup) {
293 asked_for_wakeup = 1;
294 sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m));
295 }
296 }
297 }
298
299 return glob_error;
300 }
301
302
303 static void
304 amfs_nfsx_cont(int rc, int term, opaque_t arg)
305 {
306 mntfs *mf = (mntfs *) arg;
307 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
308 am_node *mp = nx->nx_mp;
309 amfs_nfsx_mnt *n = nx->nx_try;
310
311 n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
312 mf->mf_flags &= ~MFF_ERROR;
313
314 /*
315 * Wakeup anything waiting for this mount
316 */
317 wakeup(get_mntfs_wchan(n->n_mnt));
318
319 if (rc || term) {
320 if (term) {
321 /*
322 * Not sure what to do for an error code.
323 */
324 plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
325 n->n_error = EIO;
326 } else {
327 /*
328 * Check for exit status
329 */
330 errno = rc; /* XXX */
331 plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
332 n->n_error = rc;
333 }
334 free_mntfs(n->n_mnt);
335 n->n_mnt = new_mntfs();
336 n->n_mnt->mf_error = n->n_error;
337 n->n_mnt->mf_flags |= MFF_ERROR;
338 } else {
339 /*
340 * The mount worked.
341 */
342 mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */
343 n->n_error = 0;
344 }
345
346 /*
347 * Do the remaining bits
348 */
349 if (amfs_nfsx_mount(mp, mf) >= 0)
350 wakeup(get_mntfs_wchan(mf));
351 }
352
353
354 static int
355 try_amfs_nfsx_mount(opaque_t mv)
356 {
357 mntfs *mf = (mntfs *) mv;
358 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
359 am_node *mp = nx->nx_mp;
360 int error;
361
362 error = mf->mf_ops->mount_fs(mp, mf);
363
364 return error;
365 }
366
367
368 static int
369 amfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
370 {
371 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
372 amfs_nfsx_mnt *n;
373 int glob_error = -1;
374
375 /* Save the am_node pointer for later use */
376 nx->nx_mp = am;
377
378 /*
379 * Iterate through the mntfs's and mount each filesystem
380 * which is not yet mounted.
381 */
382 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
383 mntfs *m = n->n_mnt;
384
385 if (m->mf_flags & MFF_MOUNTING)
386 break;
387
388 if (m->mf_flags & MFF_MOUNTED) {
389 mf_mounted(m, FALSE); /* FALSE => don't free the m->am_opts */
390 n->n_error = glob_error = 0;
391 continue;
392 }
393
394 if (n->n_error < 0) {
395 /* Create the mountpoint, if and as required */
396 if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) {
397 if (!mkdirs(m->mf_mount, 0555))
398 m->mf_flags |= MFF_MKMNT;
399 }
400
401 dlog("calling underlying mount on %s", m->mf_mount);
402 if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) {
403 m->mf_flags |= MFF_MOUNTING;
404 dlog("backgrounding mount of \"%s\"", m->mf_info);
405 nx->nx_try = n;
406 run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf);
407 n->n_error = -1;
408 return -1;
409 } else {
410 dlog("foreground mount of \"%s\" ...", mf->mf_info);
411 n->n_error = m->mf_ops->mount_fs(am, m);
412 }
413
414 if (n->n_error > 0)
415 dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error));
416
417 if (n->n_error == 0) {
418 glob_error = 0;
419 } else if (glob_error < 0) {
420 glob_error = n->n_error;
421 }
422 }
423 }
424
425 return glob_error < 0 ? 0 : glob_error;
426 }
427
428
429 static int
430 amfs_nfsx_mount(am_node *am, mntfs *mf)
431 {
432 return amfs_nfsx_remount(am, mf, FALSE);
433 }
434
435
436 /*
437 * Unmount an NFS hierarchy.
438 * Note that this is called in the foreground
439 * and so may hang under extremely rare conditions.
440 */
441 static int
442 amfs_nfsx_umount(am_node *am, mntfs *mf)
443 {
444 struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
445 amfs_nfsx_mnt *n;
446 int glob_error = 0;
447
448 /*
449 * Iterate in reverse through the mntfs's and unmount each filesystem
450 * which is mounted.
451 */
452 for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
453 mntfs *m = n->n_mnt;
454 /*
455 * If this node has not been messed with
456 * and there has been no error so far
457 * then try and unmount.
458 * If an error had occurred then zero
459 * the error code so that the remount
460 * only tries to unmount those nodes
461 * which had been successfully unmounted.
462 */
463 if (n->n_error == 0) {
464 dlog("calling underlying fumount on %s", m->mf_mount);
465 n->n_error = m->mf_ops->umount_fs(am, m);
466 if (n->n_error) {
467 glob_error = n->n_error;
468 n->n_error = 0;
469 } else {
470 /*
471 * Make sure remount gets this node
472 */
473 n->n_error = -1;
474 }
475 }
476 }
477
478 /*
479 * If any unmounts failed then remount the
480 * whole lot...
481 */
482 if (glob_error) {
483 glob_error = amfs_nfsx_remount(am, mf, TRUE);
484 if (glob_error) {
485 errno = glob_error; /* XXX */
486 plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
487 }
488 glob_error = EBUSY;
489 } else {
490 /*
491 * Remove all the mount points
492 */
493 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
494 mntfs *m = n->n_mnt;
495 dlog("calling underlying umounted on %s", m->mf_mount);
496 if (m->mf_ops->umounted)
497 m->mf_ops->umounted(m);
498
499 if (n->n_error < 0) {
500 if (m->mf_fsflags & FS_MKMNT) {
501 (void) rmdirs(m->mf_mount);
502 m->mf_flags &= ~MFF_MKMNT;
503 }
504 }
505 free_mntfs(m);
506 n->n_mnt = 0;
507 n->n_error = -1;
508 }
509 }
510
511 return glob_error;
512 }
513