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