Commit | Line | Data |
---|---|---|
e1a31032 | 1 | /* |
e1a31032 KM |
2 | * Copyright (c) 1990 Jan-Simon Pendry |
3 | * Copyright (c) 1990 Imperial College of Science, Technology & Medicine | |
4 | * Copyright (c) 1990 The Regents of the University of California. | |
5 | * All rights reserved. | |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Jan-Simon Pendry at Imperial College, London. | |
9 | * | |
af359dea C |
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. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
37 | * | |
38 | * @(#)host_ops.c 5.3 (Berkeley) 5/12/91 | |
39 | * | |
40 | * $Id: host_ops.c,v 5.2.1.6 91/05/07 22:17:53 jsp Alpha $ | |
e1a31032 | 41 | * |
e1a31032 KM |
42 | */ |
43 | ||
44 | #include "am.h" | |
45 | ||
46 | #ifdef HAS_HOST | |
47 | ||
48 | #include "mount.h" | |
49 | #include <sys/stat.h> | |
50 | ||
51 | /* | |
2664d859 JSP |
52 | * NFS host file system. |
53 | * Mounts all exported filesystems from a given host. | |
54 | * This has now degenerated into a mess but will not | |
55 | * be rewritten. Amd 6 will support the abstractions | |
56 | * needed to make this work correctly. | |
e1a31032 KM |
57 | */ |
58 | ||
59 | /* | |
60 | * Define HOST_RPC_UDP to use dgram instead of stream RPC. | |
61 | * Datagrams are generally much faster. | |
62 | */ | |
63 | #define HOST_RPC_UDP | |
64 | ||
65 | /* | |
66 | * Define HOST_MKDIRS to make Amd automatically try | |
67 | * to create the mount points. | |
68 | */ | |
69 | #define HOST_MKDIRS | |
70 | ||
2664d859 JSP |
71 | /* |
72 | * Determine the mount point | |
73 | */ | |
74 | #define MAKE_MNTPT(mntpt, ex, mf) { \ | |
75 | if (strcmp((ex)->ex_dir, "/") == 0) \ | |
76 | strcpy((mntpt), (mf)->mf_mount); \ | |
77 | else \ | |
78 | sprintf((mntpt), "%s%s", (mf)->mf_mount, (ex)->ex_dir); \ | |
79 | } | |
80 | ||
e1a31032 KM |
81 | /* |
82 | * Execute needs the same as NFS plus a helper command | |
83 | */ | |
2664d859 JSP |
84 | static char *host_match P((am_opts *fo)); |
85 | static char *host_match(fo) | |
e1a31032 KM |
86 | am_opts *fo; |
87 | { | |
88 | #ifdef HOST_EXEC | |
89 | if (!host_helper) { | |
90 | plog(XLOG_USER, "No host helper command given"); | |
91 | return FALSE; | |
92 | } | |
93 | #endif /* HOST_EXEC */ | |
94 | ||
95 | /* | |
96 | * Make sure rfs is specified to keep nfs_match happy... | |
97 | */ | |
98 | if (!fo->opt_rfs) | |
99 | fo->opt_rfs = "/"; | |
100 | ||
2664d859 JSP |
101 | |
102 | return (*nfs_ops.fs_match)(fo); | |
e1a31032 KM |
103 | } |
104 | ||
105 | static int host_init(mf) | |
106 | mntfs *mf; | |
107 | { | |
108 | if (strchr(mf->mf_info, ':') == 0) | |
109 | return ENOENT; | |
110 | return 0; | |
111 | } | |
112 | ||
113 | /* | |
114 | * Two implementations: | |
115 | * HOST_EXEC gets you the external version. The program specified with | |
116 | * the -h option is called. The external program is not published... | |
117 | * roll your own. | |
118 | * | |
119 | * Otherwise you get the native version. Faster but makes the program | |
120 | * bigger. | |
121 | */ | |
122 | ||
123 | #ifndef HOST_EXEC | |
124 | ||
125 | static bool_t | |
126 | xdr_pri_free(xdr_args, args_ptr) | |
127 | xdrproc_t xdr_args; | |
128 | caddr_t args_ptr; | |
129 | { | |
130 | XDR xdr; | |
131 | xdr.x_op = XDR_FREE; | |
132 | return ((*xdr_args)(&xdr, args_ptr)); | |
133 | } | |
134 | ||
2664d859 | 135 | static int do_mount P((fhstatus *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)); |
e1a31032 KM |
136 | static int do_mount(fhp, dir, fs_name, opts, mf) |
137 | fhstatus *fhp; | |
138 | char *dir; | |
139 | char *fs_name; | |
140 | char *opts; | |
141 | mntfs *mf; | |
142 | { | |
143 | struct stat stb; | |
144 | #ifdef DEBUG | |
145 | dlog("host: mounting fs %s on %s\n", fs_name, dir); | |
146 | #endif /* DEBUG */ | |
147 | #ifdef HOST_MKDIRS | |
148 | (void) mkdirs(dir, 0555); | |
149 | #endif /* HOST_MKDIRS */ | |
150 | if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { | |
151 | plog(XLOG_ERROR, "No mount point for %s - skipping", dir); | |
152 | return ENOENT; | |
153 | } | |
154 | ||
155 | return mount_nfs_fh(fhp, dir, fs_name, opts, mf); | |
156 | } | |
157 | ||
2664d859 JSP |
158 | static int sortfun P((exports *a, exports *b)); |
159 | static int sortfun(a, b) | |
e1a31032 KM |
160 | exports *a,*b; |
161 | { | |
162 | return strcmp((*a)->ex_dir, (*b)->ex_dir); | |
163 | } | |
164 | ||
165 | /* | |
166 | * Get filehandle | |
167 | */ | |
2664d859 | 168 | static int fetch_fhandle P((CLIENT *client, char *dir, fhstatus *fhp)); |
e1a31032 KM |
169 | static int fetch_fhandle(client, dir, fhp) |
170 | CLIENT *client; | |
171 | char *dir; | |
172 | fhstatus *fhp; | |
173 | { | |
174 | struct timeval tv; | |
175 | enum clnt_stat clnt_stat; | |
176 | ||
177 | /* | |
178 | * Pick a number, any number... | |
179 | */ | |
af359dea | 180 | tv.tv_sec = 20; |
e1a31032 KM |
181 | tv.tv_usec = 0; |
182 | ||
183 | #ifdef DEBUG | |
184 | dlog("Fetching fhandle for %s", dir); | |
185 | #endif /* DEBUG */ | |
186 | /* | |
187 | * Call the mount daemon on the remote host to | |
188 | * get the filehandle. | |
189 | */ | |
190 | clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_fhstatus, fhp, tv); | |
191 | if (clnt_stat != RPC_SUCCESS) { | |
192 | extern char *clnt_sperrno(); | |
193 | char *msg = clnt_sperrno(clnt_stat); | |
194 | plog(XLOG_ERROR, "mountd rpc failed: %s", msg); | |
195 | return EIO; | |
196 | } | |
197 | /* | |
198 | * Check status of filehandle | |
199 | */ | |
200 | if (fhp->fhs_status) { | |
201 | #ifdef DEBUG | |
202 | errno = fhp->fhs_status; | |
203 | dlog("fhandle fetch failed: %m"); | |
204 | #endif /* DEBUG */ | |
205 | return fhp->fhs_status; | |
206 | } | |
207 | return 0; | |
208 | } | |
209 | ||
2664d859 JSP |
210 | /* |
211 | * Scan mount table to see if something already mounted | |
212 | */ | |
213 | static int already_mounted P((mntlist *mlist, char*dir)); | |
214 | static int already_mounted(mlist, dir) | |
215 | mntlist *mlist; | |
216 | char *dir; | |
217 | { | |
218 | mntlist *ml; | |
219 | ||
220 | for (ml = mlist; ml; ml = ml->mnext) | |
221 | if (strcmp(ml->mnt->mnt_dir, dir) == 0) | |
222 | return 1; | |
223 | return 0; | |
224 | } | |
225 | ||
e1a31032 KM |
226 | /* |
227 | * Mount the export tree from a host | |
228 | */ | |
2664d859 JSP |
229 | static int host_fmount P((mntfs *mf)); |
230 | static int host_fmount(mf) | |
231 | mntfs *mf; | |
e1a31032 KM |
232 | { |
233 | struct timeval tv2; | |
234 | CLIENT *client; | |
235 | enum clnt_stat clnt_stat; | |
236 | int n_export; | |
2664d859 | 237 | int j, k; |
e1a31032 KM |
238 | exports exlist = 0, ex; |
239 | exports *ep = 0; | |
240 | fhstatus *fp = 0; | |
e1a31032 KM |
241 | char *host = mf->mf_server->fs_host; |
242 | int error = 0; | |
243 | struct sockaddr_in sin; | |
244 | int sock = RPC_ANYSOCK; | |
245 | int ok = FALSE; | |
2664d859 JSP |
246 | mntlist *mlist; |
247 | char fs_name[MAXPATHLEN], *rfs_dir; | |
248 | char mntpt[MAXPATHLEN]; | |
249 | ||
e1a31032 KM |
250 | #ifdef HOST_RPC_UDP |
251 | struct timeval tv; | |
252 | tv.tv_sec = 10; tv.tv_usec = 0; | |
253 | #endif /* HOST_RPC_UDP */ | |
254 | ||
2664d859 JSP |
255 | /* |
256 | * Read the mount list | |
257 | */ | |
258 | mlist = read_mtab(mf->mf_mount); | |
259 | ||
260 | /* | |
261 | * Unlock the mount list | |
262 | */ | |
263 | unlock_mntlist(); | |
264 | ||
e1a31032 KM |
265 | /* |
266 | * Take a copy of the server address | |
267 | */ | |
268 | sin = *mf->mf_server->fs_ip; | |
269 | ||
270 | /* | |
271 | * Zero out the port - make sure we recompute | |
272 | */ | |
273 | sin.sin_port = 0; | |
274 | /* | |
275 | * Make a client end-point | |
276 | */ | |
277 | #ifdef HOST_RPC_UDP | |
278 | if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) | |
279 | #else | |
280 | if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL) | |
281 | #endif /* HOST_RPC_UDP */ | |
282 | { | |
283 | plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host); | |
284 | error = EIO; | |
285 | goto out; | |
286 | } | |
287 | ||
288 | if (!nfs_auth) { | |
af359dea C |
289 | error = make_nfs_auth(); |
290 | if (error) | |
e1a31032 | 291 | goto out; |
e1a31032 KM |
292 | } |
293 | ||
294 | client->cl_auth = nfs_auth; | |
295 | ||
296 | #ifdef DEBUG | |
297 | dlog("Fetching export list from %s", host); | |
298 | #endif /* DEBUG */ | |
299 | ||
300 | /* | |
301 | * Fetch the export list | |
302 | */ | |
303 | tv2.tv_sec = 10; tv2.tv_usec = 0; | |
304 | clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2); | |
305 | if (clnt_stat != RPC_SUCCESS) { | |
306 | /*clnt_perror(client, "rpc");*/ | |
307 | error = EIO; | |
308 | goto out; | |
309 | } | |
310 | ||
311 | /* | |
312 | * Figure out how many exports were returned | |
313 | */ | |
314 | for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { | |
315 | /*printf("export %s\n", ex->ex_dir);*/ | |
316 | n_export++; | |
317 | } | |
318 | #ifdef DEBUG | |
319 | /*dlog("%d exports returned\n", n_export);*/ | |
320 | #endif /* DEBUG */ | |
321 | ||
322 | /* | |
323 | * Allocate an array of pointers into the list | |
2664d859 JSP |
324 | * so that they can be sorted. If the filesystem |
325 | * is already mounted then ignore it. | |
e1a31032 KM |
326 | */ |
327 | ep = (exports *) xmalloc(n_export * sizeof(exports)); | |
2664d859 JSP |
328 | for (j = 0, ex = exlist; ex; ex = ex->ex_next) { |
329 | MAKE_MNTPT(mntpt, ex, mf); | |
330 | if (!already_mounted(mlist, mntpt)) | |
331 | ep[j++] = ex; | |
332 | } | |
333 | n_export = j; | |
e1a31032 KM |
334 | |
335 | /* | |
336 | * Sort into order. | |
337 | * This way the mounts are done in order down the tree, | |
338 | * instead of any random order returned by the mount | |
339 | * daemon (the protocol doesn't specify...). | |
340 | */ | |
341 | qsort(ep, n_export, sizeof(exports), sortfun); | |
342 | ||
343 | /* | |
344 | * Allocate an array of filehandles | |
345 | */ | |
346 | fp = (fhstatus *) xmalloc(n_export * sizeof(fhstatus)); | |
347 | ||
348 | /* | |
349 | * Try to obtain filehandles for each directory. | |
350 | * If a fetch fails then just zero out the array | |
351 | * reference but discard the error. | |
352 | */ | |
2664d859 JSP |
353 | for (j = k = 0; j < n_export; j++) { |
354 | /* Check and avoid a duplicated export entry */ | |
355 | if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) { | |
356 | #ifdef DEBUG | |
357 | dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); | |
358 | #endif | |
e1a31032 | 359 | ep[j] = 0; |
2664d859 JSP |
360 | } else { |
361 | k = j; | |
362 | if (error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j])) | |
363 | ep[j] = 0; | |
364 | } | |
e1a31032 KM |
365 | } |
366 | ||
367 | /* | |
368 | * Mount each filesystem for which we have a filehandle. | |
369 | * If any of the mounts succeed then mark "ok" and return | |
370 | * error code 0 at the end. If they all fail then return | |
371 | * the last error code. | |
372 | */ | |
2664d859 JSP |
373 | strncpy(fs_name, mf->mf_info, sizeof(fs_name)); |
374 | if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { | |
375 | plog(XLOG_FATAL, "host_fmount: mf_info has no colon"); | |
376 | error = EINVAL; | |
377 | goto out; | |
378 | } | |
379 | ++rfs_dir; | |
e1a31032 KM |
380 | for (j = 0; j < n_export; j++) { |
381 | ex = ep[j]; | |
382 | if (ex) { | |
2664d859 JSP |
383 | strcpy(rfs_dir, ex->ex_dir); |
384 | MAKE_MNTPT(mntpt, ex, mf); | |
385 | if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0) | |
e1a31032 KM |
386 | ok = TRUE; |
387 | } | |
388 | } | |
389 | ||
390 | /* | |
391 | * Clean up and exit | |
392 | */ | |
393 | out: | |
2664d859 | 394 | discard_mntlist(mlist); |
e1a31032 KM |
395 | if (ep) |
396 | free(ep); | |
397 | if (fp) | |
398 | free(fp); | |
399 | if (client) | |
400 | clnt_destroy(client); | |
401 | if (exlist) | |
402 | xdr_pri_free(xdr_exports, &exlist); | |
403 | if (ok) | |
404 | return 0; | |
405 | return error; | |
406 | } | |
407 | ||
408 | /* | |
409 | * Return true if pref is a directory prefix of dir. | |
410 | * | |
411 | * TODO: | |
412 | * Does not work if pref is "/". | |
413 | */ | |
2664d859 | 414 | static int directory_prefix P((char *pref, char *dir)); |
e1a31032 KM |
415 | static int directory_prefix(pref, dir) |
416 | char *pref; | |
417 | char *dir; | |
418 | { | |
419 | int len = strlen(pref); | |
420 | if (strncmp(pref, dir, len) != 0) | |
421 | return FALSE; | |
422 | if (dir[len] == '/' || dir[len] == '\0') | |
423 | return TRUE; | |
424 | return FALSE; | |
425 | } | |
426 | ||
427 | /* | |
428 | * Unmount a mount tree | |
429 | */ | |
2664d859 JSP |
430 | static int host_fumount P((mntfs *mf)); |
431 | static int host_fumount(mf) | |
432 | mntfs *mf; | |
e1a31032 | 433 | { |
e1a31032 KM |
434 | mntlist *ml, *mprev; |
435 | int xerror = 0; | |
436 | ||
437 | /* | |
438 | * Read the mount list | |
439 | */ | |
440 | mntlist *mlist = read_mtab(mf->mf_mount); | |
441 | ||
442 | /* | |
443 | * Unlock the mount list | |
444 | */ | |
445 | unlock_mntlist(); | |
446 | ||
447 | /* | |
448 | * Reverse list... | |
449 | */ | |
450 | ml = mlist; | |
451 | mprev = 0; | |
452 | while (ml) { | |
453 | mntlist *ml2 = ml->mnext; | |
454 | ml->mnext = mprev; | |
455 | mprev = ml; | |
456 | ml = ml2; | |
457 | } | |
458 | mlist = mprev; | |
459 | ||
460 | /* | |
461 | * Unmount all filesystems... | |
462 | */ | |
2664d859 | 463 | for (ml = mlist; ml && !xerror; ml = ml->mnext) { |
e1a31032 KM |
464 | char *dir = ml->mnt->mnt_dir; |
465 | if (directory_prefix(mf->mf_mount, dir)) { | |
466 | int error; | |
467 | #ifdef DEBUG | |
468 | dlog("host: unmounts %s", dir); | |
469 | #endif /* DEBUG */ | |
470 | /* | |
471 | * Unmount "dir" | |
472 | */ | |
473 | error = UMOUNT_FS(dir); | |
474 | /* | |
475 | * Keep track of errors | |
476 | */ | |
477 | if (error) { | |
478 | if (!xerror) | |
479 | xerror = error; | |
480 | if (error != EBUSY) { | |
481 | errno = error; | |
482 | plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir); | |
483 | } | |
484 | } else { | |
485 | #ifdef HOST_MKDIRS | |
486 | (void) rmdirs(dir); | |
487 | #endif /* HOST_MKDIRS */ | |
488 | } | |
489 | } | |
490 | } | |
491 | ||
492 | /* | |
493 | * Throw away mount list | |
494 | */ | |
495 | discard_mntlist(mlist); | |
496 | ||
2664d859 JSP |
497 | /* |
498 | * Try to remount, except when we are shutting down. | |
499 | */ | |
500 | if (xerror && amd_state != Finishing) { | |
501 | xerror = host_fmount(mf); | |
502 | if (!xerror) { | |
af359dea C |
503 | /* |
504 | * Don't log this - it's usually too verbose | |
2664d859 | 505 | plog(XLOG_INFO, "Remounted host %s", mf->mf_info); |
af359dea | 506 | */ |
2664d859 JSP |
507 | xerror = EBUSY; |
508 | } | |
509 | } | |
e1a31032 KM |
510 | return xerror; |
511 | } | |
512 | ||
513 | #else /* HOST_EXEC */ | |
514 | ||
2664d859 | 515 | static int host_exec P((char*op, char*host, char*fs, char*opts)); |
e1a31032 KM |
516 | static int host_exec(op, host, fs, opts) |
517 | char *op; | |
518 | char *host; | |
519 | char *fs; | |
520 | char *opts; | |
521 | { | |
522 | int error; | |
523 | char *argv[7]; | |
524 | ||
525 | /* | |
526 | * Build arg vector | |
527 | */ | |
528 | argv[0] = host_helper; | |
529 | argv[1] = host_helper; | |
530 | argv[2] = op; | |
531 | argv[3] = host; | |
532 | argv[4] = fs; | |
533 | argv[5] = opts && *opts ? opts : "rw,default"; | |
534 | argv[6] = 0; | |
535 | ||
536 | /* | |
537 | * Put stdout to stderr | |
538 | */ | |
539 | (void) fclose(stdout); | |
540 | (void) dup(fileno(logfp)); | |
541 | if (fileno(logfp) != fileno(stderr)) { | |
542 | (void) fclose(stderr); | |
543 | (void) dup(fileno(logfp)); | |
544 | } | |
545 | /* | |
546 | * Try the exec | |
547 | */ | |
548 | #ifdef DEBUG | |
549 | Debug(D_FULL) { | |
550 | char **cp = argv; | |
551 | plog(XLOG_DEBUG, "executing (un)mount command..."); | |
552 | while (*cp) { | |
553 | plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp); | |
554 | cp++; | |
555 | } | |
556 | } | |
557 | #endif /* DEBUG */ | |
558 | if (argv[0] == 0 || argv[1] == 0) { | |
559 | errno = EINVAL; | |
560 | plog(XLOG_USER, "1st/2nd args missing to (un)mount program"); | |
561 | } else { | |
562 | (void) execv(argv[0], argv+1); | |
563 | } | |
564 | /* | |
565 | * Save error number | |
566 | */ | |
567 | error = errno; | |
568 | plog(XLOG_ERROR, "exec %s failed: %m", argv[0]); | |
569 | ||
570 | /* | |
571 | * Return error | |
572 | */ | |
573 | return error; | |
574 | } | |
575 | ||
2664d859 | 576 | static int host_mount P((am_node *mp)); |
e1a31032 KM |
577 | static int host_mount(mp) |
578 | am_node *mp; | |
579 | { | |
580 | mntfs *mf = mp->am_mnt; | |
581 | ||
2664d859 | 582 | return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts); |
e1a31032 KM |
583 | } |
584 | ||
2664d859 | 585 | static int host_umount P((am_node *mp)); |
e1a31032 KM |
586 | static int host_umount(mp) |
587 | am_node *mp; | |
588 | { | |
589 | mntfs *mf = mp->am_mnt; | |
590 | ||
591 | return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx"); | |
592 | } | |
593 | ||
594 | #endif /* HOST_EXEC */ | |
595 | ||
596 | /* | |
597 | * Ops structure | |
598 | */ | |
599 | am_ops host_ops = { | |
600 | "host", | |
601 | host_match, | |
602 | host_init, | |
2664d859 JSP |
603 | auto_fmount, |
604 | host_fmount, | |
605 | auto_fumount, | |
606 | host_fumount, | |
e1a31032 KM |
607 | efs_lookuppn, |
608 | efs_readdir, | |
609 | 0, /* host_readlink */ | |
610 | 0, /* host_mounted */ | |
611 | 0, /* host_umounted */ | |
612 | find_nfs_srvr, | |
613 | FS_MKMNT|FS_BACKGROUND|FS_AMQINFO | |
614 | }; | |
615 | ||
616 | #endif /* HAS_HOST */ |