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