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