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