xref: /netbsd-src/external/bsd/am-utils/dist/amd/amfs_nfsx.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /*	$NetBSD: amfs_nfsx.c,v 1.1.1.2 2009/03/20 20:26:49 christos Exp $	*/
2 
3 /*
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.
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. 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.
28  *
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
39  * SUCH DAMAGE.
40  *
41  *
42  * File: am-utils/amd/amfs_nfsx.c
43  *
44  */
45 
46 /*
47  * NFS hierarchical mounts
48  *
49  * TODO: Re-implement.
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 # include <config.h>
54 #endif /* HAVE_CONFIG_H */
55 #include <am_defs.h>
56 #include <amd.h>
57 
58 /*
59  * The rfs field contains a list of mounts to be done from
60  * the remote host.
61  */
62 typedef struct amfs_nfsx_mnt {
63   mntfs *n_mnt;
64   int n_error;
65 } amfs_nfsx_mnt;
66 
67 struct amfs_nfsx {
68   int nx_c;			/* Number of elements in nx_v */
69   amfs_nfsx_mnt *nx_v;		/* Underlying mounts */
70   amfs_nfsx_mnt *nx_try;
71   am_node *nx_mp;
72 };
73 
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);
79 
80 /*
81  * Ops structure
82  */
83 am_ops amfs_nfsx_ops =
84 {
85   "nfsx",
86   amfs_nfsx_match,
87   amfs_nfsx_init,
88   amfs_nfsx_mount,
89   amfs_nfsx_umount,
90   amfs_error_lookup_child,
91   amfs_error_mount_child,
92   amfs_error_readdir,
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 */
99 #ifdef HAVE_FS_AUTOFS
100   AUTOFS_NFSX_FS_FLAGS,
101 #endif /* HAVE_FS_AUTOFS */
102 };
103 
104 
105 static char *
106 amfs_nfsx_match(am_opts *fo)
107 {
108   char *xmtab;
109   char *ptr;
110   int len;
111 
112   if (!fo->opt_rfs) {
113     plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
114     return FALSE;
115   }
116 
117   if (!fo->opt_rhost) {
118     plog(XLOG_USER, "amfs_nfsx: no remote host specified");
119     return FALSE;
120   }
121 
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);
127   }
128 
129   /*
130    * Remove trailing ",..." from ${fs}
131    * After deslashifying, overwrite the end of ${fs} with "/"
132    * to make sure it is unique.
133    */
134   if ((ptr = strchr(fo->opt_fs, ',')))
135     *ptr = '\0';
136   deslashify(fo->opt_fs);
137 
138   /*
139    * Bump string length to allow trailing /
140    */
141   len = strlen(fo->opt_fs);
142   fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
143   ptr = fo->opt_fs + len;
144 
145   /*
146    * Make unique...
147    */
148   *ptr++ = '/';
149   *ptr = '\0';
150 
151   /*
152    * Determine magic cookie to put in mtab
153    */
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);
157 
158   return xmtab;
159 }
160 
161 
162 static void
163 amfs_nfsx_prfree(opaque_t vp)
164 {
165   struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
166   int i;
167 
168   for (i = 0; i < nx->nx_c; i++) {
169     mntfs *m = nx->nx_v[i].n_mnt;
170     if (m)
171       free_mntfs(m);
172   }
173 
174   XFREE(nx->nx_v);
175   XFREE(nx);
176 }
177 
178 
179 static int
180 amfs_nfsx_init(mntfs *mf)
181 {
182   /*
183    * mf_info has the form:
184    *   host:/prefix/path,sub,sub,sub
185    */
186   int i;
187   int glob_error;
188   struct amfs_nfsx *nx;
189   int asked_for_wakeup = 0;
190 
191   nx = (struct amfs_nfsx *) mf->mf_private;
192 
193   if (nx == 0) {
194     char **ivec;
195     char *info = NULL;
196     char *host;
197     char *pref;
198     int error = 0;
199 
200     info = strdup(mf->mf_info);
201     host = strchr(info, ':');
202     if (!host) {
203       error = EINVAL;
204       goto errexit;
205     }
206     pref = host + 1;
207     host = info;
208 
209     /*
210      * Split the prefix off from the suffices
211      */
212     ivec = strsplit(pref, ',', '\'');
213 
214     /*
215      * Count array size
216      */
217     for (i = 0; ivec[i]; i++)
218       /* nothing */;
219 
220     nx = ALLOC(struct amfs_nfsx);
221     mf->mf_private = (opaque_t) nx;
222     mf->mf_prfree = amfs_nfsx_prfree;
223 
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));
226     nx->nx_mp = NULL;
227     {
228       char *mp = NULL;
229       char *xinfo = NULL;
230       char *fs = mf->mf_fo->opt_fs;
231       char *rfs = NULL;
232       for (i = 0; i < nx->nx_c; i++) {
233 	char *path = ivec[i + 1];
234 	rfs = str3cat(rfs, pref, "/", path);
235 	/*
236 	 * Determine the mount point.
237 	 * If this is the root, then don't remove
238 	 * the trailing slash to avoid mntfs name clashes.
239 	 */
240 	mp = str3cat(mp, fs, "/", rfs);
241 	normalize_slash(mp);
242 	deslashify(mp);
243 	/*
244 	 * Determine the mount info
245 	 */
246 	xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
247 	normalize_slash(xinfo);
248 	if (pref[1] != '\0')
249 	  deslashify(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;
255       }
256       if (rfs)
257 	XFREE(rfs);
258       if (mp)
259 	XFREE(mp);
260       if (xinfo)
261 	XFREE(xinfo);
262     }
263 
264     XFREE(ivec);
265   errexit:
266     if (info)
267       XFREE(info);
268     if (error)
269       return error;
270   }
271 
272   /*
273    * Iterate through the mntfs's and call
274    * the underlying init routine on each
275    */
276   glob_error = 0;
277 
278   for (i = 0; i < nx->nx_c; i++) {
279     amfs_nfsx_mnt *n = &nx->nx_v[i];
280     mntfs *m = n->n_mnt;
281     int error = 0;
282     if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART))
283       error = m->mf_ops->fs_init(m);
284     /*
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
287      * here before.
288      */
289     if (error > 0)
290       n->n_error = error;
291 
292     else if (error < 0) {
293       glob_error = -1;
294       if (!asked_for_wakeup) {
295 	asked_for_wakeup = 1;
296 	sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m));
297       }
298     }
299   }
300 
301   return glob_error;
302 }
303 
304 
305 static void
306 amfs_nfsx_cont(int rc, int term, opaque_t arg)
307 {
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;
312 
313   n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
314   mf->mf_flags &= ~MFF_ERROR;
315 
316   /*
317    * Wakeup anything waiting for this mount
318    */
319   wakeup(get_mntfs_wchan(n->n_mnt));
320 
321   if (rc || term) {
322     if (term) {
323       /*
324        * Not sure what to do for an error code.
325        */
326       plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
327       n->n_error = EIO;
328     } else {
329       /*
330        * Check for exit status
331        */
332       errno = rc;		/* XXX */
333       plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
334       n->n_error = rc;
335     }
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;
340   } else {
341     /*
342      * The mount worked.
343      */
344     mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */
345     n->n_error = 0;
346   }
347 
348   /*
349    * Do the remaining bits
350    */
351   if (amfs_nfsx_mount(mp, mf) >= 0)
352     wakeup(get_mntfs_wchan(mf));
353 }
354 
355 
356 static int
357 try_amfs_nfsx_mount(opaque_t mv)
358 {
359   mntfs *mf = (mntfs *) mv;
360   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
361   am_node *mp = nx->nx_mp;
362   int error;
363 
364   error = mf->mf_ops->mount_fs(mp, mf);
365 
366   return error;
367 }
368 
369 
370 static int
371 amfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
372 {
373   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
374   amfs_nfsx_mnt *n;
375   int glob_error = -1;
376 
377   /* Save the am_node pointer for later use */
378   nx->nx_mp = am;
379 
380   /*
381    * Iterate through the mntfs's and mount each filesystem
382    * which is not yet mounted.
383    */
384   for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
385     mntfs *m = n->n_mnt;
386 
387     if (m->mf_flags & MFF_MOUNTING)
388       break;
389 
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;
393       continue;
394     }
395 
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;
401       }
402 
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);
407 	nx->nx_try = n;
408 	run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf);
409 	n->n_error = -1;
410 	return -1;
411       } else {
412 	dlog("foreground mount of \"%s\" ...", mf->mf_info);
413 	n->n_error = m->mf_ops->mount_fs(am, m);
414       }
415 
416       if (n->n_error > 0)
417 	dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error));
418 
419       if (n->n_error == 0) {
420 	glob_error = 0;
421       } else if (glob_error < 0) {
422 	glob_error = n->n_error;
423       }
424     }
425   }
426 
427   return glob_error < 0 ? 0 : glob_error;
428 }
429 
430 
431 static int
432 amfs_nfsx_mount(am_node *am, mntfs *mf)
433 {
434   return amfs_nfsx_remount(am, mf, FALSE);
435 }
436 
437 
438 /*
439  * Unmount an NFS hierarchy.
440  * Note that this is called in the foreground
441  * and so may hang under extremely rare conditions.
442  */
443 static int
444 amfs_nfsx_umount(am_node *am, mntfs *mf)
445 {
446   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
447   amfs_nfsx_mnt *n;
448   int glob_error = 0;
449 
450   /*
451    * Iterate in reverse through the mntfs's and unmount each filesystem
452    * which is mounted.
453    */
454   for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
455     mntfs *m = n->n_mnt;
456     /*
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.
464      */
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);
468       if (n->n_error) {
469 	glob_error = n->n_error;
470 	n->n_error = 0;
471       } else {
472 	/*
473 	 * Make sure remount gets this node
474 	 */
475 	n->n_error = -1;
476       }
477     }
478   }
479 
480   /*
481    * If any unmounts failed then remount the
482    * whole lot...
483    */
484   if (glob_error) {
485     glob_error = amfs_nfsx_remount(am, mf, TRUE);
486     if (glob_error) {
487       errno = glob_error;	/* XXX */
488       plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
489     }
490     glob_error = EBUSY;
491   } else {
492     /*
493      * Remove all the mount points
494      */
495     for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
496       mntfs *m = n->n_mnt;
497       dlog("calling underlying umounted on %s", m->mf_mount);
498       if (m->mf_ops->umounted)
499 	m->mf_ops->umounted(m);
500 
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;
505 	}
506       }
507       free_mntfs(m);
508       n->n_mnt = NULL;
509       n->n_error = -1;
510     }
511   }
512 
513   return glob_error;
514 }
515