amfs_host.c revision 1.1.1.3 1 1.1 christos /* $NetBSD: amfs_host.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $ */
2 1.1 christos
3 1.1 christos /*
4 1.1.1.3 christos * Copyright (c) 1997-2014 Erez Zadok
5 1.1 christos * Copyright (c) 1990 Jan-Simon Pendry
6 1.1 christos * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 1.1 christos * Copyright (c) 1990 The Regents of the University of California.
8 1.1 christos * All rights reserved.
9 1.1 christos *
10 1.1 christos * This code is derived from software contributed to Berkeley by
11 1.1 christos * Jan-Simon Pendry at Imperial College, London.
12 1.1 christos *
13 1.1 christos * Redistribution and use in source and binary forms, with or without
14 1.1 christos * modification, are permitted provided that the following conditions
15 1.1 christos * are met:
16 1.1 christos * 1. Redistributions of source code must retain the above copyright
17 1.1 christos * notice, this list of conditions and the following disclaimer.
18 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
19 1.1 christos * notice, this list of conditions and the following disclaimer in the
20 1.1 christos * documentation and/or other materials provided with the distribution.
21 1.1.1.3 christos * 3. Neither the name of the University nor the names of its contributors
22 1.1 christos * may be used to endorse or promote products derived from this software
23 1.1 christos * without specific prior written permission.
24 1.1 christos *
25 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 1.1 christos * SUCH DAMAGE.
36 1.1 christos *
37 1.1 christos *
38 1.1 christos * File: am-utils/amd/amfs_host.c
39 1.1 christos *
40 1.1 christos */
41 1.1 christos
42 1.1 christos /*
43 1.1 christos * NFS host file system.
44 1.1 christos * Mounts all exported filesystems from a given host.
45 1.1 christos * This has now degenerated into a mess but will not
46 1.1 christos * be rewritten. Amd 6 will support the abstractions
47 1.1 christos * needed to make this work correctly.
48 1.1 christos */
49 1.1 christos
50 1.1 christos #ifdef HAVE_CONFIG_H
51 1.1 christos # include <config.h>
52 1.1 christos #endif /* HAVE_CONFIG_H */
53 1.1 christos #include <am_defs.h>
54 1.1 christos #include <amd.h>
55 1.1 christos
56 1.1 christos static char *amfs_host_match(am_opts *fo);
57 1.1 christos static int amfs_host_init(mntfs *mf);
58 1.1 christos static int amfs_host_mount(am_node *am, mntfs *mf);
59 1.1 christos static int amfs_host_umount(am_node *am, mntfs *mf);
60 1.1 christos static void amfs_host_umounted(mntfs *mf);
61 1.1 christos
62 1.1 christos /*
63 1.1 christos * Ops structure
64 1.1 christos */
65 1.1 christos am_ops amfs_host_ops =
66 1.1 christos {
67 1.1 christos "host",
68 1.1 christos amfs_host_match,
69 1.1 christos amfs_host_init,
70 1.1 christos amfs_host_mount,
71 1.1 christos amfs_host_umount,
72 1.1 christos amfs_error_lookup_child,
73 1.1 christos amfs_error_mount_child,
74 1.1 christos amfs_error_readdir,
75 1.1 christos 0, /* amfs_host_readlink */
76 1.1 christos 0, /* amfs_host_mounted */
77 1.1 christos amfs_host_umounted,
78 1.1 christos find_nfs_srvr,
79 1.1 christos 0, /* amfs_host_get_wchan */
80 1.1 christos FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
81 1.1 christos #ifdef HAVE_FS_AUTOFS
82 1.1 christos AUTOFS_HOST_FS_FLAGS,
83 1.1 christos #endif /* HAVE_FS_AUTOFS */
84 1.1 christos };
85 1.1 christos
86 1.1 christos
87 1.1 christos /*
88 1.1 christos * Determine the mount point:
89 1.1 christos *
90 1.1 christos * The next change we put in to better handle PCs. This is a bit
91 1.1 christos * disgusting, so you'd better sit down. We change the make_mntpt function
92 1.1 christos * to look for exported file systems without a leading '/'. If they don't
93 1.1 christos * have a leading '/', we add one. If the export is 'a:' through 'z:'
94 1.1 christos * (without a leading slash), we change it to 'a%' (or b% or z%). This
95 1.1 christos * allows the entire PC disk to be mounted.
96 1.1 christos */
97 1.1 christos static void
98 1.1 christos make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
99 1.1 christos {
100 1.1 christos if (ex->ex_dir[0] == '/') {
101 1.1 christos if (ex->ex_dir[1] == 0)
102 1.1 christos xstrlcpy(mntpt, mf_mount, l);
103 1.1 christos else
104 1.1 christos xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
105 1.1 christos } else if (ex->ex_dir[0] >= 'a' &&
106 1.1 christos ex->ex_dir[0] <= 'z' &&
107 1.1 christos ex->ex_dir[1] == ':' &&
108 1.1 christos ex->ex_dir[2] == '/' &&
109 1.1 christos ex->ex_dir[3] == 0)
110 1.1 christos xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
111 1.1 christos else
112 1.1 christos xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
113 1.1 christos }
114 1.1 christos
115 1.1 christos
116 1.1 christos /*
117 1.1 christos * Execute needs the same as NFS plus a helper command
118 1.1 christos */
119 1.1 christos static char *
120 1.1 christos amfs_host_match(am_opts *fo)
121 1.1 christos {
122 1.1 christos extern am_ops nfs_ops;
123 1.1 christos
124 1.1 christos /*
125 1.1 christos * Make sure rfs is specified to keep nfs_match happy...
126 1.1 christos */
127 1.1 christos if (!fo->opt_rfs)
128 1.1 christos fo->opt_rfs = "/";
129 1.1 christos
130 1.1 christos return (*nfs_ops.fs_match) (fo);
131 1.1 christos }
132 1.1 christos
133 1.1 christos
134 1.1 christos static int
135 1.1 christos amfs_host_init(mntfs *mf)
136 1.1 christos {
137 1.1 christos u_short mountd_port;
138 1.1 christos
139 1.1 christos if (strchr(mf->mf_info, ':') == 0)
140 1.1 christos return ENOENT;
141 1.1 christos
142 1.1 christos /*
143 1.1 christos * This is primarily to schedule a wakeup so that as soon
144 1.1 christos * as our fileserver is ready, we can continue setting up
145 1.1 christos * the host filesystem. If we don't do this, the standard
146 1.1 christos * amfs_auto code will set up a fileserver structure, but it will
147 1.1 christos * have to wait for another nfs request from the client to come
148 1.1 christos * in before finishing. Our way is faster since we don't have
149 1.1 christos * to wait for the client to resend its request (which could
150 1.1 christos * take a second or two).
151 1.1 christos */
152 1.1 christos /*
153 1.1 christos * First, we find the fileserver for this mntfs and then call
154 1.1 christos * get_mountd_port with our mntfs passed as the wait channel.
155 1.1 christos * get_mountd_port will check some things and then schedule
156 1.1 christos * it so that when the fileserver is ready, a wakeup is done
157 1.1 christos * on this mntfs. amfs_cont() is already sleeping on this mntfs
158 1.1 christos * so as soon as that wakeup happens amfs_cont() is called and
159 1.1 christos * this mount is retried.
160 1.1 christos */
161 1.1 christos if (mf->mf_server)
162 1.1 christos /*
163 1.1 christos * We don't really care if there's an error returned.
164 1.1 christos * Since this is just to help speed things along, the
165 1.1 christos * error will get handled properly elsewhere.
166 1.1 christos */
167 1.1 christos get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
168 1.1 christos
169 1.1 christos return 0;
170 1.1 christos }
171 1.1 christos
172 1.1 christos
173 1.1 christos static int
174 1.1 christos do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
175 1.1 christos {
176 1.1 christos struct stat stb;
177 1.1 christos
178 1.1 christos dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
179 1.1 christos
180 1.1 christos (void) mkdirs(mntdir, 0555);
181 1.1 christos if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
182 1.1 christos plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
183 1.1 christos return ENOENT;
184 1.1 christos }
185 1.1 christos
186 1.1 christos return mount_nfs_fh(fhp, mntdir, fs_name, mf);
187 1.1 christos }
188 1.1 christos
189 1.1 christos
190 1.1 christos static int
191 1.1 christos sortfun(const voidp x, const voidp y)
192 1.1 christos {
193 1.1 christos exports *a = (exports *) x;
194 1.1 christos exports *b = (exports *) y;
195 1.1 christos
196 1.1 christos return strcmp((*a)->ex_dir, (*b)->ex_dir);
197 1.1 christos }
198 1.1 christos
199 1.1 christos
200 1.1 christos /*
201 1.1 christos * Get filehandle
202 1.1 christos */
203 1.1 christos static int
204 1.1 christos fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
205 1.1 christos {
206 1.1 christos struct timeval tv;
207 1.1 christos enum clnt_stat clnt_stat;
208 1.1 christos struct fhstatus res;
209 1.1 christos #ifdef HAVE_FS_NFS3
210 1.1 christos struct am_mountres3 res3;
211 1.1 christos #endif /* HAVE_FS_NFS3 */
212 1.1 christos
213 1.1 christos /*
214 1.1 christos * Pick a number, any number...
215 1.1 christos */
216 1.1 christos tv.tv_sec = 20;
217 1.1 christos tv.tv_usec = 0;
218 1.1 christos
219 1.1 christos dlog("Fetching fhandle for %s", dir);
220 1.1 christos
221 1.1 christos /*
222 1.1 christos * Call the mount daemon on the remote host to
223 1.1 christos * get the filehandle. Use NFS version specific call.
224 1.1 christos */
225 1.1 christos
226 1.1 christos plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
227 1.1 christos #ifdef HAVE_FS_NFS3
228 1.1.1.3 christos if (nfs_version == NFS_VERSION3
229 1.1.1.3 christos #ifdef HAVE_FS_NFS4
230 1.1.1.3 christos #ifndef NO_FALLBACK
231 1.1.1.3 christos || nfs_version == NFS_VERSION4
232 1.1.1.3 christos #endif /* NO_FALLBACK */
233 1.1.1.3 christos #endif /* HAVE_FS_NFS4 */
234 1.1.1.3 christos ) {
235 1.1.1.3 christos
236 1.1 christos memset((char *) &res3, 0, sizeof(res3));
237 1.1 christos clnt_stat = clnt_call(client,
238 1.1 christos MOUNTPROC_MNT,
239 1.1 christos (XDRPROC_T_TYPE) xdr_dirpath,
240 1.1 christos (SVC_IN_ARG_TYPE) &dir,
241 1.1 christos (XDRPROC_T_TYPE) xdr_am_mountres3,
242 1.1 christos (SVC_IN_ARG_TYPE) &res3,
243 1.1 christos tv);
244 1.1 christos if (clnt_stat != RPC_SUCCESS) {
245 1.1 christos plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
246 1.1 christos return EIO;
247 1.1 christos }
248 1.1 christos /* Check the status of the filehandle */
249 1.1 christos if ((errno = res3.fhs_status)) {
250 1.1 christos dlog("fhandle fetch for mount version 3 failed: %m");
251 1.1 christos return errno;
252 1.1 christos }
253 1.1 christos memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
254 1.1 christos fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
255 1.1 christos memmove(fhp->v3.am_fh3_data,
256 1.1 christos res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
257 1.1 christos fhp->v3.am_fh3_length);
258 1.1 christos } else { /* not NFS_VERSION3 mount */
259 1.1 christos #endif /* HAVE_FS_NFS3 */
260 1.1 christos clnt_stat = clnt_call(client,
261 1.1 christos MOUNTPROC_MNT,
262 1.1 christos (XDRPROC_T_TYPE) xdr_dirpath,
263 1.1 christos (SVC_IN_ARG_TYPE) &dir,
264 1.1 christos (XDRPROC_T_TYPE) xdr_fhstatus,
265 1.1 christos (SVC_IN_ARG_TYPE) &res,
266 1.1 christos tv);
267 1.1 christos if (clnt_stat != RPC_SUCCESS) {
268 1.1 christos plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
269 1.1 christos return EIO;
270 1.1 christos }
271 1.1 christos /* Check status of filehandle */
272 1.1 christos if (res.fhs_status) {
273 1.1 christos errno = res.fhs_status;
274 1.1 christos dlog("fhandle fetch for mount version 1 failed: %m");
275 1.1 christos return errno;
276 1.1 christos }
277 1.1 christos memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
278 1.1 christos #ifdef HAVE_FS_NFS3
279 1.1 christos } /* end of "if (nfs_version == NFS_VERSION3)" statement */
280 1.1 christos #endif /* HAVE_FS_NFS3 */
281 1.1 christos
282 1.1 christos /* all is well */
283 1.1 christos return 0;
284 1.1 christos }
285 1.1 christos
286 1.1 christos
287 1.1 christos /*
288 1.1 christos * Scan mount table to see if something already mounted
289 1.1 christos */
290 1.1 christos static int
291 1.1 christos already_mounted(mntlist *mlist, char *dir)
292 1.1 christos {
293 1.1 christos mntlist *ml;
294 1.1 christos
295 1.1 christos for (ml = mlist; ml; ml = ml->mnext)
296 1.1 christos if (STREQ(ml->mnt->mnt_dir, dir))
297 1.1 christos return 1;
298 1.1 christos return 0;
299 1.1 christos }
300 1.1 christos
301 1.1 christos
302 1.1 christos static int
303 1.1 christos amfs_host_mount(am_node *am, mntfs *mf)
304 1.1 christos {
305 1.1 christos struct timeval tv2;
306 1.1 christos CLIENT *client;
307 1.1 christos enum clnt_stat clnt_stat;
308 1.1 christos int n_export;
309 1.1 christos int j, k;
310 1.1 christos exports exlist = 0, ex;
311 1.1 christos exports *ep = NULL;
312 1.1 christos am_nfs_handle_t *fp = NULL;
313 1.1 christos char *host;
314 1.1 christos int error = 0;
315 1.1 christos struct sockaddr_in sin;
316 1.1 christos int sock = RPC_ANYSOCK;
317 1.1 christos int ok = FALSE;
318 1.1 christos mntlist *mlist;
319 1.1 christos char fs_name[MAXPATHLEN], *rfs_dir;
320 1.1 christos char mntpt[MAXPATHLEN];
321 1.1 christos struct timeval tv;
322 1.1 christos u_long mnt_version;
323 1.1 christos
324 1.1 christos /*
325 1.1 christos * WebNFS servers don't necessarily run mountd.
326 1.1 christos */
327 1.1 christos if (mf->mf_flags & MFF_WEBNFS) {
328 1.1 christos plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
329 1.1 christos return EIO;
330 1.1 christos }
331 1.1 christos
332 1.1 christos /*
333 1.1 christos * Read the mount list
334 1.1 christos */
335 1.1 christos mlist = read_mtab(mf->mf_mount, mnttab_file_name);
336 1.1 christos
337 1.1 christos #ifdef MOUNT_TABLE_ON_FILE
338 1.1 christos /*
339 1.1 christos * Unlock the mount list
340 1.1 christos */
341 1.1 christos unlock_mntlist();
342 1.1 christos #endif /* MOUNT_TABLE_ON_FILE */
343 1.1 christos
344 1.1 christos /*
345 1.1 christos * Take a copy of the server hostname, address, and nfs version
346 1.1 christos * to mount version conversion.
347 1.1 christos */
348 1.1 christos host = mf->mf_server->fs_host;
349 1.1 christos sin = *mf->mf_server->fs_ip;
350 1.1 christos plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
351 1.1 christos #ifdef HAVE_FS_NFS3
352 1.1 christos if (mf->mf_server->fs_version == NFS_VERSION3)
353 1.1 christos mnt_version = AM_MOUNTVERS3;
354 1.1 christos else
355 1.1 christos #endif /* HAVE_FS_NFS3 */
356 1.1 christos mnt_version = MOUNTVERS;
357 1.1 christos
358 1.1 christos /*
359 1.1 christos * The original 10 second per try timeout is WAY too large, especially
360 1.1 christos * if we're only waiting 10 or 20 seconds max for the response.
361 1.1 christos * That would mean we'd try only once in 10 seconds, and we could
362 1.1 christos * lose the transmit or receive packet, and never try again.
363 1.1 christos * A 2-second per try timeout here is much more reasonable.
364 1.1 christos * 09/28/92 Mike Mitchell, mcm (at) unx.sas.com
365 1.1 christos */
366 1.1 christos tv.tv_sec = 2;
367 1.1 christos tv.tv_usec = 0;
368 1.1 christos
369 1.1 christos /*
370 1.1 christos * Create a client attached to mountd
371 1.1 christos */
372 1.1 christos client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
373 1.1 christos if (client == NULL) {
374 1.1 christos #ifdef HAVE_CLNT_SPCREATEERROR
375 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
376 1.1 christos host, clnt_spcreateerror(""));
377 1.1 christos #else /* not HAVE_CLNT_SPCREATEERROR */
378 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s", host);
379 1.1 christos #endif /* not HAVE_CLNT_SPCREATEERROR */
380 1.1 christos error = EIO;
381 1.1 christos goto out;
382 1.1 christos }
383 1.1 christos if (!nfs_auth) {
384 1.1 christos error = make_nfs_auth();
385 1.1 christos if (error)
386 1.1 christos goto out;
387 1.1 christos }
388 1.1 christos client->cl_auth = nfs_auth;
389 1.1 christos
390 1.1 christos dlog("Fetching export list from %s", host);
391 1.1 christos
392 1.1 christos /*
393 1.1 christos * Fetch the export list
394 1.1 christos */
395 1.1 christos tv2.tv_sec = 10;
396 1.1 christos tv2.tv_usec = 0;
397 1.1 christos clnt_stat = clnt_call(client,
398 1.1 christos MOUNTPROC_EXPORT,
399 1.1 christos (XDRPROC_T_TYPE) xdr_void,
400 1.1 christos 0,
401 1.1 christos (XDRPROC_T_TYPE) xdr_exports,
402 1.1 christos (SVC_IN_ARG_TYPE) & exlist,
403 1.1 christos tv2);
404 1.1 christos if (clnt_stat != RPC_SUCCESS) {
405 1.1 christos const char *msg = clnt_sperrno(clnt_stat);
406 1.1 christos plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
407 1.1 christos /* clnt_perror(client, "rpc"); */
408 1.1 christos error = EIO;
409 1.1 christos goto out;
410 1.1 christos }
411 1.1 christos
412 1.1 christos /*
413 1.1 christos * Figure out how many exports were returned
414 1.1 christos */
415 1.1 christos for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
416 1.1 christos n_export++;
417 1.1 christos }
418 1.1 christos
419 1.1 christos /*
420 1.1 christos * Allocate an array of pointers into the list
421 1.1 christos * so that they can be sorted. If the filesystem
422 1.1 christos * is already mounted then ignore it.
423 1.1 christos */
424 1.1 christos ep = (exports *) xmalloc(n_export * sizeof(exports));
425 1.1 christos for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
426 1.1 christos make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
427 1.1 christos if (already_mounted(mlist, mntpt))
428 1.1 christos /* we have at least one mounted f/s, so don't fail the mount */
429 1.1 christos ok = TRUE;
430 1.1 christos else
431 1.1 christos ep[j++] = ex;
432 1.1 christos }
433 1.1 christos n_export = j;
434 1.1 christos
435 1.1 christos /*
436 1.1 christos * Sort into order.
437 1.1 christos * This way the mounts are done in order down the tree,
438 1.1 christos * instead of any random order returned by the mount
439 1.1 christos * daemon (the protocol doesn't specify...).
440 1.1 christos */
441 1.1 christos qsort(ep, n_export, sizeof(exports), sortfun);
442 1.1 christos
443 1.1 christos /*
444 1.1 christos * Allocate an array of filehandles
445 1.1 christos */
446 1.1 christos fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
447 1.1 christos
448 1.1 christos /*
449 1.1 christos * Try to obtain filehandles for each directory.
450 1.1 christos * If a fetch fails then just zero out the array
451 1.1 christos * reference but discard the error.
452 1.1 christos */
453 1.1 christos for (j = k = 0; j < n_export; j++) {
454 1.1 christos /* Check and avoid a duplicated export entry */
455 1.1 christos if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
456 1.1 christos dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
457 1.1 christos ep[j] = NULL;
458 1.1 christos } else {
459 1.1 christos k = j;
460 1.1 christos error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
461 1.1 christos mf->mf_server->fs_version);
462 1.1 christos if (error)
463 1.1 christos ep[j] = NULL;
464 1.1 christos }
465 1.1 christos }
466 1.1 christos
467 1.1 christos /*
468 1.1 christos * Mount each filesystem for which we have a filehandle.
469 1.1 christos * If any of the mounts succeed then mark "ok" and return
470 1.1 christos * error code 0 at the end. If they all fail then return
471 1.1 christos * the last error code.
472 1.1 christos */
473 1.1 christos xstrlcpy(fs_name, mf->mf_info, sizeof(fs_name));
474 1.1 christos if ((rfs_dir = strchr(fs_name, ':')) == (char *) NULL) {
475 1.1 christos plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
476 1.1 christos error = EINVAL;
477 1.1 christos goto out;
478 1.1 christos }
479 1.1 christos ++rfs_dir;
480 1.1 christos for (j = 0; j < n_export; j++) {
481 1.1 christos ex = ep[j];
482 1.1 christos if (ex) {
483 1.1 christos /*
484 1.1 christos * Note: the sizeof space left in rfs_dir is what's left in fs_name
485 1.1 christos * after strchr() above returned a pointer _inside_ fs_name. The
486 1.1 christos * calculation below also takes into account that rfs_dir was
487 1.1 christos * incremented by the ++ above.
488 1.1 christos */
489 1.1 christos xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
490 1.1 christos make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
491 1.1 christos if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
492 1.1 christos ok = TRUE;
493 1.1 christos }
494 1.1 christos }
495 1.1 christos
496 1.1 christos /*
497 1.1 christos * Clean up and exit
498 1.1 christos */
499 1.1 christos out:
500 1.1 christos discard_mntlist(mlist);
501 1.1.1.3 christos XFREE(ep);
502 1.1.1.3 christos XFREE(fp);
503 1.1 christos if (sock != RPC_ANYSOCK)
504 1.1 christos (void) amu_close(sock);
505 1.1 christos if (client)
506 1.1 christos clnt_destroy(client);
507 1.1 christos if (exlist)
508 1.1 christos xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
509 1.1 christos if (ok)
510 1.1 christos return 0;
511 1.1 christos return error;
512 1.1 christos }
513 1.1 christos
514 1.1 christos
515 1.1 christos /*
516 1.1 christos * Return true if pref is a directory prefix of dir.
517 1.1 christos *
518 1.1 christos * XXX TODO:
519 1.1 christos * Does not work if pref is "/".
520 1.1 christos */
521 1.1 christos static int
522 1.1 christos directory_prefix(char *pref, char *dir)
523 1.1 christos {
524 1.1 christos int len = strlen(pref);
525 1.1 christos
526 1.1 christos if (!NSTREQ(pref, dir, len))
527 1.1 christos return FALSE;
528 1.1 christos if (dir[len] == '/' || dir[len] == '\0')
529 1.1 christos return TRUE;
530 1.1 christos return FALSE;
531 1.1 christos }
532 1.1 christos
533 1.1 christos
534 1.1 christos /*
535 1.1 christos * Unmount a mount tree
536 1.1 christos */
537 1.1 christos static int
538 1.1 christos amfs_host_umount(am_node *am, mntfs *mf)
539 1.1 christos {
540 1.1 christos mntlist *ml, *mprev;
541 1.1 christos int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
542 1.1 christos int xerror = 0;
543 1.1 christos
544 1.1 christos /*
545 1.1 christos * Read the mount list
546 1.1 christos */
547 1.1 christos mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
548 1.1 christos
549 1.1 christos #ifdef MOUNT_TABLE_ON_FILE
550 1.1 christos /*
551 1.1 christos * Unlock the mount list
552 1.1 christos */
553 1.1 christos unlock_mntlist();
554 1.1 christos #endif /* MOUNT_TABLE_ON_FILE */
555 1.1 christos
556 1.1 christos /*
557 1.1 christos * Reverse list...
558 1.1 christos */
559 1.1 christos ml = mlist;
560 1.1 christos mprev = NULL;
561 1.1 christos while (ml) {
562 1.1 christos mntlist *ml2 = ml->mnext;
563 1.1 christos ml->mnext = mprev;
564 1.1 christos mprev = ml;
565 1.1 christos ml = ml2;
566 1.1 christos }
567 1.1 christos mlist = mprev;
568 1.1 christos
569 1.1 christos /*
570 1.1 christos * Unmount all filesystems...
571 1.1 christos */
572 1.1 christos for (ml = mlist; ml && !xerror; ml = ml->mnext) {
573 1.1 christos char *dir = ml->mnt->mnt_dir;
574 1.1 christos if (directory_prefix(mf->mf_mount, dir)) {
575 1.1 christos int error;
576 1.1 christos dlog("amfs_host: unmounts %s", dir);
577 1.1 christos /*
578 1.1 christos * Unmount "dir"
579 1.1 christos */
580 1.1 christos error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
581 1.1 christos /*
582 1.1 christos * Keep track of errors
583 1.1 christos */
584 1.1 christos if (error) {
585 1.1 christos /*
586 1.1 christos * If we have not already set xerror and error is not ENOENT,
587 1.1 christos * then set xerror equal to error and log it.
588 1.1 christos * 'xerror' is the return value for this function.
589 1.1 christos *
590 1.1 christos * We do not want to pass ENOENT as an error because if the
591 1.1 christos * directory does not exists our work is done anyway.
592 1.1 christos */
593 1.1 christos if (!xerror && error != ENOENT)
594 1.1 christos xerror = error;
595 1.1 christos if (error != EBUSY) {
596 1.1 christos errno = error;
597 1.1 christos plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
598 1.1 christos }
599 1.1 christos } else {
600 1.1 christos (void) rmdirs(dir);
601 1.1 christos }
602 1.1 christos }
603 1.1 christos }
604 1.1 christos
605 1.1 christos /*
606 1.1 christos * Throw away mount list
607 1.1 christos */
608 1.1 christos discard_mntlist(mlist);
609 1.1 christos
610 1.1 christos /*
611 1.1 christos * Try to remount, except when we are shutting down.
612 1.1 christos */
613 1.1 christos if (xerror && amd_state != Finishing) {
614 1.1 christos xerror = amfs_host_mount(am, mf);
615 1.1 christos if (!xerror) {
616 1.1 christos /*
617 1.1 christos * Don't log this - it's usually too verbose
618 1.1 christos plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
619 1.1 christos */
620 1.1 christos xerror = EBUSY;
621 1.1 christos }
622 1.1 christos }
623 1.1 christos return xerror;
624 1.1 christos }
625 1.1 christos
626 1.1 christos
627 1.1 christos /*
628 1.1 christos * Tell mountd we're done.
629 1.1 christos * This is not quite right, because we may still
630 1.1 christos * have other filesystems mounted, but the existing
631 1.1 christos * mountd protocol is badly broken anyway.
632 1.1 christos */
633 1.1 christos static void
634 1.1 christos amfs_host_umounted(mntfs *mf)
635 1.1 christos {
636 1.1 christos char *host;
637 1.1 christos CLIENT *client;
638 1.1 christos enum clnt_stat clnt_stat;
639 1.1 christos struct sockaddr_in sin;
640 1.1 christos int sock = RPC_ANYSOCK;
641 1.1 christos struct timeval tv;
642 1.1 christos u_long mnt_version;
643 1.1 christos
644 1.1 christos if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
645 1.1 christos return;
646 1.1 christos
647 1.1 christos /*
648 1.1 christos * WebNFS servers shouldn't ever get here.
649 1.1 christos */
650 1.1 christos if (mf->mf_flags & MFF_WEBNFS) {
651 1.1 christos plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
652 1.1 christos return;
653 1.1 christos }
654 1.1 christos
655 1.1 christos /*
656 1.1 christos * Take a copy of the server hostname, address, and NFS version
657 1.1 christos * to mount version conversion.
658 1.1 christos */
659 1.1 christos host = mf->mf_server->fs_host;
660 1.1 christos sin = *mf->mf_server->fs_ip;
661 1.1 christos plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
662 1.1 christos #ifdef HAVE_FS_NFS3
663 1.1 christos if (mf->mf_server->fs_version == NFS_VERSION3)
664 1.1 christos mnt_version = AM_MOUNTVERS3;
665 1.1 christos else
666 1.1 christos #endif /* HAVE_FS_NFS3 */
667 1.1 christos mnt_version = MOUNTVERS;
668 1.1 christos
669 1.1 christos /*
670 1.1 christos * Create a client attached to mountd
671 1.1 christos */
672 1.1 christos tv.tv_sec = 10;
673 1.1 christos tv.tv_usec = 0;
674 1.1 christos client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
675 1.1 christos if (client == NULL) {
676 1.1 christos #ifdef HAVE_CLNT_SPCREATEERROR
677 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
678 1.1 christos host, clnt_spcreateerror(""));
679 1.1 christos #else /* not HAVE_CLNT_SPCREATEERROR */
680 1.1 christos plog(XLOG_ERROR, "get_mount_client failed for %s", host);
681 1.1 christos #endif /* not HAVE_CLNT_SPCREATEERROR */
682 1.1 christos goto out;
683 1.1 christos }
684 1.1 christos
685 1.1 christos if (!nfs_auth) {
686 1.1 christos if (make_nfs_auth())
687 1.1 christos goto out;
688 1.1 christos }
689 1.1 christos client->cl_auth = nfs_auth;
690 1.1 christos
691 1.1 christos dlog("Unmounting all from %s", host);
692 1.1 christos
693 1.1 christos clnt_stat = clnt_call(client,
694 1.1 christos MOUNTPROC_UMNTALL,
695 1.1 christos (XDRPROC_T_TYPE) xdr_void,
696 1.1 christos 0,
697 1.1 christos (XDRPROC_T_TYPE) xdr_void,
698 1.1 christos 0,
699 1.1 christos tv);
700 1.1 christos if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
701 1.1 christos /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
702 1.1 christos const char *msg = clnt_sperrno(clnt_stat);
703 1.1 christos plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
704 1.1 christos goto out;
705 1.1 christos }
706 1.1 christos
707 1.1 christos out:
708 1.1 christos if (sock != RPC_ANYSOCK)
709 1.1 christos (void) amu_close(sock);
710 1.1 christos if (client)
711 1.1 christos clnt_destroy(client);
712 1.1 christos }
713