xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_log.c (revision 168c213023b7f347f11abfc72f448b0c621ab718)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/cred.h>
29 #include <sys/cmn_err.h>
30 #include <sys/debug.h>
31 #include <sys/systm.h>
32 #include <sys/kmem.h>
33 #include <sys/disp.h>
34 #include <sys/atomic.h>
35 #include <rpc/types.h>
36 #include <nfs/nfs.h>
37 #include <nfs/nfssys.h>
38 #include <nfs/export.h>
39 #include <nfs/rnode.h>
40 #include <rpc/auth.h>
41 #include <rpc/svc.h>
42 #include <rpc/xdr.h>
43 #include <rpc/clnt.h>
44 #include <nfs/nfs_log.h>
45 
46 #define	NUM_RECORDS_TO_WRITE 256
47 #define	NUM_BYTES_TO_WRITE 65536
48 
49 extern krwlock_t exported_lock;
50 
51 static int nfslog_num_records_to_write = NUM_RECORDS_TO_WRITE;
52 static int nfslog_num_bytes_to_write = NUM_BYTES_TO_WRITE;
53 
54 /*
55  * This struct is used to 'hide' the details of managing the log
56  * records internally to the logging code.  Allocation routines
57  * are used to obtain pieces of memory for XDR encoding.  This struct
58  * is a 'header' to those areas and a opaque cookie is used to pass
59  * this data structure between the allocating function and the put
60  * function.
61  */
62 struct lr_alloc {
63 	struct lr_alloc		*next;		/* links for write queuing */
64 	struct lr_alloc		*prev;
65 #define	LR_ALLOC_NOFREE	0x1			/* not present, call free */
66 	int			lr_flags;
67 	caddr_t			log_record;	/* address to XDR encoding */
68 	size_t			size;		/* final size of encoding */
69 	struct kmem_cache	*alloc_cache;	/* keep track of cache ptr */
70 	struct exportinfo	*exi;		/* who are we related to? */
71 	struct log_buffer	*lb;
72 };
73 
74 struct flush_thread_params {
75 	struct nfsl_flush_args tp_args;
76 	int tp_error;
77 };
78 
79 static int log_file_create(caddr_t, struct log_file **);
80 static void log_file_rele(struct log_file *);
81 static struct log_buffer *log_buffer_create(caddr_t);
82 static void log_buffer_rele(struct log_buffer *);
83 static int nfslog_record_append2all(struct lr_alloc *);
84 static int nfslog_logbuffer_rename(struct log_buffer *);
85 static void nfslog_logfile_wait(struct log_file *);
86 static int nfslog_logfile_rename(char *, char *);
87 static void nfslog_do_flush(struct flush_thread_params *);
88 static void create_buffer_header(caddr_t *, size_t *, size_t *);
89 
90 static int nfslog_write_logrecords(struct log_file *, struct lr_alloc *, int);
91 static void nfslog_free_logrecords(struct lr_alloc *);
92 static int nfslog_records_flush_to_disk(struct log_buffer *);
93 static int nfslog_records_flush_to_disk_nolock(struct log_buffer *);
94 
95 /*
96  * Read/Write lock that protects 'nfslog_buffer_list'.
97  * This lock must be held when searching or modifying 'nfslog_buffer_list'.
98  */
99 static krwlock_t nfslog_buffer_list_lock;
100 
101 /*
102  * The list of "log_buffer" structures.
103  */
104 struct log_buffer *nfslog_buffer_list = NULL;
105 
106 
107 #define	LOG_BUFFER_HOLD(lbp)	{ \
108 	mutex_enter(&(lbp)->lb_lock); \
109 	(lbp)->lb_refcnt++; \
110 	mutex_exit(&(lbp)->lb_lock); \
111 }
112 
113 #define	LOG_FILE_HOLD(lfp)	{ \
114 	mutex_enter(&(lfp)->lf_lock); \
115 	(lfp)->lf_refcnt++; \
116 	mutex_exit(&(lfp)->lf_lock); \
117 }
118 
119 #define	LOG_FILE_RELE(lfp)	{ \
120 	log_file_rele(lfp); \
121 }
122 
123 /*
124  * These two macros are used to prep a logfile data structure and
125  * associated file for writing data.  Note that the lf_lock is
126  * held as a result of the call to the first macro.  This is used
127  * for serialization correctness between the logbuffer struct and
128  * the logfile struct.
129  */
130 #define	LOG_FILE_LOCK_TO_WRITE(lfp)	{ \
131 	mutex_enter(&(lfp)->lf_lock); \
132 	(lfp)->lf_refcnt++; \
133 	(lfp)->lf_writers++; \
134 }
135 
136 #define	LOG_FILE_UNLOCK_FROM_WRITE(lfp)	{ \
137 	(lfp)->lf_writers--; \
138 	if ((lfp)->lf_writers == 0 && ((lfp)->lf_flags & L_WAITING)) { \
139 		(lfp)->lf_flags &= ~L_WAITING; \
140 		cv_broadcast(&(lfp)->lf_cv_waiters); \
141 	} \
142 	mutex_exit(&(lfp)->lf_lock); \
143 	log_file_rele(lfp); \
144 }
145 
146 int rfsl_log_buffer = 0;
147 static int rfsl_log_file = 0;
148 
149 /* This array is used for memory allocation of record encoding spaces */
150 static struct {
151 	int	size;
152 	struct kmem_cache *mem_cache;
153 	char	*cache_name;
154 } nfslog_mem_alloc[] = {
155 #define	SMALL_INDX 0
156 	{ NFSLOG_SMALL_RECORD_SIZE - sizeof (struct lr_alloc),
157 	NULL, NFSLOG_SMALL_REC_NAME },
158 #define	MEDIUM_INDX 1
159 	{ NFSLOG_MEDIUM_RECORD_SIZE - sizeof (struct lr_alloc),
160 	NULL, NFSLOG_MEDIUM_REC_NAME },
161 #define	LARGE_INDX 2
162 	{ NFSLOG_LARGE_RECORD_SIZE - sizeof (struct lr_alloc),
163 	NULL, NFSLOG_LARGE_REC_NAME },
164 	{ (-1), NULL }
165 };
166 
167 /* Used to calculate the 'real' allocation size */
168 #define	ALLOC_SIZE(index) \
169 	(nfslog_mem_alloc[index].size + sizeof (struct lr_alloc))
170 
171 /*
172  * Initialize logging data buffer cache
173  */
174 void
175 nfslog_init()
176 {
177 	int indx;
178 
179 	rw_init(&nfslog_buffer_list_lock, NULL, RW_DEFAULT, NULL);
180 
181 	/*
182 	 * Initialize the kmem caches for encoding
183 	 */
184 	for (indx = 0; nfslog_mem_alloc[indx].size != (-1); indx++) {
185 		nfslog_mem_alloc[indx].mem_cache =
186 			kmem_cache_create(nfslog_mem_alloc[indx].cache_name,
187 				ALLOC_SIZE(indx),
188 				0, NULL, NULL, NULL, NULL, NULL, 0);
189 	}
190 }
191 
192 /*
193  * Sets up the necessary log file and related buffers to enable logging
194  * on the given export point.
195  * Returns 0 on success, non-zero on failure.
196  */
197 int
198 nfslog_setup(struct exportinfo *exi)
199 {
200 	struct exportdata *kex;
201 	struct log_buffer *lbp;
202 	struct log_buffer *nlbp;
203 
204 	kex = &exi->exi_export;
205 	ASSERT(kex->ex_flags & EX_LOG);
206 
207 	/*
208 	 * Logging is enabled for the new export point, check
209 	 * the existing log_buffer structures to see if the
210 	 * desired buffer has already been opened. If so, point
211 	 * the new exportinfo's exi_logbuffer to the existing
212 	 * one.
213 	 */
214 	rw_enter(&nfslog_buffer_list_lock, RW_READER);
215 	for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) {
216 		LOGGING_DPRINT((10,
217 		    "searching for buffer... found log_buffer '%s'\n",
218 				lbp->lb_path));
219 		if (strcmp(lbp->lb_path, kex->ex_log_buffer) == 0) {
220 			/* Found our match. Ref it and return */
221 			LOG_BUFFER_HOLD(lbp);
222 			exi->exi_logbuffer = lbp;
223 			LOGGING_DPRINT((10,  "\tfound log_buffer for '%s'\n",
224 				kex->ex_log_buffer));
225 			rw_exit(&nfslog_buffer_list_lock);
226 			return (0);
227 		}
228 	}
229 	rw_exit(&nfslog_buffer_list_lock);
230 
231 	/*
232 	 * New buffer needed, allocate it.
233 	 * The buffer list lock has been dropped so we will need to search
234 	 * the list again to ensure that another thread has not added
235 	 * a matching buffer.
236 	 */
237 	if ((nlbp = log_buffer_create(kex->ex_log_buffer)) == NULL) {
238 		/*
239 		 * Failed the buffer creation for some reason so we
240 		 * will need to return.
241 		 */
242 		return (EIO);
243 	}
244 
245 	rw_enter(&nfslog_buffer_list_lock, RW_WRITER);
246 	for (lbp = nfslog_buffer_list; lbp != NULL;
247 		lbp = lbp->lb_next) {
248 		if (strcmp(lbp->lb_path, kex->ex_log_buffer) == 0) {
249 				/*
250 				 * A log_buffer already exists for the
251 				 * indicated buffer, use it instead.
252 				 */
253 			LOG_BUFFER_HOLD(lbp);
254 
255 			exi->exi_logbuffer = lbp;
256 
257 			LOGGING_DPRINT((10,
258 				"found log_buffer for '%s' "
259 				"after allocation\n",
260 				kex->ex_log_buffer));
261 
262 			rw_exit(&nfslog_buffer_list_lock);
263 
264 			log_buffer_rele(nlbp);
265 
266 			return (0);
267 		}
268 	}
269 	/*
270 	 * Didn't find an existing log_buffer for this buffer,
271 	 * use the the newly created one, and add to list.  We
272 	 * increment the reference count because the node is
273 	 * entered into the global list.
274 	 */
275 	LOGGING_DPRINT((10, "exportfs: adding nlbp=%p to list\n",
276 		nlbp));
277 
278 	nlbp->lb_next = nfslog_buffer_list;
279 	nfslog_buffer_list = nlbp;
280 
281 	LOG_BUFFER_HOLD(nlbp);	/* hold is for export entry */
282 	exi->exi_logbuffer = nlbp;
283 
284 	rw_exit(&nfslog_buffer_list_lock);
285 
286 	return (0);
287 }
288 
289 /*
290  * Disables logging for the given export point.
291  */
292 void
293 nfslog_disable(struct exportinfo *exi)
294 {
295 	log_buffer_rele(exi->exi_logbuffer);
296 }
297 
298 /*
299  * Creates the corresponding log_buffer and log_file structures
300  * for the the buffer named 'name'.
301  * Returns a pointer to the log_buffer structure with reference one.
302  */
303 static struct log_buffer *
304 log_buffer_create(caddr_t name)
305 {
306 	struct log_buffer *buffer;
307 	struct log_file *logfile;
308 	int namelen = strlen(name);
309 
310 	LOGGING_DPRINT((10,  "log_buffer_create: %s\n", name));
311 	if (log_file_create(name, &logfile))
312 		return (NULL);
313 
314 	buffer = (struct log_buffer *)kmem_alloc(sizeof (*buffer), KM_SLEEP);
315 	buffer->lb_refcnt = 1;
316 	buffer->lb_rec_id = 0;
317 	buffer->lb_path = (caddr_t)kmem_alloc(namelen + 1, KM_SLEEP);
318 	bcopy(name, buffer->lb_path, namelen + 1);
319 	buffer->lb_logfile = logfile;
320 	buffer->lb_records = NULL;
321 	buffer->lb_num_recs = 0;
322 	buffer->lb_size_queued = 0;
323 	mutex_init(&buffer->lb_lock, NULL, MUTEX_DEFAULT, NULL);
324 	rfsl_log_buffer++;
325 
326 	return (buffer);
327 }
328 
329 /*
330  * Release a log_buffer structure
331  */
332 static void
333 log_buffer_rele(struct log_buffer *lbp)
334 {
335 	int len;
336 
337 	mutex_enter(&lbp->lb_lock);
338 	if (--lbp->lb_refcnt > 1) {
339 		mutex_exit(&lbp->lb_lock);
340 		return;
341 	}
342 
343 	if (lbp->lb_refcnt < 0) {
344 		panic("log_rele: log_buffer refcnt < 0");
345 		/*NOTREACHED*/
346 	}
347 
348 	/*
349 	 * Need to drop the lb_lock before acquiring the
350 	 * nfslog_buffer_list_lock. To avoid double free we need
351 	 * to hold an additional reference to the log buffer.
352 	 * This will ensure that no two threads will simultaneously
353 	 * be trying to free the same log buffer.
354 	 */
355 
356 	if (lbp->lb_refcnt == 1) {
357 
358 		/*
359 		 * If the ref count is 1, then the last
360 		 * unshare/reference has been given up and we need to
361 		 * clean up the buffer and remove it from the buffer
362 		 * list.
363 		 */
364 		LOGGING_DPRINT((10,
365 			"log_buffer_rele lbp=%p disconnecting\n", lbp));
366 		/*
367 		 * Hold additional reference before dropping the lb_lock
368 		 */
369 
370 		lbp->lb_refcnt++;
371 		mutex_exit(&lbp->lb_lock);
372 
373 		/*
374 		 * Make sure that all of the buffered records are written.
375 		 * Don't bother checking the write return value since there
376 		 * isn't much we can do at this point.
377 		 */
378 		(void) nfslog_records_flush_to_disk(lbp);
379 
380 		rw_enter(&nfslog_buffer_list_lock, RW_WRITER);
381 		mutex_enter(&lbp->lb_lock);
382 		/*
383 		 * Drop the reference count held above.
384 		 * If the ref count is still > 1 then someone has
385 		 * stepped in to use this log buffer.  unlock and return.
386 		 */
387 		if (--lbp->lb_refcnt > 1) {
388 			mutex_exit(&lbp->lb_lock);
389 			rw_exit(&nfslog_buffer_list_lock);
390 			return;
391 		}
392 
393 		if (lbp == nfslog_buffer_list) {
394 			nfslog_buffer_list = lbp->lb_next;
395 		} else {
396 			struct log_buffer *tlbp;
397 
398 			/* Drop the log_buffer from the master list */
399 			for (tlbp = nfslog_buffer_list; tlbp->lb_next != NULL;
400 					tlbp = tlbp->lb_next) {
401 				if (tlbp->lb_next == lbp) {
402 					tlbp->lb_next = lbp->lb_next;
403 					break;
404 				}
405 			}
406 		}
407 
408 		mutex_exit(&lbp->lb_lock);
409 		rw_exit(&nfslog_buffer_list_lock);
410 	}
411 	/*
412 	 * ref count zero; finish clean up.
413 	 */
414 	LOGGING_DPRINT((10, "log_buffer_rele lbp=%p freeing\n", lbp));
415 
416 	log_file_rele(lbp->lb_logfile);
417 	len = strlen(lbp->lb_path) + 1;
418 	kmem_free(lbp->lb_path, len);
419 	kmem_free(lbp, sizeof (*lbp));
420 	rfsl_log_buffer--;
421 }
422 
423 /*
424  * Creates the corresponding log_file structure for the buffer
425  * named 'log_file_name'.
426  * 'log_file_name' is created by concatenating 'origname' and LOG_INPROG_STRING.
427  * 'logfile' is set to be the log_file structure with reference one.
428  */
429 static int
430 log_file_create(caddr_t origname, struct log_file **lfpp)
431 {
432 	vnode_t *vp = NULL;
433 	char *name;
434 	int namelen;
435 	int error;
436 	struct log_file *logfile = NULL;
437 	vattr_t va;
438 	caddr_t loghdr = NULL;
439 	size_t loghdr_len = 0;
440 	size_t loghdr_free = 0;
441 
442 	namelen = strlen(origname) + strlen(LOG_INPROG_STRING);
443 	name = (caddr_t)kmem_alloc(namelen + 1, KM_SLEEP);
444 	(void) sprintf(name, "%s%s", origname, LOG_INPROG_STRING);
445 
446 	LOGGING_DPRINT((3, "log_file_create: %s\n", name));
447 	if (error = vn_open(name, UIO_SYSSPACE, FCREAT|FWRITE|FOFFMAX,
448 			LOG_MODE, &vp, CRCREAT, 0)) {
449 		nfs_cmn_err(error, CE_WARN,
450 			"log_file_create: Can not open %s - error %m", name);
451 		goto out;
452 	}
453 	LOGGING_DPRINT((3, "log_file_create: %s vp=%p v_count=%d\n",
454 		name, vp, vp->v_count));
455 
456 	logfile = (struct log_file *)kmem_zalloc(sizeof (*logfile), KM_SLEEP);
457 	logfile->lf_path = name;
458 	/*
459 	 * No need to bump the vnode reference count since it is set
460 	 * to one by vn_open().
461 	 */
462 	logfile->lf_vp = vp;
463 	logfile->lf_refcnt = 1;
464 	mutex_init(&logfile->lf_lock, NULL, MUTEX_DEFAULT, NULL);
465 	rfsl_log_file++;
466 
467 	va.va_mask = AT_SIZE;
468 	error = VOP_GETATTR(vp, &va, 0, CRED());
469 	if (error) {
470 		nfs_cmn_err(error, CE_WARN,
471 			"log_file_create: Can not stat %s - error = %m",
472 			name);
473 		goto out;
474 	}
475 
476 	if (va.va_size == 0) {
477 		struct lr_alloc lr;
478 
479 		/*
480 		 * Write Header.
481 		 */
482 		create_buffer_header(&loghdr, &loghdr_len, &loghdr_free);
483 		/*
484 		 * Dummy up a lr_alloc struct for the write
485 		 */
486 		lr.next = lr.prev = &lr;
487 		lr.lr_flags = 0;
488 		lr.log_record = loghdr;
489 		lr.size = loghdr_len;
490 		lr.alloc_cache = NULL;
491 		lr.exi = NULL;
492 		lr.lb = NULL;
493 
494 		mutex_enter(&logfile->lf_lock);
495 
496 		error = nfslog_write_logrecords(logfile, &lr, 1);
497 
498 		mutex_exit(&logfile->lf_lock);
499 
500 		if (error != 0) {
501 			nfs_cmn_err(error, CE_WARN,
502 				"log_file_create: Can not write header "
503 				"on %s - error = %m", name);
504 			goto out;
505 		}
506 	}
507 	*lfpp = logfile;
508 
509 	if (loghdr != NULL)
510 		kmem_free(loghdr, loghdr_free);
511 
512 	return (0);
513 
514 out:
515 	if (vp != NULL) {
516 		int error1;
517 		error1 = VOP_CLOSE(vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0,
518 				CRED());
519 		if (error1) {
520 			nfs_cmn_err(error1, CE_WARN,
521 				"log_file_create: Can not close %s - "
522 				"error = %m", name);
523 		}
524 		VN_RELE(vp);
525 	}
526 
527 	kmem_free(name, namelen + 1);
528 	if (logfile != NULL) {
529 		mutex_destroy(&logfile->lf_lock);
530 		kmem_free(logfile, sizeof (*logfile));
531 		rfsl_log_file--;
532 	}
533 	if (loghdr != NULL)
534 		kmem_free(loghdr, loghdr_free);
535 
536 	return (error);
537 }
538 
539 /*
540  * Release a log_file structure
541  */
542 static void
543 log_file_rele(struct log_file *lfp)
544 {
545 	int len;
546 	int error;
547 
548 	mutex_enter(&lfp->lf_lock);
549 	if (--lfp->lf_refcnt > 0) {
550 		LOGGING_DPRINT((10,
551 			"log_file_rele lfp=%p decremented refcnt to %d\n",
552 			lfp, lfp->lf_refcnt));
553 		mutex_exit(&lfp->lf_lock);
554 		return;
555 	}
556 	if (lfp->lf_refcnt < 0) {
557 		panic("log_file_rele: log_file refcnt < 0");
558 		/*NOTREACHED*/
559 	}
560 
561 	LOGGING_DPRINT((10, "log_file_rele lfp=%p freeing node\n", lfp));
562 
563 	lfp->lf_flags &= ~(L_PRINTED | L_ERROR);
564 
565 	ASSERT(lfp->lf_flags == 0);
566 	ASSERT(lfp->lf_writers == 0);
567 
568 	if (error = VOP_CLOSE(lfp->lf_vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0,
569 		CRED())) {
570 		nfs_cmn_err(error, CE_WARN,
571 			"NFS: Could not close log buffer %s - error = %m",
572 			lfp->lf_path);
573 #ifdef DEBUG
574 	} else {
575 		LOGGING_DPRINT((3,
576 			"log_file_rele: %s has been closed vp=%p "
577 			"v_count=%d\n",
578 			lfp->lf_path, lfp->lf_vp, lfp->lf_vp->v_count));
579 #endif
580 	}
581 	VN_RELE(lfp->lf_vp);
582 
583 	len = strlen(lfp->lf_path) + 1;
584 	kmem_free(lfp->lf_path, len);
585 	kmem_free(lfp, sizeof (*lfp));
586 	rfsl_log_file--;
587 }
588 
589 /*
590  * Allocates a record of the size specified.
591  * 'exi' identifies the exportinfo structure being logged.
592  * 'size' indicates how much memory should be allocated
593  * 'cookie' is used to store an opaque value for the caller for later use
594  * 'flags' currently ignored.
595  *
596  * Returns a pointer to the beginning of the allocated memory.
597  * 'cookie' is a pointer to the 'lr_alloc' struct; this will be used
598  * to keep track of the encoded record and contains all the info
599  * for enqueuing the record on the log buffer for later writing.
600  *
601  * nfslog_record_put() must be used to 'free' this record or allocation.
602  */
603 /* ARGSUSED */
604 void *
605 nfslog_record_alloc(
606 	struct exportinfo *exi,
607 	int alloc_indx,
608 	void **cookie,
609 	int flags)
610 {
611 	struct lr_alloc *lrp;
612 
613 	lrp = (struct lr_alloc *)
614 		kmem_cache_alloc(nfslog_mem_alloc[alloc_indx].mem_cache,
615 			KM_NOSLEEP);
616 
617 	if (lrp == NULL) {
618 		*cookie = NULL;
619 		return (NULL);
620 	}
621 
622 	lrp->next = lrp;
623 	lrp->prev = lrp;
624 	lrp->lr_flags = 0;
625 
626 	lrp->log_record = (caddr_t)((uintptr_t)lrp +
627 		(uintptr_t)sizeof (struct lr_alloc));
628 	lrp->size = nfslog_mem_alloc[alloc_indx].size;
629 	lrp->alloc_cache = nfslog_mem_alloc[alloc_indx].mem_cache;
630 	lrp->exi = exi;
631 
632 	if (exi->exi_export.ex_flags & EX_LOG) {
633 		LOG_BUFFER_HOLD(exi->exi_logbuffer);
634 		lrp->lb = exi->exi_logbuffer;
635 	} else {
636 		lrp->lb = NULL;
637 	}
638 
639 	*cookie = (void *)lrp;
640 
641 	LOGGING_DPRINT((3,
642 		"nfslog_record_alloc(log_buffer=%p mem=%p size=%lu)\n",
643 		exi->exi_logbuffer, lrp->log_record, lrp->size));
644 	return (lrp->log_record);
645 }
646 
647 /*
648  * After the above nfslog_record_alloc() has been called and a record
649  * encoded into the buffer that was returned, this function is called
650  * to handle appropriate disposition of the newly created record.
651  * The cookie value is the one that was returned from nfslog_record_alloc().
652  * Size is the actual size of the record that was encoded.  This is
653  * passed in because the size used for the alloc was just an approximation.
654  * The sync parameter is used to tell us if we need to force this record
655  * to disk and if not it will be queued for later writing.
656  *
657  * Note that if the size parameter has a value of 0, then the record is
658  * not written to the log and the associated data structures are released.
659  */
660 void
661 nfslog_record_put(void *cookie, size_t size, bool_t sync,
662 	unsigned int which_buffers)
663 {
664 	struct lr_alloc *lrp = (struct lr_alloc *)cookie;
665 	struct log_buffer *lbp = lrp->lb;
666 
667 	/*
668 	 * If the caller has nothing to write or if there is
669 	 * an apparent error, rele the buffer and free.
670 	 */
671 	if (size == 0 || size > lrp->size) {
672 		nfslog_free_logrecords(lrp);
673 		return;
674 	}
675 
676 	/*
677 	 * Reset the size to what actually needs to be written
678 	 * This is used later on when the iovec is built for
679 	 * writing the records to the log file.
680 	 */
681 	lrp->size = size;
682 
683 	/* append to all if public exi */
684 	if (which_buffers == NFSLOG_ALL_BUFFERS) {
685 		(void) nfslog_record_append2all(lrp);
686 		nfslog_free_logrecords(lrp);
687 		return;
688 	}
689 
690 	/* Insert the record on the list to be written */
691 	mutex_enter(&lbp->lb_lock);
692 	if (lbp->lb_records == NULL) {
693 		lbp->lb_records = (caddr_t)lrp;
694 		lbp->lb_num_recs = 1;
695 		lbp->lb_size_queued = lrp->size;
696 	} else {
697 		insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev);
698 		lbp->lb_num_recs++;
699 		lbp->lb_size_queued += lrp->size;
700 	}
701 
702 	/*
703 	 * Determine if the queue for this log buffer should be flushed.
704 	 * This is done by either the number of records queued, the total
705 	 * size of all records queued or by the request of the caller
706 	 * via the sync parameter.
707 	 */
708 	if (lbp->lb_size_queued >= nfslog_num_bytes_to_write ||
709 		lbp->lb_num_recs > nfslog_num_records_to_write ||
710 		sync == TRUE) {
711 		mutex_exit(&lbp->lb_lock);
712 		(void) nfslog_records_flush_to_disk(lbp);
713 	} else {
714 		mutex_exit(&lbp->lb_lock);
715 	}
716 
717 }
718 
719 /*
720  * Examine the log_buffer struct to see if there are queue log records
721  * that need to be written to disk.  If some exist, pull them off of
722  * the log buffer and write them to the log file.
723  */
724 static int
725 nfslog_records_flush_to_disk(struct log_buffer *lbp)
726 {
727 
728 	mutex_enter(&lbp->lb_lock);
729 
730 	if (lbp->lb_records == NULL) {
731 		mutex_exit(&lbp->lb_lock);
732 		return (0);
733 	}
734 	return	(nfslog_records_flush_to_disk_nolock(lbp));
735 }
736 
737 /*
738  * Function requires that the caller holds lb_lock.
739  * Function flushes any records in the log buffer to the disk.
740  * Function drops the lb_lock on return.
741  */
742 
743 static int
744 nfslog_records_flush_to_disk_nolock(struct log_buffer *lbp)
745 {
746 	struct log_file *lfp = NULL;
747 	struct lr_alloc *lrp_writers;
748 	int num_recs;
749 	int error = 0;
750 
751 	ASSERT(MUTEX_HELD(&lbp->lb_lock));
752 
753 	lfp = lbp->lb_logfile;
754 
755 	LOG_FILE_LOCK_TO_WRITE(lfp);
756 	ASSERT(lbp->lb_records != NULL);
757 
758 	lrp_writers = (struct lr_alloc *)lbp->lb_records;
759 	lbp->lb_records = NULL;
760 	num_recs = lbp->lb_num_recs;
761 	lbp->lb_num_recs = 0;
762 	lbp->lb_size_queued = 0;
763 	mutex_exit(&lbp->lb_lock);
764 	error = nfslog_write_logrecords(lfp, lrp_writers, num_recs);
765 
766 	LOG_FILE_UNLOCK_FROM_WRITE(lfp);
767 
768 	nfslog_free_logrecords(lrp_writers);
769 	return (error);
770 }
771 
772 
773 /*
774  * Take care of writing the provided log record(s) to the log file.
775  * We group the log records with an iovec and use VOP_WRITE to append
776  * them to the end of the log file.
777  */
778 static int
779 nfslog_write_logrecords(struct log_file *lfp,
780 	struct lr_alloc *lrp_writers, int num_recs)
781 {
782 	struct uio uio;
783 	struct iovec *iovp;
784 	int size_iovecs;
785 	vnode_t *vp;
786 	struct vattr va;
787 	struct lr_alloc *lrp;
788 	int i;
789 	ssize_t len;
790 	int ioflag = FAPPEND;
791 	int error = 0;
792 
793 	ASSERT(MUTEX_HELD(&lfp->lf_lock));
794 
795 	vp = lfp->lf_vp;
796 
797 	size_iovecs = sizeof (struct iovec) * num_recs;
798 	iovp = (struct iovec *)kmem_alloc(size_iovecs, KM_NOSLEEP);
799 
800 	if (iovp == NULL) {
801 		error = ENOMEM;
802 		goto out;
803 	}
804 
805 	/* Build the iovec based on the list of log records */
806 	i = 0;
807 	len = 0;
808 	lrp = lrp_writers;
809 	do {
810 		iovp[i].iov_base = lrp->log_record;
811 		iovp[i].iov_len = lrp->size;
812 		len += lrp->size;
813 		lrp = lrp->next;
814 		i++;
815 	} while (lrp != lrp_writers);
816 
817 	ASSERT(i == num_recs);
818 
819 	uio.uio_iov = iovp;
820 	uio.uio_iovcnt = num_recs;
821 	uio.uio_loffset = 0;
822 	uio.uio_segflg = (short)UIO_SYSSPACE;
823 	uio.uio_resid = len;
824 	uio.uio_llimit = (rlim64_t)MAXOFFSET_T;
825 	uio.uio_fmode = FWRITE;
826 	uio.uio_extflg = UIO_COPY_DEFAULT;
827 
828 	/*
829 	 * Save the size. If the write fails, reset the size to avoid
830 	 * corrupted log buffer files.
831 	 */
832 	va.va_mask = AT_SIZE;
833 
834 	(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);  /* UIO_WRITE */
835 	if ((error = VOP_GETATTR(vp, &va, 0, CRED())) == 0) {
836 		if ((len + va.va_size) < (MAXOFF32_T)) {
837 			error = VOP_WRITE(vp, &uio, ioflag, CRED(), NULL);
838 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
839 			if (uio.uio_resid)
840 				error = ENOSPC;
841 			if (error)
842 				(void) VOP_SETATTR(vp, &va, 0, CRED(), NULL);
843 		} else {
844 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
845 			if (!(lfp->lf_flags & L_PRINTED)) {
846 				cmn_err(CE_WARN,
847 				    "NFS Logging: buffer file %s exceeds 2GB; "
848 				    "stopped writing buffer \n", lfp->lf_path);
849 			}
850 			error = ENOSPC;
851 		}
852 	} else {
853 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
854 	}
855 
856 	kmem_free(iovp, size_iovecs);
857 
858 out:
859 	if (error) {
860 		if (!(lfp->lf_flags & L_PRINTED)) {
861 			nfs_cmn_err(error, CE_WARN,
862 				"NFS Logging disabled for buffer %s - "
863 				"write error = %m\n", lfp->lf_path);
864 			lfp->lf_flags |= L_PRINTED;
865 		}
866 	} else if (lfp->lf_flags & (L_ERROR | L_PRINTED)) {
867 		lfp->lf_flags &= ~(L_ERROR | L_PRINTED);
868 		cmn_err(CE_WARN,
869 			"NFS Logging re-enabled for buffer %s\n", lfp->lf_path);
870 	}
871 
872 	return (error);
873 }
874 
875 static void
876 nfslog_free_logrecords(struct lr_alloc *lrp_writers)
877 {
878 	struct lr_alloc *lrp = lrp_writers;
879 	struct lr_alloc *lrp_free;
880 
881 	do {
882 		lrp_free = lrp;
883 
884 		lrp = lrp->next;
885 
886 		/*
887 		 * Check to see if we are supposed to free this structure
888 		 * and relese the log_buffer ref count.
889 		 * It may be the case that the caller does not want this
890 		 * structure and its record contents freed just yet.
891 		 */
892 		if ((lrp_free->lr_flags & LR_ALLOC_NOFREE) == 0) {
893 			if (lrp_free->lb != NULL)
894 				log_buffer_rele(lrp_free->lb);
895 			if (lrp_free->alloc_cache) /* double check */
896 				kmem_cache_free(lrp_free->alloc_cache,
897 					(void *)lrp_free);
898 		} else {
899 			/*
900 			 * after being pulled from the list the
901 			 * pointers need to be reinitialized.
902 			 */
903 			lrp_free->next = lrp_free;
904 			lrp_free->prev = lrp_free;
905 		}
906 
907 	} while (lrp != lrp_writers);
908 }
909 
910 /*
911  * Rename lbp->lb_logfile to reflect the true name requested by 'share'
912  */
913 static int
914 nfslog_logbuffer_rename(struct log_buffer *lbp)
915 {
916 	struct log_file *lf;
917 	int error;
918 	struct log_file *logfile;
919 
920 	/*
921 	 * Try our best to get the cache records into the log file
922 	 * before the rename occurs.
923 	 */
924 	(void) nfslog_records_flush_to_disk(lbp);
925 
926 	/*
927 	 * Hold lb_lock before retrieving
928 	 * lb_logfile.
929 	 * Hold a reference to the
930 	 * "lf" structure. this is
931 	 * same as LOG_FILE_HOLD()
932 	 */
933 	mutex_enter(&(lbp)->lb_lock);
934 	lf = lbp->lb_logfile;
935 	mutex_enter(&(lf)->lf_lock);
936 	mutex_exit(&(lbp)->lb_lock);
937 	lf->lf_refcnt++;
938 	mutex_exit(&(lf)->lf_lock);
939 
940 	LOGGING_DPRINT((10, "nfslog_logbuffer_rename: renaming %s to %s\n",
941 		lf->lf_path, lbp->lb_path));
942 
943 	/*
944 	 * rename the current buffer to what the daemon expects
945 	 */
946 	if (error = nfslog_logfile_rename(lf->lf_path, lbp->lb_path))
947 		goto out;
948 
949 	/*
950 	 * Create a new working buffer file and have all new data sent there.
951 	 */
952 	if (error = log_file_create(lbp->lb_path, &logfile)) {
953 		/* Attempt to rename to original */
954 		(void) nfslog_logfile_rename(lbp->lb_path, lf->lf_path);
955 		goto out;
956 	}
957 
958 	/*
959 	 * Hold the lb_lock here, this will make
960 	 * all the threads trying to access lb->logfile block
961 	 * and get a new logfile structure instead of old one.
962 	 */
963 	mutex_enter(&(lbp)->lb_lock);
964 	lbp->lb_logfile = logfile;
965 	mutex_exit(&(lbp)->lb_lock);
966 
967 	LOG_FILE_RELE(lf);	/* release log_buffer's reference */
968 
969 	/*
970 	 * Wait for log_file to be in a quiescent state before we
971 	 * return to our caller to let it proceed with the reading of
972 	 * this file.
973 	 */
974 	nfslog_logfile_wait(lf);
975 
976 out:
977 	/*
978 	 * Release our reference on "lf" in two different cases.
979 	 * 1. Error condition, release only the reference
980 	 *    that we held at the begining of this
981 	 *    routine on "lf" structure.
982 	 * 2. Fall through condition, no errors but the old
983 	 *    logfile structure "lf" has been replaced with
984 	 *    the new "logfile" structure, so release the
985 	 *    reference that was part of the creation of
986 	 *    "lf" structure to free up the resources.
987 	 */
988 
989 	LOG_FILE_RELE(lf);
990 
991 	return (error);
992 }
993 
994 /*
995  * Renames the 'from' file to 'new'.
996  */
997 static int
998 nfslog_logfile_rename(char *from, char *new)
999 {
1000 	int error;
1001 
1002 	if (error = vn_rename(from, new, UIO_SYSSPACE)) {
1003 		cmn_err(CE_WARN,
1004 			"nfslog_logfile_rename: couldn't rename %s to %s\n",
1005 			from, new);
1006 	}
1007 	return (error);
1008 }
1009 
1010 /*
1011  * Wait for the log_file writers to finish before returning
1012  */
1013 static void
1014 nfslog_logfile_wait(struct log_file *lf)
1015 {
1016 	mutex_enter(&lf->lf_lock);
1017 	while (lf->lf_writers > 0) {
1018 		lf->lf_flags |= L_WAITING;
1019 		(void) cv_wait_sig(&lf->lf_cv_waiters, &lf->lf_lock);
1020 	}
1021 	mutex_exit(&lf->lf_lock);
1022 }
1023 
1024 static int
1025 nfslog_record_append2all(struct lr_alloc *lrp)
1026 {
1027 	struct log_buffer *lbp, *nlbp;
1028 	int error, ret_error = 0;
1029 	int lr_flags = lrp->lr_flags;
1030 
1031 	rw_enter(&nfslog_buffer_list_lock, RW_READER);
1032 	if ((lbp = nfslog_buffer_list) != NULL)
1033 		LOG_BUFFER_HOLD(lbp);
1034 	for (nlbp = NULL; lbp != NULL; lbp = nlbp) {
1035 		if ((nlbp = lbp->lb_next) != NULL) {
1036 			/*
1037 			 * Remember next element in the list
1038 			 */
1039 			LOG_BUFFER_HOLD(nlbp);
1040 		}
1041 		rw_exit(&nfslog_buffer_list_lock);
1042 
1043 		/*
1044 		 * Insert the record on the buffer's list to be written
1045 		 * and then flush the records to the log file.
1046 		 * Make sure to set the no free flag so that the
1047 		 * record can be used for the next write
1048 		 */
1049 		lrp->lr_flags = LR_ALLOC_NOFREE;
1050 
1051 		ASSERT(lbp != NULL);
1052 		mutex_enter(&lbp->lb_lock);
1053 		if (lbp->lb_records == NULL) {
1054 			lbp->lb_records = (caddr_t)lrp;
1055 			lbp->lb_num_recs = 1;
1056 			lbp->lb_size_queued = lrp->size;
1057 		} else {
1058 			insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev);
1059 			lbp->lb_num_recs++;
1060 			lbp->lb_size_queued += lrp->size;
1061 		}
1062 
1063 		/*
1064 		 * Flush log records to disk.
1065 		 * Function is called with lb_lock held.
1066 		 * Function drops the lb_lock on return.
1067 		 */
1068 		error = nfslog_records_flush_to_disk_nolock(lbp);
1069 
1070 		if (error) {
1071 			ret_error = -1;
1072 			nfs_cmn_err(error, CE_WARN,
1073 				"rfsl_log_pubfh: could not append record to "
1074 				"\"%s\" error = %m\n", lbp->lb_path);
1075 		}
1076 		log_buffer_rele(lbp);
1077 		rw_enter(&nfslog_buffer_list_lock, RW_READER);
1078 	}
1079 	rw_exit(&nfslog_buffer_list_lock);
1080 
1081 	lrp->lr_flags = lr_flags;
1082 
1083 	return (ret_error);
1084 }
1085 
1086 #ifdef DEBUG
1087 static int logging_debug = 0;
1088 
1089 /*
1090  * 0) no debugging
1091  * 3) current test software
1092  * 10) random stuff
1093  */
1094 void
1095 nfslog_dprint(const int level, const char *fmt, ...)
1096 {
1097 	va_list args;
1098 
1099 	if (logging_debug == level ||
1100 	    (logging_debug > 10 && (logging_debug - 10) >= level)) {
1101 		va_start(args, fmt);
1102 		(void) vprintf(fmt, args);
1103 		va_end(args);
1104 	}
1105 }
1106 
1107 #endif /* DEBUG */
1108 
1109 /*
1110  * NFS Log Flush system call
1111  * Caller must check privileges.
1112  */
1113 /* ARGSUSED */
1114 int
1115 nfsl_flush(struct nfsl_flush_args *args, model_t model)
1116 {
1117 	struct flush_thread_params *tparams;
1118 	struct nfsl_flush_args *nfsl_args;
1119 	int error = 0;
1120 	ulong_t buffer_len;
1121 	STRUCT_HANDLE(nfsl_flush_args, uap);
1122 
1123 	STRUCT_SET_HANDLE(uap, model, args);
1124 
1125 	tparams = (struct flush_thread_params *)
1126 		kmem_zalloc(sizeof (*tparams), KM_SLEEP);
1127 
1128 	nfsl_args = &tparams->tp_args;
1129 	nfsl_args->version =  STRUCT_FGET(uap, version);
1130 	if (nfsl_args->version != NFSL_FLUSH_ARGS_VERS) {
1131 		cmn_err(CE_WARN, "nfsl_flush: exected version %d, got %d",
1132 			NFSL_FLUSH_ARGS_VERS, nfsl_args->version);
1133 		return (EIO);
1134 	}
1135 
1136 	nfsl_args->directive = STRUCT_FGET(uap, directive);
1137 	if ((nfsl_args->directive & NFSL_ALL) == 0) {
1138 		/*
1139 		 * Process a specific buffer
1140 		 */
1141 		nfsl_args->buff_len = STRUCT_FGET(uap, buff_len);
1142 
1143 		nfsl_args->buff = (char *)
1144 			kmem_alloc(nfsl_args->buff_len, KM_NOSLEEP);
1145 		if (nfsl_args->buff == NULL)
1146 			return (ENOMEM);
1147 
1148 		error = copyinstr((const char *)STRUCT_FGETP(uap, buff),
1149 			nfsl_args->buff, nfsl_args->buff_len, &buffer_len);
1150 		if (error)
1151 			return (EFAULT);
1152 
1153 		if (nfsl_args->buff_len != buffer_len)
1154 			return (EFAULT);
1155 	}
1156 
1157 	LOGGING_DPRINT((10, "nfsl_flush: Flushing %s buffer(s)\n",
1158 		nfsl_args->directive & NFSL_ALL ? "all" : nfsl_args->buff));
1159 
1160 	if (nfsl_args->directive & NFSL_SYNC) {
1161 		/*
1162 		 * Do the work synchronously
1163 		 */
1164 		nfslog_do_flush(tparams);
1165 		error = tparams->tp_error;
1166 		kmem_free(nfsl_args->buff, nfsl_args->buff_len);
1167 		kmem_free(tparams, sizeof (*tparams));
1168 	} else {
1169 		/*
1170 		 * Do the work asynchronously
1171 		 */
1172 		(void) thread_create(NULL, 0, nfslog_do_flush,
1173 		    tparams, 0, &p0, TS_RUN, minclsyspri);
1174 	}
1175 
1176 	return (error);
1177 }
1178 
1179 /*
1180  * This is where buffer flushing would occur, but there is no buffering
1181  * at this time.
1182  * Possibly rename the log buffer for processing.
1183  * Sets tparams->ta_error equal to the value of the error that occured,
1184  * 0 otherwise.
1185  * Returns ENOENT if the buffer is not found.
1186  */
1187 static void
1188 nfslog_do_flush(struct flush_thread_params *tparams)
1189 {
1190 	struct nfsl_flush_args *args;
1191 	struct log_buffer *lbp;
1192 	int error = ENOENT;
1193 	int found = 0;
1194 	char *buf_inprog;	/* name of buff in progress */
1195 	int buf_inprog_len;
1196 
1197 	/*
1198 	 * Sanity check on the arguments.
1199 	 */
1200 	if (!tparams)
1201 		return;
1202 	args = &tparams->tp_args;
1203 	if (!args)
1204 		return;
1205 
1206 	rw_enter(&nfslog_buffer_list_lock, RW_READER);
1207 	for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) {
1208 		if (args->directive & NFSL_ALL) {
1209 			(void) nfslog_records_flush_to_disk(lbp);
1210 		} else {
1211 			if ((strcmp(lbp->lb_path, args->buff) == 0) &&
1212 				(args->directive & NFSL_RENAME)) {
1213 
1214 				error = nfslog_logbuffer_rename(lbp);
1215 				found++;
1216 				break;
1217 			}
1218 		}
1219 	}
1220 	rw_exit(&nfslog_buffer_list_lock);
1221 
1222 	if (!found && ((args->directive & NFSL_ALL) == 0) &&
1223 	    (args->directive & NFSL_RENAME)) {
1224 		/*
1225 		 * The specified buffer is not currently in use,
1226 		 * simply rename the file indicated.
1227 		 */
1228 		buf_inprog_len = strlen(args->buff) +
1229 			strlen(LOG_INPROG_STRING) + 1;
1230 		buf_inprog = (caddr_t)kmem_alloc(buf_inprog_len, KM_SLEEP);
1231 		(void) sprintf(buf_inprog, "%s%s",
1232 			args->buff, LOG_INPROG_STRING);
1233 
1234 		error = nfslog_logfile_rename(buf_inprog, args->buff);
1235 
1236 		kmem_free(buf_inprog, buf_inprog_len);
1237 	}
1238 
1239 out:
1240 	if ((args->directive & NFSL_SYNC) == 0) {
1241 		/*
1242 		 * Work was performed asynchronously, the caller is
1243 		 * no longer waiting for us.
1244 		 * Free the thread arguments and exit.
1245 		 */
1246 		kmem_free(args->buff, args->buff_len);
1247 		kmem_free(tparams, sizeof (*tparams));
1248 		thread_exit();
1249 		/* NOTREACHED */
1250 	}
1251 
1252 	tparams->tp_error = error;
1253 }
1254 
1255 /*
1256  * Generate buffer_header.
1257  * 'loghdr' points the the buffer_header, and *reclen
1258  * contains the length of the buffer.
1259  */
1260 static void
1261 create_buffer_header(caddr_t *loghdr, size_t *reclen, size_t *freesize)
1262 {
1263 	timestruc_t		now;
1264 	nfslog_buffer_header	lh;
1265 	XDR			xdrs;
1266 	unsigned int		final_size;
1267 
1268 
1269 	/* pick some size that will hold the buffer_header */
1270 	*freesize = NFSLOG_SMALL_RECORD_SIZE;
1271 
1272 	/*
1273 	 * Fill header
1274 	 */
1275 	lh.bh_length = 0;	/* don't know yet how large it will be */
1276 	lh.bh_version = NFSLOG_BUF_VERSION;
1277 	lh.bh_flags = 0;
1278 	lh.bh_offset = 0;
1279 	gethrestime(&now);
1280 	TIMESPEC_TO_TIMESPEC32(&lh.bh_timestamp, &now);
1281 
1282 	/*
1283 	 * Encode the header
1284 	 */
1285 	*loghdr = (caddr_t)kmem_alloc(*freesize, KM_SLEEP);
1286 	xdrmem_create(&xdrs, *loghdr, *freesize, XDR_ENCODE);
1287 
1288 	(void) xdr_nfslog_buffer_header(&xdrs, &lh);
1289 
1290 	/*
1291 	 * Reset with final size of the encoded data
1292 	 */
1293 	final_size = xdr_getpos(&xdrs);
1294 	xdr_setpos(&xdrs, 0);
1295 	(void) xdr_u_int(&xdrs, &final_size);
1296 
1297 	*reclen = (size_t)final_size;
1298 }
1299 
1300 /*
1301  * ****************************************************************
1302  * RPC dispatch table for logging
1303  * Indexed by program, version, proc
1304  * Based on NFS dispatch table.
1305  */
1306 struct nfslog_proc_disp {
1307 	bool_t	(*xdrargs)();
1308 	bool_t	(*xdrres)();
1309 	bool_t	affects_transactions;	/* Operation affects transaction */
1310 					/* processing */
1311 };
1312 
1313 struct nfslog_vers_disp {
1314 	int	nfslog_dis_nprocs;			/* number of procs */
1315 	struct nfslog_proc_disp	*nfslog_dis_proc_table;	/* proc array */
1316 };
1317 
1318 struct nfslog_prog_disp {
1319 	int	nfslog_dis_prog;		/* program number */
1320 	int	nfslog_dis_versmin;		/* Minimum version value */
1321 	int	nfslog_dis_nvers;		/* Number of version values */
1322 	struct nfslog_vers_disp	*nfslog_dis_vers_table;	/* versions array */
1323 };
1324 
1325 static int rfs_log_bad = 0;	/* incremented on bad log attempts */
1326 static int rfs_log_good = 0;	/* incremented on successful log attempts */
1327 
1328 /*
1329  * Define the actions taken per prog/vers/proc:
1330  *
1331  * In some cases, the nl types are the same as the nfs types and a simple
1332  * bcopy should suffice. Rather that define tens of identical procedures,
1333  * simply define these to bcopy. Similarly this takes care of different
1334  * procs that use same parameter struct.
1335  */
1336 
1337 static struct nfslog_proc_disp nfslog_proc_v2[] = {
1338 	/*
1339 	 * NFS VERSION 2
1340 	 */
1341 
1342 	/* RFS_NULL = 0 */
1343 	{xdr_void, xdr_void, FALSE},
1344 
1345 	/* RFS_GETATTR = 1 */
1346 	{xdr_fhandle, xdr_nfslog_getattrres, FALSE},
1347 
1348 	/* RFS_SETATTR = 2 */
1349 	{xdr_nfslog_setattrargs, xdr_nfsstat, TRUE},
1350 
1351 	/* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */
1352 	{xdr_void, xdr_void, FALSE},
1353 
1354 	/* RFS_LOOKUP = 4 */
1355 	{xdr_nfslog_diropargs, xdr_nfslog_diropres, TRUE},
1356 
1357 	/* RFS_READLINK = 5 */
1358 	{xdr_fhandle, xdr_nfslog_rdlnres, FALSE},
1359 
1360 	/* RFS_READ = 6 */
1361 	{xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult, TRUE},
1362 
1363 	/* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */
1364 	{xdr_void, xdr_void, FALSE},
1365 
1366 	/* RFS_WRITE = 8 */
1367 	{xdr_nfslog_writeargs, xdr_nfslog_writeresult, TRUE},
1368 
1369 	/* RFS_CREATE = 9 */
1370 	{xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE},
1371 
1372 	/* RFS_REMOVE = 10 */
1373 	{xdr_nfslog_diropargs, xdr_nfsstat, TRUE},
1374 
1375 	/* RFS_RENAME = 11 */
1376 	{xdr_nfslog_rnmargs, xdr_nfsstat, TRUE},
1377 
1378 	/* RFS_LINK = 12 */
1379 	{xdr_nfslog_linkargs, xdr_nfsstat, TRUE},
1380 
1381 	/* RFS_SYMLINK = 13 */
1382 	{xdr_nfslog_symlinkargs, xdr_nfsstat, TRUE},
1383 
1384 	/* RFS_MKDIR = 14 */
1385 	{xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE},
1386 
1387 	/* RFS_RMDIR = 15 */
1388 	{xdr_nfslog_diropargs, xdr_nfsstat, TRUE},
1389 
1390 	/* RFS_READDIR = 16 */
1391 	{xdr_nfslog_rddirargs, xdr_nfslog_rddirres, TRUE},
1392 
1393 	/* RFS_STATFS = 17 */
1394 	{xdr_fhandle, xdr_nfslog_statfs, FALSE},
1395 };
1396 
1397 
1398 /*
1399  * NFS VERSION 3
1400  */
1401 
1402 static struct nfslog_proc_disp nfslog_proc_v3[] = {
1403 
1404 	/* NFSPROC3_NULL = 0 */
1405 	{xdr_void, xdr_void, FALSE},
1406 
1407 	/* NFSPROC3_GETATTR = 1 */
1408 	{xdr_nfslog_nfs_fh3, xdr_nfslog_GETATTR3res, FALSE},
1409 
1410 	/* NFSPROC3_SETATTR = 2 */
1411 	{xdr_nfslog_SETATTR3args, xdr_nfslog_SETATTR3res, TRUE},
1412 
1413 	/* NFSPROC3_LOOKUP = 3 */
1414 	{xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE},
1415 
1416 	/* NFSPROC3_ACCESS = 4 */
1417 	{xdr_nfslog_ACCESS3args, xdr_nfslog_ACCESS3res, FALSE},
1418 
1419 	/* NFSPROC3_READLINK = 5 */
1420 	{xdr_nfslog_nfs_fh3, xdr_nfslog_READLINK3res, FALSE},
1421 
1422 	/* NFSPROC3_READ = 6 */
1423 	{xdr_nfslog_READ3args, xdr_nfslog_READ3res, TRUE},
1424 
1425 	/* NFSPROC3_WRITE = 7 */
1426 	{xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res, TRUE},
1427 
1428 	/* NFSPROC3_CREATE = 8 */
1429 	{xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res, TRUE},
1430 
1431 	/* NFSPROC3_MKDIR = 9 */
1432 	{xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res, TRUE},
1433 
1434 	/* NFSPROC3_SYMLINK = 10 */
1435 	{xdr_nfslog_SYMLINK3args, xdr_nfslog_SYMLINK3res, TRUE},
1436 
1437 	/* NFSPROC3_MKNOD = 11 */
1438 	{xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res, TRUE},
1439 
1440 	/* NFSPROC3_REMOVE = 12 */
1441 	{xdr_nfslog_REMOVE3args, xdr_nfslog_REMOVE3res, TRUE},
1442 
1443 	/* NFSPROC3_RMDIR = 13 */
1444 	{xdr_nfslog_RMDIR3args, xdr_nfslog_RMDIR3res, TRUE},
1445 
1446 	/* NFSPROC3_RENAME = 14 */
1447 	{xdr_nfslog_RENAME3args, xdr_nfslog_RENAME3res, TRUE},
1448 
1449 	/* NFSPROC3_LINK = 15 */
1450 	{xdr_nfslog_LINK3args, xdr_nfslog_LINK3res, TRUE},
1451 
1452 	/* NFSPROC3_READDIR = 16 */
1453 	{xdr_nfslog_READDIR3args, xdr_nfslog_READDIR3res, TRUE},
1454 
1455 	/* NFSPROC3_READDIRPLUS = 17 */
1456 	{xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res, TRUE},
1457 
1458 	/* NFSPROC3_FSSTAT = 18 */
1459 	{xdr_nfslog_FSSTAT3args, xdr_nfslog_FSSTAT3res, FALSE},
1460 
1461 	/* NFSPROC3_FSINFO = 19 */
1462 	{xdr_nfslog_FSINFO3args, xdr_nfslog_FSINFO3res, FALSE},
1463 
1464 	/* NFSPROC3_PATHCONF = 20 */
1465 	{xdr_nfslog_PATHCONF3args, xdr_nfslog_PATHCONF3res, FALSE},
1466 
1467 	/* NFSPROC3_COMMIT = 21 */
1468 	{xdr_nfslog_COMMIT3args, xdr_nfslog_COMMIT3res, FALSE},
1469 };
1470 
1471 static struct nfslog_proc_disp nfslog_proc_v1[] = {
1472 	/*
1473 	 * NFSLOG VERSION 1
1474 	 */
1475 
1476 	/* NFSLOG_NULL = 0 */
1477 	{xdr_void, xdr_void, TRUE},
1478 
1479 	/* NFSLOG_SHARE = 1 */
1480 	{xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE},
1481 
1482 	/* NFSLOG_UNSHARE = 2 */
1483 	{xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE},
1484 
1485 	/* NFSLOG_LOOKUP = 3 */
1486 	{xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE},
1487 
1488 	/* NFSLOG_GETFH = 4 */
1489 	{xdr_nfslog_getfhargs, xdr_nfsstat, TRUE},
1490 };
1491 
1492 static struct nfslog_vers_disp nfslog_vers_disptable[] = {
1493 	{sizeof (nfslog_proc_v2) / sizeof (nfslog_proc_v2[0]),
1494 	    nfslog_proc_v2},
1495 	{sizeof (nfslog_proc_v3) / sizeof (nfslog_proc_v3[0]),
1496 	    nfslog_proc_v3},
1497 };
1498 
1499 static struct nfslog_vers_disp nfslog_nfslog_vers_disptable[] = {
1500 	{sizeof (nfslog_proc_v1) / sizeof (nfslog_proc_v1[0]),
1501 	    nfslog_proc_v1},
1502 };
1503 
1504 static struct nfslog_prog_disp nfslog_dispatch_table[] = {
1505 	{NFS_PROGRAM, NFS_VERSMIN,
1506 		(sizeof (nfslog_vers_disptable) /
1507 		sizeof (nfslog_vers_disptable[0])),
1508 		nfslog_vers_disptable},
1509 
1510 	{NFSLOG_PROGRAM, NFSLOG_VERSMIN,
1511 		(sizeof (nfslog_nfslog_vers_disptable) /
1512 		sizeof (nfslog_nfslog_vers_disptable[0])),
1513 		nfslog_nfslog_vers_disptable},
1514 };
1515 
1516 static int	nfslog_dispatch_table_arglen = sizeof (nfslog_dispatch_table) /
1517 					sizeof (nfslog_dispatch_table[0]);
1518 
1519 /*
1520  * This function will determine the appropriate export info struct to use
1521  * and allocate a record id to be used in the written log buffer.
1522  * Usually this is a straightforward operation but the existence of the
1523  * multicomponent lookup and its semantics of crossing file system
1524  * boundaries add to the complexity.  See the comments below...
1525  */
1526 struct exportinfo *
1527 nfslog_get_exi(
1528 	struct exportinfo *exi,
1529 	struct svc_req *req,
1530 	caddr_t res,
1531 	unsigned int *nfslog_rec_id)
1532 {
1533 	struct log_buffer *lb;
1534 	struct exportinfo *exi_ret = NULL;
1535 	fhandle_t		*fh;
1536 	nfs_fh3			*fh3;
1537 
1538 	if (exi == NULL)
1539 		return (NULL);
1540 
1541 	/*
1542 	 * If the exi is marked for logging, allocate a record id and return
1543 	 */
1544 	if (exi->exi_export.ex_flags & EX_LOG) {
1545 		lb = exi->exi_logbuffer;
1546 
1547 		/* obtain the unique record id for the caller */
1548 		*nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1);
1549 
1550 		/*
1551 		 * The caller will expect to be able to exi_rele() it,
1552 		 * so exi->exi_count must be incremented before it can
1553 		 * be returned, to make it uniform with exi_ret->exi_count
1554 		 */
1555 		mutex_enter(&exi->exi_lock);
1556 		exi->exi_count++;
1557 		mutex_exit(&exi->exi_lock);
1558 
1559 		return (exi);
1560 	}
1561 
1562 	if (exi != exi_public)
1563 		return (NULL);
1564 
1565 	/*
1566 	 * Here we have an exi that is not marked for logging.
1567 	 * It is possible that this request is a multicomponent lookup
1568 	 * that was done from the public file handle (not logged) and
1569 	 * the resulting file handle being returned to the client exists
1570 	 * in a file system that is being logged.  If this is the case
1571 	 * we need to log this multicomponent lookup to the appropriate
1572 	 * log buffer.  This will allow for the appropriate path name
1573 	 * mapping to occur at user level.
1574 	 */
1575 	if (req->rq_prog == NFS_PROGRAM) {
1576 		switch (req->rq_vers) {
1577 		case NFS_V3:
1578 			if ((req->rq_proc == NFSPROC3_LOOKUP) &&
1579 				(((LOOKUP3res *)res)->status == NFS3_OK)) {
1580 				fh3 = &((LOOKUP3res *)res)->res_u.ok.object;
1581 				exi_ret = checkexport(&fh3->fh3_fsid,
1582 					FH3TOXFIDP(fh3));
1583 			}
1584 			break;
1585 
1586 		case NFS_VERSION:
1587 			if ((req->rq_proc == RFS_LOOKUP) &&
1588 				(((struct nfsdiropres *)
1589 					res)->dr_status == NFS_OK)) {
1590 				fh =
1591 		&((struct nfsdiropres *)res)->dr_u.dr_drok_u.drok_fhandle;
1592 				exi_ret = checkexport(&fh->fh_fsid,
1593 					(fid_t *)&fh->fh_xlen);
1594 			}
1595 			break;
1596 		default:
1597 			break;
1598 		}
1599 	}
1600 
1601 	if (exi_ret != NULL && exi_ret->exi_export.ex_flags & EX_LOG) {
1602 		lb = exi_ret->exi_logbuffer;
1603 		/* obtain the unique record id for the caller */
1604 		*nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1);
1605 
1606 		return (exi_ret);
1607 	}
1608 	return (NULL);
1609 }
1610 
1611 #ifdef DEBUG
1612 static long long rfslog_records_ignored = 0;
1613 #endif
1614 
1615 /*
1616  * nfslog_write_record - Fill in the record buffer for writing out.
1617  * If logrecp is null, log it, otherwise, malloc the record and return it.
1618  *
1619  * It is the responsibility of the caller to check whether this exportinfo
1620  * has logging enabled.
1621  * Note that nfslog_share_public_record() only needs to check for the
1622  * existence of at least one logbuffer to which the public filehandle record
1623  * needs to be logged.
1624  */
1625 void
1626 nfslog_write_record(struct exportinfo *exi, struct svc_req *req,
1627 	caddr_t args, caddr_t res, cred_t *cr, struct netbuf *pnb,
1628 	unsigned int record_id, unsigned int which_buffers)
1629 {
1630 	struct nfslog_prog_disp	*progtable;	/* prog struct */
1631 	struct nfslog_vers_disp	*verstable;	/* version struct */
1632 	struct nfslog_proc_disp	*disp = NULL;	/* proc struct */
1633 	int			i, vers;
1634 	void			*log_cookie;	/* for logrecord if */
1635 	caddr_t			buffer;
1636 	XDR			xdrs;
1637 	unsigned int		final_size;
1638 	int			encode_ok;
1639 	int			alloc_indx;
1640 
1641 	ASSERT(exi != NULL); ASSERT(req != NULL); ASSERT(args != NULL);
1642 	ASSERT(res != NULL); ASSERT(cr != NULL);
1643 
1644 	/*
1645 	 * Find program element
1646 	 * Search the list since program can not be used as index
1647 	 */
1648 	for (i = 0; (i < nfslog_dispatch_table_arglen); i++) {
1649 		if (req->rq_prog == nfslog_dispatch_table[i].nfslog_dis_prog)
1650 			break;
1651 	}
1652 	if (i >= nfslog_dispatch_table_arglen) {	/* program not logged */
1653 		/* not an error */
1654 		return;
1655 	}
1656 
1657 	/*
1658 	 * Extract the dispatch functions based on program/version
1659 	 */
1660 	progtable = &nfslog_dispatch_table[i];
1661 	vers = req->rq_vers - progtable->nfslog_dis_versmin;
1662 	verstable = &progtable->nfslog_dis_vers_table[vers];
1663 	disp = &verstable->nfslog_dis_proc_table[req->rq_proc];
1664 
1665 	if (!(exi->exi_export.ex_flags & EX_LOG_ALLOPS) &&
1666 	    !disp->affects_transactions) {
1667 		/*
1668 		 * Only interested in logging operations affecting
1669 		 * transaction generation. This is not one of them.
1670 		 */
1671 #ifdef DEBUG
1672 		rfslog_records_ignored++;
1673 #endif
1674 		return;
1675 	}
1676 
1677 	switch (req->rq_prog) {
1678 	case NFS_PROGRAM:
1679 		switch (req->rq_vers) {
1680 		case NFS_V3:
1681 			switch (req->rq_proc) {
1682 			case NFSPROC3_READDIRPLUS:
1683 				alloc_indx = MEDIUM_INDX;
1684 				break;
1685 			default:
1686 				alloc_indx = SMALL_INDX;
1687 				break;
1688 			}
1689 			break;
1690 		default:
1691 			alloc_indx = SMALL_INDX;
1692 			break;
1693 		}
1694 		break;
1695 	case NFSLOG_PROGRAM:
1696 		alloc_indx = MEDIUM_INDX;
1697 		break;
1698 	default:
1699 		alloc_indx = SMALL_INDX;
1700 		break;
1701 	}
1702 
1703 	do {
1704 		encode_ok = FALSE;
1705 
1706 		/* Pick the size to alloc; end of the road - return */
1707 		if (nfslog_mem_alloc[alloc_indx].size == (-1)) {
1708 			cmn_err(CE_WARN,
1709 				"NFSLOG: unable to encode record - prog=%d "
1710 				"proc = %d", req->rq_prog, req->rq_proc);
1711 			return;
1712 		}
1713 
1714 		buffer = nfslog_record_alloc(exi, alloc_indx, &log_cookie, 0);
1715 		if (buffer == NULL) {
1716 			/* Error processing - no space alloced */
1717 			rfs_log_bad++;
1718 			cmn_err(CE_WARN, "NFSLOG: can't get record");
1719 			return;
1720 		}
1721 
1722 		xdrmem_create(&xdrs, buffer,
1723 			nfslog_mem_alloc[alloc_indx].size, XDR_ENCODE);
1724 
1725 		/*
1726 		 * Encode the header, args and results of the record
1727 		 */
1728 		if (xdr_nfslog_request_record(&xdrs, exi, req, cr, pnb,
1729 			nfslog_mem_alloc[alloc_indx].size, record_id) &&
1730 			(*disp->xdrargs)(&xdrs, args) &&
1731 			(*disp->xdrres)(&xdrs, res)) {
1732 				encode_ok = TRUE;
1733 
1734 				rfs_log_good++;
1735 				/*
1736 				 * Get the final size of the encoded
1737 				 * data and insert that length at the
1738 				 * beginning.
1739 				 */
1740 				final_size = xdr_getpos(&xdrs);
1741 				xdr_setpos(&xdrs, 0);
1742 				(void) xdr_u_int(&xdrs, &final_size);
1743 		} else {
1744 			/* Oops, the encode failed so we need to free memory */
1745 			nfslog_record_put(log_cookie, 0, FALSE, which_buffers);
1746 			alloc_indx++;
1747 		}
1748 
1749 	} while (encode_ok == FALSE);
1750 
1751 
1752 	/*
1753 	 * Take the final log record and put it in the log file.
1754 	 * This may be queued to the file internally and written
1755 	 * later unless the last parameter is TRUE.
1756 	 * If the record_id is 0 then this is most likely a share/unshare
1757 	 * request and it should be written synchronously to the log file.
1758 	 */
1759 	nfslog_record_put(log_cookie, final_size,
1760 		(record_id == 0), which_buffers);
1761 }
1762 
1763 static char *
1764 get_publicfh_path(int *alloc_length)
1765 {
1766 	extern struct exportinfo *exi_public;
1767 	char *pubpath;
1768 
1769 	rw_enter(&exported_lock, RW_READER);
1770 
1771 	*alloc_length = exi_public->exi_export.ex_pathlen + 1;
1772 	pubpath = kmem_alloc(*alloc_length, KM_SLEEP);
1773 
1774 	(void) strcpy(pubpath, exi_public->exi_export.ex_path);
1775 
1776 	rw_exit(&exported_lock);
1777 
1778 	return (pubpath);
1779 }
1780 
1781 static void
1782 log_public_record(struct exportinfo *exi, cred_t *cr)
1783 {
1784 	struct svc_req	req;
1785 	struct netbuf	nb = {0, 0, NULL};
1786 	int free_length = 0;
1787 	diropargs3 args;
1788 	LOOKUP3res res;
1789 
1790 	bzero(&req, sizeof (req));
1791 	req.rq_prog = NFSLOG_PROGRAM;
1792 	req.rq_vers = NFSLOG_VERSION;
1793 	req.rq_proc = NFSLOG_LOOKUP;
1794 	req.rq_cred.oa_flavor = AUTH_NONE;
1795 
1796 	bzero(&args, sizeof (diropargs3));
1797 	bzero(&res, sizeof (LOOKUP3res));
1798 
1799 	args.dir.fh3_length = 0;
1800 	if ((args.name = get_publicfh_path(&free_length)) == NULL)
1801 		return;
1802 	args.dirp = &args.dir;
1803 
1804 	res.status = NFS3_OK;
1805 	res.res_u.ok.object.fh3_length = 0;
1806 
1807 	/*
1808 	 * Calling this function with the exi_public
1809 	 * will have the effect of appending the record
1810 	 * to each of the open log buffers
1811 	 */
1812 	nfslog_write_record(exi, &req,
1813 		(caddr_t)&args, (caddr_t)&res, cr, &nb, 0, NFSLOG_ALL_BUFFERS);
1814 
1815 	kmem_free(args.name, free_length);
1816 }
1817 
1818 /*
1819  * nfslog_share_record - logs a share request.
1820  * This is not an NFS request, but we pretend here...
1821  */
1822 void
1823 nfslog_share_record(struct exportinfo *exi, cred_t *cr)
1824 {
1825 	struct svc_req	req;
1826 	int		res = 0;
1827 	struct netbuf	nb = {0, 0, NULL};
1828 
1829 	ASSERT(exi != NULL);
1830 
1831 	if (nfslog_buffer_list == NULL)
1832 		return;
1833 
1834 	if (exi->exi_export.ex_flags & EX_LOG) {
1835 		bzero(&req, sizeof (req));
1836 		req.rq_prog = NFSLOG_PROGRAM;
1837 		req.rq_vers = NFSLOG_VERSION;
1838 		req.rq_proc = NFSLOG_SHARE;
1839 		req.rq_cred.oa_flavor = AUTH_NONE;
1840 		nfslog_write_record(exi, &req,
1841 			(caddr_t)exi, (caddr_t)&res, cr,
1842 			&nb, 0, NFSLOG_ONE_BUFFER);
1843 	}
1844 
1845 	log_public_record(exi, cr);
1846 }
1847 
1848 /*
1849  * nfslog_unshare_record - logs an unshare request.
1850  * This is not an NFS request, but we pretend here...
1851  */
1852 void
1853 nfslog_unshare_record(struct exportinfo *exi, cred_t *cr)
1854 {
1855 	struct svc_req	req;
1856 	int		res = 0;
1857 	struct netbuf	nb = {0, 0, NULL};
1858 
1859 	ASSERT(exi != NULL);
1860 	ASSERT(exi->exi_export.ex_flags & EX_LOG);
1861 
1862 	bzero(&req, sizeof (req));
1863 	req.rq_prog = NFSLOG_PROGRAM;
1864 	req.rq_vers = NFSLOG_VERSION;
1865 	req.rq_proc = NFSLOG_UNSHARE;
1866 	req.rq_cred.oa_flavor = AUTH_NONE;
1867 	nfslog_write_record(exi, &req,
1868 		(caddr_t)exi, (caddr_t)&res, cr, &nb, 0, NFSLOG_ONE_BUFFER);
1869 }
1870 
1871 
1872 void
1873 nfslog_getfh(struct exportinfo *exi,
1874 	fhandle *fh,
1875 	char *fname,
1876 	enum uio_seg seg,
1877 	cred_t *cr)
1878 {
1879 	struct svc_req	req;
1880 	int		res = 0;
1881 	struct netbuf	nb = {0, 0, NULL};
1882 	int		error = 0;
1883 	char		*namebuf;
1884 	size_t		len;
1885 	nfslog_getfhargs gfh;
1886 
1887 	ASSERT(exi != NULL);
1888 	ASSERT(exi->exi_export.ex_flags & EX_LOG);
1889 
1890 	bzero(&req, sizeof (req));
1891 	req.rq_prog = NFSLOG_PROGRAM;
1892 	req.rq_vers = NFSLOG_VERSION;
1893 	req.rq_proc = NFSLOG_GETFH;
1894 	req.rq_cred.oa_flavor = AUTH_NONE;
1895 
1896 	namebuf = kmem_alloc(MAXPATHLEN + 4, KM_SLEEP);
1897 	if (seg == UIO_USERSPACE) {
1898 		error = copyinstr(fname, namebuf, MAXPATHLEN, &len);
1899 	} else {
1900 		error = copystr(fname, namebuf, MAXPATHLEN, &len);
1901 	}
1902 
1903 	if (!error) {
1904 		gfh.gfh_fh_buf = *fh;
1905 		gfh.gfh_path = namebuf;
1906 
1907 		nfslog_write_record(exi, &req,
1908 			(caddr_t)&gfh, (caddr_t)&res, cr, &nb, 0,
1909 			NFSLOG_ONE_BUFFER);
1910 	}
1911 	kmem_free(namebuf, MAXPATHLEN + 4);
1912 }
1913