diff --git a/.travis.yml b/.travis.yml index 21c3f553e9ab..e7cd83879daa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,8 +48,11 @@ install: fi script: - if [ $ZFS_BUILD_TAGS = 0 ]; then - cd cmd/ztest; - travis_wait 60 ./ztest -V; + cd cmd/uzfs_test; + truncate -s 2G /tmp/ztest.1a; + truncate -s 2G /tmp/ztest.2a; + truncate -s 256M /tmp/ztest.log; + travis_wait 5 ./uzfs_test; else travis_wait 100 /usr/share/zfs/zfs-tests.sh -v; fi diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 58c018fde022..62e0bd803ce0 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest zpios +SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest uzfs_test zpios SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat dbufstat zed SUBDIRS += arc_summary raidz_test zgenhostid tgt diff --git a/cmd/uzfs_test/Makefile.am b/cmd/uzfs_test/Makefile.am new file mode 100644 index 000000000000..7e39030f549f --- /dev/null +++ b/cmd/uzfs_test/Makefile.am @@ -0,0 +1,24 @@ +include $(top_srcdir)/config/Rules.am + +# -Wnoformat-truncation to get rid of compiler warning for unchecked +# truncating snprintfs on gcc 7.1.1. +AM_CFLAGS += $(DEBUG_STACKFLAGS) $(FRAME_LARGER_THAN) $(NO_FORMAT_TRUNCATION) +AM_CPPFLAGS += -DDEBUG + +DEFAULT_INCLUDES += \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/libspl/include + +sbin_PROGRAMS = uzfs_test + +uzfs_test_SOURCES = \ + uzfs_test.c + +uzfs_test_LDADD = \ + $(top_builddir)/lib/libnvpair/libnvpair.la \ + $(top_builddir)/lib/libuutil/libuutil.la \ + $(top_builddir)/lib/libzpool/libzpool.la \ + $(top_builddir)/lib/libzfs/libzfs.la \ + $(top_builddir)/lib/libzfs_core/libzfs_core.la + +uzfs_test_LDADD += -lm diff --git a/cmd/uzfs_test/uzfs_test.c b/cmd/uzfs_test/uzfs_test.c new file mode 100644 index 000000000000..729b6d46f48a --- /dev/null +++ b/cmd/uzfs_test/uzfs_test.c @@ -0,0 +1,492 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#include +#include +#include +#include + +int total_time_in_sec = 60; +uint64_t io_block_size = 1024; +uint64_t block_size = 4096; +uint64_t active_size = 0; +uint64_t vol_size = 0; + +typedef struct worker_args { + void *zv; + kmutex_t *mtx; + kcondvar_t *cv; + int *threads_done; + uint64_t io_block_size; + uint64_t active_size; +} worker_args_t; + +void reader_thread(void *zv); + +extern unsigned long zfs_arc_max; +extern unsigned long zfs_arc_min; + +void +verify_data(char *buf, uint64_t offset, int idx, uint64_t block_size) +{ + int i; + int err = 0; + if ((buf[0] != ((offset / 256) % 128)) && (buf[0] != 0)) { + printf("error0 in data..\n"); + err = 1; + } + + for (i = 0; i < idx; i++) { + if ((buf[((i + 1) * block_size) - 1] != + (((offset + (i * block_size)) / 4096) % 128)) && + (buf[((i + 1) * block_size) - 1] != 0)) { + printf("error0 %d in data..\n", i); + err = 1; + } + if ((buf[((i + 1) * block_size)] != + (((offset + ((i + 1) * block_size)) / 256) % 128)) && + (buf[((i + 1) * block_size)] != 0)) { + printf("error1 %d in data..\n", i); + err = 1; + } + } + if ((buf[((i + 1) * block_size) - 1] != + (((offset + (i * block_size)) / 4096) % 128)) && + (buf[((i + 1) * block_size) - 1] != 0)) { + printf("error1 %lu %d in data..\n", offset+i*block_size, + buf[((i+1)*block_size)-1]); + err = 1; + } + if (err == 1) + exit(1); +} + +void +reader_thread(void *arg) +{ + worker_args_t *warg = (worker_args_t *)arg; + char *buf[15]; + int idx, j, err; + uint64_t blk_offset, offset, vol_blocks, iops = 0, data_iops = 0; + hrtime_t end, now; + void *zv = warg->zv; + kmutex_t *mtx = warg->mtx; + kcondvar_t *cv = warg->cv; + int *threads_done = warg->threads_done; + uint64_t vol_size = warg->active_size; + uint64_t block_size = warg->io_block_size; + + for (j = 0; j < 15; j++) + buf[j] = (char *)umem_alloc(sizeof (char)*(j+1)* block_size, + UMEM_NOFAIL); + + now = gethrtime(); + end = now + (hrtime_t)(total_time_in_sec * (hrtime_t)(NANOSEC)); + + vol_blocks = (vol_size) / block_size; + + printf("Starting read..\n"); + + while (1) { + blk_offset = uzfs_random(vol_blocks - 16); + offset = blk_offset * block_size; + + idx = uzfs_random(15); + err = uzfs_read_data(zv, buf[idx], offset, + (idx + 1) * block_size); + + if (err != 0) + printf("IO error at offset: %lu len: %lu\n", offset, + (idx + 1) * block_size); + verify_data(buf[idx], offset, idx, block_size); + + if (buf[idx][0] != 0) + data_iops += (idx + 1); + + iops += (idx + 1); + + now = gethrtime(); + if (now > end) + break; + } + for (j = 0; j < 15; j++) + umem_free(buf[j], sizeof (char) * (j + 1) * block_size); + printf("Stopping read.. ios done: %lu data iops: %lu\n", iops, + data_iops); + + mutex_enter(mtx); + *threads_done = *threads_done + 1; + cv_signal(cv); + mutex_exit(mtx); + zk_thread_exit(); +} + +void +populate_data(char *buf, uint64_t offset, int idx, uint64_t block_size) +{ + int i; + buf[0] = (offset / 256) % 128; + + for (i = 0; i < idx; i++) { + buf[((i + 1) * block_size) - 1] = + ((offset + (i * block_size)) / 4096) % 128; + buf[((i + 1) * block_size)] = + ((offset + ((i + 1) * block_size)) / 256) % 128; + } + buf[((i + 1) * block_size) - 1] = + ((offset + (i * block_size)) / 4096) % 128; +} + +void +writer_thread(void *arg) +{ + worker_args_t *warg = (worker_args_t *)arg; + char *buf[15]; + int idx, j, err; + uint64_t blk_offset, offset, vol_blocks, iops = 0; + hrtime_t end, now; + void *zv = warg->zv; + kmutex_t *mtx = warg->mtx; + kcondvar_t *cv = warg->cv; + int *threads_done = warg->threads_done; + uint64_t vol_size = warg->active_size; + uint64_t block_size = warg->io_block_size; + + for (j = 0; j < 15; j++) + buf[j] = (char *)umem_alloc(sizeof (char)*(j+1)*block_size, + UMEM_NOFAIL); + + now = gethrtime(); + end = now + (hrtime_t)(total_time_in_sec * (hrtime_t)(NANOSEC)); + + vol_blocks = (vol_size) / block_size; + + printf("Starting write..\n"); + + while (1) { + blk_offset = uzfs_random(vol_blocks - 16); + offset = blk_offset * block_size; + + idx = uzfs_random(15); + + populate_data(buf[idx], offset, idx, block_size); + + err = uzfs_write_data(zv, buf[idx], offset, + (idx + 1) * block_size); + if (err != 0) + printf("IO error at offset: %lu len: %lu\n", offset, + (idx + 1) * block_size); + iops += (idx + 1); + now = gethrtime(); + + if (now > end) + break; + } + for (j = 0; j < 15; j++) + umem_free(buf[j], sizeof (char) * (j + 1) * block_size); + printf("Stopping write.. ios done: %lu\n", iops); + + mutex_enter(mtx); + *threads_done = *threads_done + 1; + cv_signal(cv); + mutex_exit(mtx); + zk_thread_exit(); +} + +void +make_vdev(char *path) +{ + int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd == -1) { + printf("can't open %s", path); + exit(1); + } + if (ftruncate(fd, vol_size) != 0) { + printf("can't ftruncate %s", path); + exit(1); + } + (void) close(fd); +} + +void +setup_unit_test(void) +{ + make_vdev("/tmp/uztest.1a"); + make_vdev("/tmp/uztest.2a"); + make_vdev("/tmp/uztest.log"); + unlink("/tmp/uztest.xyz"); +} + +void +unit_test_create_pool_ds(void) +{ + void *spa1, *spa2, *spa3, *spa4, *spa; + void *zv1, *zv2, *zv3, *zv4, *zv5, *zv; + int err, err1, err2, err3, err4, err5; + + err1 = uzfs_create_pool("testp", "/tmp/uztest.xyz", &spa1); + if (spa1 != NULL) { + printf("shouldn't create pool with non existing disk..\n"); + exit(1); + } + + err = uzfs_create_pool("testp", "/tmp/uztest.1a", &spa); + if (err != 0 || spa == NULL) { + printf("creating pool errored %d..\n", err); + exit(1); + } + + err1 = uzfs_create_pool("testp", "/tmp/uztest.1a", &spa1); + err2 = uzfs_create_pool("testp1", "/tmp/uztest.xyz", &spa2); + err3 = uzfs_open_pool("testp", &spa3); + err4 = uzfs_open_pool("testp1", &spa4); + if (spa1 != NULL || spa2 != NULL || spa4 != NULL || + err1 == 0 || err2 == 0 || err4 == 0) { + printf("shouldn't create/open, but succeeded..\n"); + exit(1); + } + + if (err3 != 0 || spa3 == NULL) { + printf("opening pool errored %d..\n", err3); + exit(1); + } + + err = uzfs_create_dataset(spa, "ds0", vol_size, block_size, 0, &zv); + if (zv == NULL || err != 0) { + printf("creating ds errored %d..\n", err); + exit(1); + } + + err1 = uzfs_create_dataset(spa, "ds0", vol_size, block_size, 0, &zv1); + err2 = uzfs_open_dataset(spa, "ds0", 0, &zv2); + err3 = uzfs_open_dataset(spa, "ds1", 0, &zv3); + err4 = uzfs_open_dataset(NULL, "ds1", 0, &zv4); + err5 = uzfs_create_dataset(NULL, "ds0", vol_size, block_size, 0, &zv5); + if (zv1 != NULL || zv2 != NULL || zv3 != NULL || zv4 != NULL || + zv5 != NULL || err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || + err5 == 0) { + printf("shouldn't create/open, but succeeded..\n"); + exit(1); + } + + err1 = uzfs_vdev_add(spa, "/tmp/uztest.xyz", 12, 0); + err2 = uzfs_vdev_add(spa, "/tmp/uztest.1a", 12, 0); + err3 = uzfs_vdev_add(spa, "/tmp/uztest.2a", 12, 0); + err4 = uzfs_vdev_add(spa, "/tmp/uztest.log", 12, 1); + if (err1 == 0 || err2 == 0) { + printf("shouldn't add vdev, but succeeded..\n"); + exit(1); + } + + if (err3 != 0 || err4 != 0) { + printf("vdev add errored %d %d..\n", err3, err4); + exit(1); + } + + uzfs_close_pool(spa, zv); +} + +static void usage(int num) +{ + printf("uzfs_test -t -a " + " -b -i -v \n"); + if (num == 0) + exit(1); +} + +static int +str2shift(const char *buf) +{ + const char *ends = "BKMGTPEZ"; + int i; + + if (buf[0] == '\0') + return (0); + for (i = 0; i < strlen(ends); i++) { + if (toupper(buf[0]) == ends[i]) + break; + } + if (i == strlen(ends)) { + printf("uztest: invalid bytes suffix: %s\n", buf); + usage(B_FALSE); + } + if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0')) { + return (10*i); + } + printf("uztest: invalid bytes suffix: %s\n", buf); + usage(B_FALSE); + return (-1); +} + + +static uint64_t +nicenumtoull(const char *buf) +{ + char *end; + uint64_t val; + + val = strtoull(buf, &end, 0); + if (end == buf) { + printf("uztest: bad numeric value: %s\n", buf); + usage(B_FALSE); + } else if (end[0] == '.') { + double fval = strtod(buf, &end); + fval *= pow(2, str2shift(end)); + if (fval > UINT64_MAX) { + printf("uztest: value too large: %s\n", buf); + usage(B_FALSE); + } + val = (uint64_t)fval; + } else { + int shift = str2shift(end); + if (shift >= 64 || (val << shift) >> shift != val) { + printf("uztest: value too large: %s\n", buf); + usage(B_FALSE); + } + val <<= shift; + } + return (val); +} + + +static void process_options(int argc, char **argv) +{ + int opt; + uint64_t val = 0; + while ((opt = getopt(argc, argv, "a:b:i:v:t:")) != EOF) { + if (optarg != NULL) + val = nicenumtoull(optarg); + switch (opt) { + case 'a': + active_size = val; + if (vol_size == 0) + vol_size = active_size; + else + active_size = (active_size < vol_size) + ? (active_size) : (vol_size); + break; + case 'b': + block_size = val; + break; + case 'i': + io_block_size = val; + break; + case 'v': + vol_size = val; + if (active_size == 0) + active_size = vol_size; + else + active_size = (active_size < vol_size) + ? (active_size) : (vol_size); + break; + case 't': + total_time_in_sec = val; + break; + default: + usage(0); + } + } + if (active_size == 0 || vol_size == 0) + active_size = vol_size = 1024*1024*1024ULL; + + printf("vol size: %lu active size: %lu\n", vol_size, active_size); + printf("block size: %lu io blksize: %lu\n", block_size, io_block_size); + printf("total run time in seconds: %d\n", total_time_in_sec); +} + +int +main(int argc, char **argv) +{ + void *spa, *zv; + kthread_t *reader1; + kthread_t *writer[3]; + int i, err; + kmutex_t mtx; + kcondvar_t cv; + int threads_done = 0; + int num_threads = 0; + worker_args_t reader1_args, writer_args[3]; + + mutex_init(&mtx, NULL, MUTEX_DEFAULT, NULL); + cv_init(&cv, NULL, CV_DEFAULT, NULL); + + process_options(argc, argv); + + zfs_arc_max = (512 << 20); + zfs_arc_min = (256 << 20); + + err = uzfs_init(); + if (err != 0) { + printf("initialization errored.. %d\n", err); + exit(1); + } + printf("zarcmax: %lu zarcmin:%lu\n", zfs_arc_max, zfs_arc_min); + + setup_unit_test(); + + unit_test_create_pool_ds(); + + err = uzfs_open_pool("testp", &spa); + if (err != 0) { + printf("pool open errored.. %d\n", err); + exit(1); + } + err = uzfs_open_dataset(spa, "ds0", 0, &zv); + if (err != 0) { + printf("ds open errored.. %d\n", err); + exit(1); + } + + reader1_args.zv = zv; + reader1_args.threads_done = &threads_done; + reader1_args.mtx = &mtx; + reader1_args.cv = &cv; + reader1_args.io_block_size = io_block_size; + reader1_args.active_size = active_size; + + reader1 = zk_thread_create(NULL, 0, (thread_func_t)reader_thread, + &reader1_args, 0, NULL, TS_RUN, 0, PTHREAD_CREATE_DETACHED); + num_threads++; + + for (i = 0; i < 3; i++) { + writer_args[i].zv = zv; + writer_args[i].threads_done = &threads_done; + writer_args[i].mtx = &mtx; + writer_args[i].cv = &cv; + writer_args[i].io_block_size = io_block_size; + writer_args[i].active_size = active_size; + + writer[i] = zk_thread_create(NULL, 0, + (thread_func_t)writer_thread, &writer_args[i], 0, NULL, + TS_RUN, 0, PTHREAD_CREATE_DETACHED); + num_threads++; + } + + mutex_enter(&mtx); + while (threads_done != num_threads) + cv_wait(&cv, &mtx); + mutex_exit(&mtx); + + cv_destroy(&cv); + mutex_destroy(&mtx); + + uzfs_close_pool(spa, zv); + uzfs_fini(); +} diff --git a/configure.ac b/configure.ac index 7333f8adc84a..8695634846fd 100644 --- a/configure.ac +++ b/configure.ac @@ -100,6 +100,7 @@ AC_CONFIG_FILES([ lib/libzfs_core/Makefile lib/libshare/Makefile cmd/Makefile + cmd/uzfs_test/Makefile cmd/zdb/Makefile cmd/zhack/Makefile cmd/zfs/Makefile diff --git a/include/Makefile.am b/include/Makefile.am index c5984f1deb77..26ebc43af94e 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -21,7 +21,9 @@ USER_H = \ $(top_srcdir)/include/libuzfs.h \ $(top_srcdir)/include/gtest_helper.h \ $(top_srcdir)/include/libzfs_core.h \ - $(top_srcdir)/include/libzfs_impl.h + $(top_srcdir)/include/libzfs_impl.h \ + $(top_srcdir)/include/uzfs_io.h \ + $(top_srcdir)/include/uzfs_mgmt.h EXTRA_DIST = $(COMMON_H) $(KERNEL_H) $(USER_H) diff --git a/include/sys/Makefile.am b/include/sys/Makefile.am index be606b8c6060..0fd04ee3cd51 100644 --- a/include/sys/Makefile.am +++ b/include/sys/Makefile.am @@ -120,7 +120,8 @@ KERNEL_H = \ ${top_srcdir}/include/sys/zpl.h \ $(top_srcdir)/include/sys/zvol.h -USER_H = +USER_H = \ + $(top_srcdir)/include/sys/uzfs_zvol.h EXTRA_DIST = $(COMMON_H) $(KERNEL_H) $(USER_H) diff --git a/include/sys/uzfs_zvol.h b/include/sys/uzfs_zvol.h new file mode 100644 index 000000000000..268fe550e6f7 --- /dev/null +++ b/include/sys/uzfs_zvol.h @@ -0,0 +1,60 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _SYS_UZFS_ZVOL_H +#define _SYS_UZFS_ZVOL_H + +#include +#include + +#if !defined(_KERNEL) + +typedef struct zvol_properties { + uint64_t vol_size; + uint64_t block_size; +} zvol_properties_t; + +/* + * The in-core state of each volume. + */ +struct zvol_state { + char zv_name[MAXNAMELEN]; /* name */ + uint64_t zv_volsize; /* advertised space */ + uint64_t zv_volblocksize; /* volume block size */ + objset_t *zv_objset; /* objset handle */ + zilog_t *zv_zilog; /* ZIL handle */ + dnode_t *zv_dn; /* dnode hold */ + zfs_rlock_t zv_range_lock; /* range lock */ + spa_t *zv_spa; /* spa */ + int zv_sync; /* sync property of zv */ +}; + +typedef struct zvol_state zvol_state_t; + +#define UZFS_IO_TX_ASSIGN_FAIL 1 +#define UZFS_IO_READ_FAIL 2 + +extern int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio); +extern void zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset, + uint64_t size, int sync); + +#endif +#endif diff --git a/include/sys/zfs_znode.h b/include/sys/zfs_znode.h index c292f03739e3..eb7cfa9144d1 100644 --- a/include/sys/zfs_znode.h +++ b/include/sys/zfs_znode.h @@ -156,6 +156,7 @@ extern "C" { #define ZFS_DIRENT_TYPE(de) BF64_GET(de, 60, 4) #define ZFS_DIRENT_OBJ(de) BF64_GET(de, 0, 48) +extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value); /* * Directory entry locks control access to directory entries. * They are used to protect creates, deletes, and renames. @@ -310,7 +311,6 @@ extern void zfs_remove_op_tables(void); extern int zfs_create_op_tables(void); extern int zfs_sync(struct super_block *, int, cred_t *); extern dev_t zfs_cmpldev(uint64_t); -extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value); extern int zfs_get_stats(objset_t *os, nvlist_t *nv); extern boolean_t zfs_get_vfs_flag_unmounted(objset_t *os); extern void zfs_znode_dmu_fini(znode_t *); diff --git a/include/sys/zvol.h b/include/sys/zvol.h index e8b084762a2d..d3d4acc5112f 100644 --- a/include/sys/zvol.h +++ b/include/sys/zvol.h @@ -39,15 +39,15 @@ extern void zvol_create_minors(spa_t *spa, const char *name, boolean_t async); extern void zvol_remove_minors(spa_t *spa, const char *name, boolean_t async); extern void zvol_rename_minors(spa_t *spa, const char *oldname, const char *newname, boolean_t async); +extern void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); +extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize); +extern int zvol_check_volblocksize(const char *name, uint64_t volblocksize); +extern int zvol_get_stats(objset_t *os, nvlist_t *nv); #ifdef _KERNEL typedef struct zvol_state zvol_state_t; -extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize); -extern int zvol_check_volblocksize(const char *name, uint64_t volblocksize); -extern int zvol_get_stats(objset_t *os, nvlist_t *nv); extern boolean_t zvol_is_zvol(const char *); -extern void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); extern int zvol_set_volsize(const char *, uint64_t); extern int zvol_set_volblocksize(const char *, uint64_t); extern int zvol_set_snapdev(const char *, zprop_source_t, uint64_t); diff --git a/include/uzfs_io.h b/include/uzfs_io.h new file mode 100644 index 000000000000..bb6031d5f14e --- /dev/null +++ b/include/uzfs_io.h @@ -0,0 +1,28 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _UZFS_IO_H +#define _UZFS_IO_H + +extern int uzfs_write_data(void *zv, char *buf, uint64_t offset, uint64_t len); +extern int uzfs_read_data(void *zv, char *buf, uint64_t offset, uint64_t len); + +#endif diff --git a/include/uzfs_mgmt.h b/include/uzfs_mgmt.h new file mode 100644 index 000000000000..a3b2991c0422 --- /dev/null +++ b/include/uzfs_mgmt.h @@ -0,0 +1,35 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _UZFS_MGMT_H +#define _UZFS_MGMT_H + +extern int uzfs_init(void); +extern int uzfs_create_pool(char *name, char *path, void **spa); +extern int uzfs_open_pool(char *name, void **spa); +extern int uzfs_vdev_add(void *spa, char *path, int ashift, int log); +extern int uzfs_create_dataset(void *spa, char *ds, uint64_t vol_size, + uint64_t block_size, int sync, void **zv); +extern int uzfs_open_dataset(void *spa, char *ds, int sync, void **zv); +extern void uzfs_close_pool(void *spa, void *zv); +extern void uzfs_fini(void); +extern uint64_t uzfs_random(uint64_t); +#endif diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am index 390069df4191..3a8099fd3e63 100644 --- a/lib/libzfs/Makefile.am +++ b/lib/libzfs/Makefile.am @@ -1,5 +1,9 @@ include $(top_srcdir)/config/Rules.am +VPATH = \ + $(top_srcdir)/module/zfs \ + $(top_srcdir)/lib/libzfs + libzfs_pcdir = $(datarootdir)/pkgconfig libzfs_pc_DATA = libzfs.pc libzfs_core.pc @@ -22,12 +26,14 @@ USER_C = \ libzfs_sendrecv.c \ libzfs_status.c \ libzfs_util.c \ - libuzfs_ioctl.c \ libuzfs_handle.c \ libuzfs_server.c \ libuzfs_gtest.c -KERNEL_C = +KERNEL_C = \ + zfs_ioctl.c \ + zfs_vfsops.c \ + zvol.c nodist_libzfs_la_SOURCES = \ $(USER_C) \ diff --git a/lib/libzfs/libuzfs_ioctl.c b/lib/libzfs/libuzfs_ioctl.c deleted file mode 100644 index 0cde16e6c3cf..000000000000 --- a/lib/libzfs/libuzfs_ioctl.c +++ /dev/null @@ -1,1597 +0,0 @@ -/* **************************************************************************** - * (C) Copyright 2017 CloudByte, Inc. - * All Rights Reserved. - * - * This program is an unpublished copyrighted work which is proprietary - * to CloudByte, Inc. and contains confidential information that is not - * to be reproduced or disclosed to any other person or entity without - * prior written consent from CloudByte, Inc. in each and every instance. - * - * WARNING: Unauthorized reproduction of this program as well as - * unauthorized preparation of derivative works based upon the - * program or distribution of copies by sale, rental, lease or - * lending are violations of federal copyright laws and state trade - * secret laws, punishable by civil and criminal penalties. - * - ****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zfs_fletcher.h" -#include "zfs_namecheck.h" -#include "zfs_comutil.h" -#include "zfs_prop.h" -#include "zfeature_common.h" - -static int -get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) -{ - char *packed; - int error; - nvlist_t *list = NULL; - - /* - * Read in and unpack the user-supplied nvlist. - */ - if (size == 0) - return EINVAL; - - packed = (void *) (uintptr_t) nvl; - - if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) - return (error); - - *nvp = list; - return (0); -} - -static int -put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) -{ - char *packed = NULL; - int error = 0; - size_t size; - - size = fnvlist_size(nvl); - - if (size > zc->zc_nvlist_dst_size) { - error = SET_ERROR(ENOMEM); - } else { - packed = fnvlist_pack(nvl, &size); - memcpy((void *) (uintptr_t) zc->zc_nvlist_dst, packed, size); - fnvlist_pack_free(packed, size); - } - - zc->zc_nvlist_dst_size = size; - zc->zc_nvlist_dst_filled = B_TRUE; - return (error); -} - -/* - * Return non-zero if the spa version is less than requested version. - */ -static int -zfs_earlier_version(const char *name, int version) -{ - spa_t *spa; - - if (spa_open(name, &spa, FTAG) == 0) { - if (spa_version(spa) < version) { - spa_close(spa, FTAG); - return (1); - } - spa_close(spa, FTAG); - } - return (0); -} - -static int -zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) -{ - int writable = 1; - - /* - * The dataset must be visible by this zone -- check this first - * so they don't see EPERM on something they shouldn't know about. - */ - if (!INGLOBALZONE(curproc) && - !zone_dataset_visible(dataset, &writable)) - return (SET_ERROR(ENOENT)); - - if (INGLOBALZONE(curproc)) { - /* - * If the fs is zoned, only root can access it from the - * global zone. - */ - if (secpolicy_zfs(cr) && zoned) - return (SET_ERROR(EPERM)); - } else { - /* - * If we are in a local zone, the 'zoned' property must be set. - */ - if (!zoned) - return (SET_ERROR(EPERM)); - - /* must be writable by this zone */ - if (!writable) - return (SET_ERROR(EPERM)); - } - return (0); -} - -static int -zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) -{ - uint64_t zoned; - - if (dsl_prop_get_int_ds(ds, "zoned", &zoned)) - return (SET_ERROR(ENOENT)); - - return (zfs_dozonecheck_impl(dataset, zoned, cr)); -} - -static int -zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, - const char *perm, cred_t *cr) -{ - int error; - - error = zfs_dozonecheck_ds(name, ds, cr); - if (error == 0) { - error = secpolicy_zfs(cr); - if (error != 0) - error = dsl_deleg_access_impl(ds, perm, cr); - } - return (error); -} - -static int -zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) -{ - int error; - dsl_dataset_t *ds; - dsl_pool_t *dp; - - /* - * First do a quick check for root in the global zone, which - * is allowed to do all write_perms. This ensures that zfs_ioc_* - * will get to handle nonexistent datasets. - */ - if (INGLOBALZONE(curproc) && secpolicy_zfs(cr) == 0) - return (0); - - error = dsl_pool_hold(name, FTAG, &dp); - if (error != 0) - return (error); - - error = dsl_dataset_hold(dp, name, FTAG, &ds); - if (error != 0) { - dsl_pool_rele(dp, FTAG); - return (error); - } - - error = zfs_secpolicy_write_perms_ds(name, ds, perm, cr); - - dsl_dataset_rele(ds, FTAG); - dsl_pool_rele(dp, FTAG); - return (error); -} - -/* - * Check that all the properties are valid user properties. - */ -static int -zfs_check_userprops(const char *fsname, nvlist_t *nvl) -{ - nvpair_t *pair = NULL; - int error = 0; - - while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { - const char *propname = nvpair_name(pair); - - if (!zfs_prop_user(propname) || - nvpair_type(pair) != DATA_TYPE_STRING) - return (SET_ERROR(EINVAL)); - - if ((error = zfs_secpolicy_write_perms(fsname, - ZFS_DELEG_PERM_USERPROP, - CRED()))) - return (error); - - if (strlen(propname) >= ZAP_MAXNAMELEN) - return (SET_ERROR(ENAMETOOLONG)); - - if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN) - return (SET_ERROR(E2BIG)); - } - return (0); -} - -static boolean_t -dataset_name_hidden(const char *name) -{ - /* - * Skip over datasets that are not visible in this zone, - * internal datasets (which have a $ in their name), and - * temporary datasets (which have a % in their name). - */ - if (strchr(name, '$') != NULL) - return (B_TRUE); - if (strchr(name, '%') != NULL) - return (B_TRUE); - if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) - return (B_TRUE); - return (B_FALSE); -} - -void -zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) -{ - zfs_creat_t *zct = arg; - nvlist_t *nvprops = zct->zct_props; - int error; - uint64_t volblocksize, volsize; - - VERIFY(nvlist_lookup_uint64(nvprops, - zfs_prop_to_name(ZFS_PROP_VOLSIZE), - &volsize) == 0); - if (nvlist_lookup_uint64(nvprops, - zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), - &volblocksize) != 0) - volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); - - /* - * These properties must be removed from the list so the generic - * property setting step won't apply to them. - */ - VERIFY(nvlist_remove_all(nvprops, - zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0); - (void) nvlist_remove_all(nvprops, - zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE)); - - error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, volblocksize, - DMU_OT_NONE, 0, tx); - ASSERT(error == 0); - - error = zap_create_claim(os, ZVOL_ZAP_OBJ, DMU_OT_ZVOL_PROP, - DMU_OT_NONE, 0, tx); - ASSERT(error == 0); - - error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize, tx); - ASSERT(error == 0); -} - -#define ZFS_PROP_UNDEFINED ((uint64_t)-1) - -int -zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value) -{ - const char *pname; - int error = SET_ERROR(ENOENT); - - /* - * Look up the file system's value for the property. For the - * version property, we look up a slightly different string. - */ - if (prop == ZFS_PROP_VERSION) - pname = ZPL_VERSION_STR; - else - pname = zfs_prop_to_name(prop); - - if (os != NULL) { - ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS); - error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value); - } - - if (error == ENOENT) { - /* No value set, use the default value */ - switch (prop) { - case ZFS_PROP_VERSION: - *value = ZPL_VERSION; - break; - case ZFS_PROP_NORMALIZE: - case ZFS_PROP_UTF8ONLY: - *value = 0; - break; - case ZFS_PROP_CASE: - *value = ZFS_CASE_SENSITIVE; - break; - case ZFS_PROP_ACLTYPE: - *value = ZFS_ACLTYPE_OFF; - break; - default: - return (error); - } - error = 0; - } - return (error); -} - -static int -zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, - boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, - nvlist_t *zplprops, boolean_t *is_ci) -{ - uint64_t sense = ZFS_PROP_UNDEFINED; - uint64_t norm = ZFS_PROP_UNDEFINED; - uint64_t u8 = ZFS_PROP_UNDEFINED; - int error; - - ASSERT(zplprops != NULL); - - if (os != NULL && os->os_phys->os_type != DMU_OST_ZFS) - return (SET_ERROR(EINVAL)); - - /* - * Pull out creator prop choices, if any. - */ - if (createprops) { - (void) nvlist_lookup_uint64(createprops, - zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); - (void) nvlist_lookup_uint64(createprops, - zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); - (void) nvlist_remove_all(createprops, - zfs_prop_to_name(ZFS_PROP_NORMALIZE)); - (void) nvlist_lookup_uint64(createprops, - zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); - (void) nvlist_remove_all(createprops, - zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); - (void) nvlist_lookup_uint64(createprops, - zfs_prop_to_name(ZFS_PROP_CASE), &sense); - (void) nvlist_remove_all(createprops, - zfs_prop_to_name(ZFS_PROP_CASE)); - } - - /* - * If the zpl version requested is whacky or the file system - * or pool is version is too "young" to support normalization - * and the creator tried to set a value for one of the props, - * error out. - */ - if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || - (zplver >= ZPL_VERSION_FUID && !fuids_ok) || - (zplver >= ZPL_VERSION_SA && !sa_ok) || - (zplver < ZPL_VERSION_NORMALIZATION && - (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || - sense != ZFS_PROP_UNDEFINED))) - return (SET_ERROR(ENOTSUP)); - - /* - * Put the version in the zplprops - */ - VERIFY(nvlist_add_uint64(zplprops, - zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); - - if (norm == ZFS_PROP_UNDEFINED && - (error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm)) != 0) - return (error); - VERIFY(nvlist_add_uint64(zplprops, - zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); - - /* - * If we're normalizing, names must always be valid UTF-8 strings. - */ - if (norm) - u8 = 1; - if (u8 == ZFS_PROP_UNDEFINED && - (error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8)) != 0) - return (error); - VERIFY(nvlist_add_uint64(zplprops, - zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); - - if (sense == ZFS_PROP_UNDEFINED && - (error = zfs_get_zplprop(os, ZFS_PROP_CASE, &sense)) != 0) - return (error); - VERIFY(nvlist_add_uint64(zplprops, - zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); - - if (is_ci) - *is_ci = (sense == ZFS_CASE_INSENSITIVE); - - return (0); -} - -static int -zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, - nvlist_t *zplprops, boolean_t *is_ci) -{ - boolean_t fuids_ok; - boolean_t sa_ok; - uint64_t zplver = ZPL_VERSION; - int error; - - zplver = zfs_zpl_version_map(spa_vers); - fuids_ok = (zplver >= ZPL_VERSION_FUID); - sa_ok = (zplver >= ZPL_VERSION_SA); - - error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, - createprops, zplprops, is_ci); - return (error); -} - -static int -uzfs_ioc_pool_create(zfs_cmd_t *zc) -{ - int error; - nvlist_t *config, *props = NULL; - nvlist_t *rootprops = NULL; - nvlist_t *zplprops = NULL; - - if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, - zc->zc_iflags, &config))) - return (error); - - if (zc->zc_nvlist_src_size != 0 && - (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, - zc->zc_iflags, &props))) { - nvlist_free(config); - return (error); - } - - if (props) { - nvlist_t *nvl = NULL; - uint64_t version = SPA_VERSION; - - (void) nvlist_lookup_uint64(props, zpool_prop_to_name( - ZPOOL_PROP_VERSION), - &version); - if (!SPA_VERSION_IS_SUPPORTED(version)) { - error = SET_ERROR(EINVAL); - goto pool_props_bad; - } - (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); - if (nvl) { - error = nvlist_dup(nvl, &rootprops, KM_SLEEP); - if (error != 0) { - nvlist_free(config); - nvlist_free(props); - return (error); - } - (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); - } - VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); - error = - zfs_fill_zplprops_root(version, rootprops, zplprops, NULL); - if (error != 0) - goto pool_props_bad; - } - - error = spa_create(zc->zc_name, config, props, NULL); - - /* - * Set the remaining root properties - */ - if (!error && - (error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL, - rootprops, NULL)) != 0) - (void) spa_destroy(zc->zc_name); - -pool_props_bad: - nvlist_free(rootprops); - nvlist_free(zplprops); - nvlist_free(config); - nvlist_free(props); - - return (error); -} - -static int -uzfs_ioc_pool_import(zfs_cmd_t *zc) -{ - nvlist_t *config, *props = NULL; - uint64_t guid; - int error; - - if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, - zc->zc_iflags, &config)) != 0) - return (error); - - if (zc->zc_nvlist_src_size != 0 && - (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, - zc->zc_iflags, &props))) { - nvlist_free(config); - return (error); - } - - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || - guid != zc->zc_guid) - error = SET_ERROR(EINVAL); - else - error = spa_import(zc->zc_name, config, props, zc->zc_cookie); - - if (zc->zc_nvlist_dst != 0) { - int err; - - if ((err = put_nvlist(zc, config)) != 0) - error = err; - } - - nvlist_free(config); - nvlist_free(props); - - return (error); -} - -static int -uzfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) -{ - nvlist_t *snaps; - nvlist_t *props = NULL; - int error, poollen; - nvpair_t *pair, *pair2; - - (void) nvlist_lookup_nvlist(innvl, "props", &props); - if ((error = zfs_check_userprops(poolname, props)) != 0) - return (error); - - if (!nvlist_empty(props) && - zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) - return (SET_ERROR(ENOTSUP)); - - if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) - return (SET_ERROR(EINVAL)); - poollen = strlen(poolname); - for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; - pair = nvlist_next_nvpair(snaps, pair)) { - const char *name = nvpair_name(pair); - const char *cp = strchr(name, '@'); - - /* - * The snap name must contain an @, and the part after it must - * contain only valid characters. - */ - if (cp == NULL || - zfs_component_namecheck(cp + 1, NULL, NULL) != 0) - return (SET_ERROR(EINVAL)); - - /* - * The snap must be in the specified pool. - */ - if (strncmp(name, poolname, poollen) != 0 || - (name[poollen] != '/' && name[poollen] != '@')) - return (SET_ERROR(EXDEV)); - - /* This must be the only snap of this fs. */ - for (pair2 = nvlist_next_nvpair(snaps, pair); pair2 != NULL; - pair2 = nvlist_next_nvpair(snaps, pair2)) { - if (strncmp(name, nvpair_name(pair2), cp - name + 1) == - 0) { - return (SET_ERROR(EXDEV)); - } - } - } - - error = dsl_dataset_snapshot(snaps, props, outnvl); - - return (error); -} - -static int -zvol_get_stats(objset_t *os, nvlist_t *nv) -{ - int error; - dmu_object_info_t *doi; - uint64_t val; - - error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &val); - if (error) - return (SET_ERROR(error)); - - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VOLSIZE, val); - doi = kmem_alloc(sizeof(dmu_object_info_t), KM_SLEEP); - error = dmu_object_info(os, ZVOL_OBJ, doi); - - if (error == 0) { - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VOLBLOCKSIZE, - doi->doi_data_block_size); - } - - kmem_free(doi, sizeof(dmu_object_info_t)); - - return (SET_ERROR(error)); -} - -static int -uzfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) -{ - int error = 0; - nvlist_t *nv; - - dmu_objset_fast_stat(os, &zc->zc_objset_stats); - - if (zc->zc_nvlist_dst != 0 && - (error = dsl_prop_get_all(os, &nv)) == 0) { - dmu_objset_stats(os, nv); - /* - * NB: zvol_get_stats() will read the objset contents, - * which we aren't supposed to do with a - * DS_MODE_USER hold, because it could be - * inconsistent. So this is a bit of a workaround... - * XXX reading with out owning - */ - if (!zc->zc_objset_stats.dds_inconsistent && - dmu_objset_type(os) == DMU_OST_ZVOL) { - error = zvol_get_stats(os, nv); - if (error == EIO) { - nvlist_free(nv); - return (error); - } - VERIFY0(error); - } - if (error == 0) - error = put_nvlist(zc, nv); - nvlist_free(nv); - } - - return (error); -} - -static int -uzfs_ioc_objset_stats(zfs_cmd_t *zc) -{ - objset_t *os; - int error; - - error = dmu_objset_hold(zc->zc_name, FTAG, &os); - if (error == 0) { - error = uzfs_ioc_objset_stats_impl(zc, os); - dmu_objset_rele(os, FTAG); - } - - return (error); -} - -static int -uzfs_ioc_snapshot_list_next(zfs_cmd_t *zc) -{ - objset_t *os; - int error; - - error = dmu_objset_hold(zc->zc_name, FTAG, &os); - if (error != 0) { - return (error == ENOENT ? ESRCH : error); - } - - /* - * A dataset name of maximum length cannot have any snapshots, - * so exit immediately. - */ - if (strlcat(zc->zc_name, "@", sizeof(zc->zc_name)) >= - ZFS_MAX_DATASET_NAME_LEN) { - dmu_objset_rele(os, FTAG); - return (SET_ERROR(ESRCH)); - } - - error = dmu_snapshot_list_next(os, sizeof(zc->zc_name) - - strlen(zc->zc_name), - zc->zc_name + strlen(zc->zc_name), - &zc->zc_obj, &zc->zc_cookie, NULL); - - if (error == 0 && !zc->zc_simple) { - dsl_dataset_t *ds; - dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; - - error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); - if (error == 0) { - objset_t *ossnap; - - error = dmu_objset_from_ds(ds, &ossnap); - if (error == 0) - error = uzfs_ioc_objset_stats_impl(zc, ossnap); - dsl_dataset_rele(ds, FTAG); - } - } else if (error == ENOENT) { - error = SET_ERROR(ESRCH); - } - - dmu_objset_rele(os, FTAG); - /* if we failed, undo the @ that we tacked on to zc_name */ - if (error != 0) - *strchr(zc->zc_name, '@') = '\0'; - return (error); -} - -static int -uzfs_ioc_pool_stats(zfs_cmd_t *zc) -{ - nvlist_t *config; - int error; - int ret = 0; - - error = spa_get_stats(zc->zc_name, &config, zc->zc_value, - sizeof(zc->zc_value)); - - if (config != NULL) { - ret = put_nvlist(zc, config); - nvlist_free(config); - - /* - * The config may be present even if 'error' is non-zero. - * In this case we return success, and preserve the real errno - * in 'zc_cookie'. - */ - zc->zc_cookie = error; - } else { - ret = error; - } - - return (ret); -} - -static int -uzfs_ioc_pool_tryimport(zfs_cmd_t *zc) -{ - nvlist_t *tryconfig, *config = NULL; - int error; - - if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, - zc->zc_iflags, &tryconfig)) != 0) - return (error); - - config = spa_tryimport(tryconfig); - - nvlist_free(tryconfig); - - if (config == NULL) - return (SET_ERROR(EINVAL)); - - error = put_nvlist(zc, config); - nvlist_free(config); - - return (error); -} - -/* - * Sanity check volume block size. - */ -int -zvol_check_volblocksize(const char *name, uint64_t volblocksize) -{ - /* Record sizes above 128k need the feature to be enabled */ - if (volblocksize > SPA_OLD_MAXBLOCKSIZE) { - spa_t *spa; - int error; - - if ((error = spa_open(name, &spa, FTAG)) != 0) - return (error); - - if (!spa_feature_is_enabled(spa, SPA_FEATURE_LARGE_BLOCKS)) { - spa_close(spa, FTAG); - return (SET_ERROR(ENOTSUP)); - } - - /* - * We don't allow setting the property above 1MB, - * unless the tunable has been changed. - */ - if (volblocksize > zfs_max_recordsize) - return (SET_ERROR(EDOM)); - - spa_close(spa, FTAG); - } - - if (volblocksize < SPA_MINBLOCKSIZE || - volblocksize > SPA_MAXBLOCKSIZE || !ISP2(volblocksize)) - return (SET_ERROR(EDOM)); - - return (0); -} - -/* - * Sanity check volume size. - */ -int -zvol_check_volsize(uint64_t volsize, uint64_t blocksize) -{ - if (volsize == 0) - return (SET_ERROR(EINVAL)); - - if (volsize % blocksize != 0) - return (SET_ERROR(EINVAL)); - -#ifdef _ILP32 - if (volsize - 1 > SPEC_MAXOFFSET_T) - return (SET_ERROR(EOVERFLOW)); -#endif - return (0); -} - -int -zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, - nvlist_t *errlist) -{ - nvpair_t *pair; - nvpair_t *propval; - int rv = 0; - uint64_t intval; - char *strval; - - nvlist_t *genericnvl = fnvlist_alloc(); - nvlist_t *retrynvl = fnvlist_alloc(); -retry: - pair = NULL; - while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { - const char *propname = nvpair_name(pair); - zfs_prop_t prop = zfs_name_to_prop(propname); - int err = 0; - - /* decode the property value */ - propval = pair; - if (nvpair_type(pair) == DATA_TYPE_NVLIST) { - nvlist_t *attrs; - attrs = fnvpair_value_nvlist(pair); - if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, - &propval) != 0) - err = SET_ERROR(EINVAL); - } - - /* Validate value type */ - if (err == 0 && source == ZPROP_SRC_INHERITED) { - /* inherited properties are expected to be booleans */ - if (nvpair_type(propval) != DATA_TYPE_BOOLEAN) - err = SET_ERROR(EINVAL); - } else if (err == 0 && prop == ZPROP_INVAL) { - if (zfs_prop_user(propname)) { - if (nvpair_type(propval) != DATA_TYPE_STRING) - err = SET_ERROR(EINVAL); - } else if (zfs_prop_userquota(propname)) { - if (nvpair_type(propval) != - DATA_TYPE_UINT64_ARRAY) - err = SET_ERROR(EINVAL); - } else { - err = SET_ERROR(EINVAL); - } - } else if (err == 0) { - if (nvpair_type(propval) == DATA_TYPE_STRING) { - if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) - err = SET_ERROR(EINVAL); - } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { - const char *unused; - - intval = fnvpair_value_uint64(propval); - - switch (zfs_prop_get_type(prop)) { - case PROP_TYPE_NUMBER: - break; - case PROP_TYPE_STRING: - err = SET_ERROR(EINVAL); - break; - case PROP_TYPE_INDEX: - if (zfs_prop_index_to_string(prop, - intval, &unused) != 0) - err = SET_ERROR(EINVAL); - break; - default: - cmn_err(CE_PANIC, - "unknown property type"); - } - } else { - err = SET_ERROR(EINVAL); - } - } - -#ifdef _FIX_ME - /* Validate permissions */ - if (err == 0) - err = zfs_check_settable(dsname, pair, CRED()); -#endif - if (err == 0) { - if (source == ZPROP_SRC_INHERITED) - err = -1; /* does not need special handling */ - else -#ifdef _FIX_ME - err = zfs_prop_set_special(dsname, source, pair); -#else - err = -1; -#endif - if (err == -1) { - /* - * For better performance we build up a list of - * properties to set in a single transaction. - */ - err = nvlist_add_nvpair(genericnvl, pair); - } else if (err != 0 && nvl != retrynvl) { - /* - * This may be a spurious error caused by - * receiving quota and reservation out of order. - * Try again in a second pass. - */ - err = nvlist_add_nvpair(retrynvl, pair); - } - } - - if (err != 0) { - if (errlist != NULL) - fnvlist_add_int32(errlist, propname, err); - rv = err; - } - } - - if (nvl != retrynvl && !nvlist_empty(retrynvl)) { - nvl = retrynvl; - goto retry; - } - - if (!nvlist_empty(genericnvl) && - dsl_props_set(dsname, source, genericnvl) != 0) { - /* - * If this fails, we still want to set as many properties as we - * can, so try setting them individually. - */ - pair = NULL; - while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { - const char *propname = nvpair_name(pair); - int err = 0; - - propval = pair; - if (nvpair_type(pair) == DATA_TYPE_NVLIST) { - nvlist_t *attrs; - attrs = fnvpair_value_nvlist(pair); - propval = fnvlist_lookup_nvpair(attrs, - ZPROP_VALUE); - } - - if (nvpair_type(propval) == DATA_TYPE_STRING) { - strval = fnvpair_value_string(propval); - err = dsl_prop_set_string(dsname, propname, - source, strval); - } else if (nvpair_type(propval) == DATA_TYPE_BOOLEAN) { - err = dsl_prop_inherit(dsname, propname, - source); - } else { - intval = fnvpair_value_uint64(propval); - err = dsl_prop_set_int(dsname, propname, source, - intval); - } - - if (err != 0) { - if (errlist != NULL) { - fnvlist_add_int32(errlist, propname, - err); - } - rv = err; - } - } - } - nvlist_free(genericnvl); - nvlist_free(retrynvl); - - return (rv); -} - -static int -uzfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) -{ - int error = 0; - zfs_creat_t zct = {0}; - nvlist_t *nvprops = NULL; - void (*cbfunc)(objset_t * os, void *arg, cred_t *cr, dmu_tx_t *tx); - int32_t type32; - dmu_objset_type_t type; - - if (nvlist_lookup_int32(innvl, "type", &type32) != 0) - return (SET_ERROR(EINVAL)); - type = type32; - (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); - - switch (type) { - case DMU_OST_ZVOL: - cbfunc = zvol_create_cb; - break; - - default: - cbfunc = NULL; - break; - } - if (strchr(fsname, '@') || strchr(fsname, '%')) - return (SET_ERROR(EINVAL)); - - zct.zct_props = nvprops; - - if (cbfunc == NULL) - return (SET_ERROR(EINVAL)); - - if (type == DMU_OST_ZVOL) { - uint64_t volsize, volblocksize; - - if (nvprops == NULL) - return (SET_ERROR(EINVAL)); - if (nvlist_lookup_uint64(nvprops, - zfs_prop_to_name(ZFS_PROP_VOLSIZE), - &volsize) != 0) - return (SET_ERROR(EINVAL)); - - if ((error = nvlist_lookup_uint64(nvprops, - zfs_prop_to_name( - ZFS_PROP_VOLBLOCKSIZE), - &volblocksize)) != 0 && - error != ENOENT) - return (SET_ERROR(EINVAL)); - - if (error != 0) - volblocksize = - zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); - - if ((error = zvol_check_volblocksize(fsname, volblocksize)) != - 0 || - (error = zvol_check_volsize(volsize, volblocksize)) != 0) - return (error); - } - - error = dmu_objset_create(fsname, type, 0, cbfunc, &zct); - - /* - * It would be nice to do this atomically. - */ - if (error == 0) { - error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, nvprops, - outnvl); - if (error != 0) { - spa_t *spa; - int error2; - - /* - * Volumes will return EBUSY and cannot be destroyed - * until all asynchronous minor handling has completed. - * Wait for the spa_zvol_taskq to drain then retry. - */ - error2 = dsl_destroy_head(fsname); - while ((error2 == EBUSY) && (type == DMU_OST_ZVOL)) { - error2 = spa_open(fsname, &spa, FTAG); - if (error2 == 0) { - taskq_wait(spa->spa_zvol_taskq); - spa_close(spa, FTAG); - } - error2 = dsl_destroy_head(fsname); - } - } - } - return (error); -} - -static int -uzfs_ioc_pool_configs(zfs_cmd_t *zc) -{ - nvlist_t *configs; - int error; - - if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) - return (SET_ERROR(EEXIST)); - - error = put_nvlist(zc, configs); - - nvlist_free(configs); - - return (error); -} - -static int -uzfs_ioc_dataset_list_next(zfs_cmd_t *zc) -{ - objset_t *os; - int error; - char *p; - size_t orig_len = strlen(zc->zc_name); - -top: - if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os))) { - if (error == ENOENT) - error = SET_ERROR(ESRCH); - return (error); - } - - p = strrchr(zc->zc_name, '/'); - if (p == NULL || p[1] != '\0') - (void) strlcat(zc->zc_name, "/", sizeof(zc->zc_name)); - p = zc->zc_name + strlen(zc->zc_name); - - do { - error = dmu_dir_list_next(os, sizeof(zc->zc_name) - - (p - zc->zc_name), - p, NULL, &zc->zc_cookie); - if (error == ENOENT) - error = SET_ERROR(ESRCH); - } while (error == 0 && dataset_name_hidden(zc->zc_name)); - dmu_objset_rele(os, FTAG); - - /* - * If it's an internal dataset (ie. with a '$' in its name), - * don't try to get stats for it, otherwise we'll return ENOENT. - */ - if (error == 0 && strchr(zc->zc_name, '$') == NULL) { - error = uzfs_ioc_objset_stats(zc); /* fill in the stats */ - if (error == ENOENT) { - /* We lost a race with destroy, get the next one. */ - zc->zc_name[orig_len] = '\0'; - goto top; - } - } - return (error); -} - -static int -uzfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) -{ - return (dsl_get_bookmarks(fsname, innvl, outnvl)); -} - -static int -uzfs_ioc_pool_get_props(zfs_cmd_t *zc) -{ - spa_t *spa; - int error; - nvlist_t *nvp = NULL; - - if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { - /* - * If the pool is faulted, there may be properties we can still - * get (such as altroot and cachefile), so attempt to get them - * anyway. - */ - mutex_enter(&spa_namespace_lock); - if ((spa = spa_lookup(zc->zc_name)) != NULL) - error = spa_prop_get(spa, &nvp); - mutex_exit(&spa_namespace_lock); - } else { - error = spa_prop_get(spa, &nvp); - spa_close(spa, FTAG); - } - - if (error == 0 && zc->zc_nvlist_dst != 0) - error = put_nvlist(zc, nvp); - else - error = SET_ERROR(EFAULT); - - nvlist_free(nvp); - return (error); -} - -/* - * Write out a history event. - */ -int -spa_history_log(spa_t *spa, const char *msg) -{ - int err; - nvlist_t *nvl = fnvlist_alloc(); - - fnvlist_add_string(nvl, ZPOOL_HIST_CMD, msg); - err = spa_history_log_nvl(spa, nvl); - fnvlist_free(nvl); - return (err); -} - -static char * -history_str_get(zfs_cmd_t *zc) -{ - char *buf; - - if (zc->zc_history == 0) - return (NULL); - - buf = (char *) zc->zc_history; - - return (buf); -} - -static void -zfs_log_history(zfs_cmd_t *zc) -{ - spa_t *spa; - char *buf; - - if ((buf = history_str_get(zc)) == NULL) - return; - - if (spa_open(zc->zc_name, &spa, FTAG) == 0) { - if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) - (void) spa_history_log(spa, buf); - spa_close(spa, FTAG); - } -} - -static int -uzfs_ioc_pool_export(zfs_cmd_t *zc) -{ - int error; - boolean_t force = (boolean_t) zc->zc_cookie; - boolean_t hardforce = (boolean_t) zc->zc_guid; - zfs_log_history(zc); - error = spa_export(zc->zc_name, NULL, force, hardforce); - - return (error); -} - -static int -uzfs_ioc_pool_get_history(zfs_cmd_t *zc) -{ - spa_t *spa; - char *hist_buf; - uint64_t size; - int error; - - if ((size = zc->zc_history_len) == 0) - return (SET_ERROR(EINVAL)); - - if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) - return (error); - - if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { - spa_close(spa, FTAG); - return (SET_ERROR(ENOTSUP)); - } - - hist_buf = (char *) zc->zc_history; - error = spa_history_get(spa, &zc->zc_history_offset, - &zc->zc_history_len, hist_buf); - - spa_close(spa, FTAG); - return (error); -} - -static int -uzfs_ioc_log_history(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) -{ - char *message; - spa_t *spa; - int error; - - if (poolname == NULL) - return (SET_ERROR(EINVAL)); - error = spa_open(poolname, &spa, FTAG); - if (error != 0) - return (error); - - if (nvlist_lookup_string(innvl, "message", &message) != 0) { - spa_close(spa, FTAG); - return (SET_ERROR(EINVAL)); - } - - if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { - spa_close(spa, FTAG); - return (SET_ERROR(ENOTSUP)); - } - - error = spa_history_log(spa, message); - spa_close(spa, FTAG); - return (error); -} - -static int -uzfs_ioc_pool_destroy(zfs_cmd_t *zc) -{ - int error; - zfs_log_history(zc); - error = spa_destroy(zc->zc_name); - - return (error); -} - -static int -uzfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) -{ - nvlist_t *snaps; - boolean_t defer; - - if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) - return (SET_ERROR(EINVAL)); - defer = nvlist_exists(innvl, "defer"); - - return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); -} - -static int -uzfs_ioc_destroy(zfs_cmd_t *zc) -{ - int err; - - if (strchr(zc->zc_name, '@')) { - err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); - } else { - err = dsl_destroy_head(zc->zc_name); - if (err == EEXIST) { - /* - * It is possible that the given DS may have - * hidden child (%recv) datasets - "leftovers" - * resulting from the previously interrupted - * 'zfs receive'. - * - * 6 extra bytes for /%recv - */ - char namebuf[ZFS_MAX_DATASET_NAME_LEN + 6]; - - if (snprintf(namebuf, sizeof (namebuf), "%s/%s", - zc->zc_name, recv_clone_name) >= - sizeof (namebuf)) - return (SET_ERROR(EINVAL)); - - /* - * Try to remove the hidden child (%recv) and after - * that try to remove the target dataset. - * If the hidden child (%recv) does not exist - * the original error (EEXIST) will be returned - */ - err = dsl_destroy_head(namebuf); - if (err == 0) - err = dsl_destroy_head(zc->zc_name); - else if (err == ENOENT) - err = EEXIST; - } - } - - return (err); -} - -static int -uzfs_ioc_pool_set_props(zfs_cmd_t *zc) -{ - nvlist_t *props; - spa_t *spa; - int error; - nvpair_t *pair; - - if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, - zc->zc_iflags, &props))) - return (error); - - /* - * If the only property is the configfile, then just do a spa_lookup() - * to handle the faulted case. - */ - pair = nvlist_next_nvpair(props, NULL); - if (pair != NULL && strcmp(nvpair_name(pair), - zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && - nvlist_next_nvpair(props, pair) == NULL) { - mutex_enter(&spa_namespace_lock); - if ((spa = spa_lookup(zc->zc_name)) != NULL) { - spa_configfile_set(spa, props, B_FALSE); - spa_config_sync(spa, B_FALSE, B_TRUE); - } - mutex_exit(&spa_namespace_lock); - if (spa != NULL) { - nvlist_free(props); - return (0); - } - } - - if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { - nvlist_free(props); - return (error); - } - - error = spa_prop_set(spa, props); - - nvlist_free(props); - spa_close(spa, FTAG); - - return (error); -} - -static void -props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) -{ - nvpair_t *pair; - - VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); - - pair = NULL; - while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { - if (nvlist_exists(skipped, nvpair_name(pair))) - continue; - - VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); - } -} - -static int -clear_received_props(const char *dsname, nvlist_t *props, - nvlist_t *skipped) -{ - int err = 0; - nvlist_t *cleared_props = NULL; - props_skip(props, skipped, &cleared_props); - if (!nvlist_empty(cleared_props)) { - /* - * Acts on local properties until the dataset has received - * properties at least once on or after SPA_VERSION_RECVD_PROPS. - */ - zprop_source_t flags = (ZPROP_SRC_NONE | - (dsl_prop_get_hasrecvd(dsname) ? ZPROP_SRC_RECEIVED : 0)); - err = zfs_set_prop_nvlist(dsname, flags, cleared_props, NULL); - } - nvlist_free(cleared_props); - return (err); -} - -static int -uzfs_ioc_set_prop(zfs_cmd_t *zc) -{ - nvlist_t *nvl; - boolean_t received = zc->zc_cookie; - zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : - ZPROP_SRC_LOCAL); - nvlist_t *errors; - int error; - - if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, - zc->zc_iflags, &nvl)) != 0) - return (error); - - if (received) { - nvlist_t *origprops; - - if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) { - (void) clear_received_props(zc->zc_name, - origprops, nvl); - nvlist_free(origprops); - } - - error = dsl_prop_set_hasrecvd(zc->zc_name); - } - - errors = fnvlist_alloc(); - if (error == 0) - error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors); - - if (zc->zc_nvlist_dst != 0 && errors != NULL) { - (void) put_nvlist(zc, errors); - } - - nvlist_free(errors); - nvlist_free(nvl); - return (error); -} - -int -uzfs_ioctl(int fd, unsigned long request, zfs_cmd_t *zc) -{ -#ifndef _UZFS - return ioctl(fd, request, zc); -#else - if (uzfs_send_ioctl(fd, request, zc)) { - fprintf(stderr, "ioctl send failed\n"); - exit(1); - } - - int ret = uzfs_recv_response(fd, zc); - - int err = (ret < 0 ? errno : ret); - - if (err) - return SET_ERR(err); - - return 0; -#endif -} - -int -uzfs_handle_ioctl(const char *pool, uint64_t request, zfs_cmd_t *zc) -{ - int err; - nvlist_t *innvl = NULL; - if (zc->zc_nvlist_src_size > MAX_NVLIST_SRC_SIZE) { - /* - * Make sure the user doesn't pass in an insane value for - * zc_nvlist_src_size. We have to check, since we will end - * up allocating that much memory inside of get_nvlist(). This - * prevents a nefarious user from allocating tons of kernel - * memory. - * - * Also, we return EINVAL instead of ENOMEM here. The reason - * being that returning ENOMEM from an ioctl() has a special - * connotation; that the user's size value is too small and - * needs to be expanded to hold the nvlist. See - * zcmd_expand_dst_nvlist() for details. - */ - return (EINVAL); /* User's size too big */ - } else if (zc->zc_nvlist_src_size != 0) { - err = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 0, - &innvl); - if (err != 0) - return err; - } - - err = ENOTSUP; - switch (request) { - case ZFS_IOC_OBJSET_STATS: - return (uzfs_ioc_objset_stats(zc)); - case ZFS_IOC_POOL_CREATE: - return (uzfs_ioc_pool_create(zc)); - case ZFS_IOC_POOL_IMPORT: - return (uzfs_ioc_pool_import(zc)); - case ZFS_IOC_POOL_STATS: - return (uzfs_ioc_pool_stats(zc)); - case ZFS_IOC_POOL_TRYIMPORT: - return (uzfs_ioc_pool_tryimport(zc)); - case ZFS_IOC_CREATE: { - nvlist_t *outnvl = fnvlist_alloc(); - - err = uzfs_ioc_create(zc->zc_name, innvl, outnvl); - if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) - err = put_nvlist(zc, outnvl); - nvlist_free(outnvl); - - return (err); - } - case ZFS_IOC_POOL_CONFIGS: - return (uzfs_ioc_pool_configs(zc)); - case ZFS_IOC_DATASET_LIST_NEXT: - return (uzfs_ioc_dataset_list_next(zc)); - case ZFS_IOC_GET_BOOKMARKS: { - nvlist_t *outnvl = fnvlist_alloc(); - err = uzfs_ioc_get_bookmarks(zc->zc_name, innvl, outnvl); - if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) - err = put_nvlist(zc, outnvl); - nvlist_free(outnvl); - - return (err); - } - case ZFS_IOC_POOL_GET_PROPS: - return (uzfs_ioc_pool_get_props(zc)); - case ZFS_IOC_POOL_EXPORT: - return (uzfs_ioc_pool_export(zc)); - case ZFS_IOC_POOL_GET_HISTORY: - return (uzfs_ioc_pool_get_history(zc)); - case ZFS_IOC_LOG_HISTORY: { - nvlist_t *outnvl = fnvlist_alloc(); - - err = uzfs_ioc_log_history(pool, innvl, outnvl); - if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) - err = put_nvlist(zc, outnvl); - - nvlist_free(outnvl); - return (err); - } - case ZFS_IOC_SNAPSHOT: { - nvlist_t *outnvl = fnvlist_alloc(); - - err = uzfs_ioc_snapshot(zc->zc_name, innvl, outnvl); - if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) - err = put_nvlist(zc, outnvl); - - nvlist_free(outnvl); - return (err); - } - case ZFS_IOC_SNAPSHOT_LIST_NEXT: - return (uzfs_ioc_snapshot_list_next(zc)); - case ZFS_IOC_POOL_DESTROY: - return (uzfs_ioc_pool_destroy(zc)); - case ZFS_IOC_DESTROY_SNAPS: { - nvlist_t *outnvl = fnvlist_alloc(); - - err = uzfs_ioc_destroy_snaps(zc->zc_name, innvl, outnvl); - if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) - err = put_nvlist(zc, outnvl); - - nvlist_free(outnvl); - return (err); - } - case ZFS_IOC_DESTROY: - return (uzfs_ioc_destroy(zc)); - case ZFS_IOC_POOL_SET_PROPS: - return (uzfs_ioc_pool_set_props(zc)); - case ZFS_IOC_SET_PROP: - return (uzfs_ioc_set_prop(zc)); - } - return err; -} diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 7b7446fc2701..4de4860e0365 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -16,7 +16,9 @@ lib_LTLIBRARIES = libzpool.la USER_C = \ kernel.c \ taskq.c \ - util.c + util.c \ + uzfs_io.c \ + uzfs_mgmt.c KERNEL_C = \ zfs_comutil.c \ @@ -129,7 +131,8 @@ KERNEL_C = \ zio_compress.c \ zio_inject.c \ zle.c \ - zrlock.c + zrlock.c \ + zvol.c nodist_libzpool_la_SOURCES = \ $(USER_C) \ diff --git a/lib/libzpool/uzfs_io.c b/lib/libzpool/uzfs_io.c new file mode 100644 index 000000000000..cb259237d018 --- /dev/null +++ b/lib/libzpool/uzfs_io.c @@ -0,0 +1,117 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include + +/* Writes data 'buf' to dataset 'zv' at 'offset' for 'len' */ +int +uzfs_write_data(zvol_state_t *zv, char *buf, uint64_t offset, uint64_t len) +{ + uint64_t bytes = 0, sync = zv->zv_sync; + uint64_t volsize = zv->zv_volsize; + uint64_t blocksize = zv->zv_volblocksize; + uint64_t end = len + offset; + uint64_t wrote = 0; + objset_t *os = zv->zv_objset; + rl_t *rl; + int ret = 0, error; + uint64_t r_offset, r_len; + + /* + * Taking lock on entire block at ZFS layer. + * Handling the case where readlen is smaller than blocksize. + * This can also be avoided later for better performance. + */ + r_offset = (offset / blocksize) * blocksize; + r_len = ((len + blocksize - 1) / blocksize) * blocksize; + + rl = zfs_range_lock(&zv->zv_range_lock, r_offset, r_len, RL_WRITER); + while (offset < end && offset < volsize) { + bytes = (len < blocksize) ? len : blocksize; + + if (bytes > (volsize - offset)) + bytes = volsize - offset; + + dmu_tx_t *tx = dmu_tx_create(os); + dmu_tx_hold_write(tx, ZVOL_OBJ, offset, bytes); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + ret = UZFS_IO_TX_ASSIGN_FAIL; + goto end; + } + dmu_write(os, ZVOL_OBJ, offset, bytes, buf + wrote, tx); + zvol_log_write(zv, tx, offset, bytes, sync); + + dmu_tx_commit(tx); + + if (sync) + zil_commit(zv->zv_zilog, ZVOL_OBJ); + offset += bytes; + wrote += bytes; + len -= bytes; + } +end: + zfs_range_unlock(rl); + return (ret); +} + +/* Reads data from volume 'zv', and fills up memory at buf */ +int +uzfs_read_data(zvol_state_t *zv, char *buf, uint64_t offset, uint64_t len) +{ + uint64_t bytes = 0; + int error = 0; + uint64_t volsize = zv->zv_volsize; + uint64_t blocksize = zv->zv_volblocksize; + uint64_t end = len + offset; + uint64_t read = 0; + objset_t *os = zv->zv_objset; + rl_t *rl; + int ret = 0; + uint64_t r_offset, r_len; + + + r_offset = (offset / blocksize) * blocksize; + r_len = ((len + blocksize - 1) / blocksize) * blocksize; + + rl = zfs_range_lock(&zv->zv_range_lock, r_offset, r_len, RL_READER); + while ((offset < end) && (offset < volsize)) { + bytes = (len < blocksize) ? len : blocksize; + + if (bytes > (volsize - offset)) + bytes = volsize - offset; + + error = dmu_read(os, ZVOL_OBJ, offset, bytes, buf + read, 0); + if (error) { + ret = UZFS_IO_READ_FAIL; + goto end; + } + offset += bytes; + read += bytes; + len -= bytes; + } +end: + zfs_range_unlock(rl); + return (0); +} diff --git a/lib/libzpool/uzfs_mgmt.c b/lib/libzpool/uzfs_mgmt.c new file mode 100644 index 000000000000..93cf0b75337d --- /dev/null +++ b/lib/libzpool/uzfs_mgmt.c @@ -0,0 +1,296 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include + +static int uzfs_fd_rand = -1; + +static nvlist_t * +make_root(char *path, int ashift, int log) +{ + nvlist_t *root = NULL, *child; + + if (nvlist_alloc(&child, NV_UNIQUE_NAME, 0) != 0) + goto ret; + if (nvlist_add_string(child, ZPOOL_CONFIG_TYPE, + VDEV_TYPE_DISK) != 0) + goto free_ret; + if (nvlist_add_string(child, ZPOOL_CONFIG_PATH, path) != 0) + goto free_ret; + if (nvlist_add_uint64(child, ZPOOL_CONFIG_ASHIFT, ashift) != 0) + goto free_ret; + if (nvlist_add_uint64(child, ZPOOL_CONFIG_IS_LOG, log) != 0) + goto free_ret; + + if (nvlist_alloc(&root, NV_UNIQUE_NAME, 0) != 0) { + root = NULL; + goto free_ret; + } + if (nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0) + goto free_root_ret; + if (nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN, &child, 1) + != 0) { +free_root_ret: + nvlist_free(root); + root = NULL; + goto free_ret; + } +free_ret: + nvlist_free(child); +ret: + return (root); +} + +/* Generates random number in the [0-range] */ +uint64_t +uzfs_random(uint64_t range) +{ + + uint64_t r; + + ASSERT3S(uzfs_fd_rand, >=, 0); + + if (range == 0) + return (0); + + while (read(uzfs_fd_rand, &r, sizeof (r)) != sizeof (r)) + ; + + return (r % range); +} + +int +uzfs_init(void) +{ + int err = 0; + kernel_init(FREAD | FWRITE); + uzfs_fd_rand = open("/dev/urandom", O_RDONLY); + if (uzfs_fd_rand == -1) + err = errno; + return (err); +} + +/* Opens the pool if any with 'name' */ +int +uzfs_open_pool(char *name, spa_t **s) +{ + spa_t *spa = NULL; + int err = spa_open(name, &spa, FTAG); + if (err != 0) { + spa = NULL; + goto ret; + } +ret: + *s = spa; + return (err); +} + +/* creates the pool 'name' with a disk at 'path' */ +int +uzfs_create_pool(char *name, char *path, spa_t **s) +{ + nvlist_t *nvroot; + spa_t *spa = NULL; + int err = -1; + /* + * Create the storage pool. + */ + + (void) spa_destroy(name); + + nvroot = make_root(path, 12, 0); + if (nvroot == NULL) + goto ret; + + err = spa_create(name, nvroot, NULL, NULL); + nvlist_free(nvroot); + + if (err != 0) + goto ret; + + err = uzfs_open_pool(name, &spa); + if (err != 0) { + (void) spa_destroy(name); + spa = NULL; + goto ret; + } +ret: + *s = spa; + return (err); +} + +/* Adds vdev at 'path' to pool 'spa' as either log or data device */ +int +uzfs_vdev_add(spa_t *spa, char *path, int ashift, int log) +{ + nvlist_t *nvroot; + int error = -1; + + nvroot = make_root(path, ashift, log); + if (nvroot == NULL) + goto ret; + + error = spa_vdev_add(spa, nvroot); + + nvlist_free(nvroot); +ret: + return (error); +} + +/* + * callback when a zvol objset is created + * Any error here will bring down the process + */ +void +uzfs_objset_create_cb(objset_t *new_os, void *arg, cred_t *cr, dmu_tx_t *tx) +{ + /* + * Create the objects common to all uzfs datasets. + */ + uint64_t error; + + zvol_properties_t *properties = (zvol_properties_t *)arg; + + error = dmu_object_claim(new_os, ZVOL_OBJ, DMU_OT_ZVOL, + properties->block_size, DMU_OT_NONE, 0, tx); + VERIFY(error == 0); + + error = zap_create_claim(new_os, ZVOL_ZAP_OBJ, DMU_OT_ZVOL_PROP, + DMU_OT_NONE, 0, tx); + VERIFY(error == 0); + + error = zap_update(new_os, ZVOL_ZAP_OBJ, "size", 8, 1, + &properties->vol_size, tx); + VERIFY(error == 0); +} + +/* owns objset with name 'ds_name' in pool 'spa'. Sets 'sync' property */ +int +uzfs_open_dataset(spa_t *spa, const char *ds_name, int sync, zvol_state_t **z) +{ + char name[ZFS_MAX_DATASET_NAME_LEN]; + zvol_state_t *zv = NULL; + int error = -1; + objset_t *os; + dmu_object_info_t doi; + uint64_t block_size, vol_size; + + if (spa == NULL) + goto ret; + (void) snprintf(name, sizeof (name), "%s/%s", spa_name(spa), ds_name); + + zv = kmem_zalloc(sizeof (zvol_state_t), KM_SLEEP); + if (zv == NULL) + goto ret; + zv->zv_spa = spa; + zfs_rlock_init(&zv->zv_range_lock); + + strlcpy(zv->zv_name, name, MAXNAMELEN); + + error = dmu_objset_own(name, DMU_OST_ZVOL, 1, zv, &os); + if (error) + goto free_ret; + zv->zv_objset = os; + + error = dmu_object_info(os, ZVOL_OBJ, &doi); + if (error) + goto disown_free; + block_size = doi.doi_data_block_size; + + error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &vol_size); + if (error) + goto disown_free; + + error = dnode_hold(os, ZVOL_OBJ, zv, &zv->zv_dn); + if (error) { +disown_free: + dmu_objset_disown(zv->zv_objset, zv); +free_ret: + kmem_free(zv, sizeof (zvol_state_t)); + zv = NULL; + goto ret; + } + + zv->zv_zilog = zil_open(os, zvol_get_data); + zv->zv_sync = sync; + zv->zv_volblocksize = block_size; + zv->zv_volsize = vol_size; +ret: + *z = zv; + return (error); +} + +/* + * Creates dataset 'ds_name' in pool 'spa' with volume size 'vol_size', + * block size as 'block_size' and with 'sync' property + */ +int +uzfs_create_dataset(spa_t *spa, char *ds_name, uint64_t vol_size, + uint64_t block_size, int sync, zvol_state_t **z) +{ + char name[ZFS_MAX_DATASET_NAME_LEN]; + zvol_state_t *zv = NULL; + zvol_properties_t properties; + int error = -1; + + if (spa == NULL) + goto ret; + (void) snprintf(name, sizeof (name), "%s/%s", spa_name(spa), ds_name); + + properties.vol_size = vol_size; + properties.block_size = block_size; + + error = dmu_objset_create(name, DMU_OST_ZVOL, 0, + uzfs_objset_create_cb, &properties); + + if (error) + goto ret; + + error = uzfs_open_dataset(spa, ds_name, sync, &zv); + if (error != 0) { + zv = NULL; + goto ret; + } +ret: + *z = zv; + return (error); +} + +/* disowns, closes dataset and pool */ +void +uzfs_close_pool(spa_t *spa, zvol_state_t *zv) +{ + dmu_objset_disown(zv->zv_objset, zv); + zil_close(zv->zv_zilog); + dnode_rele(zv->zv_dn, zv); + kmem_free(zv, sizeof (zvol_state_t)); + spa_close(spa, FTAG); +} + +void +uzfs_fini(void) +{ + kernel_fini(); + if (uzfs_fd_rand != -1) + close(uzfs_fd_rand); +} diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index f4f509a7efca..d0fe1b758059 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -138,7 +138,7 @@ * distinguish between the operation failing, and * deserialization failing. */ - +#if defined(_KERNEL) #include #include #include @@ -262,11 +262,153 @@ static int zfs_check_settable(const char *name, nvpair_t *property, cred_t *cr); static int zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errors); -static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, - boolean_t *); int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp); +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zfs_fletcher.h" +#include "zfs_namecheck.h" +#include "zfs_comutil.h" +#include "zfs_prop.h" +#include "zfeature_common.h" + +int +zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) +{ + return (0); +} + +static int zfs_prop_set_special(const char *dsname, zprop_source_t source, + nvpair_t *pair) { return -1; } +#undef tsd_set +#define tsd_set(k, v) (0) +void +command_not_implemented(void) +{ +#ifdef _UZFS + fprintf(stderr, "command not implemented\n"); + exit(1); +#endif +} +static int +get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) +{ + char *packed; + int error; + nvlist_t *list = NULL; + + /* + * Read in and unpack the user-supplied nvlist. + */ + if (size == 0) + return (EINVAL); + + packed = (void *)(uintptr_t)nvl; + + if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) + return (error); + + *nvp = list; + return (0); +} + +static int +put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) +{ + char *packed = NULL; + int error = 0; + size_t size; + + size = fnvlist_size(nvl); + + if (size > zc->zc_nvlist_dst_size) { + error = SET_ERROR(ENOMEM); + } else { + packed = fnvlist_pack(nvl, &size); + memcpy((void *)(uintptr_t)zc->zc_nvlist_dst, packed, size); + fnvlist_pack_free(packed, size); + } + + zc->zc_nvlist_dst_size = size; + zc->zc_nvlist_dst_filled = B_TRUE; + return (error); +} + +/* + * Write out a history event. + */ +int +spa_history_log(spa_t *spa, const char *msg) +{ + int err; + nvlist_t *nvl = fnvlist_alloc(); + + fnvlist_add_string(nvl, ZPOOL_HIST_CMD, msg); + err = spa_history_log_nvl(spa, nvl); + fnvlist_free(nvl); + return (err); +} + +static char * +history_str_get(zfs_cmd_t *zc) +{ + char *buf; + + if (zc->zc_history == 0) + return (NULL); + + buf = (char *)zc->zc_history; + + return (buf); +} +static void +history_str_free(char *buf) +{ +} + +int +uzfs_ioctl(int fd, unsigned long request, zfs_cmd_t *zc) +{ +#ifndef _UZFS + return (ioctl(fd, request, zc)); +#else + if (uzfs_send_ioctl(fd, request, zc)) { + fprintf(stderr, "ioctl send failed\n"); + exit(1); + } + + int ret = uzfs_recv_response(fd, zc); + + int err = (ret < 0 ? errno : ret); + + if (err) + return (SET_ERR(err)); + + return (0); +#endif +} + +#endif + +static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, + boolean_t *); + +#if defined(_KERNEL) static void history_str_free(char *buf) { @@ -309,6 +451,7 @@ zfs_is_bootfs(const char *name) } return (B_FALSE); } +#endif /* * Return non-zero if the spa version is less than requested version. @@ -328,6 +471,7 @@ zfs_earlier_version(const char *name, int version) return (0); } +#if defined(_KERNEL) /* * Return TRUE if the ZPL version is less than requested version. */ @@ -351,6 +495,7 @@ zpl_earlier_version(const char *name, int version) } return (rc); } +#endif static void zfs_log_history(zfs_cmd_t *zc) @@ -369,6 +514,7 @@ zfs_log_history(zfs_cmd_t *zc) history_str_free(buf); } +#if defined(_KERNEL) /* * Policy for top-level read operations (list pools). Requires no privileges, * and can be used in the local zone, as there is no associated dataset. @@ -394,6 +540,7 @@ zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) return (SET_ERROR(ENOENT)); } +#endif static int zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) @@ -429,6 +576,7 @@ zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) return (0); } +#if defined(_KERNEL) static int zfs_dozonecheck(const char *dataset, cred_t *cr) { @@ -439,6 +587,7 @@ zfs_dozonecheck(const char *dataset, cred_t *cr) return (zfs_dozonecheck_impl(dataset, zoned, cr)); } +#endif static int zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) @@ -498,6 +647,7 @@ zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) return (error); } +#if defined(_KERNEL) /* * Policy for setting the security label property. * @@ -1466,6 +1616,7 @@ zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) zfsvfs_free(zfsvfs); } } +#endif static int zfs_ioc_pool_create(zfs_cmd_t *zc) @@ -1670,6 +1821,7 @@ zfs_ioc_pool_tryimport(zfs_cmd_t *zc) return (error); } +#if defined(_KERNEL) /* * inputs: * zc_name name of the pool @@ -1734,6 +1886,7 @@ zfs_ioc_pool_upgrade(zfs_cmd_t *zc) return (error); } +#endif static int zfs_ioc_pool_get_history(zfs_cmd_t *zc) @@ -1754,6 +1907,7 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc) return (SET_ERROR(ENOTSUP)); } +#if defined(_KERNEL) hist_buf = vmem_alloc(size, KM_SLEEP); if ((error = spa_history_get(spa, &zc->zc_history_offset, &zc->zc_history_len, hist_buf)) == 0) { @@ -1761,12 +1915,18 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc) (void *)(uintptr_t)zc->zc_history, zc->zc_history_len, zc->zc_iflags); } +#else + hist_buf = (char *)zc->zc_history; + error = spa_history_get(spa, &zc->zc_history_offset, + &zc->zc_history_len, hist_buf); +#endif spa_close(spa, FTAG); vmem_free(hist_buf, size); return (error); } +#if defined(_KERNEL) static int zfs_ioc_pool_reguid(zfs_cmd_t *zc) { @@ -2032,6 +2192,7 @@ zfs_ioc_vdev_setfru(zfs_cmd_t *zc) spa_close(spa, FTAG); return (error); } +#endif static int zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) @@ -2093,6 +2254,7 @@ zfs_ioc_objset_stats(zfs_cmd_t *zc) return (error); } +#if defined(_KERNEL) /* * inputs: * zc_name name of filesystem @@ -2189,6 +2351,7 @@ zfs_ioc_objset_zplprops(zfs_cmd_t *zc) dmu_objset_rele(os, FTAG); return (err); } +#endif boolean_t dataset_name_hidden(const char *name) @@ -2326,6 +2489,7 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) return (error); } +#if defined(_KERNEL) static int zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) { @@ -2470,6 +2634,7 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source, return (err); } +#endif /* * This function is best effort. If it fails to set any of the given properties, @@ -2554,11 +2719,9 @@ zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, err = SET_ERROR(EINVAL); } } - /* Validate permissions */ if (err == 0) err = zfs_check_settable(dsname, pair, CRED()); - if (err == 0) { if (source == ZPROP_SRC_INHERITED) err = -1; /* does not need special handling */ @@ -2668,7 +2831,6 @@ zfs_check_userprops(const char *fsname, nvlist_t *nvl) } return (0); } - static void props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) { @@ -2754,6 +2916,7 @@ zfs_ioc_set_prop(zfs_cmd_t *zc) return (error); } +#if defined(_KERNEL) /* * inputs: * zc_name name of filesystem @@ -2831,6 +2994,7 @@ zfs_ioc_inherit_prop(zfs_cmd_t *zc) nvlist_free(dummy); return (err); } +#endif static int zfs_ioc_pool_set_props(zfs_cmd_t *zc) @@ -2908,6 +3072,7 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc) return (error); } +#if defined(_KERNEL) /* * inputs: * zc_name name of filesystem @@ -2988,6 +3153,7 @@ zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) zfs_create_fs(os, cr, zct->zct_zplprops, tx); } +#endif #define ZFS_PROP_UNDEFINED ((uint64_t)-1) /* @@ -3094,6 +3260,7 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, return (0); } +#if defined(_KERNEL) static int zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, nvlist_t *zplprops, boolean_t *is_ci) @@ -3133,6 +3300,7 @@ zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, dmu_objset_rele(os, FTAG); return (error); } +#endif static int zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, @@ -3177,10 +3345,11 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); switch (type) { +#if defined(_KERNEL) case DMU_OST_ZFS: cbfunc = zfs_create_cb; break; - +#endif case DMU_OST_ZVOL: cbfunc = zvol_create_cb; break; @@ -3221,7 +3390,9 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) (error = zvol_check_volsize(volsize, volblocksize)) != 0) return (error); - } else if (type == DMU_OST_ZFS) { + } +#if defined(_KERNEL) + else if (type == DMU_OST_ZFS) { int error; /* @@ -3239,7 +3410,7 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) return (error); } } - +#endif error = dmu_objset_create(fsname, type, is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); nvlist_free(zct.zct_zplprops); @@ -3273,6 +3444,7 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) return (error); } +#if defined(_KERNEL) /* * innvl: { * "origin" -> name of origin snapshot @@ -3314,6 +3486,7 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) } return (error); } +#endif /* * innvl: { @@ -3381,12 +3554,17 @@ zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) * innvl: "message" -> string */ /* ARGSUSED */ -static int +int +#if defined(_KERNEL) zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) +#else +zfs_ioc_log_history(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) +#endif { char *message; spa_t *spa; int error; +#if defined(_KERNEL) char *poolname; /* @@ -3397,11 +3575,14 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) * we clear the TSD here. */ poolname = tsd_get(zfs_allow_log_key); +#endif if (poolname == NULL) return (SET_ERROR(EINVAL)); (void) tsd_set(zfs_allow_log_key, NULL); error = spa_open(poolname, &spa, FTAG); +#if defined(_KERNEL) strfree(poolname); +#endif if (error != 0) return (error); @@ -3420,6 +3601,7 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) return (error); } +#if defined(_KERNEL) /* * The dp_config_rwlock must not be held when calling this, because the * unmount may need to write out data. @@ -3477,6 +3659,7 @@ zfs_destroy_unmount_origin(const char *fsname) dmu_objset_rele(os, FTAG); } } +#endif /* * innvl: { @@ -3491,21 +3674,23 @@ static int zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) { nvlist_t *snaps; - nvpair_t *pair; boolean_t defer; if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) return (SET_ERROR(EINVAL)); defer = nvlist_exists(innvl, "defer"); +#if defined(_KERNEL) + nvpair_t *pair; for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) { (void) zfs_unmount_snap(nvpair_name(pair)); } - +#endif return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); } +#if defined(_KERNEL) /* * Create bookmarks. Bookmark names are of the form #. * All bookmarks must be in the same pool. @@ -3544,6 +3729,7 @@ zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) return (dsl_bookmark_create(innvl, outnvl)); } +#endif /* * innvl: { @@ -3562,6 +3748,7 @@ zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) return (dsl_get_bookmarks(fsname, innvl, outnvl)); } +#if defined(_KERNEL) /* * innvl: { * bookmark name 1, bookmark name 2 @@ -3602,6 +3789,7 @@ zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, error = dsl_bookmark_destroy(innvl, outnvl); return (error); } +#endif /* * inputs: @@ -3616,12 +3804,13 @@ zfs_ioc_destroy(zfs_cmd_t *zc) { int err; +#if defined(_KERNEL) if (zc->zc_objset_type == DMU_OST_ZFS) { err = zfs_unmount_snap(zc->zc_name); if (err != 0) return (err); } - +#endif if (strchr(zc->zc_name, '@')) { err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); } else { @@ -3659,6 +3848,7 @@ zfs_ioc_destroy(zfs_cmd_t *zc) return (err); } +#if defined(_KERNEL) /* * fsname is name of dataset to rollback (to most recent snapshot) * @@ -6771,3 +6961,118 @@ MODULE_AUTHOR(ZFS_META_AUTHOR); MODULE_LICENSE(ZFS_META_LICENSE); MODULE_VERSION(ZFS_META_VERSION "-" ZFS_META_RELEASE); #endif /* HAVE_SPL */ +#else +int +uzfs_handle_ioctl(const char *pool, uint64_t request, zfs_cmd_t *zc) +{ + int err; + nvlist_t *innvl = NULL; + if (zc->zc_nvlist_src_size > MAX_NVLIST_SRC_SIZE) { + /* + * Make sure the user doesn't pass in an insane value for + * zc_nvlist_src_size. We have to check, since we will end + * up allocating that much memory inside of get_nvlist(). This + * prevents a nefarious user from allocating tons of kernel + * memory. + * + * Also, we return EINVAL instead of ENOMEM here. The reason + * being that returning ENOMEM from an ioctl() has a special + * connotation; that the user's size value is too small and + * needs to be expanded to hold the nvlist. See + * zcmd_expand_dst_nvlist() for details. + */ + return (EINVAL); /* User's size too big */ + } else if (zc->zc_nvlist_src_size != 0) { + err = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 0, + &innvl); + if (err != 0) + return (err); + } + + err = ENOTSUP; + switch (request) { + case ZFS_IOC_OBJSET_STATS: + return (zfs_ioc_objset_stats(zc)); + case ZFS_IOC_POOL_CREATE: + return (zfs_ioc_pool_create(zc)); + case ZFS_IOC_POOL_IMPORT: + return (zfs_ioc_pool_import(zc)); + case ZFS_IOC_POOL_STATS: + return (zfs_ioc_pool_stats(zc)); + case ZFS_IOC_POOL_TRYIMPORT: + return (zfs_ioc_pool_tryimport(zc)); + case ZFS_IOC_CREATE: { + nvlist_t *outnvl = fnvlist_alloc(); + + err = zfs_ioc_create(zc->zc_name, innvl, outnvl); + if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) + err = put_nvlist(zc, outnvl); + nvlist_free(outnvl); + + return (err); + } + case ZFS_IOC_POOL_CONFIGS: + return (zfs_ioc_pool_configs(zc)); + case ZFS_IOC_DATASET_LIST_NEXT: + return (zfs_ioc_dataset_list_next(zc)); + case ZFS_IOC_GET_BOOKMARKS: { + nvlist_t *outnvl = fnvlist_alloc(); + err = zfs_ioc_get_bookmarks(zc->zc_name, innvl, outnvl); + if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) + err = put_nvlist(zc, outnvl); + nvlist_free(outnvl); + + return (err); + } + case ZFS_IOC_POOL_GET_PROPS: + return (zfs_ioc_pool_get_props(zc)); + case ZFS_IOC_POOL_EXPORT: + return (zfs_ioc_pool_export(zc)); + case ZFS_IOC_POOL_GET_HISTORY: + return (zfs_ioc_pool_get_history(zc)); + case ZFS_IOC_LOG_HISTORY: { + nvlist_t *outnvl = fnvlist_alloc(); + + err = zfs_ioc_log_history(pool, innvl, outnvl); + if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) + err = put_nvlist(zc, outnvl); + + nvlist_free(outnvl); + return (err); + } + case ZFS_IOC_SNAPSHOT: { + nvlist_t *outnvl = fnvlist_alloc(); + + err = zfs_ioc_snapshot(zc->zc_name, innvl, outnvl); + if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) + err = put_nvlist(zc, outnvl); + + nvlist_free(outnvl); + return (err); + } + case ZFS_IOC_SNAPSHOT_LIST_NEXT: + return (zfs_ioc_snapshot_list_next(zc)); + case ZFS_IOC_POOL_DESTROY: + return (zfs_ioc_pool_destroy(zc)); + case ZFS_IOC_DESTROY_SNAPS: { + nvlist_t *outnvl = fnvlist_alloc(); + + err = zfs_ioc_destroy_snaps(zc->zc_name, innvl, outnvl); + if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) + err = put_nvlist(zc, outnvl); + + nvlist_free(outnvl); + return (err); + } + case ZFS_IOC_DESTROY: + return (zfs_ioc_destroy(zc)); + case ZFS_IOC_POOL_SET_PROPS: + return (zfs_ioc_pool_set_props(zc)); + case ZFS_IOC_SET_PROP: + return (zfs_ioc_set_prop(zc)); + } + return (err); +} + + +#endif diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 761133936bc1..0299c397631b 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -26,6 +26,7 @@ /* Portions Copyright 2010 Robert Milkowski */ #include +#if defined(_KERNEL) #include #include #include @@ -38,7 +39,6 @@ #include #include #include "fs/fs_subr.h" -#include #include #include #include @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -57,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -67,7 +65,15 @@ #include #include #include "zfs_comutil.h" +#endif +#include +#include +#include +#include +#include "zfs_prop.h" + +#if defined(_KERNEL) enum { TOKEN_RO, TOKEN_RW, @@ -2047,6 +2053,7 @@ zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers) return (0); } +#endif /* * Read a property stored within the master node. @@ -2095,6 +2102,7 @@ zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value) return (error); } +#if defined(_KERNEL) /* * Return true if the coresponding vfs's unmounted flag is set. * Otherwise return false. @@ -2138,6 +2146,7 @@ zfs_fini(void) zfs_znode_fini(); zfsctl_fini(); } +#endif #if defined(_KERNEL) && defined(HAVE_SPL) EXPORT_SYMBOL(zfs_suspend_fs); diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 3e7059b340bb..21c807fcc6c3 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -87,8 +87,14 @@ #include #include #include -#include + +#if defined(_KERNEL) #include +#include +#else +#include +#include +#endif unsigned int zvol_inhibit_dev = 0; unsigned int zvol_major = ZVOL_MAJOR; @@ -98,6 +104,7 @@ unsigned int zvol_prefetch_bytes = (128 * 1024); unsigned long zvol_max_discard_blocks = 16384; unsigned int zvol_volmode = ZFS_VOLMODE_GEOM; +#if defined(_KERNEL) static taskq_t *zvol_taskq; static kmutex_t zvol_state_lock; static list_t zvol_state_list; @@ -132,6 +139,7 @@ struct zvol_state { atomic_t zv_suspend_ref; /* refcount for suspend */ krwlock_t zv_suspend_lock; /* suspend lock */ }; +#endif typedef enum { ZVOL_ASYNC_CREATE_MINORS, @@ -153,6 +161,7 @@ typedef struct { #define ZVOL_RDONLY 0x1 +#if defined(_KERNEL) static uint64_t zvol_name_hash(const char *name) { @@ -272,6 +281,7 @@ zvol_is_zvol(const char *device) return (B_FALSE); } +#endif /* * ZFS_IOC_CREATE callback handles dmu zvol and zap object creation. @@ -339,6 +349,7 @@ zvol_get_stats(objset_t *os, nvlist_t *nv) return (SET_ERROR(error)); } +#if defined(_KERNEL) static void zvol_size_changed(zvol_state_t *zv, uint64_t volsize) { @@ -356,6 +367,7 @@ zvol_size_changed(zvol_state_t *zv, uint64_t volsize) bdput(bdev); } +#endif /* * Sanity check volume size. @@ -376,6 +388,7 @@ zvol_check_volsize(uint64_t volsize, uint64_t blocksize) return (0); } +#if defined(_KERNEL) /* * Ensure the zap is flushed then inform the VFS of the capacity change. */ @@ -490,6 +503,7 @@ zvol_set_volsize(const char *name, uint64_t volsize) return (SET_ERROR(error)); } +#endif /* * Sanity check volume block size. @@ -528,6 +542,7 @@ zvol_check_volblocksize(const char *name, uint64_t volblocksize) return (0); } +#if defined(_KERNEL) /* * Set ZFS_PROP_VOLBLOCKSIZE set entry point. */ @@ -657,6 +672,7 @@ zil_replay_func_t zvol_replay_vector[TX_MAX_TYPE] = { (zil_replay_func_t)zvol_replay_err, /* TX_SETATTR */ (zil_replay_func_t)zvol_replay_err, /* TX_ACL */ }; +#endif /* * zvol_log_write() handles synchronous writes using TX_WRITE ZIL transactions. @@ -666,7 +682,7 @@ zil_replay_func_t zvol_replay_vector[TX_MAX_TYPE] = { */ ssize_t zvol_immediate_write_sz = 32768; -static void +void zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset, uint64_t size, int sync) { @@ -726,6 +742,7 @@ zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset, } } +#if defined(_KERNEL) typedef struct zv_request { zvol_state_t *zv; struct bio *bio; @@ -1032,6 +1049,7 @@ zvol_request(struct request_queue *q, struct bio *bio) return (BLK_QC_T_NONE); #endif } +#endif static void zvol_get_done(zgd_t *zgd, int error) @@ -1050,7 +1068,7 @@ zvol_get_done(zgd_t *zgd, int error) /* * Get data to generate a TX_WRITE intent log record. */ -static int +int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) { zvol_state_t *zv = arg; @@ -1114,6 +1132,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) return (SET_ERROR(error)); } +#if defined(_KERNEL) /* * The zvol_state_t's are inserted into zvol_state_list and zvol_htable. */ @@ -2716,3 +2735,4 @@ MODULE_PARM_DESC(zvol_prefetch_bytes, "Prefetch N bytes at zvol start+end"); module_param(zvol_volmode, uint, 0644); MODULE_PARM_DESC(zvol_volmode, "Default volmode property value"); /* END CSTYLED */ +#endif