GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/amd/amd/nfs_ops.c Lines: 0 187 0.0 %
Date: 2017-11-07 Branches: 0 105 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: nfs_ops.c,v 1.26 2014/10/26 03:28:41 guenther Exp $	*/
2
3
/*-
4
 * Copyright (c) 1990 Jan-Simon Pendry
5
 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6
 * Copyright (c) 1990, 1993
7
 *	The Regents of the University of California.  All rights reserved.
8
 *
9
 * This code is derived from software contributed to Berkeley by
10
 * Jan-Simon Pendry at Imperial College, London.
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions
14
 * are met:
15
 * 1. Redistributions of source code must retain the above copyright
16
 *    notice, this list of conditions and the following disclaimer.
17
 * 2. Redistributions in binary form must reproduce the above copyright
18
 *    notice, this list of conditions and the following disclaimer in the
19
 *    documentation and/or other materials provided with the distribution.
20
 * 3. Neither the name of the University nor the names of its contributors
21
 *    may be used to endorse or promote products derived from this software
22
 *    without specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34
 * SUCH DAMAGE.
35
 */
36
37
#include "am.h"
38
#include <sys/stat.h>
39
40
#ifdef HAS_NFS
41
42
#define NFS
43
#define NFSCLIENT
44
45
#include "mount.h"
46
47
/*
48
 * Network file system
49
 */
50
51
/*
52
 * Convert from nfsstat to UN*X error code
53
 */
54
#define unx_error(e)	((int)(e))
55
56
/*
57
 * The NFS layer maintains a cache of file handles.
58
 * This is *fundamental* to the implementation and
59
 * also allows quick remounting when a filesystem
60
 * is accessed soon after timing out.
61
 *
62
 * The NFS server layer knows to flush this cache
63
 * when a server goes down so avoiding stale handles.
64
 *
65
 * Each cache entry keeps a hard reference to
66
 * the corresponding server.  This ensures that
67
 * the server keepalive information is maintained.
68
 *
69
 * The copy of the sockaddr_in here is taken so
70
 * that the port can be twiddled to talk to mountd
71
 * instead of portmap or the NFS server as used
72
 * elsewhere.
73
 * The port# is flushed if a server goes down.
74
 * The IP address is never flushed - we assume
75
 * that the address of a mounted machine never
76
 * changes.  If it does, then you have other
77
 * problems...
78
 */
79
typedef struct fh_cache fh_cache;
80
struct fh_cache {
81
	qelem	fh_q;			/* List header */
82
	void	*fh_wchan;		/* Wait channel */
83
	int	fh_error;		/* Valid data? */
84
	int	fh_id;			/* Unique id */
85
	int	fh_cid;			/* Callout id */
86
	fhstatus fh_handle;		/* Handle on filesystem */
87
	struct sockaddr_in fh_sin;	/* Address of mountd */
88
	fserver *fh_fs;			/* Server holding filesystem */
89
	char	*fh_path;		/* Filesystem on host */
90
};
91
92
/*
93
 * FH_TTL is the time a file handle will remain in the cache since
94
 * last being used.  If the file handle becomes invalid, then it
95
 * will be flushed anyway.
96
 */
97
#define	FH_TTL		(5 * 60)		/* five minutes */
98
#define	FH_TTL_ERROR	(30)			/* 30 seconds */
99
100
static int fh_id = 0;
101
#define	FHID_ALLOC()	(++fh_id)
102
extern qelem fh_head;
103
qelem fh_head = { &fh_head, &fh_head };
104
105
static int call_mountd(fh_cache*, unsigned long, fwd_fun, void *);
106
107
AUTH *nfs_auth;
108
109
static fh_cache *
110
find_nfs_fhandle_cache(void *idv, int done)
111
{
112
	fh_cache *fp, *fp2 = 0;
113
	/* XXX EVIL XXX */
114
	int id = (int) ((long)idv);
115
116
	ITER(fp, fh_cache, &fh_head) {
117
		if (fp->fh_id == id) {
118
			fp2 = fp;
119
			break;
120
		}
121
	}
122
123
#ifdef DEBUG
124
	if (fp2) {
125
		dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path);
126
	} else {
127
		dlog("fh cache search failed");
128
	}
