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
nvf_init(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
nvf_fini(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
nvf_load(void)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
nvf_update(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
nvf_list_check(char * id)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
nvf_node_value_set(char * id,uint32_t value)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
nvf_node_value_get(char * id,uint32_t * value)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
nvf_node_name_set(char * id,char * name)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
nvf_node_name_get(char * id,char * name,uint_t nsize)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
nvf_node_data_set(char * name,void * data,uint_t dsize)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
nvf_node_data_get(char * name,void * data,uint_t dsize)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
nvf_node_data_clear(char * name)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
nvf_data_set(char * id,char * name,void * data,uint_t dsize)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
nvf_data_get(char * id,char * name,void * data,uint_t dsize)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
nvf_data_next(char * id,void ** v,char * name,void * data,uint_t dsize)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
nvf_data_clear(char * id,char * name)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
nvf_chksum(char * buf,int64_t buflen)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
nvf_thread(void * arg)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
nvf_flush(void)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
nvf_parse(char * filename)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 *
nvf_getf(int fdes)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
nvf_releasef(int fdes)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
nvf_setf(file_t * fp)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
nvf_freef(int fdes)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
nvf_open(char * path,int flags,int mode)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
nvf_close(int fdes)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
nvf_remove(char * filename)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
nvf_rename(char * oldname,char * newname)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
nvf_rw(int fdes,void * cbuf,ssize_t count,enum uio_rw rw)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
nvf_write(int fdes,void * cbuf,ssize_t count)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
nvf_read(int fdes,void * cbuf,ssize_t count)1425 nvf_read(int fdes, void *cbuf, ssize_t count)
1426 {
1427 return (nvf_rw(fdes, cbuf, count, UIO_READ));
1428 }
1429