]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/amd/amfs_nfsx.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / amd / amfs_nfsx.c
1 /*
2  * Copyright (c) 1997-2006 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 }