129
#endif /* DEBUG */
130
131
	if (fp2 && !done) {
132
		fp2->fh_error = ETIMEDOUT;
133
		return 0;
134
	}
135
136
	return fp2;
137
}
138
139
/*
140
 * Called when a filehandle appears
141
 */
142
static void
143
got_nfs_fh(void *pkt, int len, struct sockaddr_in *sa,
144
    struct sockaddr_in *ia, void *idv, int done)
145
{
146
	fh_cache *fp = find_nfs_fhandle_cache(idv, done);
147
	if (fp) {
148
		fp->fh_handle.fhs_vers = MOUNTVERS;
149
		fp->fh_error = pickup_rpc_reply(pkt, len, &fp->fh_handle,
150
		    xdr_fhstatus);
151
		if (!fp->fh_error) {
152
#ifdef DEBUG
153
			dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
154
#endif /* DEBUG */
155
			/*
156
			 * Wakeup anything sleeping on this filehandle
157
			 */
158
			if (fp->fh_wchan) {
159
#ifdef DEBUG
160
				dlog("Calling wakeup on %#x", fp->fh_wchan);
161
#endif /* DEBUG */
162
				wakeup(fp->fh_wchan);
163
			}
164
		}
165
	}
166
}
167
168
void
169
flush_nfs_fhandle_cache(fserver *fs)
170
{
171
	fh_cache *fp;
172
	ITER(fp, fh_cache, &fh_head) {
173
		if (fp->fh_fs == fs || fs == 0) {
174
			fp->fh_sin.sin_port = (u_short) 0;
175
			fp->fh_error = -1;
176
		}
177
	}
178
}
179
180
static void
181
discard_fh(void *arg)
182
{
183
	fh_cache *fp = arg;
184
185
	rem_que(&fp->fh_q);
186
#ifdef DEBUG
187
	dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
188
#endif /* DEBUG */
189
	free_srvr(fp->fh_fs);
190
	free(fp->fh_path);
191
	free(fp);
192
}
193
194
/*
195
 * Determine the file handle for a node
196
 */
197
static int
198
prime_nfs_fhandle_cache(char *path, fserver *fs, fhstatus *fhbuf, void *wchan)
199
{
200
	fh_cache *fp, *fp_save = 0;
201
	int error;
202
	int reuse_id = FALSE;
203
204
#ifdef DEBUG
205
	dlog("Searching cache for %s:%s", fs->fs_host, path);
206
#endif /* DEBUG */
207
208
	/*
209
	 * First search the cache
210
	 */
211
	ITER(fp, fh_cache, &fh_head) {
212
		if (fs == fp->fh_fs && strcmp(path, fp->fh_path) == 0) {
213
			switch (fp->fh_error) {
214
			case 0:
215
				error = fp->fh_error = unx_error(fp->fh_handle.fhs_stat);
216
				if (error == 0) {
217
					if (fhbuf)
218
						bcopy(&fp->fh_handle, fhbuf,
219
							sizeof(fp->fh_handle));
220
					if (fp->fh_cid)
221
						untimeout(fp->fh_cid);
222
					fp->fh_cid = timeout(FH_TTL,
223
					    discard_fh, fp);
224
				} else if (error == EACCES) {
225
					/*
226
					 * Now decode the file handle return code.
227
					 */
228
					plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
229
						fs->fs_host, path);
230
				} else {
231
					errno = error;	/* XXX */
232
					plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
233
						fs->fs_host, path);
234
				}
235
236
				/*
237
				 * The error was returned from the remote mount daemon.
238
				 * Policy: this error will be cached for now...
239
				 */
240
				return error;
241
242
			case -1:
243
				/*
244
				 * Still thinking about it, but we can re-use.
245
				 */
246
				fp_save = fp;
247
				reuse_id = TRUE;
248
				break;
249
250
			default:
251
				/*
252
				 * Return the error.
253
				 * Policy: make sure we recompute if required again
254
				 * in case this was caused by a network failure.
255
				 * This can thrash mountd's though...  If you find
256
				 * your mountd going slowly then:
257
				 * 1.  Add a fork() loop to main.
258
				 * 2.  Remove the call to innetgr() and don't use
259
				 *     netgroups, especially if you don't use YP.
260
				 */
261
				error = fp->fh_error;
262
				fp->fh_error = -1;
263
				return error;
264
			}
265
			break;
266
		}
