xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_log.c (revision 80ab886d)
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 			if (uio.uio_resid)
839 				error = ENOSPC;
840 			if (error)
841 				(void) VOP_SETATTR(vp, &va, 0, CRED(), NULL);
842 		} else {
843 			if (!(lfp->lf_flags & L_PRINTED)) {
844 				cmn_err(CE_WARN,
845 				    "NFS Logging: buffer file %s exceeds 2GB; "
846 				    "stopped writing buffer \n", lfp->lf_path);
847 			}
848 			error = ENOSPC;
849 		}
850 	}
851 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
852 
853 	kmem_free(iovp, size_iovecs);
854 
855 out:
856 	if (error) {
857 		if (!(lfp->lf_flags & L_PRINTED)) {
858 			nfs_cmn_err(error, CE_WARN,
859 				"NFS Logging disabled for buffer %s - "
860 				"write error = %m\n", lfp->lf_path);
861 			lfp->lf_flags |= L_PRINTED;
862 		}
863 	} else if (lfp->lf_flags & (L_ERROR | L_PRINTED)) {
864 		lfp->lf_flags &= ~(L_ERROR | L_PRINTED);
865 		cmn_err(CE_WARN,
866 			"NFS Logging re-enabled for buffer %s\n", lfp->lf_path);
867 	}
868 
869 	return (error);
870 }
871 
872 static void
873 nfslog_free_logrecords(struct lr_alloc *lrp_writers)
874 {
875 	struct lr_alloc *lrp = lrp_writers;
876 	struct lr_alloc *lrp_free;
877 
878 	do {
879 		lrp_free = lrp;
880 
881 		lrp = lrp->next;
882 
883 		/*
884 		 * Check to see if we are supposed to free this structure
885 		 * and relese the log_buffer ref count.
886 		 * It may be the case that the caller does not want this
887 		 * structure and its record contents freed just yet.
888 		 */
889 		if ((lrp_free->lr_flags & LR_ALLOC_NOFREE) == 0) {
890 			if (lrp_free->lb != NULL)
891 				log_buffer_rele(lrp_free->lb);
892 			if (lrp_free->alloc_cache) /* double check */
893 				kmem_cache_free(lrp_free->alloc_cache,
894 					(void *)lrp_free);
895 		} else {
896 			/*
897 			 * after being pulled from the list the
898 			 * pointers need to be reinitialized.
899 			 */
900 			lrp_free->next = lrp_free;
901 			lrp_free->prev = lrp_free;
902 		}
903 
904 	} while (lrp != lrp_writers);
905 }
906 
907 /*
908  * Rename lbp->lb_logfile to reflect the true name requested by 'share'
909  */
910 static int
911 nfslog_logbuffer_rename(struct log_buffer *lbp)
912 {
913 	struct log_file *lf;
914 	int error;
915 	struct log_file *logfile;
916 
917 	/*
918 	 * Try our best to get the cache records into the log file
919 	 * before the rename occurs.
920 	 */
921 	(void) nfslog_records_flush_to_disk(lbp);
922 
923 	/*
924 	 * Hold lb_lock before retrieving
925 	 * lb_logfile.
926 	 * Hold a reference to the
927 	 * "lf" structure. this is
928 	 * same as LOG_FILE_HOLD()
929 	 */
930 	mutex_enter(&(lbp)->lb_lock);
931 	lf = lbp->lb_logfile;
932 	mutex_enter(&(lf)->lf_lock);
933 	mutex_exit(&(lbp)->lb_lock);
934 	lf->lf_refcnt++;
935 	mutex_exit(&(lf)->lf_lock);
936 
937 	LOGGING_DPRINT((10, "nfslog_logbuffer_rename: renaming %s to %s\n",
938 		lf->lf_path, lbp->lb_path));
939 
940 	/*
941 	 * rename the current buffer to what the daemon expects
942 	 */
943 	if (error = nfslog_logfile_rename(lf->lf_path, lbp->lb_path))
944 		goto out;
945 
946 	/*
947 	 * Create a new working buffer file and have all new data sent there.
948 	 */
949 	if (error = log_file_create(lbp->lb_path, &logfile)) {
950 		/* Attempt to rename to original */
951 		(void) nfslog_logfile_rename(lbp->lb_path, lf->lf_path);
952 		goto out;
953 	}
954 
955 	/*
956 	 * Hold the lb_lock here, this will make
957 	 * all the threads trying to access lb->logfile block
958 	 * and get a new logfile structure instead of old one.
959 	 */
960 	mutex_enter(&(lbp)->lb_lock);
961 	lbp->lb_logfile = logfile;
962 	mutex_exit(&(lbp)->lb_lock);
963 
964 	LOG_FILE_RELE(lf);	/* release log_buffer's reference */
965 
966 	/*
967 	 * Wait for log_file to be in a quiescent state before we
968 	 * return to our caller to let it proceed with the reading of
969 	 * this file.
970 	 */
971 	nfslog_logfile_wait(lf);
972 
973 out:
974 	/*
975 	 * Release our reference on "lf" in two different cases.
976 	 * 1. Error condition, release only the reference
977 	 *    that we held at the begining of this
978 	 *    routine on "lf" structure.
979 	 * 2. Fall through condition, no errors but the old
980 	 *    logfile structure "lf" has been replaced with
981 	 *    the new "logfile" structure, so release the
982 	 *    reference that was part of the creation of
983 	 *    "lf" structure to free up the resources.
984 	 */
985 
986 	LOG_FILE_RELE(lf);
987 
988 	return (error);
989 }
990 
991 /*
992  * Renames the 'from' file to 'new'.
993  */
994 static int
995 nfslog_logfile_rename(char *from, char *new)
996 {
997 	int error;
998 
999 	if (error = vn_rename(from, new, UIO_SYSSPACE)) {
1000 		cmn_err(CE_WARN,
1001 			"nfslog_logfile_rename: couldn't rename %s to %s\n",
1002 			from, new);
1003 	}
1004 	return (error);
1005 }
1006 
1007 /*
1008  * Wait for the log_file writers to finish before returning
1009  */
1010 static void
1011 nfslog_logfile_wait(struct log_file *lf)
1012 {
1013 	mutex_enter(&lf->lf_lock);
1014 	while (lf->lf_writers > 0) {
1015 		lf->lf_flags |= L_WAITING;
1016 		(void) cv_wait_sig(&lf->lf_cv_waiters, &lf->lf_lock);
1017 	}
1018 	mutex_exit(&lf->lf_lock);
1019 }
1020 
1021 static int
1022 nfslog_record_append2all(struct lr_alloc *lrp)
1023 {
1024 	struct log_buffer *lbp, *nlbp;
1025 	int error, ret_error = 0;
1026 	int lr_flags = lrp->lr_flags;
1027 
1028 	rw_enter(&nfslog_buffer_list_lock, RW_READER);
1029 	if ((lbp = nfslog_buffer_list) != NULL)
1030 		LOG_BUFFER_HOLD(lbp);
1031 	for (nlbp = NULL; lbp != NULL; lbp = nlbp) {
1032 		if ((nlbp = lbp->lb_next) != NULL) {
1033 			/*
1034 			 * Remember next element in the list
1035 			 */
1036 			LOG_BUFFER_HOLD(nlbp);
1037 		}
1038 		rw_exit(&nfslog_buffer_list_lock);
1039 
1040 		/*
1041 		 * Insert the record on the buffer's list to be written
1042 		 * and then flush the records to the log file.
1043 		 * Make sure to set the no free flag so that the
1044 		 * record can be used for the next write
1045 		 */
1046 		lrp->lr_flags = LR_ALLOC_NOFREE;
1047 
1048 		ASSERT(lbp != NULL);
1049 		mutex_enter(&lbp->lb_lock);
1050 		if (lbp->lb_records == NULL) {
1051 			lbp->lb_records = (caddr_t)lrp;
1052 			lbp->lb_num_recs = 1;
1053 			lbp->lb_size_queued = lrp->size;
1054 		} else {
1055 			insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev);
1056 			lbp->lb_num_recs++;
1057 			lbp->lb_size_queued += lrp->size;
1058 		}
1059 
1060 		/*
1061 		 * Flush log records to disk.
1062 		 * Function is called with lb_lock held.
1063 		 * Function drops the lb_lock on return.
1064 		 */
1065 		error = nfslog_records_flush_to_disk_nolock(lbp);
1066 
1067 		if (error) {
1068 			ret_error = -1;
1069 			nfs_cmn_err(error, CE_WARN,
1070 				"rfsl_log_pubfh: could not append record to "
1071 				"\"%s\" error = %m\n", lbp->lb_path);
1072 		}
1073 		log_buffer_rele(lbp);
1074 		rw_enter(&nfslog_buffer_list_lock, RW_READER);
1075 	}
1076 	rw_exit(&nfslog_buffer_list_lock);
1077 
1078 	lrp->lr_flags = lr_flags;
1079 
1080 	return (ret_error);
1081 }
1082 
1083 #ifdef DEBUG
1084 static int logging_debug = 0;
1085 
1086 /*
1087  * 0) no debugging
1088  * 3) current test software
1089  * 10) random stuff
1090  */
1091 void
1092 nfslog_dprint(const int level, const char *fmt, ...)
1093 {
1094 	va_list args;
1095 
1096 	if (logging_debug == level ||
1097 	    (logging_debug > 10 && (logging_debug - 10) >= level)) {
1098 		va_start(args, fmt);
1099 		(void) vprintf(fmt, args);
1100 		va_end(args);
1101 	}
1102 }
1103 
1104 #endif /* DEBUG */
1105 
1106 /*
1107  * NFS Log Flush system call
1108  * Caller must check privileges.
1109  */
1110 /* ARGSUSED */
1111 int
1112 nfsl_flush(struct nfsl_flush_args *args, model_t model)
1113 {
1114 	struct flush_thread_params *tparams;
1115 	struct nfsl_flush_args *nfsl_args;
1116 	int error = 0;
1117 	ulong_t buffer_len;
1118 	STRUCT_HANDLE(nfsl_flush_args, uap);
1119 
1120 	STRUCT_SET_HANDLE(uap, model, args);
1121 
1122 	tparams = (struct flush_thread_params *)
1123 		kmem_zalloc(sizeof (*tparams), KM_SLEEP);
1124 
1125 	nfsl_args = &tparams->tp_args;
1126 	nfsl_args->version =  STRUCT_FGET(uap, version);
1127 	if (nfsl_args->version != NFSL_FLUSH_ARGS_VERS) {
1128 		cmn_err(CE_WARN, "nfsl_flush: exected version %d, got %d",
1129 			NFSL_FLUSH_ARGS_VERS, nfsl_args->version);
1130 		return (EIO);
1131 	}
1132 
1133 	nfsl_args->directive = STRUCT_FGET(uap, directive);
1134 	if ((nfsl_args->directive & NFSL_ALL) == 0) {
1135 		/*
1136 		 * Process a specific buffer
1137 		 */
1138 		nfsl_args->buff_len = STRUCT_FGET(uap, buff_len);
1139 
1140 		nfsl_args->buff = (char *)
1141 			kmem_alloc(nfsl_args->buff_len, KM_NOSLEEP);
1142 		if (nfsl_args->buff == NULL)
1143 			return (ENOMEM);
1144 
1145 		error = copyinstr((const char *)STRUCT_FGETP(uap, buff),
1146 			nfsl_args->buff, nfsl_args->buff_len, &buffer_len);
1147 		if (error)
1148 			return (EFAULT);
1149 
1150 		if (nfsl_args->buff_len != buffer_len)
1151 			return (EFAULT);
1152 	}
1153 
1154 	LOGGING_DPRINT((10, "nfsl_flush: Flushing %s buffer(s)\n",
1155 		nfsl_args->directive & NFSL_ALL ? "all" : nfsl_args->buff));
1156 
1157 	if (nfsl_args->directive & NFSL_SYNC) {
1158 		/*
1159 		 * Do the work synchronously
1160 		 */
1161 		nfslog_do_flush(tparams);
1162 		error = tparams->tp_error;
1163 		kmem_free(nfsl_args->buff, nfsl_args->buff_len);
1164 		kmem_free(tparams, sizeof (*tparams));
1165 	} else {
1166 		/*
1167 		 * Do the work asynchronously
1168 		 */
1169 		(void) thread_create(NULL, 0, nfslog_do_flush,
1170 		    tparams, 0, &p0, TS_RUN, minclsyspri);
1171 	}
1172 
1173 	return (error);
1174 }
1175 
1176 /*
1177  * This is where buffer flushing would occur, but there is no buffering
1178  * at this time.
1179  * Possibly rename the log buffer for processing.
1180  * Sets tparams->ta_error equal to the value of the error that occured,
1181  * 0 otherwise.
1182  * Returns ENOENT if the buffer is not found.
1183  */
1184 static void
1185 nfslog_do_flush(struct flush_thread_params *tparams)
1186 {
1187 	struct nfsl_flush_args *args;
1188 	struct log_buffer *lbp;
1189 	int error = ENOENT;
1190 	int found = 0;
1191 	char *buf_inprog;	/* name of buff in progress */
1192 	int buf_inprog_len;
1193 
1194 	/*
1195 	 * Sanity check on the arguments.
1196 	 */
1197 	if (!tparams)
1198 		return;
1199 	args = &tparams->tp_args;
1200 	if (!args)
1201 		return;
1202 
1203 	rw_enter(&nfslog_buffer_list_lock, RW_READER);
1204 	for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) {
1205 		if (args->directive & NFSL_ALL) {
1206 			(void) nfslog_records_flush_to_disk(lbp);
1207 		} else {
1208 			if ((strcmp(lbp->lb_path, args->buff) == 0) &&
1209 				(args->directive & NFSL_RENAME)) {
1210 
1211 				error = nfslog_logbuffer_rename(lbp);
1212 				found++;
1213 				break;
1214 			}
1215 		}
1216 	}
1217 	rw_exit(&nfslog_buffer_list_lock);
1218 
1219 	if (!found && ((args->directive & NFSL_ALL) == 0) &&
1220 	    (args->directive & NFSL_RENAME)) {
1221 		/*
1222 		 * The specified buffer is not currently in use,
1223 		 * simply rename the file indicated.
1224 		 */
1225 		buf_inprog_len = strlen(args->buff) +
1226 			strlen(LOG_INPROG_STRING) + 1;
1227 		buf_inprog = (caddr_t)kmem_alloc(buf_inprog_len, KM_SLEEP);
1228 		(void) sprintf(buf_inprog, "%s%s",
1229 			args->buff, LOG_INPROG_STRING);
1230 
1231 		error = nfslog_logfile_rename(buf_inprog, args->buff);
1232 
1233 		kmem_free(buf_inprog, buf_inprog_len);
1234 	}
1235 
1236 out:
1237 	if ((args->directive & NFSL_SYNC) == 0) {
1238 		/*
1239 		 * Work was performed asynchronously, the caller is
1240 		 * no longer waiting for us.
1241 		 * Free the thread arguments and exit.
1242 		 */
1243 		kmem_free(args->buff, args->buff_len);
1244 		kmem_free(tparams, sizeof (*tparams));
1245 		thread_exit();
1246 		/* NOTREACHED */
1247 	}
1248 
1249 	tparams->tp_error = error;
1250 }
1251 
1252 /*
1253  * Generate buffer_header.
1254  * 'loghdr' points the the buffer_header, and *reclen
1255  * contains the length of the buffer.
1256  */
1257 static void
1258 create_buffer_header(caddr_t *loghdr, size_t *reclen, size_t *freesize)
1259 {
1260 	timestruc_t		now;
1261 	nfslog_buffer_header	lh;
1262 	XDR			xdrs;
1263 	unsigned int		final_size;
1264 
1265 
1266 	/* pick some size that will hold the buffer_header */
1267 	*freesize = NFSLOG_SMALL_RECORD_SIZE;
1268 
1269 	/*
1270 	 * Fill header
1271 	 */
1272 	lh.bh_length = 0;	/* don't know yet how large it will be */
1273 	lh.bh_version = NFSLOG_BUF_VERSION;
1274 	lh.bh_flags = 0;
1275 	lh.bh_offset = 0;
1276 	gethrestime(&now);
1277 	TIMESPEC_TO_TIMESPEC32(&lh.bh_timestamp, &now);
1278 
1279 	/*
1280 	 * Encode the header
1281 	 */
1282 	*loghdr = (caddr_t)kmem_alloc(*freesize, KM_SLEEP);
1283 	xdrmem_create(&xdrs, *loghdr, *freesize, XDR_ENCODE);
1284 
1285 	(void) xdr_nfslog_buffer_header(&xdrs, &lh);
1286 
1287 	/*
1288 	 * Reset with final size of the encoded data
1289 	 */
1290 	final_size = xdr_getpos(&xdrs);
1291 	xdr_setpos(&xdrs, 0);
1292 	(void) xdr_u_int(&xdrs, &final_size);
1293 
1294 	*reclen = (size_t)final_size;
1295 }
1296 
1297 /*
1298  * ****************************************************************
1299  * RPC dispatch table for logging
1300  * Indexed by program, version, proc
1301  * Based on NFS dispatch table.
1302  */
1303 struct nfslog_proc_disp {
1304 	bool_t	(*xdrargs)();
1305 	bool_t	(*xdrres)();
1306 	bool_t	affects_transactions;	/* Operation affects transaction */
1307 					/* processing */
1308 };
1309 
1310 struct nfslog_vers_disp {
1311 	int	nfslog_dis_nprocs;			/* number of procs */
1312 	struct nfslog_proc_disp	*nfslog_dis_proc_table;	/* proc array */
1313 };
1314 
1315 struct nfslog_prog_disp {
1316 	int	nfslog_dis_prog;		/* program number */
1317 	int	nfslog_dis_versmin;		/* Minimum version value */
1318 	int	nfslog_dis_nvers;		/* Number of version values */
1319 	struct nfslog_vers_disp	*nfslog_dis_vers_table;	/* versions array */
1320 };
1321 
1322 static int rfs_log_bad = 0;	/* incremented on bad log attempts */
1323 static int rfs_log_good = 0;	/* incremented on successful log attempts */
1324 
1325 /*
1326  * Define the actions taken per prog/vers/proc:
1327  *
1328  * In some cases, the nl types are the same as the nfs types and a simple
1329  * bcopy should suffice. Rather that define tens of identical procedures,
1330  * simply define these to bcopy. Similarly this takes care of different
1331  * procs that use same parameter struct.
1332  */
1333 
1334 static struct nfslog_proc_disp nfslog_proc_v2[] = {
1335 	/*
1336 	 * NFS VERSION 2
1337 	 */
1338 
1339 	/* RFS_NULL = 0 */
1340 	{xdr_void, xdr_void, FALSE},
1341 
1342 	/* RFS_GETATTR = 1 */
1343 	{xdr_fhandle, xdr_nfslog_getattrres, FALSE},
1344 
1345 	/* RFS_SETATTR = 2 */
1346 	{xdr_nfslog_setattrargs, xdr_nfsstat, TRUE},
1347 
1348 	/* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */
1349 	{xdr_void, xdr_void, FALSE},
1350 
1351 	/* RFS_LOOKUP = 4 */
1352 	{xdr_nfslog_diropargs, xdr_nfslog_diropres, TRUE},
1353 
1354 	/* RFS_READLINK = 5 */
1355 	{xdr_fhandle, xdr_nfslog_rdlnres, FALSE},
1356 
1357 	/* RFS_READ = 6 */
1358 	{xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult, TRUE},
1359 
1360 	/* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */
1361 	{xdr_void, xdr_void, FALSE},
1362 
1363 	/* RFS_WRITE = 8 */
1364 	{xdr_nfslog_writeargs, xdr_nfslog_writeresult, TRUE},
1365 
1366 	/* RFS_CREATE = 9 */
1367 	{xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE},
1368 
1369 	/* RFS_REMOVE = 10 */
1370 	{xdr_nfslog_diropargs, xdr_nfsstat, TRUE},
1371 
1372 	/* RFS_RENAME = 11 */
1373 	{xdr_nfslog_rnmargs, xdr_nfsstat, TRUE},
1374 
1375 	/* RFS_LINK = 12 */
1376 	{xdr_nfslog_linkargs, xdr_nfsstat, TRUE},
1377 
1378 	/* RFS_SYMLINK = 13 */
1379 	{xdr_nfslog_symlinkargs, xdr_nfsstat, TRUE},
1380 
1381 	/* RFS_MKDIR = 14 */
1382 	{xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE},
1383 
1384 	/* RFS_RMDIR = 15 */
1385 	{xdr_nfslog_diropargs, xdr_nfsstat, TRUE},
1386 
1387 	/* RFS_READDIR = 16 */
1388 	{xdr_nfslog_rddirargs, xdr_nfslog_rddirres, TRUE},
1389 
1390 	/* RFS_STATFS = 17 */
1391 	{xdr_fhandle, xdr_nfslog_statfs, FALSE},
1392 };
1393 
1394 
1395 /*
1396  * NFS VERSION 3
1397  */
1398 
1399 static struct nfslog_proc_disp nfslog_proc_v3[] = {
1400 
1401 	/* NFSPROC3_NULL = 0 */
1402 	{xdr_void, xdr_void, FALSE},
1403 
1404 	/* NFSPROC3_GETATTR = 1 */
1405 	{xdr_nfslog_nfs_fh3, xdr_nfslog_GETATTR3res, FALSE},
1406 
1407 	/* NFSPROC3_SETATTR = 2 */
1408 	{xdr_nfslog_SETATTR3args, xdr_nfslog_SETATTR3res, TRUE},
1409 
1410 	/* NFSPROC3_LOOKUP = 3 */
1411 	{xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE},
1412 
1413 	/* NFSPROC3_ACCESS = 4 */
1414 	{xdr_nfslog_ACCESS3args, xdr_nfslog_ACCESS3res, FALSE},
1415 
1416 	/* NFSPROC3_READLINK = 5 */
1417 	{xdr_nfslog_nfs_fh3, xdr_nfslog_READLINK3res, FALSE},
1418 
1419 	/* NFSPROC3_READ = 6 */
1420 	{xdr_nfslog_READ3args, xdr_nfslog_READ3res, TRUE},
1421 
1422 	/* NFSPROC3_WRITE = 7 */
1423 	{xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res, TRUE},
1424 
1425 	/* NFSPROC3_CREATE = 8 */
1426 	{xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res, TRUE},
1427 
1428 	/* NFSPROC3_MKDIR = 9 */
1429 	{xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res, TRUE},
1430 
1431 	/* NFSPROC3_SYMLINK = 10 */
1432 	{xdr_nfslog_SYMLINK3args, xdr_nfslog_SYMLINK3res, TRUE},
1433 
1434 	/* NFSPROC3_MKNOD = 11 */
1435 	{xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res, TRUE},
1436 
1437 	/* NFSPROC3_REMOVE = 12 */
1438 	{xdr_nfslog_REMOVE3args, xdr_nfslog_REMOVE3res, TRUE},
1439 
1440 	/* NFSPROC3_RMDIR = 13 */
1441 	{xdr_nfslog_RMDIR3args, xdr_nfslog_RMDIR3res, TRUE},
1442 
1443 	/* NFSPROC3_RENAME = 14 */
1444 	{xdr_nfslog_RENAME3args, xdr_nfslog_RENAME3res, TRUE},
1445 
1446 	/* NFSPROC3_LINK = 15 */
1447 	{xdr_nfslog_LINK3args, xdr_nfslog_LINK3res, TRUE},
1448 
1449 	/* NFSPROC3_READDIR = 16 */
1450 	{xdr_nfslog_READDIR3args, xdr_nfslog_READDIR3res, TRUE},
1451 
1452 	/* NFSPROC3_READDIRPLUS = 17 */
1453 	{xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res, TRUE},
1454 
1455 	/* NFSPROC3_FSSTAT = 18 */
1456 	{xdr_nfslog_FSSTAT3args, xdr_nfslog_FSSTAT3res, FALSE},
1457 
1458 	/* NFSPROC3_FSINFO = 19 */
1459 	{xdr_nfslog_FSINFO3args, xdr_nfslog_FSINFO3res, FALSE},
1460 
1461 	/* NFSPROC3_PATHCONF = 20 */
1462 	{xdr_nfslog_PATHCONF3args, xdr_nfslog_PATHCONF3res, FALSE},
1463 
1464 	/* NFSPROC3_COMMIT = 21 */
1465 	{xdr_nfslog_COMMIT3args, xdr_nfslog_COMMIT3res, FALSE},
1466 };
1467 
1468 static struct nfslog_proc_disp nfslog_proc_v1[] = {
1469 	/*
1470 	 * NFSLOG VERSION 1
1471 	 */
1472 
1473 	/* NFSLOG_NULL = 0 */
1474 	{xdr_void, xdr_void, TRUE},
1475 
1476 	/* NFSLOG_SHARE = 1 */
1477 	{xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE},
1478 
1479 	/* NFSLOG_UNSHARE = 2 */
1480 	{xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE},
1481 
1482 	/* NFSLOG_LOOKUP = 3 */
1483 	{xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE},
1484 
1485 	/* NFSLOG_GETFH = 4 */
1486 	{xdr_nfslog_getfhargs, xdr_nfsstat, TRUE},
1487 };
1488 
1489 static struct nfslog_vers_disp nfslog_vers_disptable[] = {
1490 	{sizeof (nfslog_proc_v2) / sizeof (nfslog_proc_v2[0]),
1491 	    nfslog_proc_v2},
1492 	{sizeof (nfslog_proc_v3) / sizeof (nfslog_proc_v3[0]),
1493 	    nfslog_proc_v3},
1494 };
1495 
1496 static struct nfslog_vers_disp nfslog_nfslog_vers_disptable[] = {
1497 	{sizeof (nfslog_proc_v1) / sizeof (nfslog_proc_v1[0]),
1498 	    nfslog_proc_v1},
1499 };
1500 
1501 static struct nfslog_prog_disp nfslog_dispatch_table[] = {
1502 	{NFS_PROGRAM, NFS_VERSMIN,
1503 		(sizeof (nfslog_vers_disptable) /
1504 		sizeof (nfslog_vers_disptable[0])),
1505 		nfslog_vers_disptable},
1506 
1507 	{NFSLOG_PROGRAM, NFSLOG_VERSMIN,
1508 		(sizeof (nfslog_nfslog_vers_disptable) /
1509 		sizeof (nfslog_nfslog_vers_disptable[0])),
1510 		nfslog_nfslog_vers_disptable},
1511 };
1512 
1513 static int	nfslog_dispatch_table_arglen = sizeof (nfslog_dispatch_table) /
1514 					sizeof (nfslog_dispatch_table[0]);
1515 
1516 /*
1517  * This function will determine the appropriate export info struct to use
1518  * and allocate a record id to be used in the written log buffer.
1519  * Usually this is a straightforward operation but the existence of the
1520  * multicomponent lookup and its semantics of crossing file system
1521  * boundaries add to the complexity.  See the comments below...
1522  */
1523 struct exportinfo *
1524 nfslog_get_exi(
1525 	struct exportinfo *exi,
1526 	struct svc_req *req,
1527 	caddr_t res,
1528 	unsigned int *nfslog_rec_id)
1529 {
1530 	struct log_buffer *lb;
1531 	struct exportinfo *exi_ret = NULL;
1532 	fhandle_t		*fh;
1533 	nfs_fh3			*fh3;
1534 
1535 	if (exi == NULL)
1536 		return (NULL);
1537 
1538 	/*
1539 	 * If the exi is marked for logging, allocate a record id and return
1540 	 */
1541 	if (exi->exi_export.ex_flags & EX_LOG) {
1542 		lb = exi->exi_logbuffer;
1543 
1544 		/* obtain the unique record id for the caller */
1545 		*nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1);
1546 
1547 		/*
1548 		 * The caller will expect to be able to exi_rele() it,
1549 		 * so exi->exi_count must be incremented before it can
1550 		 * be returned, to make it uniform with exi_ret->exi_count
1551 		 */
1552 		mutex_enter(&exi->exi_lock);
1553 		exi->exi_count++;
1554 		mutex_exit(&exi->exi_lock);
1555 
1556 		return (exi);
1557 	}
1558 
1559 	if (exi != exi_public)
1560 		return (NULL);
1561 
1562 	/*
1563 	 * Here we have an exi that is not marked for logging.
1564 	 * It is possible that this request is a multicomponent lookup
1565 	 * that was done from the public file handle (not logged) and
1566 	 * the resulting file handle being returned to the client exists
1567 	 * in a file system that is being logged.  If this is the case
1568 	 * we need to log this multicomponent lookup to the appropriate
1569 	 * log buffer.  This will allow for the appropriate path name
1570 	 * mapping to occur at user level.
1571 	 */
1572 	if (req->rq_prog == NFS_PROGRAM) {
1573 		switch (req->rq_vers) {
1574 		case NFS_V3:
1575 			if ((req->rq_proc == NFSPROC3_LOOKUP) &&
1576 				(((LOOKUP3res *)res)->status == NFS3_OK)) {
1577 				fh3 = &((LOOKUP3res *)res)->res_u.ok.object;
1578 				exi_ret = checkexport(&fh3->fh3_fsid,
1579 					FH3TOXFIDP(fh3));
1580 			}
1581 			break;
1582 
1583 		case NFS_VERSION:
1584 			if ((req->rq_proc == RFS_LOOKUP) &&
1585 				(((struct nfsdiropres *)
1586 					res)->dr_status == NFS_OK)) {
1587 				fh =
1588 		&((struct nfsdiropres *)res)->dr_u.dr_drok_u.drok_fhandle;
1589 				exi_ret = checkexport(&fh->fh_fsid,
1590 					(fid_t *)&fh->fh_xlen);
1591 			}
1592 			break;
1593 		default:
1594 			break;
1595 		}
1596 	}
1597 
1598 	if (exi_ret != NULL && exi_ret->exi_export.ex_flags & EX_LOG) {
1599 		lb = exi_ret->exi_logbuffer;
1600 		/* obtain the unique record id for the caller */
1601 		*nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1);
1602 
1603 		return (exi_ret);
1604 	}
1605 	return (NULL);
1606 }
1607 
1608 #ifdef DEBUG
1609 static long long rfslog_records_ignored = 0;
1610 #endif
1611 
1612 /*
1613  * nfslog_write_record - Fill in the record buffer for writing out.
1614  * If logrecp is null, log it, otherwise, malloc the record and return it.
1615  *
1616  * It is the responsibility of the caller to check whether this exportinfo
1617  * has logging enabled.
1618  * Note that nfslog_share_public_record() only needs to check for the
1619  * existence of at least one logbuffer to which the public filehandle record
1620  * needs to be logged.
1621  */
1622 void
1623 nfslog_write_record(struct exportinfo *exi, struct svc_req *req,
1624 	caddr_t args, caddr_t res, cred_t *cr, struct netbuf *pnb,
1625 	unsigned int record_id, unsigned int which_buffers)
1626 {
1627 	struct nfslog_prog_disp	*progtable;	/* prog struct */
1628 	struct nfslog_vers_disp	*verstable;	/* version struct */
1629 	struct nfslog_proc_disp	*disp = NULL;	/* proc struct */
1630 	int			i, vers;
1631 	void			*log_cookie;	/* for logrecord if */
1632 	caddr_t			buffer;
1633 	XDR			xdrs;
1634 	unsigned int		final_size;
1635 	int			encode_ok;
1636 	int			alloc_indx;
1637 
1638 	ASSERT(exi != NULL); ASSERT(req != NULL); ASSERT(args != NULL);
1639 	ASSERT(res != NULL); ASSERT(cr != NULL);
1640 
1641 	/*
1642 	 * Find program element
1643 	 * Search the list since program can not be used as index
1644 	 */
1645 	for (i = 0; (i < nfslog_dispatch_table_arglen); i++) {
1646 		if (req->rq_prog == nfslog_dispatch_table[i].nfslog_dis_prog)
1647 			break;
1648 	}
1649 	if (i >= nfslog_dispatch_table_arglen) {	/* program not logged */
1650 		/* not an error */
1651 		return;
1652 	}
1653 
1654 	/*
1655 	 * Extract the dispatch functions based on program/version
1656 	 */
1657 	progtable = &nfslog_dispatch_table[i];
1658 	vers = req->rq_vers - progtable->nfslog_dis_versmin;
1659 	verstable = &progtable->nfslog_dis_vers_table[vers];
1660 	disp = &verstable->nfslog_dis_proc_table[req->rq_proc];
1661 
1662 	if (!(exi->exi_export.ex_flags & EX_LOG_ALLOPS) &&
1663 	    !disp->affects_transactions) {
1664 		/*
1665 		 * Only interested in logging operations affecting
1666 		 * transaction generation. This is not one of them.
1667 		 */
1668 #ifdef DEBUG
1669 		rfslog_records_ignored++;
1670 #endif
1671 		return;
1672 	}
1673 
1674 	switch (req->rq_prog) {
1675 	case NFS_PROGRAM:
1676 		switch (req->rq_vers) {
1677 		case NFS_V3:
1678 			switch (req->rq_proc) {
1679 			case NFSPROC3_READDIRPLUS:
1680 				alloc_indx = MEDIUM_INDX;
1681 				break;
1682 			default:
1683 				alloc_indx = SMALL_INDX;
1684 				break;
1685 			}
1686 			break;
1687 		default:
1688 			alloc_indx = SMALL_INDX;
1689 			break;
1690 		}
1691 		break;
1692 	case NFSLOG_PROGRAM:
1693 		alloc_indx = MEDIUM_INDX;
1694 		break;
1695 	default:
1696 		alloc_indx = SMALL_INDX;
1697 		break;
1698 	}
1699 
1700 	do {
1701 		encode_ok = FALSE;
1702 
1703 		/* Pick the size to alloc; end of the road - return */
1704 		if (nfslog_mem_alloc[alloc_indx].size == (-1)) {
1705 			cmn_err(CE_WARN,
1706 				"NFSLOG: unable to encode record - prog=%d "
1707 				"proc = %d", req->rq_prog, req->rq_proc);
1708 			return;
1709 		}
1710 
1711 		buffer = nfslog_record_alloc(exi, alloc_indx, &log_cookie, 0);
1712 		if (buffer == NULL) {
1713 			/* Error processing - no space alloced */
1714 			rfs_log_bad++;
1715 			cmn_err(CE_WARN, "NFSLOG: can't get record");
1716 			return;
1717 		}
1718 
1719 		xdrmem_create(&xdrs, buffer,
1720 			nfslog_mem_alloc[alloc_indx].size, XDR_ENCODE);
1721 
1722 		/*
1723 		 * Encode the header, args and results of the record
1724 		 */
1725 		if (xdr_nfslog_request_record(&xdrs, exi, req, cr, pnb,
1726 			nfslog_mem_alloc[alloc_indx].size, record_id) &&
1727 			(*disp->xdrargs)(&xdrs, args) &&
1728 			(*disp->xdrres)(&xdrs, res)) {
1729 				encode_ok = TRUE;
1730 
1731 				rfs_log_good++;
1732 				/*
1733 				 * Get the final size of the encoded
1734 				 * data and insert that length at the
1735 				 * beginning.
1736 				 */
1737 				final_size = xdr_getpos(&xdrs);
1738 				xdr_setpos(&xdrs, 0);
1739 				(void) xdr_u_int(&xdrs, &final_size);
1740 		} else {
1741 			/* Oops, the encode failed so we need to free memory */
1742 			nfslog_record_put(log_cookie, 0, FALSE, which_buffers);
1743 			alloc_indx++;
1744 		}
1745 
1746 	} while (encode_ok == FALSE);
1747 
1748 
1749 	/*
1750 	 * Take the final log record and put it in the log file.
1751 	 * This may be queued to the file internally and written
1752 	 * later unless the last parameter is TRUE.
1753 	 * If the record_id is 0 then this is most likely a share/unshare
1754 	 * request and it should be written synchronously to the log file.
1755 	 */
1756 	nfslog_record_put(log_cookie, final_size,
1757 		(record_id == 0), which_buffers);
1758 }
1759 
1760 static char *
1761 get_publicfh_path(int *alloc_length)
1762 {
1763 	extern struct exportinfo *exi_public;
1764 	char *pubpath;
1765 
1766 	rw_enter(&exported_lock, RW_READER);
1767 
1768 	*alloc_length = exi_public->exi_export.ex_pathlen + 1;
1769 	pubpath = kmem_alloc(*alloc_length, KM_SLEEP);
1770 
1771 	(void) strcpy(pubpath, exi_public->exi_export.ex_path);
1772 
1773 	rw_exit(&exported_lock);
1774 
1775 	return (pubpath);
1776 }
1777 
1778 static void
1779 log_public_record(struct exportinfo *exi, cred_t *cr)
1780 {
1781 	struct svc_req	req;
1782 	struct netbuf	nb = {0, 0, NULL};
1783 	int free_length = 0;
1784 	diropargs3 args;
1785 	LOOKUP3res res;
1786 
1787 	bzero(&req, sizeof (req));
1788 	req.rq_prog = NFSLOG_PROGRAM;
1789 	req.rq_vers = NFSLOG_VERSION;
1790 	req.rq_proc = NFSLOG_LOOKUP;
1791 	req.rq_cred.oa_flavor = AUTH_NONE;
1792 
1793 	bzero(&args, sizeof (diropargs3));
1794 	bzero(&res, sizeof (LOOKUP3res));
1795 
1796 	args.dir.fh3_length = 0;
1797 	if ((args.name = get_publicfh_path(&free_length)) == NULL)
1798 		return;
1799 	args.dirp = &args.dir;
1800 
1801 	res.status = NFS3_OK;
1802 	res.res_u.ok.object.fh3_length = 0;
1803 
1804 	/*
1805 	 * Calling this function with the exi_public
1806 	 * will have the effect of appending the record
1807 	 * to each of the open log buffers
1808 	 */
1809 	nfslog_write_record(exi, &req,
1810 		(caddr_t)&args, (caddr_t)&res, cr, &nb, 0, NFSLOG_ALL_BUFFERS);
1811 
1812 	kmem_free(args.name, free_length);
1813 }
1814 
1815 /*
1816  * nfslog_share_record - logs a share request.
1817  * This is not an NFS request, but we pretend here...
1818  */
1819 void
1820 nfslog_share_record(struct exportinfo *exi, cred_t *cr)
1821 {
1822 	struct svc_req	req;
1823 	int		res = 0;
1824 	struct netbuf	nb = {0, 0, NULL};
1825 
1826 	ASSERT(exi != NULL);
1827 
1828 	if (nfslog_buffer_list == NULL)
1829 		return;
1830 
1831 	if (exi->exi_export.ex_flags & EX_LOG) {
1832 		bzero(&req, sizeof (req));
1833 		req.rq_prog = NFSLOG_PROGRAM;
1834 		req.rq_vers = NFSLOG_VERSION;
1835 		req.rq_proc = NFSLOG_SHARE;
1836 		req.rq_cred.oa_flavor = AUTH_NONE;
1837 		nfslog_write_record(exi, &req,
1838 			(caddr_t)exi, (caddr_t)&res, cr,
1839 			&nb, 0, NFSLOG_ONE_BUFFER);
1840 	}
1841 
1842 	log_public_record(exi, cr);
1843 }
1844 
1845 /*
1846  * nfslog_unshare_record - logs an unshare request.
1847  * This is not an NFS request, but we pretend here...
1848  */
1849 void
1850 nfslog_unshare_record(struct exportinfo *exi, cred_t *cr)
1851 {
1852 	struct svc_req	req;
1853 	int		res = 0;
1854 	struct netbuf	nb = {0, 0, NULL};
1855 
1856 	ASSERT(exi != NULL);
1857 	ASSERT(exi->exi_export.ex_flags & EX_LOG);
1858 
1859 	bzero(&req, sizeof (req));
1860 	req.rq_prog = NFSLOG_PROGRAM;
1861 	req.rq_vers = NFSLOG_VERSION;
1862 	req.rq_proc = NFSLOG_UNSHARE;
1863 	req.rq_cred.oa_flavor = AUTH_NONE;
1864 	nfslog_write_record(exi, &req,
1865 		(caddr_t)exi, (caddr_t)&res, cr, &nb, 0, NFSLOG_ONE_BUFFER);
1866 }
1867 
1868 
1869 void
1870 nfslog_getfh(struct exportinfo *exi,
1871 	fhandle *fh,
1872 	char *fname,
1873 	enum uio_seg seg,
1874 	cred_t *cr)
1875 {
1876 	struct svc_req	req;
1877 	int		res = 0;
1878 	struct netbuf	nb = {0, 0, NULL};
1879 	int		error = 0;
1880 	char		*namebuf;
1881 	size_t		len;
1882 	nfslog_getfhargs gfh;
1883 
1884 	ASSERT(exi != NULL);
1885 	ASSERT(exi->exi_export.ex_flags & EX_LOG);
1886 
1887 	bzero(&req, sizeof (req));
1888 	req.rq_prog = NFSLOG_PROGRAM;
1889 	req.rq_vers = NFSLOG_VERSION;
1890 	req.rq_proc = NFSLOG_GETFH;
1891 	req.rq_cred.oa_flavor = AUTH_NONE;
1892 
1893 	namebuf = kmem_alloc(MAXPATHLEN + 4, KM_SLEEP);
1894 	if (seg == UIO_USERSPACE) {
1895 		error = copyinstr(fname, namebuf, MAXPATHLEN, &len);
1896 	} else {
1897 		error = copystr(fname, namebuf, MAXPATHLEN, &len);
1898 	}
1899 
1900 	if (!error) {
1901 		gfh.gfh_fh_buf = *fh;
1902 		gfh.gfh_path = namebuf;
1903 
1904 		nfslog_write_record(exi, &req,
1905 			(caddr_t)&gfh, (caddr_t)&res, cr, &nb, 0,
1906 			NFSLOG_ONE_BUFFER);
1907 	}
1908 	kmem_free(namebuf, MAXPATHLEN + 4);
1909 }
1910