Merge from vendor branch TNFTP:
[dragonfly.git] / contrib / amd / amd / amfs_nfsx.c
1 /*
2  * Copyright (c) 1997-1999 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  *      %W% (Berkeley) %G%
40  *
41  * $Id: amfs_nfsx.c,v 1.2 1999/01/10 21:53:42 ezk Exp $
42  *
43  */
44
45 /*
46  * NFS hierarchical mounts
47  *
48  * TODO: Re-implement.
49  */
50
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif /* HAVE_CONFIG_H */
54 #include <am_defs.h>
55 #include <amd.h>
56
57 /*
58  * The rfs field contains a list of mounts to be done from
59  * the remote host.
60  */
61 typedef struct amfs_nfsx_mnt {
62   mntfs *n_mnt;
63   int n_error;
64 } amfs_nfsx_mnt;
65
66 struct amfs_nfsx {
67   int nx_c;                     /* Number of elements in nx_v */
68   amfs_nfsx_mnt *nx_v;          /* Underlying mounts */
69   amfs_nfsx_mnt *nx_try;
70 };
71
72 /* forward definitions */
73 static char *amfs_nfsx_match(am_opts *fo);
74 static int amfs_nfsx_fmount (mntfs *);
75 static int amfs_nfsx_fmount(mntfs *mf);
76 static int amfs_nfsx_fumount(mntfs *mf);
77 static int amfs_nfsx_init(mntfs *mf);
78
79 /*
80  * Ops structure
81  */
82 am_ops amfs_nfsx_ops =
83 {
84   "nfsx",
85   amfs_nfsx_match,
86   amfs_nfsx_init,
87   amfs_auto_fmount,
88   amfs_nfsx_fmount,
89   amfs_auto_fumount,
90   amfs_nfsx_fumount,
91   amfs_error_lookuppn,
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         /* FS_UBACKGROUND| */ FS_AMQINFO
98 };
99
100
101 static char *
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 == 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 *) 0, fo->opt_rhost, ":", fo->opt_rfs);
151 #ifdef DEBUG
152   dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
153        fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
154 #endif /* DEBUG */
155
156   return xmtab;
157 }
158
159
160 static void
161 amfs_nfsx_prfree(voidp 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
217     nx = ALLOC(struct amfs_nfsx);
218     mf->mf_private = (voidp) 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     {
224       char *mp = 0;
225       char *xinfo = 0;
226       char *fs = mf->mf_fo->opt_fs;
227       char *rfs = 0;
228       for (i = 0; i < nx->nx_c; i++) {
229         char *path = ivec[i + 1];
230         rfs = str3cat(rfs, pref, "/", path);
231         /*
232          * Determine the mount point.
233          * If this is the root, then don't remove
234          * the trailing slash to avoid mntfs name clashes.
235          */
236         mp = str3cat(mp, fs, "/", rfs);
237         normalize_slash(mp);
238         deslashify(mp);
239         /*
240          * Determine the mount info
241          */
242         xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
243         normalize_slash(xinfo);
244         if (pref[1] != '\0')
245           deslashify(xinfo);
246 #ifdef DEBUG
247         dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
248 #endif /* DEBUG */
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       }
252       if (rfs)
253         XFREE(rfs);
254       if (mp)
255         XFREE(mp);
256       if (xinfo)
257         XFREE(xinfo);
258     }
259
260     XFREE(ivec);
261   errexit:
262     if (info)
263       XFREE(info);
264     if (error)
265       return error;
266   }
267
268   /*
269    * Iterate through the mntfs's and call
270    * the underlying init routine on each
271    */
272   glob_error = 0;
273
274   for (i = 0; i < nx->nx_c; i++) {
275     amfs_nfsx_mnt *n = &nx->nx_v[i];
276     mntfs *m = n->n_mnt;
277     int error = (*m->mf_ops->fs_init) (m);
278     /*
279      * if you just "return error" here, you will have made a failure
280      * in any submounts to fail the whole group.  There was old unused code
281      * here before.
282      */
283     if (error > 0)
284       n->n_error = error;
285
286     else if (error < 0) {
287       glob_error = -1;
288       if (!asked_for_wakeup) {
289         asked_for_wakeup = 1;
290         sched_task(wakeup_task, (voidp) mf, (voidp) m);
291       }
292     }
293   }
294
295   return glob_error;
296 }
297
298
299 static void
300 amfs_nfsx_cont(int rc, int term, voidp closure)
301 {
302   mntfs *mf = (mntfs *) closure;
303   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
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((voidp) 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);
338     n->n_error = 0;
339   }
340
341   /*
342    * Do the remaining bits
343    */
344   if (amfs_nfsx_fmount(mf) >= 0) {
345     wakeup((voidp) mf);
346     mf->mf_flags &= ~MFF_MOUNTING;
347     mf_mounted(mf);
348   }
349 }
350
351
352 static int
353 try_amfs_nfsx_mount(voidp mv)
354 {
355   mntfs *mf = (mntfs *) mv;
356   int error;
357
358   mf->mf_flags |= MFF_MOUNTING;
359   error = (*mf->mf_ops->fmount_fs) (mf);
360   mf->mf_flags &= ~MFF_MOUNTING;
361
362   return error;
363 }
364
365
366 static int
367 amfs_nfsx_remount(mntfs *mf, int fg)
368 {
369   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
370   amfs_nfsx_mnt *n;
371   int glob_error = -1;
372
373   for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
374     mntfs *m = n->n_mnt;
375     if (n->n_error < 0) {
376       if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
377         int error = mkdirs(m->mf_mount, 0555);
378         if (!error)
379           m->mf_flags |= MFF_MKMNT;
380       }
381     }
382   }
383
384   /*
385    * Iterate through the mntfs's and mount each filesystem
386    * which is not yet mounted.
387    */
388   for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
389     mntfs *m = n->n_mnt;
390     if (n->n_error < 0) {
391       /*
392        * Check fmount entry pt. exists
393        * and then mount...
394        */
395       if (!m->mf_ops->fmount_fs) {
396         n->n_error = EINVAL;
397       } else {
398 #ifdef DEBUG
399         dlog("calling underlying fmount on %s", m->mf_mount);
400 #endif /* DEBUG */
401         if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
402           m->mf_flags |= MFF_MOUNTING;  /* XXX */
403 #ifdef DEBUG
404           dlog("backgrounding mount of \"%s\"", m->mf_info);
405 #endif /* DEBUG */
406           nx->nx_try = n;
407           run_task(try_amfs_nfsx_mount, (voidp) m, amfs_nfsx_cont, (voidp) mf);
408           n->n_error = -1;
409           return -1;
410         } else {
411 #ifdef DEBUG
412           dlog("foreground mount of \"%s\" ...", mf->mf_info);
413 #endif /* DEBUG */
414           n->n_error = (*m->mf_ops->fmount_fs) (m);
415         }
416       }
417
418 #ifdef DEBUG
419       if (n->n_error > 0) {
420         errno = n->n_error;     /* XXX */
421         dlog("underlying fmount of %s failed: %m", m->mf_mount);
422       }
423 #endif /* DEBUG */
424
425       if (n->n_error == 0) {
426         glob_error = 0;
427       } else if (glob_error < 0) {
428         glob_error = n->n_error;
429       }
430     }
431   }
432
433   return glob_error < 0 ? 0 : glob_error;
434 }
435
436
437 static int
438 amfs_nfsx_fmount(mntfs *mf)
439 {
440   return amfs_nfsx_remount(mf, FALSE);
441 }
442
443
444 /*
445  * Unmount an NFS hierarchy.
446  * Note that this is called in the foreground
447  * and so may hang under extremely rare conditions.
448  */
449 static int
450 amfs_nfsx_fumount(mntfs *mf)
451 {
452   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
453   amfs_nfsx_mnt *n;
454   int glob_error = 0;
455
456   /*
457    * Iterate in reverse through the mntfs's and unmount each filesystem
458    * which is mounted.
459    */
460   for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
461     mntfs *m = n->n_mnt;
462     /*
463      * If this node has not been messed with
464      * and there has been no error so far
465      * then try and unmount.
466      * If an error had occurred then zero
467      * the error code so that the remount
468      * only tries to unmount those nodes
469      * which had been successfully unmounted.
470      */
471     if (n->n_error == 0) {
472 #ifdef DEBUG
473       dlog("calling underlying fumount on %s", m->mf_mount);
474 #endif /* DEBUG */
475       n->n_error = (*m->mf_ops->fumount_fs) (m);
476       if (n->n_error) {
477         glob_error = n->n_error;
478         n->n_error = 0;
479       } else {
480         /*
481          * Make sure remount gets this node
482          */
483         n->n_error = -1;
484       }
485     }
486   }
487
488   /*
489    * If any unmounts failed then remount the
490    * whole lot...
491    */
492   if (glob_error) {
493     glob_error = amfs_nfsx_remount(mf, TRUE);
494     if (glob_error) {
495       errno = glob_error;       /* XXX */
496       plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
497     }
498     glob_error = EBUSY;
499   } else {
500     /*
501      * Remove all the mount points
502      */
503     for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
504       mntfs *m = n->n_mnt;
505       am_node am;
506
507       /*
508        * XXX: all the umounted handler needs is a
509        * mntfs pointer, so pass an am_node with the right
510        * pointer in it.
511        */
512       memset((voidp) &am, 0, sizeof(am));
513       am.am_mnt = m;
514 #ifdef DEBUG
515       dlog("calling underlying umounted on %s", m->mf_mount);
516 #endif /* DEBUG */
517       (*m->mf_ops->umounted) (&am);
518
519       if (n->n_error < 0) {
520         if (m->mf_ops->fs_flags & FS_MKMNT) {
521           (void) rmdirs(m->mf_mount);
522           m->mf_flags &= ~MFF_MKMNT;
523         }
524       }
525       free_mntfs(m);
526       n->n_mnt = 0;
527       n->n_error = -1;
528     }
529   }
530
531   return glob_error;
532 }