xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/iscsi/nvfile.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include "iscsi.h"
27 #include "nvfile.h"
28 #include <sys/file.h>	    /* defines:	FKIOCTL */
29 #include <sys/kobj.h>
30 
31 #define	NVF_GETF	16
32 static kmutex_t		nvf_getf_lock;
33 static file_t		*nvf_fd[NVF_GETF];
34 extern int modrootloaded;
35 
36 /*
37  * file names
38  */
39 #define	NVF_FILENAME		"/etc/iscsi/iscsi"
40 #define	NVF_CURR_FILE_SUFFIX	"dbc"
41 #define	NVF_PREV_FILE_SUFFIX	"dbp"
42 #define	NVF_TMP_FILENAME	"/etc/iscsi/iscsi.dbt"
43 #define	NVF_MAX_FILENAME_LEN	40
44 
45 /*
46  * file header definitions
47  */
48 #define	NVF_HDR_MAGIC		0x15C510DB		/* iSCSI DB */
49 #define	NVF_HDR_VERSION		1
50 #define	NVF_HDR_SIZE		128
51 
52 
53 /*
54  * file flag definitions
55  *
56  * These flags describe the current state of reading or writing
57  * the NVFILE/NVPAIR or the backing file.
58  *
59  * These flags are derived from a like NVPAIR/NVFILE implementation
60  * in usr/src/uts/common/sys/devctl_impl.h
61  */
62 #define	NVF_ACTIVE	0x01	/* nvlist/nvpair file is active */
63 #define	NVF_DIRTY	0x02	/* needs to be flushed */
64 #define	NVF_SCHED	0x04	/* flush thread is currently scheduled */
65 #define	NVF_FLUSHING	0x08	/* in process of being flushed */
66 #define	NVF_ERROR	0x10	/* most recent flush failed */
67 
68 #define	NVF_IS_ACTIVE(flag)	(flag & NVF_ACTIVE)
69 #define	NVF_MARK_ACTIVE(flag)	(flag |= NVF_ACTIVE)
70 #define	NVF_CLEAR_ACTIVE(flag)	(flag &= ~NVF_ACTIVE)
71 
72 #define	NVF_IS_DIRTY(flag)	(flag & NVF_DIRTY)
73 #define	NVF_MARK_DIRTY(flag)	(flag |= NVF_DIRTY)
74 #define	NVF_CLEAR_DIRTY(flag)	(flag &= ~NVF_DIRTY)
75 
76 #define	NVF_IS_SCHED(flag)	(flag & NVF_SCHED)
77 #define	NVF_MARK_SCHED(flag)	(flag |= NVF_SCHED)
78 #define	NVF_CLEAR_SCHED(flag)	(flag &= ~NVF_SCHED)
79 
80 /*
81  * file flush time constants
82  */
83 #define	NVF_FLUSH_DELAY		10	/* number of ticks before flush */
84 #define	NVF_RESCHED_MIN_TICKS	5	/* min # of ticks to resched thread */
85 #define	NVF_FLUSH_BACKOFF_DELAY	(SEC_TO_TICK(300))   /* re-try flush in 5 min */
86 
87 /*
88  * file access operations
89  */
90 static file_t		*nvf_getf(int fdes);
91 static void		nvf_releasef(int fdes);
92 static int		nvf_setf(file_t *fp);
93 
94 static int		nvf_open(char *path, int flags, int mode);
95 static int		nvf_close(int fdes);
96 static int		nvf_remove(char *filename);
97 static int		nvf_rename(char *oldname, char *newname);
98 static ssize_t		nvf_read(int fdes, void *cbuf, ssize_t count);
99 static ssize_t		nvf_write(int fdes, void *cbuf, ssize_t count);
100 
101 int			nvf_errno;
102 
103 /*
104  * file header data structure definition
105  *
106  * This data structure definition was derived from a like data structure
107  * (nvpf_hdr_t) in usr/src/uts/common/sys/devctl_impl.h
108  *
109  * This header is larger than need in order to support extensability in the
110  * future
111  *
112  */
113 typedef struct nvf_hdr {
114 	union {
115 		struct hdr {
116 			uint32_t	h_magic;
117 			int32_t		h_ver;
118 			int64_t		h_size;
119 			uint16_t	h_hdrsum;
120 			uint16_t	h_datasum;
121 		} h_info;
122 		uchar_t h_pad[NVF_HDR_SIZE];
123 	} h_u;
124 } nvf_hdr_t;
125 
126 #define	nvfh_magic	h_u.h_info.h_magic
127 #define	nvfh_ver	h_u.h_info.h_ver
128 #define	nvfh_size	h_u.h_info.h_size
129 #define	nvfh_hdrsum	h_u.h_info.h_hdrsum
130 #define	nvfh_datasum	h_u.h_info.h_datasum
131 
132 
133 /*
134  *  Local Global Variables
135  */
136 static nvlist_t		*nvf_list;		/* pointer to root nvlist */
137 static uint32_t		nvf_flags;		/* nvfile state flags */
138 static kmutex_t		nvf_lock;		/* lock	for file */
139 static krwlock_t	nvf_list_lock;		/* lock for nvlist access */
140 static timeout_id_t	nvf_thread_id;		/* thread identifier */
141 static clock_t		nvf_thread_ticks;	/* timeout tick value */
142 static char		nvf_curr_filename[NVF_MAX_FILENAME_LEN];
143 static char		nvf_prev_filename[NVF_MAX_FILENAME_LEN];
144 static boolean_t	nvf_written_once; 	/* File has been written once */
145 /*
146  *  Local Function Prototypes
147  */
148 static uint16_t		nvf_chksum(char *buf, int64_t buflen);
149 static void		nvf_thread(void *arg);
150 static boolean_t	nvf_flush(void);
151 static boolean_t	nvf_parse(char *filename);
152 
153 /*
154  *  NVLIST/NVPAIR FILE External Interfaces
155  */
156 
157 void
158 nvf_init(void)
159 {
160 	mutex_init(&nvf_getf_lock, NULL, MUTEX_DRIVER, NULL);
161 	nvf_list = NULL;
162 	nvf_flags = 0;
163 	NVF_MARK_ACTIVE(nvf_flags);
164 	nvf_thread_id = 0;
165 	nvf_thread_ticks = 0;
166 	nvf_written_once = B_FALSE;
167 	(void) snprintf(nvf_curr_filename, NVF_MAX_FILENAME_LEN, "%s_v%d.%s",
168 	    NVF_FILENAME, NVF_HDR_VERSION, NVF_CURR_FILE_SUFFIX);
169 	(void) snprintf(nvf_prev_filename, NVF_MAX_FILENAME_LEN, "%s_v%d.%s",
170 	    NVF_FILENAME, NVF_HDR_VERSION, NVF_PREV_FILE_SUFFIX);
171 	mutex_init(&nvf_lock, NULL, MUTEX_DRIVER, NULL);
172 	rw_init(&nvf_list_lock, NULL, RW_DRIVER, NULL);
173 }
174 
175 void
176 nvf_fini(void)
177 {
178 	mutex_enter(&nvf_lock);
179 	NVF_CLEAR_ACTIVE(nvf_flags);
180 	if (NVF_IS_SCHED(nvf_flags)) {
181 		nvf_thread_ticks = 0;
182 		mutex_exit(&nvf_lock);
183 		(void) untimeout(nvf_thread_id);
184 		mutex_enter(&nvf_lock);
185 	}
186 
187 	rw_enter(&nvf_list_lock, RW_WRITER);
188 	if (nvf_list) {
189 		nvlist_free(nvf_list);
190 	}
191 	nvf_list = NULL;
192 	rw_exit(&nvf_list_lock);
193 	mutex_exit(&nvf_lock);
194 
195 	rw_destroy(&nvf_list_lock);
196 	mutex_destroy(&nvf_lock);
197 }
198 
199 /*
200  * nvf_load - load contents of NVLIST/NVPAIR file into memory.
201  */
202 boolean_t
203 nvf_load(void)
204 {
205 	char		corrupt_filename[NVF_MAX_FILENAME_LEN];
206 	boolean_t	rval;
207 
208 	mutex_enter(&nvf_lock);
209 
210 	/*
211 	 * try to load current file
212 	 */
213 	if (!modrootloaded) {
214 		mutex_exit(&nvf_lock);
215 		return (B_TRUE);
216 	} else {
217 		rval = nvf_parse(nvf_curr_filename);
218 	}
219 	if (rval == B_TRUE) {
220 		mutex_exit(&nvf_lock);
221 		return (rval);
222 	} else {
223 		/*
224 		 * Rename current file to add corrupted suffix
225 		 */
226 		(void) snprintf(corrupt_filename, NVF_MAX_FILENAME_LEN,
227 		    "%s.corrupt", nvf_curr_filename);
228 		(void) nvf_rename(nvf_curr_filename, corrupt_filename);
229 	}
230 
231 	/*
232 	 * try to load previous file
233 	 */
234 	if (!modrootloaded) {
235 		mutex_exit(&nvf_lock);
236 		return (B_TRUE);
237 	} else {
238 		rval = nvf_parse(nvf_prev_filename);
239 	}
240 
241 	if (rval == B_TRUE) {
242 		mutex_exit(&nvf_lock);
243 		return (rval);
244 	} else {
245 		/*
246 		 * Rename previous file to add corrupted suffix
247 		 */
248 		(void) snprintf(corrupt_filename, NVF_MAX_FILENAME_LEN,
249 		    "%s.corrupt", nvf_prev_filename);
250 		(void) nvf_rename(nvf_prev_filename, corrupt_filename);
251 	}
252 
253 	/*
254 	 * non-existent or corrupted files are OK.  We just create
255 	 * an empty root nvlist and then write to file when
256 	 * something added.  However, ensure that any current root nvlist
257 	 * is deallocated before allocating a new one.
258 	 */
259 	rw_enter(&nvf_list_lock, RW_WRITER);
260 	if (nvf_list != NULL) {
261 		nvlist_free(nvf_list);
262 		nvf_list = NULL;
263 	}
264 	rw_exit(&nvf_list_lock);
265 
266 	rval = nvlist_alloc(&nvf_list, NV_UNIQUE_NAME, KM_SLEEP);
267 	if (rval != 0) {
268 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
269 		    "allocate root list (%d)", rval);
270 	}
271 
272 	mutex_exit(&nvf_lock);
273 	return (rval == 0 ? B_TRUE : B_FALSE);
274 }
275 
276 /*
277  * nvf_update - start process of updating the NVPAIR/NVLIST file.
278  *
279  * This function is derived from a like NVPAIR/NVFILE implementation
280  * in usr/src/uts/common/os/devctl.c
281  *
282  */
283 void
284 nvf_update(void)
285 {
286 	mutex_enter(&nvf_lock);
287 
288 	/*
289 	 * set dirty flag to indicate data flush is needed
290 	 */
291 	NVF_MARK_DIRTY(nvf_flags);
292 
293 	/*
294 	 * If thread is already started just update number of
295 	 * ticks before flush, otherwise start thread and set
296 	 * number of ticks.  The thread will is responsible
297 	 * for starting the actual store to file.
298 	 *
299 	 * If update error occured previously, reschedule flush to
300 	 * occur almost immediately.  If error still exists, the
301 	 * update thread will be backed off again
302 	 */
303 	if (!NVF_IS_SCHED(nvf_flags)) {
304 		NVF_MARK_SCHED(nvf_flags);
305 		mutex_exit(&nvf_lock);
306 		nvf_thread_id = timeout(nvf_thread, NULL, NVF_FLUSH_DELAY);
307 	} else {
308 		nvf_thread_ticks = ddi_get_lbolt() + NVF_FLUSH_DELAY;
309 		/*
310 		 * If update error occured previously, reschedule flush
311 		 * attempt to occur quickly.  If an error still exists
312 		 * after a flush attempt, the update thread will be backed
313 		 * off again
314 		 */
315 		if (nvf_flags & NVF_ERROR) {
316 			mutex_exit(&nvf_lock);
317 			(void) untimeout(nvf_thread_id);
318 			nvf_thread_id = timeout(nvf_thread, NULL,
319 			    NVF_FLUSH_DELAY);
320 		} else {
321 			mutex_exit(&nvf_lock);
322 		}
323 	}
324 }
325 
326 /*
327  * nvf_data_check -- check if specified list exists
328  */
329 boolean_t
330 nvf_list_check(char *id)
331 {
332 	nvlist_t	*list = NULL;
333 	int		rval;
334 
335 	/*
336 	 * find the specified list
337 	 */
338 	rw_enter(&nvf_list_lock, RW_READER);
339 	rval = nvlist_lookup_nvlist(nvf_list, id, &list);
340 	rw_exit(&nvf_list_lock);
341 
342 	return (rval == 0 ? B_TRUE : B_FALSE);
343 }
344 
345 /*
346  * nvf_node_value_set - store value associated with node
347  */
348 boolean_t
349 nvf_node_value_set(char *id, uint32_t value)
350 {
351 	int	rval;
352 
353 	ASSERT(id != NULL);
354 
355 	rw_enter(&nvf_list_lock, RW_WRITER);
356 
357 	if (nvf_list == NULL) {
358 		rw_exit(&nvf_list_lock);
359 		return (B_FALSE);
360 	}
361 
362 	/*
363 	 * update value.  If value already exists, it will be replaced
364 	 * by this update.
365 	 */
366 	rval =  nvlist_add_uint32(nvf_list, id, value);
367 	if (rval == 0) {
368 		/*
369 		 * value was set, so update associated file
370 		 */
371 		nvf_update();
372 	} else {
373 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
374 		    "store %s value (%d)", id, rval);
375 	}
376 
377 	rw_exit(&nvf_list_lock);
378 	return (rval == 0 ? B_TRUE : B_FALSE);
379 }
380 
381 /*
382  * nvf_node_value_get - obtain value associated with node
383  */
384 boolean_t
385 nvf_node_value_get(char *id, uint32_t *value)
386 {
387 	boolean_t	rval;
388 
389 	ASSERT(id != NULL);
390 	ASSERT(value != NULL);
391 
392 	rw_enter(&nvf_list_lock, RW_READER);
393 
394 	if (nvf_list == NULL) {
395 		rw_exit(&nvf_list_lock);
396 		return (B_FALSE);
397 	}
398 
399 	rval = nvlist_lookup_uint32(nvf_list, id, value);
400 
401 	rw_exit(&nvf_list_lock);
402 	return (rval == 0 ? B_TRUE : B_FALSE);
403 }
404 
405 /*
406  * nvf_node_name_set - store a specific type of name
407  */
408 boolean_t
409 nvf_node_name_set(char *id, char *name)
410 {
411 	boolean_t	rval = B_TRUE;
412 
413 	rw_enter(&nvf_list_lock, RW_WRITER);
414 
415 	if (nvf_list == NULL) {
416 		rw_exit(&nvf_list_lock);
417 		return (B_FALSE);
418 	}
419 
420 	rval =  nvlist_add_string(nvf_list, id, name);
421 	if (rval == 0) {
422 		nvf_update();
423 	} else {
424 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
425 		    "store %s name (%d)", id, rval);
426 	}
427 
428 	rw_exit(&nvf_list_lock);
429 	return (rval == 0 ? B_TRUE : B_FALSE);
430 }
431 
432 /*
433  * nvf_node_name_get - return a specific type of name
434  */
435 boolean_t
436 nvf_node_name_get(char *id, char *name, uint_t nsize)
437 {
438 	boolean_t	rval = B_FALSE;
439 	char		*tmpname;
440 
441 	rw_enter(&nvf_list_lock, RW_READER);
442 
443 	if (nvf_list == NULL) {
444 		rw_exit(&nvf_list_lock);
445 		return (B_FALSE);
446 	}
447 
448 	rval = nvlist_lookup_string(nvf_list, id, &tmpname);
449 	if (rval == 0) {
450 		/*
451 		 *  ensure name is able to fit into given buffer
452 		 */
453 		if (strlen(tmpname) < nsize) {
454 			(void) strcpy(name, tmpname);
455 			rval = B_TRUE;
456 		} else {
457 			cmn_err(CE_NOTE, "!iscsi persistent store "
458 			    "unable to fit %s node name into buffer %d %s",
459 			    tmpname, nsize, id);
460 			rval = B_FALSE;
461 		}
462 	} else {
463 		rval = B_FALSE;
464 	}
465 
466 	rw_exit(&nvf_list_lock);
467 	return (rval);
468 }
469 
470 /*
471  * nvf_node_data_set -- store data element associated with node
472  */
473 boolean_t
474 nvf_node_data_set(char *name, void *data, uint_t dsize)
475 {
476 	int		rval;
477 
478 	ASSERT(name != NULL);
479 	ASSERT(data != NULL);
480 
481 	rw_enter(&nvf_list_lock, RW_WRITER);
482 
483 	if (nvf_list == NULL) {
484 		rw_exit(&nvf_list_lock);
485 		return (B_FALSE);
486 	}
487 
488 	/*
489 	 * update the address configuration element in the specific
490 	 * list.  If this element already exists, it will be
491 	 * replaced by this update.
492 	 */
493 	rval = nvlist_add_byte_array(nvf_list, name, (uchar_t *)data, dsize);
494 	if (rval == 0) {
495 		/*
496 		 * data was set, so update associated file
497 		 */
498 		nvf_update();
499 	} else {
500 		cmn_err(CE_NOTE, "!iscsi persistent store failed "
501 		    "to store %s name (%d)", name, rval);
502 	}
503 
504 	rw_exit(&nvf_list_lock);
505 	return (rval == 0 ? B_TRUE : B_FALSE);
506 }
507 
508 /*
509  * nvf_node_data_get -- obtain a data element associated with node
510  */
511 iscsi_nvfile_status_t
512 nvf_node_data_get(char *name, void *data, uint_t dsize)
513 {
514 	uchar_t			*value = NULL;
515 	uint_t			vsize;
516 	int			rval = 0;
517 	iscsi_nvfile_status_t	status = ISCSI_NVFILE_SUCCESS;
518 
519 	ASSERT(name != NULL);
520 	ASSERT(data != NULL);
521 
522 	rw_enter(&nvf_list_lock, RW_READER);
523 
524 	if (nvf_list == NULL) {
525 		rw_exit(&nvf_list_lock);
526 		return (ISCSI_NVFILE_NVF_LIST_NOT_FOUND);
527 	}
528 
529 	rval = nvlist_lookup_byte_array(nvf_list, name, &value, &vsize);
530 	if (rval == 0) {
531 		/*
532 		 *  ensure data is able to fit into given buffer
533 		 */
534 		if (vsize <= dsize) {
535 			bcopy(value, data, vsize);
536 		} else {
537 			bcopy(value, data, dsize);
538 		}
539 		status = ISCSI_NVFILE_SUCCESS;
540 	} else if (rval == ENOENT) {
541 		status = ISCSI_NVFILE_NAMEVAL_NOT_FOUND;
542 	} else {
543 		status = ISCSI_NVFILE_FAILURE;
544 	}
545 
546 	rw_exit(&nvf_list_lock);
547 	return (status);
548 }
549 
550 /*
551  * nvf_node_data_clear -- remove a data element associated with node
552  */
553 boolean_t
554 nvf_node_data_clear(char *name)
555 {
556 	int	rval;
557 
558 	ASSERT(name != NULL);
559 
560 	rw_enter(&nvf_list_lock, RW_WRITER);
561 
562 	if (nvf_list == NULL) {
563 		rw_exit(&nvf_list_lock);
564 		return (B_FALSE);
565 	}
566 
567 	/*
568 	 * remove the specified data element
569 	 */
570 	rval = nvlist_remove(nvf_list, name, DATA_TYPE_BYTE_ARRAY);
571 	if (rval == 0) {
572 		/*
573 		 * data was set, so update associated file
574 		 */
575 		nvf_update();
576 	}
577 
578 	rw_exit(&nvf_list_lock);
579 	return (rval == 0 ? B_TRUE : B_FALSE);
580 }
581 
582 /*
583  * nvf_data_set -- store a data element in the specified list
584  */
585 boolean_t
586 nvf_data_set(char *id, char *name, void *data, uint_t dsize)
587 {
588 	nvlist_t	*list = NULL;
589 	int		rval;
590 	boolean_t	list_alloc = B_FALSE;
591 
592 	ASSERT(id != NULL);
593 	ASSERT(name != NULL);
594 	ASSERT(data != NULL);
595 
596 	rw_enter(&nvf_list_lock, RW_WRITER);
597 
598 	if (nvf_list == NULL) {
599 		rw_exit(&nvf_list_lock);
600 		return (B_FALSE);
601 	}
602 
603 	/*
604 	 * find the specified list
605 	 */
606 	rval = nvlist_lookup_nvlist(nvf_list, id, &list);
607 	if (rval != 0) {
608 		rval = nvlist_alloc(&list, NV_UNIQUE_NAME, KM_SLEEP);
609 		if (rval != 0) {
610 			cmn_err(CE_NOTE, "!iscsi persistent store failed to "
611 			    "allocate %s list (%d)", id, rval);
612 			rw_exit(&nvf_list_lock);
613 			return (B_FALSE);
614 		}
615 		list_alloc = B_TRUE;
616 	}
617 
618 	/*
619 	 * update the data element in the specified list.  If this element
620 	 * already exists, it will be replaced by this update.
621 	 */
622 	rval = nvlist_add_byte_array(list, name, (uchar_t *)data, dsize);
623 	if (rval == 0) {
624 		rval = nvlist_add_nvlist(nvf_list, id, list);
625 		if (rval != 0) {
626 			cmn_err(CE_NOTE, "!iscsi persistent store failed "
627 			    "to add %s list to root (%d)", id, rval);
628 			rw_exit(&nvf_list_lock);
629 			return (B_FALSE);
630 		}
631 		/*
632 		 * data was set, so update file
633 		 */
634 		nvf_update();
635 	} else {
636 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
637 		    "store %s in %s list (%d)", name, id, rval);
638 	}
639 
640 	if (list_alloc) {
641 		nvlist_free(list);
642 	}
643 
644 	rw_exit(&nvf_list_lock);
645 	return (rval == 0 ? B_TRUE : B_FALSE);
646 }
647 
648 /*
649  * nvf_data_get -- get a data element from the specified list
650  */
651 boolean_t
652 nvf_data_get(char *id, char *name, void *data, uint_t dsize)
653 {
654 	nvlist_t	*list = NULL;
655 	uchar_t		*value = NULL;
656 	uint_t		vsize;
657 	int		rval;
658 
659 	ASSERT(id != NULL);
660 	ASSERT(name != NULL);
661 	ASSERT(data != NULL);
662 
663 	rw_enter(&nvf_list_lock, RW_READER);
664 
665 	if (nvf_list == NULL) {
666 		rw_exit(&nvf_list_lock);
667 		return (B_FALSE);
668 	}
669 
670 	/*
671 	 * find the specified list
672 	 */
673 	rval = nvlist_lookup_nvlist(nvf_list, id, &list);
674 	if (rval != 0) {
675 		rw_exit(&nvf_list_lock);
676 		return (B_FALSE);
677 	}
678 
679 	/* obtain data element from list */
680 	ASSERT(list != NULL);
681 	rval = nvlist_lookup_byte_array(list, name, &value, &vsize);
682 	if (rval == 0) {
683 		/*
684 		 *  ensure data is able to fit into given buffer
685 		 */
686 		if (vsize <= dsize) {
687 			bcopy(value, data, vsize);
688 		} else {
689 			bcopy(value, data, dsize);
690 		}
691 	}
692 
693 	rw_exit(&nvf_list_lock);
694 	return (rval == 0 ? B_TRUE : B_FALSE);
695 }
696 
697 
698 /*
699  * nvf_data_next -- get the next data element in the specified list
700  */
701 boolean_t
702 nvf_data_next(char *id, void **v, char *name, void *data, uint_t dsize)
703 {
704 	nvlist_t	*list = NULL;
705 	nvpair_t	*pair = NULL;
706 	uchar_t		*value = NULL;
707 	uint_t		vsize;
708 	int		rval;
709 
710 	ASSERT(id != NULL);
711 	ASSERT(v != NULL);
712 	ASSERT(name != NULL);
713 	ASSERT(data != NULL);
714 
715 	rw_enter(&nvf_list_lock, RW_READER);
716 	if (nvf_list == NULL) {
717 		rw_exit(&nvf_list_lock);
718 		return (B_FALSE);
719 	}
720 
721 	/*
722 	 * find the specified list
723 	 */
724 	rval = nvlist_lookup_nvlist(nvf_list, id, &list);
725 	if (rval != 0) {
726 		rw_exit(&nvf_list_lock);
727 		return (B_FALSE);
728 	}
729 
730 	/*
731 	 * get the next nvpair data item in the list
732 	 */
733 	pair = nvlist_next_nvpair(list, (nvpair_t *)*v);
734 	*v = (void *)pair;
735 	if (pair == NULL) {
736 		rw_exit(&nvf_list_lock);
737 		return (B_FALSE);
738 	}
739 
740 	/*
741 	 * get the data bytes
742 	 */
743 	rval = nvpair_value_byte_array(pair, &value, &vsize);
744 	if (rval != 0) {
745 		rw_exit(&nvf_list_lock);
746 		return (B_FALSE);
747 	}
748 
749 	/*
750 	 *  ensure data is able to fit into given buffer
751 	 */
752 	(void) strcpy(name, nvpair_name(pair));
753 	if (vsize <= dsize) {
754 		bcopy(value, data, vsize);
755 	} else {
756 		bcopy(value, data, dsize);
757 	}
758 
759 	rw_exit(&nvf_list_lock);
760 	return (B_TRUE);
761 }
762 
763 /*
764  * nvf_data_clear -- remove a data element from the specified list
765  */
766 boolean_t
767 nvf_data_clear(char *id, char *name)
768 {
769 	nvlist_t	*list = NULL;
770 	int		rval = B_FALSE;
771 
772 	ASSERT(id != NULL);
773 	ASSERT(name != NULL);
774 
775 	rw_enter(&nvf_list_lock, RW_WRITER);
776 
777 	if (nvf_list == NULL) {
778 		rw_exit(&nvf_list_lock);
779 		return (B_FALSE);
780 	}
781 
782 	/*
783 	 * find the specified list
784 	 */
785 	rval = nvlist_lookup_nvlist(nvf_list, id, &list);
786 	if (rval != 0) {
787 		rw_exit(&nvf_list_lock);
788 		return (B_FALSE);
789 	}
790 
791 	/*
792 	 * remove the specified data element
793 	 */
794 	rval = nvlist_remove(list, name, DATA_TYPE_BYTE_ARRAY);
795 	if (rval == 0) {
796 		/*
797 		 * data was set, so update associated file
798 		 */
799 		nvf_update();
800 	}
801 
802 	rw_exit(&nvf_list_lock);
803 	return (rval == 0 ? B_TRUE : B_FALSE);
804 }
805 
806 /*
807  * +--------------------------------------------------------------------+
808  * | Internal Helper Functions                                          |
809  * +--------------------------------------------------------------------+
810  */
811 
812 /*
813  * nvf_cksum - calculate checksum of given buffer.
814  *
815  * This function was derived from like function (nvp_cksum) in
816  * usr/src/uts/common/os/devctl.c
817  */
818 static uint16_t
819 nvf_chksum(char *buf, int64_t buflen)
820 {
821 	uint16_t cksum = 0;
822 	uint16_t *p = (uint16_t *)buf;
823 	int64_t n;
824 
825 	if ((buflen & 0x01) != 0) {
826 		buflen--;
827 		cksum = buf[buflen];
828 	}
829 	n = buflen / 2;
830 	while (n-- > 0)
831 		cksum ^= *p++;
832 	return (cksum);
833 }
834 
835 
836 /*
837  * nvf_thread - determines when writing of NVLIST/NVPAIR data to a file
838  * should occur.
839  */
840 /* ARGSUSED */
841 static void
842 nvf_thread(void *arg)
843 {
844 	clock_t		nticks;
845 	boolean_t	rval;
846 
847 	mutex_enter(&nvf_lock);
848 	nticks = nvf_thread_ticks - ddi_get_lbolt();
849 
850 	/*
851 	 * check whether its time to write to file.  If not, reschedule self
852 	 */
853 	if ((nticks > NVF_RESCHED_MIN_TICKS) || !modrootloaded) {
854 		if (NVF_IS_ACTIVE(nvf_flags)) {
855 			mutex_exit(&nvf_lock);
856 			nvf_thread_id = timeout(nvf_thread, NULL, nticks);
857 			mutex_enter(&nvf_lock);
858 		}
859 		mutex_exit(&nvf_lock);
860 		return;
861 	}
862 
863 	/*
864 	 * flush NVLIST/NVPAIR data to file
865 	 */
866 	NVF_CLEAR_DIRTY(nvf_flags);
867 	nvf_flags |= NVF_FLUSHING;
868 	mutex_exit(&nvf_lock);
869 
870 	rval = nvf_flush();
871 
872 	mutex_enter(&nvf_lock);
873 	nvf_flags &= ~NVF_FLUSHING;
874 	if (rval == B_FALSE) {
875 		NVF_MARK_DIRTY(nvf_flags);
876 		if ((nvf_flags & NVF_ERROR) == 0) {
877 			if (nvf_written_once) {
878 				cmn_err(CE_NOTE,
879 				    "!iscsi persistent store update "
880 				    "failed file:%s", nvf_curr_filename);
881 			}
882 			nvf_flags |= NVF_ERROR;
883 		}
884 		nvf_thread_ticks = NVF_FLUSH_BACKOFF_DELAY + ddi_get_lbolt();
885 	} else if (nvf_flags & NVF_ERROR) {
886 		cmn_err(CE_NOTE, "!iscsi persistent store update ok now "
887 		    "filename:%s", nvf_curr_filename);
888 		nvf_flags &= ~NVF_ERROR;
889 	}
890 
891 	/*
892 	 * re-check whether data is dirty and reschedule if necessary
893 	 */
894 	if (NVF_IS_ACTIVE(nvf_flags) && NVF_IS_DIRTY(nvf_flags)) {
895 		nticks = nvf_thread_ticks - ddi_get_lbolt();
896 		mutex_exit(&nvf_lock);
897 		if (nticks > NVF_FLUSH_DELAY) {
898 			nvf_thread_id = timeout(nvf_thread, NULL, nticks);
899 		} else {
900 			nvf_thread_id = timeout(nvf_thread, NULL,
901 			    NVF_FLUSH_DELAY);
902 		}
903 	} else {
904 		NVF_CLEAR_SCHED(nvf_flags);
905 		mutex_exit(&nvf_lock);
906 	}
907 }
908 
909 /*
910  * nvf_flush - write contents of NVLIST/NVPAIR to a backing file.
911  *
912  * This function is derived from a like NVPAIR/NVFILE implementation
913  * in usr/src/uts/common/os/devctl.c
914  */
915 static boolean_t
916 nvf_flush(void)
917 {
918 	int		rval;
919 	nvlist_t	*tmpnvl;
920 	char		*nvfbuf;
921 	char		*nvlbuf;
922 	size_t		nvllen;
923 	size_t		nvflen;
924 	int		file;
925 	int		bytes_written;
926 
927 	/*
928 	 * duplicate data so access isn't blocked while writing to disk
929 	 */
930 	rw_enter(&nvf_list_lock, RW_READER);
931 	rval = nvlist_dup(nvf_list, &tmpnvl, KM_SLEEP);
932 	if (rval != 0) {
933 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
934 		    "duplicate nvf_list (%d)", rval);
935 		rw_exit(&nvf_list_lock);
936 		return (B_FALSE);
937 	}
938 	rw_exit(&nvf_list_lock);
939 
940 	/*
941 	 * pack duplicated list to get ready for file write
942 	 */
943 	nvlbuf = NULL;
944 	rval = nvlist_pack(tmpnvl, &nvlbuf, &nvllen, NV_ENCODE_NATIVE, 0);
945 	if (rval != 0) {
946 		cmn_err(CE_NOTE, "!iscsi persistent store failed to pack "
947 		    "nvf_list (%d)", rval);
948 		nvlist_free(tmpnvl);
949 		return (B_FALSE);
950 	}
951 
952 	/*
953 	 * allocate buffer to store both the header and the data.
954 	 */
955 	nvflen = nvllen + sizeof (nvf_hdr_t);
956 	nvfbuf = kmem_zalloc(nvflen, KM_SLEEP);
957 
958 	/*
959 	 * fill buffer with contents of file header
960 	 */
961 	((nvf_hdr_t *)nvfbuf)->nvfh_magic = NVF_HDR_MAGIC;
962 	((nvf_hdr_t *)nvfbuf)->nvfh_ver = NVF_HDR_VERSION;
963 	((nvf_hdr_t *)nvfbuf)->nvfh_size = nvllen;
964 	((nvf_hdr_t *)nvfbuf)->nvfh_datasum = nvf_chksum((char *)nvlbuf,
965 	    nvllen);
966 	((nvf_hdr_t *)nvfbuf)->nvfh_hdrsum = nvf_chksum((char *)nvfbuf,
967 	    sizeof (nvf_hdr_t));
968 
969 	/*
970 	 * copy packed nvlist into buffer
971 	 */
972 	bcopy(nvlbuf, nvfbuf + sizeof (nvf_hdr_t), nvllen);
973 
974 	/*
975 	 * free memory used for packed nvlist
976 	 */
977 	nvlist_free(tmpnvl);
978 	kmem_free(nvlbuf, nvllen);
979 
980 	/*
981 	 *  To make it unlikely we suffer data loss, write
982 	 * data to the new temporary file.  Once successful
983 	 * complete the transaction by renaming the new file
984 	 * to replace the previous.
985 	 */
986 
987 	/*
988 	 * remove temporary file to ensure data content is written correctly
989 	 */
990 	rval = nvf_remove(NVF_TMP_FILENAME);
991 	if (rval == -1) {
992 		kmem_free(nvfbuf, nvflen);
993 		return (B_FALSE);
994 	}
995 
996 	/*
997 	 * create tempororary file
998 	 */
999 	file = nvf_open(NVF_TMP_FILENAME, O_RDWR | O_CREAT, 0600);
1000 	if (file == -1) {
1001 		mutex_enter(&nvf_lock);
1002 		if (nvf_written_once) {
1003 			cmn_err(CE_NOTE,
1004 			    "!iscsi persistent store failed to create "
1005 			    "%s (errno:%d)", NVF_TMP_FILENAME, nvf_errno);
1006 		}
1007 		mutex_exit(&nvf_lock);
1008 		kmem_free(nvfbuf, nvflen);
1009 		return (B_FALSE);
1010 	}
1011 
1012 	/*
1013 	 * write data to tempororary file
1014 	 */
1015 	bytes_written = nvf_write(file, nvfbuf, nvflen);
1016 	kmem_free(nvfbuf, nvflen);
1017 	if (bytes_written == -1) {
1018 		cmn_err(CE_NOTE, "!iscsi persistent store failed to write "
1019 		    "%s (errno:%d)", NVF_TMP_FILENAME, nvf_errno);
1020 		return (B_FALSE);
1021 	}
1022 
1023 	if (bytes_written != nvflen) {
1024 		cmn_err(CE_NOTE, "!iscsi persistent store failed to write "
1025 		    "%s (errno:%d)\n\tpartial write %d of %ld bytes\n",
1026 		    NVF_TMP_FILENAME, nvf_errno, bytes_written, nvflen);
1027 		return (B_FALSE);
1028 	}
1029 
1030 	/*
1031 	 * close tempororary file
1032 	 */
1033 	rval = nvf_close(file);
1034 	if (rval == -1) {
1035 		return (B_FALSE);
1036 	}
1037 
1038 	mutex_enter(&nvf_lock);
1039 	/*
1040 	 * File has been written.  Set flag to allow the create and update
1041 	 * messages to be displayed in case of create or update failures.
1042 	 */
1043 	nvf_written_once = B_TRUE;
1044 
1045 	/*
1046 	 * rename current original file to previous original file
1047 	 */
1048 	rval = nvf_rename(nvf_curr_filename, nvf_prev_filename);
1049 	if (rval == -1) {
1050 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
1051 		    "rename %s (errno:%d)", nvf_curr_filename, nvf_errno);
1052 		mutex_exit(&nvf_lock);
1053 		return (B_FALSE);
1054 	}
1055 
1056 	/*
1057 	 * rename temporary file to current original file
1058 	 */
1059 	rval = nvf_rename(NVF_TMP_FILENAME, nvf_curr_filename);
1060 	if (rval == -1) {
1061 		cmn_err(CE_NOTE, "!iscsi persistent store failed to "
1062 		    "rename %s (errno:%d)", NVF_TMP_FILENAME, nvf_errno);
1063 		mutex_exit(&nvf_lock);
1064 		return (B_FALSE);
1065 	}
1066 
1067 	NVF_CLEAR_DIRTY(nvf_flags);
1068 
1069 	mutex_exit(&nvf_lock);
1070 	return (B_TRUE);
1071 }
1072 
1073 /*
1074  * nvf_parse - read contents of NVLIST/NVPAIR file.
1075  *
1076  * This function is derived from a like NVPAIR/NVFILE implementation
1077  * in usr/src/uts/common/os/devctl.c
1078  */
1079 static boolean_t
1080 nvf_parse(char *filename)
1081 {
1082 	int		file;
1083 	nvf_hdr_t	hdr;
1084 	int		bytes_read;
1085 	int		rval;
1086 	uint16_t	chksum;
1087 	uint16_t	hdrsum;
1088 	char		*buf;
1089 	char		overfill;
1090 	nvlist_t	*nvl;
1091 	nvlist_t	*old_nvl;
1092 
1093 
1094 	/*
1095 	 * open current file
1096 	 */
1097 	file = nvf_open(filename, O_RDONLY, 0600);
1098 	if (file == -1) {
1099 		return (B_FALSE);
1100 	}
1101 
1102 	/*
1103 	 * read file header
1104 	 */
1105 	bytes_read = nvf_read(file, (char *)&hdr, sizeof (hdr));
1106 	if (bytes_read != sizeof (hdr)) {
1107 		(void) nvf_close(file);
1108 		return (B_FALSE);
1109 	}
1110 
1111 	/*
1112 	 * calculate checksum over file header bytes
1113 	 */
1114 	chksum = hdr.nvfh_hdrsum;
1115 	hdr.nvfh_hdrsum = 0;
1116 	hdrsum = nvf_chksum((char *)&hdr, sizeof (hdr));
1117 
1118 	/*
1119 	 * validate file header is as expected
1120 	 */
1121 	if ((hdr.nvfh_magic != NVF_HDR_MAGIC) ||
1122 	    (hdr.nvfh_ver != NVF_HDR_VERSION) ||
1123 	    (hdrsum != chksum)) {
1124 		(void) nvf_close(file);
1125 		if (hdrsum != chksum) {
1126 			cmn_err(CE_NOTE, "!iscsi persistent store "
1127 			    "checksum error %s actual:0x%x expected:0x%x",
1128 			    filename, hdrsum, chksum);
1129 		}
1130 		cmn_err(CE_NOTE, "!iscsi persistent store %s has an "
1131 		    "incorrect header", filename);
1132 		return (B_FALSE);
1133 	}
1134 
1135 	ASSERT(hdr.nvfh_size >= 0);
1136 
1137 	/*
1138 	 * read expected remaining content of file
1139 	 */
1140 	buf = kmem_alloc(hdr.nvfh_size, KM_SLEEP);
1141 	bytes_read = nvf_read(file, buf, hdr.nvfh_size);
1142 	if (bytes_read != hdr.nvfh_size) {
1143 		kmem_free(buf, hdr.nvfh_size);
1144 		(void) nvf_close(file);
1145 		if (bytes_read < 0) {
1146 			cmn_err(CE_NOTE, "!iscsi persistent store failed "
1147 			    "to read %s bytes:%d", filename, bytes_read);
1148 		} else {
1149 			cmn_err(CE_NOTE, "!iscsi persistent store incomplete "
1150 			    "read %s bytes:%d/%lld", filename,
1151 			    bytes_read, (longlong_t)hdr.nvfh_size);
1152 		}
1153 		return (B_FALSE);
1154 	}
1155 
1156 	/*
1157 	 * check whether file has anymore data.  If so this is an error
1158 	 */
1159 	bytes_read = nvf_read(file, &overfill, 1);
1160 	(void) nvf_close(file);
1161 	if (bytes_read > 0) {
1162 		kmem_free(buf, hdr.nvfh_size);
1163 		cmn_err(CE_NOTE, "!iscsi persistent store file is larger "
1164 		    "than expected %s bytes:%lld",
1165 		    filename, (longlong_t)hdr.nvfh_size);
1166 		return (B_FALSE);
1167 	}
1168 
1169 	DTRACE_PROBE1(hdr, nvf_hdr_t *, &hdr);
1170 
1171 	/*
1172 	 * validate file data is as expected
1173 	 */
1174 	chksum = nvf_chksum(buf, hdr.nvfh_size);
1175 	if (hdr.nvfh_datasum != chksum) {
1176 		kmem_free(buf, hdr.nvfh_size);
1177 		cmn_err(CE_NOTE, "!iscsi persistent store checksum error %s "
1178 		    "actual:0x%x expected:0x%x", filename,
1179 		    hdr.nvfh_datasum, chksum);
1180 		return (B_FALSE);
1181 	}
1182 
1183 	nvl = NULL;
1184 	rval = nvlist_unpack(buf, hdr.nvfh_size, &nvl, 0);
1185 	if (rval != 0) {
1186 		kmem_free(buf, hdr.nvfh_size);
1187 		cmn_err(CE_NOTE, "!iscsi persistent store failed unpacking "
1188 		    "nvlist %s (%d)", filename, rval);
1189 		return (B_FALSE);
1190 	}
1191 
1192 	kmem_free(buf, hdr.nvfh_size);
1193 
1194 	/*
1195 	 * activate nvlist
1196 	 */
1197 	rw_enter(&nvf_list_lock, RW_WRITER);
1198 	old_nvl = nvf_list;
1199 	nvf_list = nvl;
1200 	rw_exit(&nvf_list_lock);
1201 
1202 	/*
1203 	 * free up old nvlist
1204 	 */
1205 	if (old_nvl) {
1206 		nvlist_free(old_nvl);
1207 	}
1208 
1209 	return (B_TRUE);
1210 }
1211 
1212 /*
1213  * iscsid_getf -- given a file descriptor returns a file pointer
1214  */
1215 static file_t *
1216 nvf_getf(int fdes)
1217 {
1218 	file_t	*fp = NULL;
1219 
1220 	mutex_enter(&nvf_getf_lock);
1221 	if ((fdes >= 0) && (fdes < NVF_GETF)) {
1222 		fp = nvf_fd[fdes];
1223 		if (fp != NULL)
1224 			mutex_enter(&fp->f_tlock);
1225 	}
1226 	mutex_exit(&nvf_getf_lock);
1227 
1228 	return (fp);
1229 }
1230 
1231 /*
1232  * nvf_releasef -- release lock on file pointer
1233  */
1234 static void
1235 nvf_releasef(int fdes)
1236 {
1237 	file_t  *fp;
1238 
1239 	mutex_enter(&nvf_getf_lock);
1240 	if ((fdes >= 0) && (fdes < NVF_GETF)) {
1241 		fp = nvf_fd[fdes];
1242 		mutex_exit(&fp->f_tlock);
1243 	}
1244 	mutex_exit(&nvf_getf_lock);
1245 }
1246 
1247 /*
1248  * nvf_setf -- stores the file pointer in an empty slot returning index
1249  */
1250 static int
1251 nvf_setf(file_t *fp)
1252 {
1253 	int	i = -1;
1254 
1255 	mutex_enter(&nvf_getf_lock);
1256 	for (i = 0; i < NVF_GETF; i++) {
1257 		if (nvf_fd[i] == 0) {
1258 			nvf_fd[i] = fp;
1259 			break;
1260 		}
1261 	}
1262 	mutex_exit(&nvf_getf_lock);
1263 	return (i);
1264 }
1265 
1266 /*
1267  * nvf_freef -- gets the file pointer based on index and releases memory.
1268  */
1269 static void
1270 nvf_freef(int fdes)
1271 {
1272 	file_t *fp;
1273 
1274 	mutex_enter(&nvf_getf_lock);
1275 	if ((fdes >= 0) && (fdes < NVF_GETF)) {
1276 		fp = nvf_fd[fdes];
1277 		unfalloc(fp);
1278 		nvf_fd[fdes] = NULL;
1279 	}
1280 	mutex_exit(&nvf_getf_lock);
1281 }
1282 
1283 /*
1284  * nvf_open -- acts like syscall open, but works for kernel
1285  *
1286  * Note: This works for regular files only. No umask is provided to
1287  * vn_open which means whatever mode is passed in will be used to
1288  * create a file.
1289  */
1290 static int
1291 nvf_open(char *path, int flags, int mode)
1292 {
1293 	file_t		*fp	= NULL;
1294 	vnode_t		*vp	= NULL;
1295 	int		fdes	= -1;
1296 	int		fflags;
1297 
1298 	/*
1299 	 * Need to convert from user mode flags to file system flags.
1300 	 * It's unfortunate that the kernel doesn't define a mask for
1301 	 * the read/write bits which would make this conversion easier.
1302 	 * Only O_RDONLY/O_WRONLY/O_RDWR are different than their FXXXXX
1303 	 * counterparts. If one was provided something like
1304 	 *	fflags = ((flags & mask) + 1) | (flags & ~mask)
1305 	 * would work. But, that would only be true if the relationship
1306 	 * be O_XXX and FXXX was defined and it's not. So we have the
1307 	 * following.
1308 	 */
1309 	if (flags & O_WRONLY)
1310 		fflags = FWRITE;
1311 	else if (flags & O_RDWR)
1312 		fflags = FWRITE | FREAD;
1313 	else
1314 		fflags = FREAD;
1315 
1316 	/*
1317 	 * Now that fflags has been initialized with the read/write bits
1318 	 * look at the other flags and OR them in.
1319 	 */
1320 	if (flags & O_CREAT)
1321 		fflags |= FCREAT;
1322 	if (flags & O_TRUNC)
1323 		fflags |= FTRUNC;
1324 
1325 	if (nvf_errno = vn_open(path, UIO_SYSSPACE, fflags,
1326 	    mode & MODEMASK, &vp, CRCREAT, 0)) {
1327 		return (-1);
1328 	}
1329 
1330 	if (falloc(vp, fflags, &fp, NULL) != 0) {
1331 		VN_RELE(vp);
1332 		return (-1);
1333 	}
1334 	/* ---- falloc returns with f_tlock held on success ---- */
1335 	mutex_exit(&fp->f_tlock);
1336 
1337 	if ((fdes = nvf_setf(fp)) == -1) {
1338 		VN_RELE(vp);
1339 	}
1340 	return (fdes);
1341 }
1342 
1343 /*
1344  * nvf_close -- closes down the file by releasing locks and memory.
1345  */
1346 static int
1347 nvf_close(int fdes)
1348 {
1349 	file_t  *fp;
1350 	vnode_t *vp;
1351 
1352 	if ((fp = nvf_getf(fdes)) == NULL)
1353 		return (-1);
1354 	vp = fp->f_vnode;
1355 
1356 	(void) VOP_CLOSE(vp, fp->f_flag, 1, 0, kcred, NULL);
1357 	VN_RELE(vp);
1358 	/*
1359 	 * unfalloc which is called from here will do a mutex_exit
1360 	 * on t_lock in the fp. So don't call nvf_releasef() here.
1361 	 */
1362 	nvf_freef(fdes);
1363 
1364 	return (0);
1365 }
1366 
1367 /*
1368  * nvf_remove -- remove file from filesystem
1369  */
1370 static int
1371 nvf_remove(char *filename)
1372 {
1373 	return (vn_remove(filename, UIO_SYSSPACE, RMFILE));
1374 }
1375 
1376 /*
1377  * nvf_rename -- rename file from one name to another
1378  */
1379 static int
1380 nvf_rename(char *oldname, char *newname)
1381 {
1382 	return (vn_rename(oldname, newname, UIO_SYSSPACE));
1383 }
1384 
1385 /*
1386  * nvf_rw -- common read/write code. Very simplistic.
1387  */
1388 static ssize_t
1389 nvf_rw(int fdes, void *cbuf, ssize_t count, enum uio_rw rw)
1390 {
1391 	file_t	*fp;
1392 	vnode_t	*vp;
1393 	ssize_t	resid   = 0;
1394 
1395 	if ((fp  = nvf_getf(fdes)) == NULL)
1396 		return (-1);
1397 	vp = fp->f_vnode;
1398 
1399 	if (nvf_errno = vn_rdwr(rw, vp, (caddr_t)cbuf, count, fp->f_offset,
1400 	    UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid)) {
1401 		nvf_releasef(fdes);
1402 		return (-1);
1403 	}
1404 
1405 	if ((count - resid) > 0)
1406 		fp->f_offset += count;
1407 
1408 	nvf_releasef(fdes);
1409 	return (count - resid);
1410 }
1411 
1412 /*
1413  * nvf_write -- kernel write function
1414  */
1415 static ssize_t
1416 nvf_write(int fdes, void *cbuf, ssize_t count)
1417 {
1418 	return (nvf_rw(fdes, cbuf, count, UIO_WRITE));
1419 }
1420 
1421 /*
1422  * nvf_read -- kernel read function
1423  */
1424 static ssize_t
1425 nvf_read(int fdes, void *cbuf, ssize_t count)
1426 {
1427 	return (nvf_rw(fdes, cbuf, count, UIO_READ));
1428 }
1429