1 /*
2 * Copyright (c) 1990 Jan-Simon Pendry
3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Jan-Simon Pendry at Imperial College, London.
9 *
10 * %sccs.include.redist.c%
11 *
12 * @(#)nfsx_ops.c 8.1 (Berkeley) 06/06/93
13 *
14 * $Id: nfsx_ops.c,v 5.2.2.3 1992/05/31 16:13:07 jsp Exp $
15 *
16 */
17
18 #include "am.h"
19
20 #ifdef HAS_NFSX
21
22 /*
23 * NFS hierarchical mounts
24 *
25 * TODO: Re-implement.
26 */
27
28 /*
29 * The rfs field contains a list of mounts to be done from
30 * the remote host.
31 */
32 typedef struct nfsx_mnt {
33 mntfs *n_mnt;
34 int n_error;
35 } nfsx_mnt;
36
37 struct nfsx {
38 int nx_c; /* Number of elements in nx_v */
39 nfsx_mnt *nx_v; /* Underlying mounts */
40 nfsx_mnt *nx_try;
41 };
42
43 static int nfsx_fmount P((mntfs*));
44
nfsx_match(fo)45 static char *nfsx_match(fo)
46 am_opts *fo;
47 {
48 char *xmtab;
49 char *ptr;
50 int len;
51
52 if (!fo->opt_rfs) {
53 plog(XLOG_USER, "nfsx: no remote filesystem specified");
54 return FALSE;
55 }
56 if (!fo->opt_rhost) {
57 plog(XLOG_USER, "nfsx: no remote host specified");
58 return FALSE;
59 }
60
61 #ifdef notdef
62 /* fiddle sublink, must be last... */
63 if (fo->opt_sublink) {
64 plog(XLOG_WARNING, "nfsx: sublink %s ignored", fo->opt_sublink);
65 free((voidp) fo->opt_sublink);
66 fo->opt_sublink = 0;
67 }
68 #endif
69
70 /* set default sublink */
71 if (fo->opt_sublink == 0) {
72 ptr = strchr(fo->opt_rfs, ',');
73 if (ptr && ptr != (fo->opt_rfs + 1))
74 fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
75 }
76
77 /*
78 * Remove trailing ",..." from ${fs}
79 * After deslashifying, overwrite the end of ${fs} with "/"
80 * to make sure it is unique.
81 */
82 if (ptr = strchr(fo->opt_fs, ','))
83 *ptr = '\0';
84 deslashify(fo->opt_fs);
85 /*
86 * Bump string length to allow trailing /
87 */
88 len = strlen(fo->opt_fs);
89 fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
90 ptr = fo->opt_fs + len;
91 /*
92 * Make unique...
93 */
94 *ptr++ = '/';
95 *ptr = '\0';
96
97 /*
98 * Determine magic cookie to put in mtab
99 */
100 xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
101 #ifdef DEBUG
102 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
103 fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
104 #endif /* DEBUG */
105
106 return xmtab;
107 }
108
109 static void nfsx_prfree P((voidp vp));
nfsx_prfree(vp)110 static void nfsx_prfree(vp)
111 voidp vp;
112 {
113 struct nfsx *nx = (struct nfsx *) vp;
114 int i;
115
116 for (i = 0; i < nx->nx_c; i++) {
117 mntfs *m = nx->nx_v[i].n_mnt;
118 if (m)
119 free_mntfs(m);
120 }
121
122 free((voidp) nx->nx_v);
123 free((voidp) nx);
124 }
125
nfsx_init(mf)126 static int nfsx_init(mf)
127 mntfs *mf;
128 {
129 /*
130 * mf_info has the form:
131 * host:/prefix/path,sub,sub,sub
132 */
133 int i;
134 int glob_error;
135 struct nfsx *nx;
136 int asked_for_wakeup = 0;
137
138 nx = (struct nfsx *) mf->mf_private;
139
140 if (nx == 0) {
141 char **ivec;
142 char *info = 0;
143 char *host;
144 char *pref;
145 int error = 0;
146
147 info = strdup(mf->mf_info);
148 host = strchr(info, ':');
149 if (!host) {
150 error = EINVAL;
151 goto errexit;
152 }
153
154 pref = host+1;
155 host = info;
156
157 /*
158 * Split the prefix off from the suffices
159 */
160 ivec = strsplit(pref, ',', '\'');
161
162 /*
163 * Count array size
164 */
165 for (i = 0; ivec[i]; i++)
166 ;
167
168 nx = ALLOC(nfsx);
169 mf->mf_private = (voidp) nx;
170 mf->mf_prfree = nfsx_prfree;
171
172 nx->nx_c = i - 1; /* i-1 because we don't want the prefix */
173 nx->nx_v = (nfsx_mnt *) xmalloc(nx->nx_c * sizeof(nfsx_mnt));
174 { char *mp = 0;
175 char *xinfo = 0;
176 char *fs = mf->mf_fo->opt_fs;
177 char *rfs = 0;
178 for (i = 0; i < nx->nx_c; i++) {
179 char *path = ivec[i+1];
180 rfs = str3cat(rfs, pref, "/", path);
181 /*
182 * Determine the mount point.
183 * If this is the root, then don't remove
184 * the trailing slash to avoid mntfs name clashes.
185 */
186 mp = str3cat(mp, fs, "/", rfs);
187 normalize_slash(mp);
188 deslashify(mp);
189 /*
190 * Determine the mount info
191 */
192 xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
193 normalize_slash(xinfo);
194 if (pref[1] != '\0')
195 deslashify(xinfo);
196 #ifdef DEBUG
197 dlog("nfsx: init mount for %s on %s", xinfo, mp);
198 #endif
199 nx->nx_v[i].n_error = -1;
200 nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
201 }
202 if (rfs) free(rfs);
203 if (mp) free(mp);
204 if (xinfo) free(xinfo);
205 }
206
207 free((voidp) ivec);
208 errexit:
209 if (info)
210 free(info);
211 if (error)
212 return error;
213 }
214
215 /*
216 * Iterate through the mntfs's and call
217 * the underlying init routine on each
218 */
219 glob_error = 0;
220 for (i = 0; i < nx->nx_c; i++) {
221 nfsx_mnt *n = &nx->nx_v[i];
222 mntfs *m = n->n_mnt;
223 int error = (*m->mf_ops->fs_init)(m);
224 /*
225 * If HARD_NFSX_ERRORS is defined, make any
226 * initialisation failure a hard error and
227 * fail the entire group. Otherwise only fail
228 * if none of the group is mountable (see nfsx_fmount).
229 */
230 #ifdef HARD_NFSX_ERRORS
231 if (error > 0)
232 return error;
233 #else
234 if (error > 0)
235 n->n_error = error;
236 #endif
237 else if (error < 0) {
238 glob_error = -1;
239 if (!asked_for_wakeup) {
240 asked_for_wakeup = 1;
241 sched_task(wakeup_task, (voidp) mf, (voidp) m);
242 }
243 }
244 }
245
246 return glob_error;
247 }
248
249 static void nfsx_cont P((int rc, int term, voidp closure));
nfsx_cont(rc,term,closure)250 static void nfsx_cont(rc, term, closure)
251 int rc;
252 int term;
253 voidp closure;
254 {
255 mntfs *mf = (mntfs *) closure;
256 struct nfsx *nx = (struct nfsx *) mf->mf_private;
257 nfsx_mnt *n = nx->nx_try;
258
259 n->n_mnt->mf_flags &= ~(MFF_ERROR|MFF_MOUNTING);
260 mf->mf_flags &= ~MFF_ERROR;
261
262 /*
263 * Wakeup anything waiting for this mount
264 */
265 wakeup((voidp) n->n_mnt);
266
267 if (rc || term) {
268 if (term) {
269 /*
270 * Not sure what to do for an error code.
271 */
272 plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
273 n->n_error = EIO;
274 } else {
275 /*
276 * Check for exit status
277 */
278 errno = rc; /* XXX */
279 plog(XLOG_ERROR, "%s: mount (nfsx_cont): %m", n->n_mnt->mf_mount);
280 n->n_error = rc;
281 }
282 free_mntfs(n->n_mnt);
283 n->n_mnt = new_mntfs();
284 n->n_mnt->mf_error = n->n_error;
285 n->n_mnt->mf_flags |= MFF_ERROR;
286 } else {
287 /*
288 * The mount worked.
289 */
290 mf_mounted(n->n_mnt);
291 n->n_error = 0;
292 }
293
294 /*
295 * Do the remaining bits
296 */
297 if (nfsx_fmount(mf) >= 0) {
298 wakeup((voidp) mf);
299 mf->mf_flags &= ~MFF_MOUNTING;
300 mf_mounted(mf);
301 }
302 }
303
304 static int try_nfsx_mount P((voidp mv));
try_nfsx_mount(mv)305 static int try_nfsx_mount(mv)
306 voidp mv;
307 {
308 mntfs *mf = (mntfs *) mv;
309 int error;
310
311 mf->mf_flags |= MFF_MOUNTING;
312 error = (*mf->mf_ops->fmount_fs)(mf);
313 mf->mf_flags &= ~MFF_MOUNTING;
314 return error;
315 }
316
317 static int nfsx_remount P((mntfs *mf, int fg));
nfsx_remount(mf,fg)318 static int nfsx_remount(mf, fg)
319 mntfs *mf;
320 int fg;
321 {
322 struct nfsx *nx = (struct nfsx *) mf->mf_private;
323 nfsx_mnt *n;
324 int glob_error = -1;
325
326 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
327 mntfs *m = n->n_mnt;
328 if (n->n_error < 0) {
329 if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
330 int error = mkdirs(m->mf_mount, 0555);
331 if (!error)
332 m->mf_flags |= MFF_MKMNT;
333 }
334 }
335 }
336
337 /*
338 * Iterate through the mntfs's and mount each filesystem
339 * which is not yet mounted.
340 */
341 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
342 mntfs *m = n->n_mnt;
343 if (n->n_error < 0) {
344 /*
345 * Check fmount entry pt. exists
346 * and then mount...
347 */
348 if (!m->mf_ops->fmount_fs) {
349 n->n_error = EINVAL;
350 } else {
351 #ifdef DEBUG
352 dlog("calling underlying fmount on %s", m->mf_mount);
353 #endif
354 if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
355 m->mf_flags |= MFF_MOUNTING; /* XXX */
356 #ifdef DEBUG
357 dlog("backgrounding mount of \"%s\"", m->mf_info);
358 #endif
359 nx->nx_try = n;
360 run_task(try_nfsx_mount, (voidp) m, nfsx_cont, (voidp) mf);
361 n->n_error = -1;
362 return -1;
363 } else {
364 #ifdef DEBUG
365 dlog("foreground mount of \"%s\" ...", mf->mf_info);
366 #endif
367 n->n_error = (*m->mf_ops->fmount_fs)(m);
368 }
369 }
370 #ifdef DEBUG
371 if (n->n_error > 0) {
372 errno = n->n_error; /* XXX */
373 dlog("underlying fmount of %s failed: %m", m->mf_mount);
374 }
375 #endif
376 if (n->n_error == 0) {
377 glob_error = 0;
378 } else if (glob_error < 0) {
379 glob_error = n->n_error;
380 }
381 }
382 }
383
384 return glob_error < 0 ? 0 : glob_error;
385 }
386
387 static int nfsx_fmount P((mntfs *mf));
nfsx_fmount(mf)388 static int nfsx_fmount(mf)
389 mntfs *mf;
390 {
391 return nfsx_remount(mf, FALSE);
392 }
393
394 /*
395 * Unmount an NFS hierarchy.
396 * Note that this is called in the foreground
397 * and so may hang under extremely rare conditions.
398 */
nfsx_fumount(mf)399 static int nfsx_fumount(mf)
400 mntfs *mf;
401 {
402 struct nfsx *nx = (struct nfsx *) mf->mf_private;
403 nfsx_mnt *n;
404 int glob_error = 0;
405
406 /*
407 * Iterate in reverse through the mntfs's and unmount each filesystem
408 * which is mounted.
409 */
410 for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
411 mntfs *m = n->n_mnt;
412 /*
413 * If this node has not been messed with
414 * and there has been no error so far
415 * then try and unmount.
416 * If an error had occured then zero
417 * the error code so that the remount
418 * only tries to unmount those nodes
419 * which had been successfully unmounted.
420 */
421 if (n->n_error == 0) {
422 #ifdef DEBUG
423 dlog("calling underlying fumount on %s", m->mf_mount);
424 #endif
425 n->n_error = (*m->mf_ops->fumount_fs)(m);
426 if (n->n_error) {
427 glob_error = n->n_error;
428 n->n_error = 0;
429 } else {
430 /*
431 * Make sure remount gets this node
432 */
433 n->n_error = -1;
434 }
435 }
436 }
437
438 /*
439 * If any unmounts failed then remount the
440 * whole lot...
441 */
442 if (glob_error) {
443 glob_error = nfsx_remount(mf, TRUE);
444 if (glob_error) {
445 errno = glob_error; /* XXX */
446 plog(XLOG_USER, "nfsx: remount of %s failed: %m", mf->mf_mount);
447 }
448 glob_error = EBUSY;
449 } else {
450 /*
451 * Remove all the mount points
452 */
453 for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
454 mntfs *m = n->n_mnt;
455 if (n->n_error < 0) {
456 if (m->mf_ops->fs_flags & FS_MKMNT) {
457 (void) rmdirs(m->mf_mount);
458 m->mf_flags &= ~MFF_MKMNT;
459 }
460 }
461 free_mntfs(m);
462 n->n_mnt = 0;
463 n->n_error = -1;
464 }
465 }
466
467 return glob_error;
468 }
469
470 /*
471 * Ops structure
472 */
473 am_ops nfsx_ops = {
474 "nfsx",
475 nfsx_match,
476 nfsx_init,
477 auto_fmount,
478 nfsx_fmount,
479 auto_fumount,
480 nfsx_fumount,
481 efs_lookuppn,
482 efs_readdir,
483 0, /* nfsx_readlink */
484 0, /* nfsx_mounted */
485 0, /* nfsx_umounted */
486 find_nfs_srvr, /* XXX */
487 /*FS_UBACKGROUND|*/FS_AMQINFO
488 };
489
490 #endif /* HAS_NFSX */
491