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