267
	}
268
269
	/*
270
	 * Not in cache
271
	 */
272
	if (fp_save) {
273
		fp = fp_save;
274
		/*
275
		 * Re-use existing slot
276
		 */
277
		untimeout(fp->fh_cid);
278
		free_srvr(fp->fh_fs);
279
		free(fp->fh_path);
280
	} else {
281
		fp = ALLOC(fh_cache);
282
		bzero(fp, sizeof(*fp));
283
		ins_que(&fp->fh_q, &fh_head);
284
	}
285
	if (!reuse_id)
286
		fp->fh_id = FHID_ALLOC();
287
	fp->fh_wchan = wchan;
288
	fp->fh_error = -1;
289
	fp->fh_cid = timeout(FH_TTL, discard_fh, fp);
290
291
	/*
292
	 * If the address has changed then don't try to re-use the
293
	 * port information
294
	 */
295
	if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
296
		fp->fh_sin = *fs->fs_ip;
297
		fp->fh_sin.sin_port = 0;
298
	}
299
	fp->fh_fs = dup_srvr(fs);
300
	fp->fh_path = strdup(path);
301
302
	error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
303
	if (error) {
304
		/*
305
		 * Local error - cache for a short period
306
		 * just to prevent thrashing.
307
		 */
308
		untimeout(fp->fh_cid);
309
		fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
310
						discard_fh, fp);
311
		fp->fh_error = error;
312
	} else {
313
		error = fp->fh_error;
314
	}
315
	return error;
316
}
317
318
int
319
make_nfs_auth(void)
320
{
321
	/*
322
	 * From: Chris Metcalf <metcalf@masala.lcs.mit.edu>
323
	 * Use hostd, not just hostname.  Note that uids
324
	 * and gids and the gidlist are type *int* and not the
325
	 * system uid_t and gid_t types.
326
	 */
327
	static int group_wheel = 0;
328
	nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
329
	if (!nfs_auth)
330
		return ENOBUFS;
331
	return 0;
332
}
333
334
static int
335
call_mountd(fh_cache *fp, u_long proc, fwd_fun f, void *wchan)
336
{
337
	struct rpc_msg mnt_msg;
338
	int len;
339
	char iobuf[8192];
340
	int error;
341
342
	if (!nfs_auth) {
343
		error = make_nfs_auth();
344
		if (error)
345
			return error;
346
	}
347
348
	if (fp->fh_sin.sin_port == 0) {
349
		u_short port;
350
		error = nfs_srvr_port(fp->fh_fs, &port, wchan);
351
		if (error)
352
			return error;
353
		fp->fh_sin.sin_port = port;
354
	}
355
356
	rpc_msg_init(&mnt_msg, MOUNTPROG, MOUNTVERS, (unsigned long) 0);
357
	len = make_rpc_packet(iobuf, sizeof(iobuf), proc,
358
			&mnt_msg, &fp->fh_path, xdr_nfspath,  nfs_auth);
359
360
	/*
361
	 * XXX EVIL!  We cast fh_id to a pointer, then back to an int
362
	 * XXX later.
363
	 */
364
	if (len > 0) {
365
		error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
366
			iobuf, len, &fp->fh_sin, &fp->fh_sin,
367
			(void *)((long)fp->fh_id), f);
368
	} else {
369
		error = -len;
370
	}
