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