#include "iscsi.h"
#include "nvfile.h"
#include <sys/file.h>
#include <sys/kobj.h>
#define NVF_GETF 16
static kmutex_t nvf_getf_lock;
static file_t *nvf_fd[NVF_GETF];
extern int modrootloaded;
#define NVF_FILENAME "/etc/iscsi/iscsi"
#define NVF_CURR_FILE_SUFFIX "dbc"
#define NVF_PREV_FILE_SUFFIX "dbp"
#define NVF_TMP_FILENAME "/etc/iscsi/iscsi.dbt"
#define NVF_MAX_FILENAME_LEN 40
#define NVF_HDR_MAGIC 0x15C510DB
#define NVF_HDR_VERSION 1
#define NVF_HDR_SIZE 128
#define NVF_ACTIVE 0x01
#define NVF_DIRTY 0x02
#define NVF_SCHED 0x04
#define NVF_FLUSHING 0x08
#define NVF_ERROR 0x10
#define NVF_IS_ACTIVE(flag) (flag & NVF_ACTIVE)
#define NVF_MARK_ACTIVE(flag) (flag |= NVF_ACTIVE)
#define NVF_CLEAR_ACTIVE(flag) (flag &= ~NVF_ACTIVE)
#define NVF_IS_DIRTY(flag) (flag & NVF_DIRTY)
#define NVF_MARK_DIRTY(flag) (flag |= NVF_DIRTY)
#define NVF_CLEAR_DIRTY(flag) (flag &= ~NVF_DIRTY)
#define NVF_IS_SCHED(flag) (flag & NVF_SCHED)
#define NVF_MARK_SCHED(flag) (flag |= NVF_SCHED)
#define NVF_CLEAR_SCHED(flag) (flag &= ~NVF_SCHED)
#define NVF_FLUSH_DELAY 10
#define NVF_RESCHED_MIN_TICKS 5
#define NVF_FLUSH_BACKOFF_DELAY (SEC_TO_TICK(300))
static file_t *nvf_getf(int fdes);
static void nvf_releasef(int fdes);
static int nvf_setf(file_t *fp);
static int nvf_open(char *path, int flags, int mode);
static int nvf_close(int fdes);
static int nvf_remove(char *filename);
static int nvf_rename(char *oldname, char *newname);
static ssize_t nvf_read(int fdes, void *cbuf, ssize_t count);
static ssize_t nvf_write(int fdes, void *cbuf, ssize_t count);
int nvf_errno;
typedef struct nvf_hdr {
union {
struct hdr {
uint32_t h_magic;
int32_t h_ver;
int64_t h_size;
uint16_t h_hdrsum;
uint16_t h_datasum;
} h_info;
uchar_t h_pad[NVF_HDR_SIZE];
} h_u;
} nvf_hdr_t;
#define nvfh_magic h_u.h_info.h_magic
#define nvfh_ver h_u.h_info.h_ver
#define nvfh_size h_u.h_info.h_size
#define nvfh_hdrsum h_u.h_info.h_hdrsum
#define nvfh_datasum h_u.h_info.h_datasum
static nvlist_t *nvf_list;
static uint32_t nvf_flags;
static kmutex_t nvf_lock;
static krwlock_t nvf_list_lock;
static timeout_id_t nvf_thread_id;
static clock_t nvf_thread_ticks;
static char nvf_curr_filename[NVF_MAX_FILENAME_LEN];
static char nvf_prev_filename[NVF_MAX_FILENAME_LEN];
static boolean_t nvf_written_once;
static uint16_t nvf_chksum(char *buf, int64_t buflen);
static void nvf_thread(void *arg);
static boolean_t nvf_flush(void);
static boolean_t nvf_parse(char *filename);
void
nvf_init(void)
{
mutex_init(&nvf_getf_lock, NULL, MUTEX_DRIVER, NULL);
nvf_list = NULL;
nvf_flags = 0;
NVF_MARK_ACTIVE(nvf_flags);
nvf_thread_id = 0;
nvf_thread_ticks = 0;
nvf_written_once = B_FALSE;
(void) snprintf(nvf_curr_filename, NVF_MAX_FILENAME_LEN, "%s_v%d.%s",
NVF_FILENAME, NVF_HDR_VERSION, NVF_CURR_FILE_SUFFIX);
(void) snprintf(nvf_prev_filename, NVF_MAX_FILENAME_LEN, "%s_v%d.%s",
NVF_FILENAME, NVF_HDR_VERSION, NVF_PREV_FILE_SUFFIX);
mutex_init(&nvf_lock, NULL, MUTEX_DRIVER, NULL);
rw_init(&nvf_list_lock, NULL, RW_DRIVER, NULL);
}
void
nvf_fini(void)
{
mutex_enter(&nvf_lock);
NVF_CLEAR_ACTIVE(nvf_flags);
if (NVF_IS_SCHED(nvf_flags)) {
nvf_thread_ticks = 0;
mutex_exit(&nvf_lock);
(void) untimeout(nvf_thread_id);
mutex_enter(&nvf_lock);
}
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list) {
nvlist_free(nvf_list);
}
nvf_list = NULL;
rw_exit(&nvf_list_lock);
mutex_exit(&nvf_lock);
rw_destroy(&nvf_list_lock);
mutex_destroy(&nvf_lock);
}
boolean_t
nvf_load(void)
{
char corrupt_filename[NVF_MAX_FILENAME_LEN];
boolean_t rval;
mutex_enter(&nvf_lock);
if (!modrootloaded) {
mutex_exit(&nvf_lock);
return (B_TRUE);
} else {
rval = nvf_parse(nvf_curr_filename);
}
if (rval == B_TRUE) {
mutex_exit(&nvf_lock);
return (rval);
} else {
(void) snprintf(corrupt_filename, NVF_MAX_FILENAME_LEN,
"%s.corrupt", nvf_curr_filename);
(void) nvf_rename(nvf_curr_filename, corrupt_filename);
}
if (!modrootloaded) {
mutex_exit(&nvf_lock);
return (B_TRUE);
} else {
rval = nvf_parse(nvf_prev_filename);
}
if (rval == B_TRUE) {
mutex_exit(&nvf_lock);
return (rval);
} else {
(void) snprintf(corrupt_filename, NVF_MAX_FILENAME_LEN,
"%s.corrupt", nvf_prev_filename);
(void) nvf_rename(nvf_prev_filename, corrupt_filename);
}
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list != NULL) {
nvlist_free(nvf_list);
nvf_list = NULL;
}
rw_exit(&nvf_list_lock);
rval = nvlist_alloc(&nvf_list, NV_UNIQUE_NAME, KM_SLEEP);
if (rval != 0) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"allocate root list (%d)", rval);
}
mutex_exit(&nvf_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
void
nvf_update(void)
{
mutex_enter(&nvf_lock);
NVF_MARK_DIRTY(nvf_flags);
if (!NVF_IS_SCHED(nvf_flags)) {
NVF_MARK_SCHED(nvf_flags);
mutex_exit(&nvf_lock);
nvf_thread_id = timeout(nvf_thread, NULL, NVF_FLUSH_DELAY);
} else {
nvf_thread_ticks = ddi_get_lbolt() + NVF_FLUSH_DELAY;
if (nvf_flags & NVF_ERROR) {
mutex_exit(&nvf_lock);
(void) untimeout(nvf_thread_id);
nvf_thread_id = timeout(nvf_thread, NULL,
NVF_FLUSH_DELAY);
} else {
mutex_exit(&nvf_lock);
}
}
}
boolean_t
nvf_list_check(char *id)
{
nvlist_t *list = NULL;
int rval;
rw_enter(&nvf_list_lock, RW_READER);
rval = nvlist_lookup_nvlist(nvf_list, id, &list);
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_node_value_set(char *id, uint32_t value)
{
int rval;
ASSERT(id != NULL);
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_add_uint32(nvf_list, id, value);
if (rval == 0) {
nvf_update();
} else {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"store %s value (%d)", id, rval);
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_node_value_get(char *id, uint32_t *value)
{
boolean_t rval;
ASSERT(id != NULL);
ASSERT(value != NULL);
rw_enter(&nvf_list_lock, RW_READER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_lookup_uint32(nvf_list, id, value);
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_node_name_set(char *id, char *name)
{
boolean_t rval = B_TRUE;
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_add_string(nvf_list, id, name);
if (rval == 0) {
nvf_update();
} else {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"store %s name (%d)", id, rval);
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_node_name_get(char *id, char *name, uint_t nsize)
{
boolean_t rval = B_FALSE;
char *tmpname;
rw_enter(&nvf_list_lock, RW_READER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_lookup_string(nvf_list, id, &tmpname);
if (rval == 0) {
if (strlen(tmpname) < nsize) {
(void) strcpy(name, tmpname);
rval = B_TRUE;
} else {
cmn_err(CE_NOTE, "!iscsi persistent store "
"unable to fit %s node name into buffer %d %s",
tmpname, nsize, id);
rval = B_FALSE;
}
} else {
rval = B_FALSE;
}
rw_exit(&nvf_list_lock);
return (rval);
}
boolean_t
nvf_node_data_set(char *name, void *data, uint_t dsize)
{
int rval;
ASSERT(name != NULL);
ASSERT(data != NULL);
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_add_byte_array(nvf_list, name, (uchar_t *)data, dsize);
if (rval == 0) {
nvf_update();
} else {
cmn_err(CE_NOTE, "!iscsi persistent store failed "
"to store %s name (%d)", name, rval);
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
iscsi_nvfile_status_t
nvf_node_data_get(char *name, void *data, uint_t dsize)
{
uchar_t *value = NULL;
uint_t vsize;
int rval = 0;
iscsi_nvfile_status_t status = ISCSI_NVFILE_SUCCESS;
ASSERT(name != NULL);
ASSERT(data != NULL);
rw_enter(&nvf_list_lock, RW_READER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (ISCSI_NVFILE_NVF_LIST_NOT_FOUND);
}
rval = nvlist_lookup_byte_array(nvf_list, name, &value, &vsize);
if (rval == 0) {
if (vsize <= dsize) {
bcopy(value, data, vsize);
} else {
bcopy(value, data, dsize);
}
status = ISCSI_NVFILE_SUCCESS;
} else if (rval == ENOENT) {
status = ISCSI_NVFILE_NAMEVAL_NOT_FOUND;
} else {
status = ISCSI_NVFILE_FAILURE;
}
rw_exit(&nvf_list_lock);
return (status);
}
boolean_t
nvf_node_data_clear(char *name)
{
int rval;
ASSERT(name != NULL);
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_remove(nvf_list, name, DATA_TYPE_BYTE_ARRAY);
if (rval == 0) {
nvf_update();
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_data_set(char *id, char *name, void *data, uint_t dsize)
{
nvlist_t *list = NULL;
int rval;
boolean_t list_alloc = B_FALSE;
ASSERT(id != NULL);
ASSERT(name != NULL);
ASSERT(data != NULL);
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_lookup_nvlist(nvf_list, id, &list);
if (rval != 0) {
rval = nvlist_alloc(&list, NV_UNIQUE_NAME, KM_SLEEP);
if (rval != 0) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"allocate %s list (%d)", id, rval);
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
list_alloc = B_TRUE;
}
rval = nvlist_add_byte_array(list, name, (uchar_t *)data, dsize);
if (rval == 0) {
rval = nvlist_add_nvlist(nvf_list, id, list);
if (rval != 0) {
cmn_err(CE_NOTE, "!iscsi persistent store failed "
"to add %s list to root (%d)", id, rval);
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
nvf_update();
} else {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"store %s in %s list (%d)", name, id, rval);
}
if (list_alloc) {
nvlist_free(list);
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_data_get(char *id, char *name, void *data, uint_t dsize)
{
nvlist_t *list = NULL;
uchar_t *value = NULL;
uint_t vsize;
int rval;
ASSERT(id != NULL);
ASSERT(name != NULL);
ASSERT(data != NULL);
rw_enter(&nvf_list_lock, RW_READER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_lookup_nvlist(nvf_list, id, &list);
if (rval != 0) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
ASSERT(list != NULL);
rval = nvlist_lookup_byte_array(list, name, &value, &vsize);
if (rval == 0) {
if (vsize <= dsize) {
bcopy(value, data, vsize);
} else {
bcopy(value, data, dsize);
}
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
boolean_t
nvf_data_next(char *id, void **v, char *name, void *data, uint_t dsize)
{
nvlist_t *list = NULL;
nvpair_t *pair = NULL;
uchar_t *value = NULL;
uint_t vsize;
int rval;
ASSERT(id != NULL);
ASSERT(v != NULL);
ASSERT(name != NULL);
ASSERT(data != NULL);
rw_enter(&nvf_list_lock, RW_READER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_lookup_nvlist(nvf_list, id, &list);
if (rval != 0) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
pair = nvlist_next_nvpair(list, (nvpair_t *)*v);
*v = (void *)pair;
if (pair == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvpair_value_byte_array(pair, &value, &vsize);
if (rval != 0) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
(void) strcpy(name, nvpair_name(pair));
if (vsize <= dsize) {
bcopy(value, data, vsize);
} else {
bcopy(value, data, dsize);
}
rw_exit(&nvf_list_lock);
return (B_TRUE);
}
boolean_t
nvf_data_clear(char *id, char *name)
{
nvlist_t *list = NULL;
int rval = B_FALSE;
ASSERT(id != NULL);
ASSERT(name != NULL);
rw_enter(&nvf_list_lock, RW_WRITER);
if (nvf_list == NULL) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_lookup_nvlist(nvf_list, id, &list);
if (rval != 0) {
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rval = nvlist_remove(list, name, DATA_TYPE_BYTE_ARRAY);
if (rval == 0) {
nvf_update();
}
rw_exit(&nvf_list_lock);
return (rval == 0 ? B_TRUE : B_FALSE);
}
static uint16_t
nvf_chksum(char *buf, int64_t buflen)
{
uint16_t cksum = 0;
uint16_t *p = (uint16_t *)buf;
int64_t n;
if ((buflen & 0x01) != 0) {
buflen--;
cksum = buf[buflen];
}
n = buflen / 2;
while (n-- > 0)
cksum ^= *p++;
return (cksum);
}
static void
nvf_thread(void *arg)
{
clock_t nticks;
boolean_t rval;
mutex_enter(&nvf_lock);
nticks = nvf_thread_ticks - ddi_get_lbolt();
if ((nticks > NVF_RESCHED_MIN_TICKS) || !modrootloaded) {
if (NVF_IS_ACTIVE(nvf_flags)) {
mutex_exit(&nvf_lock);
nvf_thread_id = timeout(nvf_thread, NULL, nticks);
mutex_enter(&nvf_lock);
}
mutex_exit(&nvf_lock);
return;
}
NVF_CLEAR_DIRTY(nvf_flags);
nvf_flags |= NVF_FLUSHING;
mutex_exit(&nvf_lock);
rval = nvf_flush();
mutex_enter(&nvf_lock);
nvf_flags &= ~NVF_FLUSHING;
if (rval == B_FALSE) {
NVF_MARK_DIRTY(nvf_flags);
if ((nvf_flags & NVF_ERROR) == 0) {
if (nvf_written_once) {
cmn_err(CE_NOTE,
"!iscsi persistent store update "
"failed file:%s", nvf_curr_filename);
}
nvf_flags |= NVF_ERROR;
}
nvf_thread_ticks = NVF_FLUSH_BACKOFF_DELAY + ddi_get_lbolt();
} else if (nvf_flags & NVF_ERROR) {
cmn_err(CE_NOTE, "!iscsi persistent store update ok now "
"filename:%s", nvf_curr_filename);
nvf_flags &= ~NVF_ERROR;
}
if (NVF_IS_ACTIVE(nvf_flags) && NVF_IS_DIRTY(nvf_flags)) {
nticks = nvf_thread_ticks - ddi_get_lbolt();
mutex_exit(&nvf_lock);
if (nticks > NVF_FLUSH_DELAY) {
nvf_thread_id = timeout(nvf_thread, NULL, nticks);
} else {
nvf_thread_id = timeout(nvf_thread, NULL,
NVF_FLUSH_DELAY);
}
} else {
NVF_CLEAR_SCHED(nvf_flags);
mutex_exit(&nvf_lock);
}
}
static boolean_t
nvf_flush(void)
{
int rval;
nvlist_t *tmpnvl;
char *nvfbuf;
char *nvlbuf;
size_t nvllen;
size_t nvflen;
int file;
int bytes_written;
rw_enter(&nvf_list_lock, RW_READER);
rval = nvlist_dup(nvf_list, &tmpnvl, KM_SLEEP);
if (rval != 0) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"duplicate nvf_list (%d)", rval);
rw_exit(&nvf_list_lock);
return (B_FALSE);
}
rw_exit(&nvf_list_lock);
nvlbuf = NULL;
rval = nvlist_pack(tmpnvl, &nvlbuf, &nvllen, NV_ENCODE_NATIVE, 0);
if (rval != 0) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to pack "
"nvf_list (%d)", rval);
nvlist_free(tmpnvl);
return (B_FALSE);
}
nvflen = nvllen + sizeof (nvf_hdr_t);
nvfbuf = kmem_zalloc(nvflen, KM_SLEEP);
((nvf_hdr_t *)nvfbuf)->nvfh_magic = NVF_HDR_MAGIC;
((nvf_hdr_t *)nvfbuf)->nvfh_ver = NVF_HDR_VERSION;
((nvf_hdr_t *)nvfbuf)->nvfh_size = nvllen;
((nvf_hdr_t *)nvfbuf)->nvfh_datasum = nvf_chksum((char *)nvlbuf,
nvllen);
((nvf_hdr_t *)nvfbuf)->nvfh_hdrsum = nvf_chksum((char *)nvfbuf,
sizeof (nvf_hdr_t));
bcopy(nvlbuf, nvfbuf + sizeof (nvf_hdr_t), nvllen);
nvlist_free(tmpnvl);
kmem_free(nvlbuf, nvllen);
rval = nvf_remove(NVF_TMP_FILENAME);
if (rval == -1) {
kmem_free(nvfbuf, nvflen);
return (B_FALSE);
}
file = nvf_open(NVF_TMP_FILENAME, O_RDWR | O_CREAT, 0600);
if (file == -1) {
mutex_enter(&nvf_lock);
if (nvf_written_once) {
cmn_err(CE_NOTE,
"!iscsi persistent store failed to create "
"%s (errno:%d)", NVF_TMP_FILENAME, nvf_errno);
}
mutex_exit(&nvf_lock);
kmem_free(nvfbuf, nvflen);
return (B_FALSE);
}
bytes_written = nvf_write(file, nvfbuf, nvflen);
kmem_free(nvfbuf, nvflen);
if (bytes_written == -1) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to write "
"%s (errno:%d)", NVF_TMP_FILENAME, nvf_errno);
return (B_FALSE);
}
if (bytes_written != nvflen) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to write "
"%s (errno:%d)\n\tpartial write %d of %ld bytes\n",
NVF_TMP_FILENAME, nvf_errno, bytes_written, nvflen);
return (B_FALSE);
}
rval = nvf_close(file);
if (rval == -1) {
return (B_FALSE);
}
mutex_enter(&nvf_lock);
nvf_written_once = B_TRUE;
rval = nvf_rename(nvf_curr_filename, nvf_prev_filename);
if (rval == -1) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"rename %s (errno:%d)", nvf_curr_filename, nvf_errno);
mutex_exit(&nvf_lock);
return (B_FALSE);
}
rval = nvf_rename(NVF_TMP_FILENAME, nvf_curr_filename);
if (rval == -1) {
cmn_err(CE_NOTE, "!iscsi persistent store failed to "
"rename %s (errno:%d)", NVF_TMP_FILENAME, nvf_errno);
mutex_exit(&nvf_lock);
return (B_FALSE);
}
NVF_CLEAR_DIRTY(nvf_flags);
mutex_exit(&nvf_lock);
return (B_TRUE);
}
static boolean_t
nvf_parse(char *filename)
{
int file;
nvf_hdr_t hdr;
int bytes_read;
int rval;
uint16_t chksum;
uint16_t hdrsum;
char *buf;
char overfill;
nvlist_t *nvl;
nvlist_t *old_nvl;
file = nvf_open(filename, O_RDONLY, 0600);
if (file == -1) {
return (B_FALSE);
}
bytes_read = nvf_read(file, (char *)&hdr, sizeof (hdr));
if (bytes_read != sizeof (hdr)) {
(void) nvf_close(file);
return (B_FALSE);
}
chksum = hdr.nvfh_hdrsum;
hdr.nvfh_hdrsum = 0;
hdrsum = nvf_chksum((char *)&hdr, sizeof (hdr));
if ((hdr.nvfh_magic != NVF_HDR_MAGIC) ||
(hdr.nvfh_ver != NVF_HDR_VERSION) ||
(hdrsum != chksum)) {
(void) nvf_close(file);
if (hdrsum != chksum) {
cmn_err(CE_NOTE, "!iscsi persistent store "
"checksum error %s actual:0x%x expected:0x%x",
filename, hdrsum, chksum);
}
cmn_err(CE_NOTE, "!iscsi persistent store %s has an "
"incorrect header", filename);
return (B_FALSE);
}
ASSERT(hdr.nvfh_size >= 0);
buf = kmem_alloc(hdr.nvfh_size, KM_SLEEP);
bytes_read = nvf_read(file, buf, hdr.nvfh_size);
if (bytes_read != hdr.nvfh_size) {
kmem_free(buf, hdr.nvfh_size);
(void) nvf_close(file);
if (bytes_read < 0) {
cmn_err(CE_NOTE, "!iscsi persistent store failed "
"to read %s bytes:%d", filename, bytes_read);
} else {
cmn_err(CE_NOTE, "!iscsi persistent store incomplete "
"read %s bytes:%d/%lld", filename,
bytes_read, (longlong_t)hdr.nvfh_size);
}
return (B_FALSE);
}
bytes_read = nvf_read(file, &overfill, 1);
(void) nvf_close(file);
if (bytes_read > 0) {
kmem_free(buf, hdr.nvfh_size);
cmn_err(CE_NOTE, "!iscsi persistent store file is larger "
"than expected %s bytes:%lld",
filename, (longlong_t)hdr.nvfh_size);
return (B_FALSE);
}
DTRACE_PROBE1(hdr, nvf_hdr_t *, &hdr);
chksum = nvf_chksum(buf, hdr.nvfh_size);
if (hdr.nvfh_datasum != chksum) {
kmem_free(buf, hdr.nvfh_size);
cmn_err(CE_NOTE, "!iscsi persistent store checksum error %s "
"actual:0x%x expected:0x%x", filename,
hdr.nvfh_datasum, chksum);
return (B_FALSE);
}
nvl = NULL;
rval = nvlist_unpack(buf, hdr.nvfh_size, &nvl, 0);
if (rval != 0) {
kmem_free(buf, hdr.nvfh_size);
cmn_err(CE_NOTE, "!iscsi persistent store failed unpacking "
"nvlist %s (%d)", filename, rval);
return (B_FALSE);
}
kmem_free(buf, hdr.nvfh_size);
rw_enter(&nvf_list_lock, RW_WRITER);
old_nvl = nvf_list;
nvf_list = nvl;
rw_exit(&nvf_list_lock);
if (old_nvl) {
nvlist_free(old_nvl);
}
return (B_TRUE);
}
static file_t *
nvf_getf(int fdes)
{
file_t *fp = NULL;
mutex_enter(&nvf_getf_lock);
if ((fdes >= 0) && (fdes < NVF_GETF)) {
fp = nvf_fd[fdes];
if (fp != NULL)
mutex_enter(&fp->f_tlock);
}
mutex_exit(&nvf_getf_lock);
return (fp);
}
static void
nvf_releasef(int fdes)
{
file_t *fp;
mutex_enter(&nvf_getf_lock);
if ((fdes >= 0) && (fdes < NVF_GETF)) {
fp = nvf_fd[fdes];
mutex_exit(&fp->f_tlock);
}
mutex_exit(&nvf_getf_lock);
}
static int
nvf_setf(file_t *fp)
{
int i = -1;
mutex_enter(&nvf_getf_lock);
for (i = 0; i < NVF_GETF; i++) {
if (nvf_fd[i] == 0) {
nvf_fd[i] = fp;
break;
}
}
mutex_exit(&nvf_getf_lock);
return (i);
}
static void
nvf_freef(int fdes)
{
file_t *fp;
mutex_enter(&nvf_getf_lock);
if ((fdes >= 0) && (fdes < NVF_GETF)) {
fp = nvf_fd[fdes];
unfalloc(fp);
nvf_fd[fdes] = NULL;
}
mutex_exit(&nvf_getf_lock);
}
static int
nvf_open(char *path, int flags, int mode)
{
file_t *fp = NULL;
vnode_t *vp = NULL;
int fdes = -1;
int fflags;
if (flags & O_WRONLY)
fflags = FWRITE;
else if (flags & O_RDWR)
fflags = FWRITE | FREAD;
else
fflags = FREAD;
if (flags & O_CREAT)
fflags |= FCREAT;
if (flags & O_TRUNC)
fflags |= FTRUNC;
if (nvf_errno = vn_open(path, UIO_SYSSPACE, fflags,
mode & MODEMASK, &vp, CRCREAT, 0)) {
return (-1);
}
if (falloc(vp, fflags, &fp, NULL) != 0) {
VN_RELE(vp);
return (-1);
}
mutex_exit(&fp->f_tlock);
if ((fdes = nvf_setf(fp)) == -1) {
VN_RELE(vp);
}
return (fdes);
}
static int
nvf_close(int fdes)
{
file_t *fp;
vnode_t *vp;
if ((fp = nvf_getf(fdes)) == NULL)
return (-1);
vp = fp->f_vnode;
(void) VOP_CLOSE(vp, fp->f_flag, 1, 0, kcred, NULL);
VN_RELE(vp);
nvf_freef(fdes);
return (0);
}
static int
nvf_remove(char *filename)
{
return (vn_remove(filename, UIO_SYSSPACE, RMFILE));
}
static int
nvf_rename(char *oldname, char *newname)
{
return (vn_rename(oldname, newname, UIO_SYSSPACE));
}
static ssize_t
nvf_rw(int fdes, void *cbuf, ssize_t count, enum uio_rw rw)
{
file_t *fp;
vnode_t *vp;
ssize_t resid = 0;
if ((fp = nvf_getf(fdes)) == NULL)
return (-1);
vp = fp->f_vnode;
if (nvf_errno = vn_rdwr(rw, vp, (caddr_t)cbuf, count, fp->f_offset,
UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid)) {
nvf_releasef(fdes);
return (-1);
}
if ((count - resid) > 0)
fp->f_offset += count;
nvf_releasef(fdes);
return (count - resid);
}
static ssize_t
nvf_write(int fdes, void *cbuf, ssize_t count)
{
return (nvf_rw(fdes, cbuf, count, UIO_WRITE));
}
static ssize_t
nvf_read(int fdes, void *cbuf, ssize_t count)
{
return (nvf_rw(fdes, cbuf, count, UIO_READ));
}