371
/*
372
 * It may be the case that we're sending to the wrong MOUNTD port.  This
373
 * occurs if mountd is restarted on the server after the port has been
374
 * looked up and stored in the filehandle cache somewhere.  The correct
375
 * solution, if we're going to cache port numbers is to catch the ICMP
376
 * port unreachable reply from the server and cause the portmap request
377
 * to be redone.  The quick solution here is to invalidate the MOUNTD
378
 * port.
379
 */
380
      fp->fh_sin.sin_port = 0;
381
382
	return error;
383
}
384
385
/*-------------------------------------------------------------------------*/
386
387
/*
388
 * NFS needs the local filesystem, remote filesystem
389
 * remote hostname.
390
 * Local filesystem defaults to remote and vice-versa.
391
 */
392
static char *
393
nfs_match(am_opts *fo)
394
{
395
	char *xmtab;
396
	if (fo->opt_fs && !fo->opt_rfs)
397
		fo->opt_rfs = fo->opt_fs;
398
	if (!fo->opt_rfs) {
399
		plog(XLOG_USER, "nfs: no remote filesystem specified");
400
		return FALSE;
401
	}
402
	if (!fo->opt_rhost) {
403
		plog(XLOG_USER, "nfs: no remote host specified");
404
		return FALSE;
405
	}
406
	/*
407
	 * Determine magic cookie to put in mtab
408
	 */
409
	xmtab = xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
410
	snprintf(xmtab, strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2,
411
		"%s:%s", fo->opt_rhost, fo->opt_rfs);
412
#ifdef DEBUG
413
	dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
414
		fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
415
#endif /* DEBUG */
416
417
	return xmtab;
418
}
419
420
/*
421
 * Initialise am structure for nfs
422
 */
423
static int
424
nfs_init(mntfs *mf)
425
{
426
	if (!mf->mf_private) {
427
		int error;
428
		fhstatus fhs;
429
430
		char *colon = strchr(mf->mf_info, ':');
431
		if (colon == 0)
432
			return ENOENT;
433
434
		error = prime_nfs_fhandle_cache(colon+1, mf->mf_server,
435
		    &fhs, mf);
436
		if (!error) {
437
			mf->mf_private = ALLOC(fhstatus);
438
			mf->mf_prfree = free;
439
			bcopy(&fhs, mf->mf_private, sizeof(fhs));
440
		}
441
		return error;
442
	}
443
444
	return 0;
445
}
446
447
int
448
mount_nfs_fh(fhstatus *fhp, char *dir, char *fs_name, char *opts,
449
    mntfs *mf)
450
{
451
	struct nfs_args nfs_args;
452
	struct mntent mnt;
453
	int retry;
454
	char *colon;
455
	/*char *path;*/
456
	char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
457
	fserver *fs = mf->mf_server;
458
	int flags;
459
	char *xopts;
460
	int error;
461
#ifdef notdef
462
	unsigned short port;
463
#endif /* notdef */
464
465
	const char *type = MOUNT_NFS;
466
467
	bzero(&nfs_args, sizeof(nfs_args));	/* Paranoid */
468
469
	/*
470
	 * Extract host name to give to kernel
471
	 */
472
	if (!(colon = strchr(fs_name, ':')))
473
		return ENOENT;
474
	strlcpy(host, fs_name, sizeof(host));
475
	/*path = colon + 1;*/
476
477
	if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr))
478
		xopts = strdup(mf->mf_remopts);
479
	else
480
		xopts = strdup(opts);
481
482
	bzero(&nfs_args, sizeof(nfs_args));
483
484
	mnt.mnt_dir = dir;
485
	mnt.mnt_fsname = fs_name;
486
	mnt.mnt_type = "nfs";
487
	mnt.mnt_opts = xopts;
488
	mnt.mnt_freq = 0;
489
	mnt.mnt_passno = 0;
490
491
	retry = hasmntval(&mnt, "retry");
492
	if (retry <= 0)
493
		retry = 1;	/* XXX */
494
495
/*again:*/
496
497
	/*
498
	 * set mount args
499
	 */
500
	nfs_args.fh = (void *)fhp->fhs_fhandle;
501
	nfs_args.fhsize = fhp->fhs_size;
502
	nfs_args.version = NFS_ARGSVERSION;
503
504
	nfs_args.hostname = host;
505
#ifdef HOSTNAMESZ
506
	/*
507
	 * Most kernels have a name length restriction.
508
	 */
509
	if (strlen(host) >= HOSTNAMESZ)
510
		strlcpy(host + HOSTNAMESZ - 3, "..", sizeof host - HOSTNAMESZ + 3);
511
#endif /* HOSTNAMESZ */
512
513
	if ((nfs_args.rsize = hasmntval(&mnt, "rsize")))
514
		nfs_args.flags |= NFSMNT_RSIZE;
515
516
#ifdef NFSMNT_READDIRSIZE
517
	if ((nfs_args.readdirsize = hasmntval(&mnt, "readdirsize"))) {
518
		nfs_args.flags |= NFSMNT_READDIRSIZE;
519
	} else if (nfs_args.rsize) {
520
		nfs_args.readdirsize = nfs_args.rsize;
521
		nfs_args.flags |= NFSMNT_READDIRSIZE;
522
	}
523
#endif
524
525
	if ((nfs_args.wsize = hasmntval(&mnt, "wsize")))
526
		nfs_args.flags |= NFSMNT_WSIZE;
527
528
	if ((nfs_args.timeo = hasmntval(&mnt, "timeo")))
529
		nfs_args.flags |= NFSMNT_TIMEO;
530
531
	if ((nfs_args.retrans = hasmntval(&mnt, "retrans")))
532
		nfs_args.flags |= NFSMNT_RETRANS;
533
534
#ifdef NFSMNT_BIODS
535
	if ((nfs_args.biods = hasmntval(&mnt, "biods")))
536
		nfs_args.flags |= NFSMNT_BIODS;
537
538
#endif /* NFSMNT_BIODS */
539
540
#ifdef NFSMNT_MAXGRPS
541
	if ((nfs_args.maxgrouplist = hasmntval(&mnt, "maxgroups")))
542
		nfs_args.flags |= NFSMNT_MAXGRPS;
543
#endif /* NFSMNT_MAXGRPS */
544
545
#ifdef NFSMNT_READAHEAD
546
	if ((nfs_args.readahead = hasmntval(&mnt, "readahead")))
547
		nfs_args.flags |= NFSMNT_READAHEAD;
548
#endif /* NFSMNT_READAHEAD */
549
550
#ifdef notdef
551
/*
552
 * This isn't supported by the ping algorithm yet.
553
 * In any case, it is all done in nfs_init().
554
 */
555
	if ((port = hasmntval(&mnt, "port")))
556
		sin.sin_port = htons(port);
557
	else
558
		sin.sin_port = htons(NFS_PORT);	/* XXX should use portmapper */
559
#endif /* notdef */
560
561
	if (hasmntopt(&mnt, "soft") != NULL)
562
		nfs_args.flags |= NFSMNT_SOFT;
563
564
#ifdef NFSMNT_SPONGY
565
	if (hasmntopt(&mnt, "spongy") != NULL) {
566
		nfs_args.flags |= NFSMNT_SPONGY;
567
		if (nfs_args.flags & NFSMNT_SOFT) {
568
			plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored");
569
			nfs_args.flags &= ~NFSMNT_SOFT;
570
		}
571
	}
572
#endif /* MNTOPT_SPONGY */
573
574
	if (hasmntopt(&mnt, "intr") != NULL)
575
		nfs_args.flags |= NFSMNT_INT;
576
577
#ifdef MNTOPT_NODEVS
578
	if (hasmntopt(&mnt, MNTOPT_NODEVS) != NULL)
579
		nfs_args.flags |= NFSMNT_NODEVS;
580
#endif /* MNTOPT_NODEVS */
581
582
583
	if (hasmntopt(&mnt, "noconn") != NULL)
584
		nfs_args.flags |= NFSMNT_NOCONN;
585
586
	if (hasmntopt(&mnt, "resvport") != NULL)
587
		nfs_args.flags |= NFSMNT_RESVPORT;
588
589
#ifdef NFSMNT_PGTHRESH
590
	if ((nfs_args.pg_thresh = hasmntval(&mnt, "pgthresh")))
591
		nfs_args.flags |= NFSMNT_PGTHRESH;
592
#endif /* NFSMNT_PGTHRESH */
593
594
	nfs_args.addr = (struct sockaddr *)fs->fs_ip;
595
	nfs_args.addrlen = sizeof(*fs->fs_ip);
596
	nfs_args.sotype = SOCK_DGRAM;
597
	nfs_args.proto = 0;
598
599
	flags = compute_mount_flags(&mnt);
600
601
#ifdef NFSMNT_NOCTO
602
	if (hasmntopt(&mnt, "nocto") != NULL)
603
		nfs_args.flags |= NFSMNT_NOCTO;
604
#endif /* NFSMNT_NOCTO */
605
606
	if (hasmntopt(&mnt, "tcp") != NULL)
607
		nfs_args.sotype = SOCK_STREAM;
608
609
610
611
	error = mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
612
	free(xopts);
613
	return error;
614
}
615
616
static int
617
mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf)
618
{
619
#ifdef notdef
620
	int error;
621
	fhstatus fhs;
622
	char *colon;
623
624
	if (!(colon = strchr(fs_name, ':')))
625
		return ENOENT;
626
627
#ifdef DEBUG
628
	dlog("locating fhandle for %s", fs_name);
629
#endif /* DEBUG */
630
	error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, NULL);
631
632
	if (error)
633
		return error;
634
635
	return mount_nfs_fh(&fhs, dir, fs_name, opts, mf);
636
#endif
637
	if (!mf->mf_private) {
638
		plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
639
		return EINVAL;
640
	}
641
642
	return mount_nfs_fh((fhstatus *) mf->mf_private, dir, fs_name, opts, mf);
643
}
644
645
static int
646
nfs_fmount(mntfs *mf)
647
{
648
	int error;
649
650
	error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
651
652
#ifdef DEBUG
653
	if (error) {
654
		errno = error;
655
		dlog("mount_nfs: %m");
656
	}
657
#endif /* DEBUG */
658
	return error;
659
}
660
661
static int
662
nfs_fumount(mntfs *mf)
663
{
664
	return (umount_fs(mf->mf_mount));
665
}
666
667
static void
668
nfs_umounted(am_node *mp)
669
{
670
671
#ifdef KICK_KERNEL
672
	/* This should go into the mainline code, not in nfs_ops... */
673
674
	/*
675
	 * Run lstat over the underlying directory in
676
	 * case this was a direct mount.  This will
677
	 * get the kernel back in sync with reality.
678
	 */
679
	if (mp->am_parent && mp->am_parent->am_path &&
680
	    STREQ(mp->am_parent->am_mnt->mf_ops->fs_type, "direct")) {
681
		struct stat stb;
682
		pid_t pid;
683
		if ((pid = background()) == 0) {
684
			if (lstat(mp->am_parent->am_path, &stb) < 0) {
685
				plog(XLOG_ERROR, "lstat(%s) after unmount: %m", mp->am_parent->am_path);
686
#ifdef DEBUG
687
			} else {
688
				dlog("hack lstat(%s): ok", mp->am_parent->am_path);
689
#endif /* DEBUG */
690
			}
691
			_exit(0);
692
		}
693
	}
694
#endif /* KICK_KERNEL */
695
}
696
697
/*
698
 * Network file system
699
 */
700
am_ops nfs_ops = {
701
	"nfs",
702
	nfs_match,
703
	nfs_init,
704
	auto_fmount,
705
	nfs_fmount,
706
	auto_fumount,
707
	nfs_fumount,
708
	efs_lookuppn,
709
	efs_readdir,
710
	0, /* nfs_readlink */
711
	0, /* nfs_mounted */
712
	nfs_umounted,
713
	find_nfs_srvr,
714
	FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
715
};
716
717
#endif /* HAS_NFS */