mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 03:09:34 +03:00
Add linux user disk support
This topic branch contains all the changes needed to integrate the user side zfs tools with Linux style devices. Primarily this includes fixing up the Solaris libefi library to be Linux friendly, and integrating with the libblkid library which is provided by e2fsprogs. Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
parent
f1fb119f6b
commit
d603ed6c27
@ -55,7 +55,6 @@
|
|||||||
#include <sys/arc.h>
|
#include <sys/arc.h>
|
||||||
#include <sys/ddt.h>
|
#include <sys/ddt.h>
|
||||||
#undef ZFS_MAXNAMELEN
|
#undef ZFS_MAXNAMELEN
|
||||||
#undef verify
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
|
||||||
#define ZDB_COMPRESS_NAME(idx) ((idx) < ZIO_COMPRESS_FUNCTIONS ? \
|
#define ZDB_COMPRESS_NAME(idx) ((idx) < ZIO_COMPRESS_FUNCTIONS ? \
|
||||||
|
@ -318,6 +318,7 @@ safe_malloc(size_t size)
|
|||||||
return (data);
|
return (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
static char *
|
static char *
|
||||||
safe_strdup(char *str)
|
safe_strdup(char *str)
|
||||||
{
|
{
|
||||||
@ -328,6 +329,7 @@ safe_strdup(char *str)
|
|||||||
|
|
||||||
return (dupstr);
|
return (dupstr);
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback routine that will print out information for each of
|
* Callback routine that will print out information for each of
|
||||||
@ -495,6 +497,7 @@ parse_depth(char *opt, int *flags)
|
|||||||
|
|
||||||
#define PROGRESS_DELAY 2 /* seconds */
|
#define PROGRESS_DELAY 2 /* seconds */
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
|
static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
|
||||||
static time_t pt_begin;
|
static time_t pt_begin;
|
||||||
static char *pt_header = NULL;
|
static char *pt_header = NULL;
|
||||||
@ -546,6 +549,8 @@ finish_progress(char *done)
|
|||||||
free(pt_header);
|
free(pt_header);
|
||||||
pt_header = NULL;
|
pt_header = NULL;
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
|
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
|
||||||
*
|
*
|
||||||
@ -626,6 +631,7 @@ zfs_do_clone(int argc, char **argv)
|
|||||||
ret = zfs_clone(zhp, argv[1], props);
|
ret = zfs_clone(zhp, argv[1], props);
|
||||||
|
|
||||||
/* create the mountpoint if necessary */
|
/* create the mountpoint if necessary */
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
zfs_handle_t *clone;
|
zfs_handle_t *clone;
|
||||||
|
|
||||||
@ -637,6 +643,7 @@ zfs_do_clone(int argc, char **argv)
|
|||||||
zfs_close(clone);
|
zfs_close(clone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
zfs_close(zhp);
|
zfs_close(zhp);
|
||||||
nvlist_free(props);
|
nvlist_free(props);
|
||||||
@ -824,6 +831,7 @@ zfs_do_create(int argc, char **argv)
|
|||||||
* verbose error message to let the user know that their filesystem was
|
* verbose error message to let the user know that their filesystem was
|
||||||
* in fact created, even if we failed to mount or share it.
|
* in fact created, even if we failed to mount or share it.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if (canmount == ZFS_CANMOUNT_ON) {
|
if (canmount == ZFS_CANMOUNT_ON) {
|
||||||
if (zfs_mount(zhp, NULL, 0) != 0) {
|
if (zfs_mount(zhp, NULL, 0) != 0) {
|
||||||
(void) fprintf(stderr, gettext("filesystem "
|
(void) fprintf(stderr, gettext("filesystem "
|
||||||
@ -835,6 +843,7 @@ zfs_do_create(int argc, char **argv)
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (zhp)
|
if (zhp)
|
||||||
@ -2940,6 +2949,7 @@ zfs_do_release(int argc, char **argv)
|
|||||||
#define SPINNER_TIME 3 /* seconds */
|
#define SPINNER_TIME 3 /* seconds */
|
||||||
#define MOUNT_TIME 5 /* seconds */
|
#define MOUNT_TIME 5 /* seconds */
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
static int
|
static int
|
||||||
get_one_dataset(zfs_handle_t *zhp, void *data)
|
get_one_dataset(zfs_handle_t *zhp, void *data)
|
||||||
{
|
{
|
||||||
@ -3387,6 +3397,7 @@ share_mount(int op, int argc, char **argv)
|
|||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* zfs mount -a [nfs]
|
* zfs mount -a [nfs]
|
||||||
@ -3397,7 +3408,11 @@ share_mount(int op, int argc, char **argv)
|
|||||||
static int
|
static int
|
||||||
zfs_do_mount(int argc, char **argv)
|
zfs_do_mount(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
return (share_mount(OP_MOUNT, argc, argv));
|
return (share_mount(OP_MOUNT, argc, argv));
|
||||||
|
#else
|
||||||
|
return ENOSYS;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3409,9 +3424,14 @@ zfs_do_mount(int argc, char **argv)
|
|||||||
static int
|
static int
|
||||||
zfs_do_share(int argc, char **argv)
|
zfs_do_share(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
return (share_mount(OP_SHARE, argc, argv));
|
return (share_mount(OP_SHARE, argc, argv));
|
||||||
|
#else
|
||||||
|
return ENOSYS;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
typedef struct unshare_unmount_node {
|
typedef struct unshare_unmount_node {
|
||||||
zfs_handle_t *un_zhp;
|
zfs_handle_t *un_zhp;
|
||||||
char *un_mountp;
|
char *un_mountp;
|
||||||
@ -3795,6 +3815,7 @@ unshare_unmount(int op, int argc, char **argv)
|
|||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* zfs unmount -a
|
* zfs unmount -a
|
||||||
@ -3805,7 +3826,11 @@ unshare_unmount(int op, int argc, char **argv)
|
|||||||
static int
|
static int
|
||||||
zfs_do_unmount(int argc, char **argv)
|
zfs_do_unmount(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
return (unshare_unmount(OP_MOUNT, argc, argv));
|
return (unshare_unmount(OP_MOUNT, argc, argv));
|
||||||
|
#else
|
||||||
|
return ENOSYS;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3817,7 +3842,11 @@ zfs_do_unmount(int argc, char **argv)
|
|||||||
static int
|
static int
|
||||||
zfs_do_unshare(int argc, char **argv)
|
zfs_do_unshare(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
return (unshare_unmount(OP_SHARE, argc, argv));
|
return (unshare_unmount(OP_SHARE, argc, argv));
|
||||||
|
#else
|
||||||
|
return ENOSYS;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ARGSUSED */
|
/* ARGSUSED */
|
||||||
@ -3833,6 +3862,7 @@ zfs_do_python(int argc, char **argv)
|
|||||||
* Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
|
* Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
|
||||||
* 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
|
* 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
|
||||||
*/
|
*/
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
static int
|
static int
|
||||||
manual_mount(int argc, char **argv)
|
manual_mount(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -3963,6 +3993,7 @@ manual_unmount(int argc, char **argv)
|
|||||||
|
|
||||||
return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
|
return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
find_command_idx(char *command, int *idx)
|
find_command_idx(char *command, int *idx)
|
||||||
@ -4061,7 +4092,9 @@ main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
char *progname;
|
char *progname;
|
||||||
|
#endif
|
||||||
char *cmdname;
|
char *cmdname;
|
||||||
|
|
||||||
(void) setlocale(LC_ALL, "");
|
(void) setlocale(LC_ALL, "");
|
||||||
@ -4086,6 +4119,7 @@ main(int argc, char **argv)
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
/*
|
/*
|
||||||
* This command also doubles as the /etc/fs mount and unmount program.
|
* This command also doubles as the /etc/fs mount and unmount program.
|
||||||
* Determine if we should take this behavior based on argv[0].
|
* Determine if we should take this behavior based on argv[0].
|
||||||
@ -4096,6 +4130,9 @@ main(int argc, char **argv)
|
|||||||
} else if (strcmp(progname, "umount") == 0) {
|
} else if (strcmp(progname, "umount") == 0) {
|
||||||
ret = manual_unmount(argc, argv);
|
ret = manual_unmount(argc, argv);
|
||||||
} else {
|
} else {
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
/*
|
/*
|
||||||
* Make sure the user has specified some command.
|
* Make sure the user has specified some command.
|
||||||
*/
|
*/
|
||||||
|
@ -954,17 +954,20 @@ main(int argc, char **argv)
|
|||||||
if (dataset[0] != '\0' && domount) {
|
if (dataset[0] != '\0' && domount) {
|
||||||
if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
|
if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
|
||||||
return (1);
|
return (1);
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if (zfs_unmount(zhp, NULL, 0) != 0)
|
if (zfs_unmount(zhp, NULL, 0) != 0)
|
||||||
return (1);
|
return (1);
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
record.zi_error = error;
|
record.zi_error = error;
|
||||||
|
|
||||||
ret = register_handler(pool, flags, &record, quiet);
|
ret = register_handler(pool, flags, &record, quiet);
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if (dataset[0] != '\0' && domount)
|
if (dataset[0] != '\0' && domount)
|
||||||
ret = (zfs_mount(zhp, NULL, 0) != 0);
|
ret = (zfs_mount(zhp, NULL, 0) != 0);
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
libzfs_fini(g_zfs);
|
libzfs_fini(g_zfs);
|
||||||
|
|
||||||
|
@ -716,7 +716,9 @@ zpool_do_create(int argc, char **argv)
|
|||||||
(strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
|
(strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
|
||||||
strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0)) {
|
strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0)) {
|
||||||
char buf[MAXPATHLEN];
|
char buf[MAXPATHLEN];
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mountpoint && mountpoint[0] != '/') {
|
if (mountpoint && mountpoint[0] != '/') {
|
||||||
(void) fprintf(stderr, gettext("invalid mountpoint "
|
(void) fprintf(stderr, gettext("invalid mountpoint "
|
||||||
@ -741,6 +743,7 @@ zpool_do_create(int argc, char **argv)
|
|||||||
mountpoint);
|
mountpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if ((dirp = opendir(buf)) == NULL && errno != ENOENT) {
|
if ((dirp = opendir(buf)) == NULL && errno != ENOENT) {
|
||||||
(void) fprintf(stderr, gettext("mountpoint '%s' : "
|
(void) fprintf(stderr, gettext("mountpoint '%s' : "
|
||||||
"%s\n"), buf, strerror(errno));
|
"%s\n"), buf, strerror(errno));
|
||||||
@ -763,6 +766,7 @@ zpool_do_create(int argc, char **argv)
|
|||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dryrun) {
|
if (dryrun) {
|
||||||
@ -793,8 +797,12 @@ zpool_do_create(int argc, char **argv)
|
|||||||
zfs_prop_to_name(
|
zfs_prop_to_name(
|
||||||
ZFS_PROP_MOUNTPOINT),
|
ZFS_PROP_MOUNTPOINT),
|
||||||
mountpoint) == 0);
|
mountpoint) == 0);
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if (zfs_mount(pool, NULL, 0) == 0)
|
if (zfs_mount(pool, NULL, 0) == 0)
|
||||||
ret = zfs_shareall(pool);
|
ret = zfs_shareall(pool);
|
||||||
|
#else
|
||||||
|
ret = 0;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
zfs_close(pool);
|
zfs_close(pool);
|
||||||
}
|
}
|
||||||
} else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
|
} else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
|
||||||
@ -1571,12 +1579,14 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
|
|||||||
if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL)
|
if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL)
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
|
#if HAVE_ZPL
|
||||||
if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
|
if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
|
||||||
!(flags & ZFS_IMPORT_ONLY) &&
|
!(flags & ZFS_IMPORT_ONLY) &&
|
||||||
zpool_enable_datasets(zhp, mntopts, 0) != 0) {
|
zpool_enable_datasets(zhp, mntopts, 0) != 0) {
|
||||||
zpool_close(zhp);
|
zpool_close(zhp);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
zpool_close(zhp);
|
zpool_close(zhp);
|
||||||
return (0);
|
return (0);
|
||||||
@ -1592,7 +1602,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
|
|||||||
* -c Read pool information from a cachefile instead of searching
|
* -c Read pool information from a cachefile instead of searching
|
||||||
* devices.
|
* devices.
|
||||||
*
|
*
|
||||||
* -d Scan in a specific directory, other than /dev/dsk. More than
|
* -d Scan in a specific directory, other than /dev/. More than
|
||||||
* one directory can be specified using multiple '-d' options.
|
* one directory can be specified using multiple '-d' options.
|
||||||
*
|
*
|
||||||
* -D Scan for previously destroyed pools or import all or only
|
* -D Scan for previously destroyed pools or import all or only
|
||||||
@ -1773,12 +1783,6 @@ zpool_do_import(int argc, char **argv)
|
|||||||
nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0)
|
nvlist_add_uint32(policy, ZPOOL_REWIND_REQUEST, rewind_policy) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (searchdirs == NULL) {
|
|
||||||
searchdirs = safe_malloc(sizeof (char *));
|
|
||||||
searchdirs[0] = "/dev/dsk";
|
|
||||||
nsearch = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check argument count */
|
/* check argument count */
|
||||||
if (do_all) {
|
if (do_all) {
|
||||||
if (argc != 0) {
|
if (argc != 0) {
|
||||||
@ -1799,7 +1803,9 @@ zpool_do_import(int argc, char **argv)
|
|||||||
if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
|
if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
|
||||||
(void) fprintf(stderr, gettext("cannot "
|
(void) fprintf(stderr, gettext("cannot "
|
||||||
"discover pools: permission denied\n"));
|
"discover pools: permission denied\n"));
|
||||||
free(searchdirs);
|
if (searchdirs != NULL)
|
||||||
|
free(searchdirs);
|
||||||
|
|
||||||
nvlist_free(policy);
|
nvlist_free(policy);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
@ -1867,7 +1873,8 @@ zpool_do_import(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
free(searchdirs);
|
if (searchdirs != NULL)
|
||||||
|
free(searchdirs);
|
||||||
nvlist_free(policy);
|
nvlist_free(policy);
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
@ -1968,7 +1975,8 @@ error:
|
|||||||
nvlist_free(props);
|
nvlist_free(props);
|
||||||
nvlist_free(pools);
|
nvlist_free(pools);
|
||||||
nvlist_free(policy);
|
nvlist_free(policy);
|
||||||
free(searchdirs);
|
if (searchdirs != NULL)
|
||||||
|
free(searchdirs);
|
||||||
|
|
||||||
return (err ? 1 : 0);
|
return (err ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
*
|
*
|
||||||
* 1. Construct the vdev specification. Performs syntax validation and
|
* 1. Construct the vdev specification. Performs syntax validation and
|
||||||
* makes sure each device is valid.
|
* makes sure each device is valid.
|
||||||
* 2. Check for devices in use. Using libdiskmgt, makes sure that no
|
* 2. Check for devices in use. Using libblkid to make sure that no
|
||||||
* devices are also in use. Some can be overridden using the 'force'
|
* devices are also in use. Some can be overridden using the 'force'
|
||||||
* flag, others cannot.
|
* flag, others cannot.
|
||||||
* 3. Check for replication errors if the 'force' flag is not specified.
|
* 3. Check for replication errors if the 'force' flag is not specified.
|
||||||
@ -60,10 +60,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <devid.h>
|
#include <devid.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <libdiskmgt.h>
|
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#include <libnvpair.h>
|
#include <libnvpair.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@ -74,13 +74,15 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/vtoc.h>
|
#include <sys/vtoc.h>
|
||||||
#include <sys/mntent.h>
|
#include <sys/mntent.h>
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
#include <blkid/blkid.h>
|
||||||
|
#else
|
||||||
|
#define blkid_cache void *
|
||||||
|
#endif /* HAVE_LIBBLKID */
|
||||||
|
|
||||||
#include "zpool_util.h"
|
#include "zpool_util.h"
|
||||||
|
|
||||||
#define DISK_ROOT "/dev/dsk"
|
|
||||||
#define RDISK_ROOT "/dev/rdsk"
|
|
||||||
#define BACKUP_SLICE "s2"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For any given vdev specification, we can have multiple errors. The
|
* For any given vdev specification, we can have multiple errors. The
|
||||||
* vdev_error() function keeps track of whether we have seen an error yet, and
|
* vdev_error() function keeps track of whether we have seen an error yet, and
|
||||||
@ -111,168 +113,6 @@ vdev_error(const char *fmt, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
libdiskmgt_error(int error)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* ENXIO/ENODEV is a valid error message if the device doesn't live in
|
|
||||||
* /dev/dsk. Don't bother printing an error message in this case.
|
|
||||||
*/
|
|
||||||
if (error == ENXIO || error == ENODEV)
|
|
||||||
return;
|
|
||||||
|
|
||||||
(void) fprintf(stderr, gettext("warning: device in use checking "
|
|
||||||
"failed: %s\n"), strerror(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Validate a device, passing the bulk of the work off to libdiskmgt.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
check_slice(const char *path, int force, boolean_t wholedisk, boolean_t isspare)
|
|
||||||
{
|
|
||||||
char *msg;
|
|
||||||
int error = 0;
|
|
||||||
dm_who_type_t who;
|
|
||||||
|
|
||||||
if (force)
|
|
||||||
who = DM_WHO_ZPOOL_FORCE;
|
|
||||||
else if (isspare)
|
|
||||||
who = DM_WHO_ZPOOL_SPARE;
|
|
||||||
else
|
|
||||||
who = DM_WHO_ZPOOL;
|
|
||||||
|
|
||||||
if (dm_inuse((char *)path, &msg, who, &error) || error) {
|
|
||||||
if (error != 0) {
|
|
||||||
libdiskmgt_error(error);
|
|
||||||
return (0);
|
|
||||||
} else {
|
|
||||||
vdev_error("%s", msg);
|
|
||||||
free(msg);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we're given a whole disk, ignore overlapping slices since we're
|
|
||||||
* about to label it anyway.
|
|
||||||
*/
|
|
||||||
error = 0;
|
|
||||||
if (!wholedisk && !force &&
|
|
||||||
(dm_isoverlapping((char *)path, &msg, &error) || error)) {
|
|
||||||
if (error == 0) {
|
|
||||||
/* dm_isoverlapping returned -1 */
|
|
||||||
vdev_error(gettext("%s overlaps with %s\n"), path, msg);
|
|
||||||
free(msg);
|
|
||||||
return (-1);
|
|
||||||
} else if (error != ENODEV) {
|
|
||||||
/* libdiskmgt's devcache only handles physical drives */
|
|
||||||
libdiskmgt_error(error);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Validate a whole disk. Iterate over all slices on the disk and make sure
|
|
||||||
* that none is in use by calling check_slice().
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
check_disk(const char *name, dm_descriptor_t disk, int force, int isspare)
|
|
||||||
{
|
|
||||||
dm_descriptor_t *drive, *media, *slice;
|
|
||||||
int err = 0;
|
|
||||||
int i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the drive associated with this disk. This should never fail,
|
|
||||||
* because we already have an alias handle open for the device.
|
|
||||||
*/
|
|
||||||
if ((drive = dm_get_associated_descriptors(disk, DM_DRIVE,
|
|
||||||
&err)) == NULL || *drive == NULL) {
|
|
||||||
if (err)
|
|
||||||
libdiskmgt_error(err);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((media = dm_get_associated_descriptors(*drive, DM_MEDIA,
|
|
||||||
&err)) == NULL) {
|
|
||||||
dm_free_descriptors(drive);
|
|
||||||
if (err)
|
|
||||||
libdiskmgt_error(err);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free_descriptors(drive);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It is possible that the user has specified a removable media drive,
|
|
||||||
* and the media is not present.
|
|
||||||
*/
|
|
||||||
if (*media == NULL) {
|
|
||||||
dm_free_descriptors(media);
|
|
||||||
vdev_error(gettext("'%s' has no media in drive\n"), name);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((slice = dm_get_associated_descriptors(*media, DM_SLICE,
|
|
||||||
&err)) == NULL) {
|
|
||||||
dm_free_descriptors(media);
|
|
||||||
if (err)
|
|
||||||
libdiskmgt_error(err);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free_descriptors(media);
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Iterate over all slices and report any errors. We don't care about
|
|
||||||
* overlapping slices because we are using the whole disk.
|
|
||||||
*/
|
|
||||||
for (i = 0; slice[i] != NULL; i++) {
|
|
||||||
char *name = dm_get_name(slice[i], &err);
|
|
||||||
|
|
||||||
if (check_slice(name, force, B_TRUE, isspare) != 0)
|
|
||||||
ret = -1;
|
|
||||||
|
|
||||||
dm_free_name(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free_descriptors(slice);
|
|
||||||
return (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Validate a device.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
check_device(const char *path, boolean_t force, boolean_t isspare)
|
|
||||||
{
|
|
||||||
dm_descriptor_t desc;
|
|
||||||
int err;
|
|
||||||
char *dev;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For whole disks, libdiskmgt does not include the leading dev path.
|
|
||||||
*/
|
|
||||||
dev = strrchr(path, '/');
|
|
||||||
assert(dev != NULL);
|
|
||||||
dev++;
|
|
||||||
if ((desc = dm_get_descriptor_by_name(DM_ALIAS, dev, &err)) != NULL) {
|
|
||||||
err = check_disk(path, desc, force, isspare);
|
|
||||||
dm_free_descriptor(desc);
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (check_slice(path, force, B_FALSE, isspare));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that a file is valid. All we can do in this case is check that it's
|
* Check that a file is valid. All we can do in this case is check that it's
|
||||||
* not in use by another pool, and not in use by swap.
|
* not in use by another pool, and not in use by swap.
|
||||||
@ -283,19 +123,9 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
|
|||||||
char *name;
|
char *name;
|
||||||
int fd;
|
int fd;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int err;
|
|
||||||
pool_state_t state;
|
pool_state_t state;
|
||||||
boolean_t inuse;
|
boolean_t inuse;
|
||||||
|
|
||||||
if (dm_inuse_swap(file, &err)) {
|
|
||||||
if (err)
|
|
||||||
libdiskmgt_error(err);
|
|
||||||
else
|
|
||||||
vdev_error(gettext("%s is currently used by swap. "
|
|
||||||
"Please see swap(1M).\n"), file);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((fd = open(file, O_RDONLY)) < 0)
|
if ((fd = open(file, O_RDONLY)) < 0)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
@ -348,6 +178,175 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
|
|||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_error(int err)
|
||||||
|
{
|
||||||
|
(void) fprintf(stderr, gettext("warning: device in use checking "
|
||||||
|
"failed: %s\n"), strerror(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_slice(const char *path, blkid_cache cache, int force, boolean_t isspare)
|
||||||
|
{
|
||||||
|
struct stat64 statbuf;
|
||||||
|
int err;
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
char *value;
|
||||||
|
#endif /* HAVE_LIBBLKID */
|
||||||
|
|
||||||
|
if (stat64(path, &statbuf) != 0) {
|
||||||
|
vdev_error(gettext("cannot stat %s: %s\n"),
|
||||||
|
path, strerror(errno));
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
/* No valid type detected device is safe to use */
|
||||||
|
value = blkid_get_tag_value(cache, "TYPE", path);
|
||||||
|
if (value == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If libblkid detects a ZFS device, we check the device
|
||||||
|
* using check_file() to see if it's safe. The one safe
|
||||||
|
* case is a spare device shared between multiple pools.
|
||||||
|
*/
|
||||||
|
if (strcmp(value, "zfs") == 0) {
|
||||||
|
err = check_file(path, force, isspare);
|
||||||
|
} else {
|
||||||
|
if (force) {
|
||||||
|
err = 0;
|
||||||
|
} else {
|
||||||
|
err = -1;
|
||||||
|
vdev_error(gettext("%s contains a filesystem of "
|
||||||
|
"type '%s'\n"), path, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(value);
|
||||||
|
#else
|
||||||
|
err = check_file(path, force, isspare);
|
||||||
|
#endif /* HAVE_LIBBLKID */
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate a whole disk. Iterate over all slices on the disk and make sure
|
||||||
|
* that none is in use by calling check_slice().
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
check_disk(const char *path, blkid_cache cache, int force,
|
||||||
|
boolean_t isspare, boolean_t iswholedisk)
|
||||||
|
{
|
||||||
|
struct dk_gpt *vtoc;
|
||||||
|
char slice_path[MAXPATHLEN];
|
||||||
|
int err = 0;
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
/* This is not a wholedisk we only check the given partition */
|
||||||
|
if (!iswholedisk)
|
||||||
|
return check_slice(path, cache, force, isspare);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the device is a whole disk try to read the efi partition
|
||||||
|
* label. If this is successful we safely check the all of the
|
||||||
|
* partitions. However, when it fails it may simply be because
|
||||||
|
* the disk is partitioned via the MBR. Since we currently can
|
||||||
|
* not easily decode the MBR return a failure and prompt to the
|
||||||
|
* user to use force option since we cannot check the partitions.
|
||||||
|
*/
|
||||||
|
if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0) {
|
||||||
|
check_error(errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
vdev_error(gettext("%s does not contain an EFI "
|
||||||
|
"label but it may contain partition\n"
|
||||||
|
"information in the MBR.\n"), path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The primary efi partition label is damaged however the secondary
|
||||||
|
* label at the end of the device is intact. Rather than use this
|
||||||
|
* label we should play it safe and treat this as a non efi device.
|
||||||
|
*/
|
||||||
|
if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
|
||||||
|
efi_free(vtoc);
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
/* Partitions will no be created using the backup */
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
vdev_error(gettext("%s contains a corrupt primary "
|
||||||
|
"EFI label.\n"), path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < vtoc->efi_nparts; i++) {
|
||||||
|
|
||||||
|
if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED ||
|
||||||
|
uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
|
||||||
|
(void) snprintf(slice_path, sizeof (slice_path),
|
||||||
|
"%s%s%d", path, "-part", i+1);
|
||||||
|
else
|
||||||
|
(void) snprintf(slice_path, sizeof (slice_path),
|
||||||
|
"%s%s%d", path, isdigit(path[strlen(path)-1]) ?
|
||||||
|
"p" : "", i+1);
|
||||||
|
|
||||||
|
err = check_slice(slice_path, cache, force, isspare);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
efi_free(vtoc);
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_device(const char *path, boolean_t force,
|
||||||
|
boolean_t isspare, boolean_t iswholedisk)
|
||||||
|
{
|
||||||
|
static blkid_cache cache = NULL;
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
/*
|
||||||
|
* There is no easy way to add a correct blkid_put_cache() call,
|
||||||
|
* memory will be reclaimed when the command exits.
|
||||||
|
*/
|
||||||
|
if (cache == NULL) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if ((err = blkid_get_cache(&cache, NULL)) != 0) {
|
||||||
|
check_error(err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = blkid_probe_all(cache)) != 0) {
|
||||||
|
blkid_put_cache(cache);
|
||||||
|
check_error(err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* HAVE_LIBBLKID */
|
||||||
|
|
||||||
|
return check_disk(path, cache, force, isspare, iswholedisk);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By "whole disk" we mean an entire physical disk (something we can
|
* By "whole disk" we mean an entire physical disk (something we can
|
||||||
@ -358,15 +357,12 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
|
|||||||
* it isn't.
|
* it isn't.
|
||||||
*/
|
*/
|
||||||
static boolean_t
|
static boolean_t
|
||||||
is_whole_disk(const char *arg)
|
is_whole_disk(const char *path)
|
||||||
{
|
{
|
||||||
struct dk_gpt *label;
|
struct dk_gpt *label;
|
||||||
int fd;
|
int fd;
|
||||||
char path[MAXPATHLEN];
|
|
||||||
|
|
||||||
(void) snprintf(path, sizeof (path), "%s%s%s",
|
if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0)
|
||||||
RDISK_ROOT, strrchr(arg, '/'), BACKUP_SLICE);
|
|
||||||
if ((fd = open(path, O_RDWR | O_NDELAY)) < 0)
|
|
||||||
return (B_FALSE);
|
return (B_FALSE);
|
||||||
if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
|
if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
|
||||||
(void) close(fd);
|
(void) close(fd);
|
||||||
@ -377,14 +373,52 @@ is_whole_disk(const char *arg)
|
|||||||
return (B_TRUE);
|
return (B_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This may be a shorthand device path or it could be total gibberish.
|
||||||
|
* Check to see if it's a known device in /dev/, /dev/disk/by-id,
|
||||||
|
* /dev/disk/by-label, /dev/disk/by-path, /dev/disk/by-uuid, or
|
||||||
|
* /dev/disk/zpool/. As part of this check, see if we've been given
|
||||||
|
* an entire disk (minus the slice number).
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_shorthand_path(const char *arg, char *path,
|
||||||
|
struct stat64 *statbuf, boolean_t *wholedisk)
|
||||||
|
{
|
||||||
|
char dirs[5][8] = {"by-id", "by-label", "by-path", "by-uuid", "zpool"};
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
/* /dev/<name> */
|
||||||
|
(void) snprintf(path, MAXPATHLEN, "%s/%s", DISK_ROOT, arg);
|
||||||
|
*wholedisk = is_whole_disk(path);
|
||||||
|
err = stat64(path, statbuf);
|
||||||
|
if (*wholedisk || err == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* /dev/disk/<dirs>/<name> */
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
(void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
|
||||||
|
UDISK_ROOT, dirs[i], arg);
|
||||||
|
*wholedisk = is_whole_disk(path);
|
||||||
|
err = stat64(path, statbuf);
|
||||||
|
if (*wholedisk || err == 0)
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(path, arg, sizeof(path));
|
||||||
|
memset(statbuf, 0, sizeof(*statbuf));
|
||||||
|
*wholedisk = B_FALSE;
|
||||||
|
|
||||||
|
return (ENOENT);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a leaf vdev. Determine if this is a file or a device. If it's a
|
* Create a leaf vdev. Determine if this is a file or a device. If it's a
|
||||||
* device, fill in the device id to make a complete nvlist. Valid forms for a
|
* device, fill in the device id to make a complete nvlist. Valid forms for a
|
||||||
* leaf vdev are:
|
* leaf vdev are:
|
||||||
*
|
*
|
||||||
* /dev/dsk/xxx Complete disk path
|
* /dev/xxx Complete disk path
|
||||||
* /xxx Full path to file
|
* /xxx Full path to file
|
||||||
* xxx Shorthand for /dev/dsk/xxx
|
* xxx Shorthand for /dev/disk/yyy/xxx
|
||||||
*/
|
*/
|
||||||
static nvlist_t *
|
static nvlist_t *
|
||||||
make_leaf_vdev(const char *arg, uint64_t is_log)
|
make_leaf_vdev(const char *arg, uint64_t is_log)
|
||||||
@ -394,6 +428,7 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
|
|||||||
nvlist_t *vdev = NULL;
|
nvlist_t *vdev = NULL;
|
||||||
char *type = NULL;
|
char *type = NULL;
|
||||||
boolean_t wholedisk = B_FALSE;
|
boolean_t wholedisk = B_FALSE;
|
||||||
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine what type of vdev this is, and put the full path into
|
* Determine what type of vdev this is, and put the full path into
|
||||||
@ -403,28 +438,31 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
|
|||||||
if (arg[0] == '/') {
|
if (arg[0] == '/') {
|
||||||
/*
|
/*
|
||||||
* Complete device or file path. Exact type is determined by
|
* Complete device or file path. Exact type is determined by
|
||||||
* examining the file descriptor afterwards.
|
* examining the file descriptor afterwards. Symbolic links
|
||||||
|
* are resolved to their real paths for the is_whole_disk()
|
||||||
|
* and S_ISBLK/S_ISREG type checks. However, we are careful
|
||||||
|
* to store the given path as ZPOOL_CONFIG_PATH to ensure we
|
||||||
|
* can leverage udev's persistent device labels.
|
||||||
*/
|
*/
|
||||||
wholedisk = is_whole_disk(arg);
|
if (realpath(arg, path) == NULL) {
|
||||||
if (!wholedisk && (stat64(arg, &statbuf) != 0)) {
|
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
gettext("cannot open '%s': %s\n"),
|
gettext("cannot resolve path '%s'\n"), arg);
|
||||||
arg, strerror(errno));
|
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) strlcpy(path, arg, sizeof (path));
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* This may be a short path for a device, or it could be total
|
|
||||||
* gibberish. Check to see if it's a known device in
|
|
||||||
* /dev/dsk/. As part of this check, see if we've been given a
|
|
||||||
* an entire disk (minus the slice number).
|
|
||||||
*/
|
|
||||||
(void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT,
|
|
||||||
arg);
|
|
||||||
wholedisk = is_whole_disk(path);
|
wholedisk = is_whole_disk(path);
|
||||||
if (!wholedisk && (stat64(path, &statbuf) != 0)) {
|
if (!wholedisk && (stat64(path, &statbuf) != 0)) {
|
||||||
|
(void) fprintf(stderr,
|
||||||
|
gettext("cannot open '%s': %s\n"),
|
||||||
|
path, strerror(errno));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* After is_whole_disk() check restore original passed path */
|
||||||
|
strlcpy(path, arg, MAXPATHLEN);
|
||||||
|
} else {
|
||||||
|
err = is_shorthand_path(arg, path, &statbuf, &wholedisk);
|
||||||
|
if (err != 0) {
|
||||||
/*
|
/*
|
||||||
* If we got ENOENT, then the user gave us
|
* If we got ENOENT, then the user gave us
|
||||||
* gibberish, so try to direct them with a
|
* gibberish, so try to direct them with a
|
||||||
@ -432,7 +470,7 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
|
|||||||
* regurgitate strerror() since it's the best we
|
* regurgitate strerror() since it's the best we
|
||||||
* can do.
|
* can do.
|
||||||
*/
|
*/
|
||||||
if (errno == ENOENT) {
|
if (err == ENOENT) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
gettext("cannot open '%s': no such "
|
gettext("cannot open '%s': no such "
|
||||||
"device in %s\n"), arg, DISK_ROOT);
|
"device in %s\n"), arg, DISK_ROOT);
|
||||||
@ -475,6 +513,7 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
|
|||||||
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
|
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
|
||||||
(uint64_t)wholedisk) == 0);
|
(uint64_t)wholedisk) == 0);
|
||||||
|
|
||||||
|
#if defined(__sun__) || defined(__sun)
|
||||||
/*
|
/*
|
||||||
* For a whole disk, defer getting its devid until after labeling it.
|
* For a whole disk, defer getting its devid until after labeling it.
|
||||||
*/
|
*/
|
||||||
@ -486,7 +525,7 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
|
|||||||
ddi_devid_t devid;
|
ddi_devid_t devid;
|
||||||
char *minor = NULL, *devid_str = NULL;
|
char *minor = NULL, *devid_str = NULL;
|
||||||
|
|
||||||
if ((fd = open(path, O_RDONLY)) < 0) {
|
if ((fd = open(path, O_RDONLY|O_EXCL)) < 0) {
|
||||||
(void) fprintf(stderr, gettext("cannot open '%s': "
|
(void) fprintf(stderr, gettext("cannot open '%s': "
|
||||||
"%s\n"), path, strerror(errno));
|
"%s\n"), path, strerror(errno));
|
||||||
nvlist_free(vdev);
|
nvlist_free(vdev);
|
||||||
@ -509,6 +548,7 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
|
|||||||
|
|
||||||
(void) close(fd);
|
(void) close(fd);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return (vdev);
|
return (vdev);
|
||||||
}
|
}
|
||||||
@ -871,6 +911,39 @@ check_replication(nvlist_t *config, nvlist_t *newroot)
|
|||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
zero_label(char *path)
|
||||||
|
{
|
||||||
|
const int size = 4096;
|
||||||
|
char buf[size];
|
||||||
|
int err, fd;
|
||||||
|
|
||||||
|
if ((fd = open(path, O_WRONLY|O_EXCL)) < 0) {
|
||||||
|
(void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
|
||||||
|
path, strerror(errno));
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buf, 0, size);
|
||||||
|
err = write(fd, buf, size);
|
||||||
|
(void) fdatasync(fd);
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
if (err == -1) {
|
||||||
|
(void) fprintf(stderr, gettext("cannot zero first %d bytes "
|
||||||
|
"of '%s': %s\n"), size, path, strerror(errno));
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != size) {
|
||||||
|
(void) fprintf(stderr, gettext("could only zero %d/%d bytes "
|
||||||
|
"of '%s'\n"), err, size, path);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go through and find any whole disks in the vdev specification, labelling them
|
* Go through and find any whole disks in the vdev specification, labelling them
|
||||||
* as appropriate. When constructing the vdev spec, we were unable to open this
|
* as appropriate. When constructing the vdev spec, we were unable to open this
|
||||||
@ -889,10 +962,7 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
|
|||||||
char *type, *path, *diskname;
|
char *type, *path, *diskname;
|
||||||
char buf[MAXPATHLEN];
|
char buf[MAXPATHLEN];
|
||||||
uint64_t wholedisk;
|
uint64_t wholedisk;
|
||||||
int fd;
|
|
||||||
int ret;
|
int ret;
|
||||||
ddi_devid_t devid;
|
|
||||||
char *minor = NULL, *devid_str = NULL;
|
|
||||||
|
|
||||||
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
|
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
|
||||||
|
|
||||||
@ -903,55 +973,66 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have a disk device. Get the path to the device
|
* We have a disk device. If this is a whole disk write
|
||||||
* and see if it's a whole disk by appending the backup
|
* out the efi partition table, otherwise write zero's to
|
||||||
* slice and stat()ing the device.
|
* the first 4k of the partition. This is to ensure that
|
||||||
|
* libblkid will not misidentify the partition due to a
|
||||||
|
* magic value left by the previous filesystem.
|
||||||
*/
|
*/
|
||||||
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
|
verify(!nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path));
|
||||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
|
verify(!nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
|
||||||
&wholedisk) != 0 || !wholedisk)
|
&wholedisk));
|
||||||
return (0);
|
|
||||||
|
|
||||||
diskname = strrchr(path, '/');
|
if (!wholedisk) {
|
||||||
|
ret = zero_label(path);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realpath(path, buf) == NULL) {
|
||||||
|
ret = errno;
|
||||||
|
(void) fprintf(stderr,
|
||||||
|
gettext("cannot resolve path '%s'\n"), path);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
diskname = strrchr(buf, '/');
|
||||||
assert(diskname != NULL);
|
assert(diskname != NULL);
|
||||||
diskname++;
|
diskname++;
|
||||||
if (zpool_label_disk(g_zfs, zhp, diskname) == -1)
|
if (zpool_label_disk(g_zfs, zhp, diskname) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill in the devid, now that we've labeled the disk.
|
* Now the we've labeled the disk and the partitions have
|
||||||
|
* been created. We still need to wait for udev to create
|
||||||
|
* the symlinks to those partitions. If we are accessing
|
||||||
|
* the devices via a udev disk path, /dev/disk, then wait
|
||||||
|
* for *-part# to be created. Otherwise just use the normal
|
||||||
|
* syntax for devices in /dev.
|
||||||
*/
|
*/
|
||||||
(void) snprintf(buf, sizeof (buf), "%ss0", path);
|
if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
|
||||||
if ((fd = open(buf, O_RDONLY)) < 0) {
|
(void) snprintf(buf, sizeof (buf),
|
||||||
|
"%s%s%s", path, "-part", FIRST_SLICE);
|
||||||
|
else
|
||||||
|
(void) snprintf(buf, sizeof (buf),
|
||||||
|
"%s%s%s", path, isdigit(path[strlen(path)-1]) ?
|
||||||
|
"p" : "", FIRST_SLICE);
|
||||||
|
|
||||||
|
if ((ret = zpool_label_disk_wait(buf, 1000)) != 0) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
gettext("cannot open '%s': %s\n"),
|
gettext( "cannot resolve path '%s'\n"), buf);
|
||||||
buf, strerror(errno));
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (devid_get(fd, &devid) == 0) {
|
|
||||||
if (devid_get_minor_name(fd, &minor) == 0 &&
|
|
||||||
(devid_str = devid_str_encode(devid, minor)) !=
|
|
||||||
NULL) {
|
|
||||||
verify(nvlist_add_string(nv,
|
|
||||||
ZPOOL_CONFIG_DEVID, devid_str) == 0);
|
|
||||||
}
|
|
||||||
if (devid_str != NULL)
|
|
||||||
devid_str_free(devid_str);
|
|
||||||
if (minor != NULL)
|
|
||||||
devid_str_free(minor);
|
|
||||||
devid_free(devid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the path to refer to the 's0' slice. The presence of
|
* Update the path to refer to FIRST_SLICE. The presence of
|
||||||
* the 'whole_disk' field indicates to the CLI that we should
|
* the 'whole_disk' field indicates to the CLI that we should
|
||||||
* chop off the slice number when displaying the device in
|
* chop off the slice number when displaying the device in
|
||||||
* future output.
|
* future output.
|
||||||
*/
|
*/
|
||||||
verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, buf) == 0);
|
verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, buf) == 0);
|
||||||
|
|
||||||
(void) close(fd);
|
/* Just in case this partition already existed. */
|
||||||
|
(void) zero_label(buf);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
@ -991,7 +1072,7 @@ is_spare(nvlist_t *config, const char *path)
|
|||||||
uint_t i, nspares;
|
uint_t i, nspares;
|
||||||
boolean_t inuse;
|
boolean_t inuse;
|
||||||
|
|
||||||
if ((fd = open(path, O_RDONLY)) < 0)
|
if ((fd = open(path, O_RDONLY|O_EXCL)) < 0)
|
||||||
return (B_FALSE);
|
return (B_FALSE);
|
||||||
|
|
||||||
if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
|
if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
|
||||||
@ -1034,25 +1115,27 @@ check_in_use(nvlist_t *config, nvlist_t *nv, boolean_t force,
|
|||||||
nvlist_t **child;
|
nvlist_t **child;
|
||||||
uint_t c, children;
|
uint_t c, children;
|
||||||
char *type, *path;
|
char *type, *path;
|
||||||
int ret;
|
int ret = 0;
|
||||||
char buf[MAXPATHLEN];
|
char buf[MAXPATHLEN];
|
||||||
uint64_t wholedisk;
|
uint64_t wholedisk = B_FALSE;
|
||||||
|
|
||||||
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
|
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
|
||||||
|
|
||||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
||||||
&child, &children) != 0) {
|
&child, &children) != 0) {
|
||||||
|
|
||||||
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
|
verify(!nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path));
|
||||||
|
if (strcmp(type, VDEV_TYPE_DISK) == 0)
|
||||||
|
verify(!nvlist_lookup_uint64(nv,
|
||||||
|
ZPOOL_CONFIG_WHOLE_DISK, &wholedisk));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As a generic check, we look to see if this is a replace of a
|
* As a generic check, we look to see if this is a replace of a
|
||||||
* hot spare within the same pool. If so, we allow it
|
* hot spare within the same pool. If so, we allow it
|
||||||
* regardless of what libdiskmgt or zpool_in_use() says.
|
* regardless of what libblkid or zpool_in_use() says.
|
||||||
*/
|
*/
|
||||||
if (replacing) {
|
if (replacing) {
|
||||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
|
if (wholedisk)
|
||||||
&wholedisk) == 0 && wholedisk)
|
|
||||||
(void) snprintf(buf, sizeof (buf), "%ss0",
|
(void) snprintf(buf, sizeof (buf), "%ss0",
|
||||||
path);
|
path);
|
||||||
else
|
else
|
||||||
@ -1063,7 +1146,7 @@ check_in_use(nvlist_t *config, nvlist_t *nv, boolean_t force,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(type, VDEV_TYPE_DISK) == 0)
|
if (strcmp(type, VDEV_TYPE_DISK) == 0)
|
||||||
ret = check_device(path, force, isspare);
|
ret = check_device(path, force, isspare, wholedisk);
|
||||||
|
|
||||||
if (strcmp(type, VDEV_TYPE_FILE) == 0)
|
if (strcmp(type, VDEV_TYPE_FILE) == 0)
|
||||||
ret = check_file(path, force, isspare);
|
ret = check_file(path, force, isspare);
|
||||||
|
@ -74,12 +74,8 @@ struct uuid {
|
|||||||
uint8_t node_addr[6];
|
uint8_t node_addr[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define UUID_LEN 16
|
|
||||||
|
|
||||||
#define UUID_PRINTABLE_STRING_LENGTH 37
|
#define UUID_PRINTABLE_STRING_LENGTH 37
|
||||||
|
|
||||||
typedef uchar_t uuid_t[UUID_LEN];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert a uuid to/from little-endian format
|
* Convert a uuid to/from little-endian format
|
||||||
*/
|
*/
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <uuid/uuid.h>
|
#include <uuid/uuid.h>
|
||||||
|
#include <zlib.h>
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/dkio.h>
|
#include <sys/dkio.h>
|
||||||
@ -38,7 +39,9 @@
|
|||||||
#include <sys/dktp/fdisk.h>
|
#include <sys/dktp/fdisk.h>
|
||||||
#include <sys/efi_partition.h>
|
#include <sys/efi_partition.h>
|
||||||
#include <sys/byteorder.h>
|
#include <sys/byteorder.h>
|
||||||
#include <sys/ddi.h>
|
#if defined(__linux__)
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct uuid_to_ptag {
|
static struct uuid_to_ptag {
|
||||||
struct uuid uuid;
|
struct uuid uuid;
|
||||||
@ -49,11 +52,11 @@ static struct uuid_to_ptag {
|
|||||||
{ EFI_SWAP },
|
{ EFI_SWAP },
|
||||||
{ EFI_USR },
|
{ EFI_USR },
|
||||||
{ EFI_BACKUP },
|
{ EFI_BACKUP },
|
||||||
{ 0 }, /* STAND is never used */
|
{ EFI_UNUSED }, /* STAND is never used */
|
||||||
{ EFI_VAR },
|
{ EFI_VAR },
|
||||||
{ EFI_HOME },
|
{ EFI_HOME },
|
||||||
{ EFI_ALTSCTR },
|
{ EFI_ALTSCTR },
|
||||||
{ 0 }, /* CACHE (cachefs) is never used */
|
{ EFI_UNUSED }, /* CACHE (cachefs) is never used */
|
||||||
{ EFI_RESERVED },
|
{ EFI_RESERVED },
|
||||||
{ EFI_SYSTEM },
|
{ EFI_SYSTEM },
|
||||||
{ EFI_LEGACY_MBR },
|
{ EFI_LEGACY_MBR },
|
||||||
@ -107,21 +110,144 @@ int efi_debug = 1;
|
|||||||
int efi_debug = 0;
|
int efi_debug = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern unsigned int efi_crc32(const unsigned char *, unsigned int);
|
static int efi_read(int, struct dk_gpt *);
|
||||||
static int efi_read(int, struct dk_gpt *);
|
|
||||||
|
/*
|
||||||
|
* Return a 32-bit CRC of the contents of the buffer. Pre-and-post
|
||||||
|
* one's conditioning will be handled by crc32() internally.
|
||||||
|
*/
|
||||||
|
static uint32_t
|
||||||
|
efi_crc32(const unsigned char *buf, unsigned int size)
|
||||||
|
{
|
||||||
|
uint32_t crc = crc32(0, Z_NULL, 0);
|
||||||
|
|
||||||
|
crc = crc32(crc, buf, size);
|
||||||
|
|
||||||
|
return (crc);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize)
|
read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize)
|
||||||
{
|
{
|
||||||
struct dk_minfo disk_info;
|
int sector_size;
|
||||||
|
unsigned long long capacity_size;
|
||||||
|
|
||||||
|
if (ioctl(fd, BLKSSZGET, §or_size) < 0)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if (ioctl(fd, BLKGETSIZE64, &capacity_size) < 0)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
*lbsize = (uint_t)sector_size;
|
||||||
|
*capacity = (diskaddr_t)(capacity_size / sector_size);
|
||||||
|
|
||||||
if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
|
|
||||||
return (errno);
|
|
||||||
*capacity = disk_info.dki_capacity;
|
|
||||||
*lbsize = disk_info.dki_lbsize;
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
efi_get_info(int fd, struct dk_cinfo *dki_info)
|
||||||
|
{
|
||||||
|
#if defined(__linux__)
|
||||||
|
char *path;
|
||||||
|
char *dev_path;
|
||||||
|
int rval = 0;
|
||||||
|
|
||||||
|
memset(dki_info, 0, sizeof(*dki_info));
|
||||||
|
|
||||||
|
path = calloc(PATH_MAX, 1);
|
||||||
|
if (path == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The simplest way to get the partition number under linux is
|
||||||
|
* to parse it out of the /dev/<disk><parition> block device name.
|
||||||
|
* The kernel creates this using the partition number when it
|
||||||
|
* populates /dev/ so it may be trusted. The tricky bit here is
|
||||||
|
* that the naming convention is based on the block device type.
|
||||||
|
* So we need to take this in to account when parsing out the
|
||||||
|
* partition information. Another issue is that the libefi API
|
||||||
|
* API only provides the open fd and not the file path. To handle
|
||||||
|
* this realpath(3) is used to resolve the block device name from
|
||||||
|
* /proc/self/fd/<fd>. Aside from the partition number we collect
|
||||||
|
* some additional device info.
|
||||||
|
*/
|
||||||
|
(void) sprintf(path, "/proc/self/fd/%d", fd);
|
||||||
|
dev_path = realpath(path, NULL);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
if (dev_path == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if ((strncmp(dev_path, "/dev/sd", 7) == 0)) {
|
||||||
|
strcpy(dki_info->dki_cname, "sd");
|
||||||
|
dki_info->dki_ctype = DKC_SCSI_CCS;
|
||||||
|
rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
|
||||||
|
dki_info->dki_dname,
|
||||||
|
&dki_info->dki_partition);
|
||||||
|
} else if ((strncmp(dev_path, "/dev/hd", 7) == 0)) {
|
||||||
|
strcpy(dki_info->dki_cname, "hd");
|
||||||
|
dki_info->dki_ctype = DKC_DIRECT;
|
||||||
|
rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu",
|
||||||
|
dki_info->dki_dname,
|
||||||
|
&dki_info->dki_partition);
|
||||||
|
} else if ((strncmp(dev_path, "/dev/md", 7) == 0)) {
|
||||||
|
strcpy(dki_info->dki_cname, "pseudo");
|
||||||
|
dki_info->dki_ctype = DKC_MD;
|
||||||
|
rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
|
||||||
|
dki_info->dki_dname,
|
||||||
|
&dki_info->dki_partition);
|
||||||
|
} else if ((strncmp(dev_path, "/dev/dm-", 8) == 0)) {
|
||||||
|
strcpy(dki_info->dki_cname, "pseudo");
|
||||||
|
dki_info->dki_ctype = DKC_VBD;
|
||||||
|
rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9-]p%hu",
|
||||||
|
dki_info->dki_dname,
|
||||||
|
&dki_info->dki_partition);
|
||||||
|
} else if ((strncmp(dev_path, "/dev/ram", 8) == 0)) {
|
||||||
|
strcpy(dki_info->dki_cname, "pseudo");
|
||||||
|
dki_info->dki_ctype = DKC_PCMCIA_MEM;
|
||||||
|
rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
|
||||||
|
dki_info->dki_dname,
|
||||||
|
&dki_info->dki_partition);
|
||||||
|
} else if ((strncmp(dev_path, "/dev/loop", 9) == 0)) {
|
||||||
|
strcpy(dki_info->dki_cname, "pseudo");
|
||||||
|
dki_info->dki_ctype = DKC_VBD;
|
||||||
|
rval = sscanf(dev_path, "/dev/%[a-zA-Z0-9]p%hu",
|
||||||
|
dki_info->dki_dname,
|
||||||
|
&dki_info->dki_partition);
|
||||||
|
} else {
|
||||||
|
strcpy(dki_info->dki_dname, "unknown");
|
||||||
|
strcpy(dki_info->dki_cname, "unknown");
|
||||||
|
dki_info->dki_ctype = DKC_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rval) {
|
||||||
|
case 0:
|
||||||
|
errno = EINVAL;
|
||||||
|
goto error;
|
||||||
|
case 1:
|
||||||
|
dki_info->dki_partition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dev_path);
|
||||||
|
#else
|
||||||
|
if (ioctl(fd, DKIOCINFO, (caddr_t)dki_info) == -1)
|
||||||
|
goto error;
|
||||||
|
#endif
|
||||||
|
return (0);
|
||||||
|
error:
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno);
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case EIO:
|
||||||
|
return (VT_EIO);
|
||||||
|
case EINVAL:
|
||||||
|
return (VT_EINVAL);
|
||||||
|
default:
|
||||||
|
return (VT_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the number of blocks the EFI label takes up (round up to nearest
|
* the number of blocks the EFI label takes up (round up to nearest
|
||||||
* block)
|
* block)
|
||||||
@ -135,12 +261,13 @@ read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize)
|
|||||||
int
|
int
|
||||||
efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc)
|
efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc)
|
||||||
{
|
{
|
||||||
diskaddr_t capacity;
|
diskaddr_t capacity = 0;
|
||||||
uint_t lbsize;
|
uint_t lbsize = 0;
|
||||||
uint_t nblocks;
|
uint_t nblocks;
|
||||||
size_t length;
|
size_t length;
|
||||||
struct dk_gpt *vptr;
|
struct dk_gpt *vptr;
|
||||||
struct uuid uuid;
|
struct uuid uuid;
|
||||||
|
struct dk_cinfo dki_info;
|
||||||
|
|
||||||
if (read_disk_info(fd, &capacity, &lbsize) != 0) {
|
if (read_disk_info(fd, &capacity, &lbsize) != 0) {
|
||||||
if (efi_debug)
|
if (efi_debug)
|
||||||
@ -148,6 +275,22 @@ efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc)
|
|||||||
"couldn't read disk information\n");
|
"couldn't read disk information\n");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
#if defined(__linux__)
|
||||||
|
if (efi_get_info(fd, &dki_info) != 0) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr,
|
||||||
|
"couldn't read disk information\n");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dki_info.dki_partition != 0)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if ((dki_info.dki_ctype == DKC_PCMCIA_MEM) ||
|
||||||
|
(dki_info.dki_ctype == DKC_VBD) ||
|
||||||
|
(dki_info.dki_ctype == DKC_UNKNOWN))
|
||||||
|
return (-1);
|
||||||
|
#endif
|
||||||
|
|
||||||
nblocks = NBLOCKS(nparts, lbsize);
|
nblocks = NBLOCKS(nparts, lbsize);
|
||||||
if ((nblocks * lbsize) < EFI_MIN_ARRAY_SIZE + lbsize) {
|
if ((nblocks * lbsize) < EFI_MIN_ARRAY_SIZE + lbsize) {
|
||||||
@ -243,14 +386,138 @@ efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
|
|||||||
{
|
{
|
||||||
void *data = dk_ioc->dki_data;
|
void *data = dk_ioc->dki_data;
|
||||||
int error;
|
int error;
|
||||||
|
#if defined(__linux__)
|
||||||
|
diskaddr_t capacity;
|
||||||
|
uint_t lbsize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the IO is not being performed in kernel as an ioctl we need
|
||||||
|
* to know the sector size so we can seek to the proper byte offset.
|
||||||
|
*/
|
||||||
|
if (read_disk_info(fd, &capacity, &lbsize) == -1) {
|
||||||
|
if (efi_debug)
|
||||||
|
fprintf(stderr,"unable to read disk info: %d",errno);
|
||||||
|
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case DKIOCGETEFI:
|
||||||
|
if (lbsize == 0) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCGETEFI assuming "
|
||||||
|
"LBA %d bytes\n", DEV_BSIZE);
|
||||||
|
|
||||||
|
lbsize = DEV_BSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET);
|
||||||
|
if (error == -1) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCGETEFI lseek "
|
||||||
|
"error: %d\n", errno);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = read(fd, data, dk_ioc->dki_length);
|
||||||
|
if (error == -1) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCGETEFI read "
|
||||||
|
"error: %d\n", errno);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != dk_ioc->dki_length) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCGETEFI short "
|
||||||
|
"read of %d bytes\n", error);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DKIOCSETEFI:
|
||||||
|
if (lbsize == 0) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCSETEFI unknown "
|
||||||
|
"LBA size\n");
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET);
|
||||||
|
if (error == -1) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCSETEFI lseek "
|
||||||
|
"error: %d\n", errno);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = write(fd, data, dk_ioc->dki_length);
|
||||||
|
if (error == -1) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCSETEFI write "
|
||||||
|
"error: %d\n", errno);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != dk_ioc->dki_length) {
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "DKIOCSETEFI short "
|
||||||
|
"write of %d bytes\n", error);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sync the new EFI table to disk */
|
||||||
|
error = fsync(fd);
|
||||||
|
if (error == -1)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* Ensure any local disk cache is also flushed */
|
||||||
|
if (ioctl(fd, BLKFLSBUF, 0) == -1)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (efi_debug)
|
||||||
|
(void) fprintf(stderr, "unsupported ioctl()\n");
|
||||||
|
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data;
|
dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data;
|
||||||
error = ioctl(fd, cmd, (void *)dk_ioc);
|
error = ioctl(fd, cmd, (void *)dk_ioc);
|
||||||
dk_ioc->dki_data = data;
|
dk_ioc->dki_data = data;
|
||||||
|
#endif
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
static int
|
||||||
|
efi_rescan(int fd)
|
||||||
|
{
|
||||||
|
int retry = 5;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/* Notify the kernel a devices partition table has been updated */
|
||||||
|
while ((error = ioctl(fd, BLKRRPART)) != 0) {
|
||||||
|
if (--retry == 0) {
|
||||||
|
(void) fprintf(stderr, "the kernel failed to rescan "
|
||||||
|
"the partition table: %d\n", errno);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
check_label(int fd, dk_efi_t *dk_ioc)
|
check_label(int fd, dk_efi_t *dk_ioc)
|
||||||
{
|
{
|
||||||
@ -305,6 +572,8 @@ efi_read(int fd, struct dk_gpt *vtoc)
|
|||||||
int rval = 0;
|
int rval = 0;
|
||||||
int md_flag = 0;
|
int md_flag = 0;
|
||||||
int vdc_flag = 0;
|
int vdc_flag = 0;
|
||||||
|
diskaddr_t capacity = 0;
|
||||||
|
uint_t lbsize = 0;
|
||||||
struct dk_minfo disk_info;
|
struct dk_minfo disk_info;
|
||||||
dk_efi_t dk_ioc;
|
dk_efi_t dk_ioc;
|
||||||
efi_gpt_t *efi;
|
efi_gpt_t *efi;
|
||||||
@ -316,19 +585,9 @@ efi_read(int fd, struct dk_gpt *vtoc)
|
|||||||
/*
|
/*
|
||||||
* get the partition number for this file descriptor.
|
* get the partition number for this file descriptor.
|
||||||
*/
|
*/
|
||||||
if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
|
if ((rval = efi_get_info(fd, &dki_info)) != 0)
|
||||||
if (efi_debug) {
|
return rval;
|
||||||
(void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno);
|
|
||||||
}
|
|
||||||
switch (errno) {
|
|
||||||
case EIO:
|
|
||||||
return (VT_EIO);
|
|
||||||
case EINVAL:
|
|
||||||
return (VT_EINVAL);
|
|
||||||
default:
|
|
||||||
return (VT_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
|
if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
|
||||||
(strncmp(dki_info.dki_dname, "md", 3) == 0)) {
|
(strncmp(dki_info.dki_dname, "md", 3) == 0)) {
|
||||||
md_flag++;
|
md_flag++;
|
||||||
@ -342,14 +601,18 @@ efi_read(int fd, struct dk_gpt *vtoc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* get the LBA size */
|
/* get the LBA size */
|
||||||
if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) {
|
if (read_disk_info(fd, &capacity, &lbsize) == -1) {
|
||||||
if (efi_debug) {
|
if (efi_debug) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
"assuming LBA 512 bytes %d\n",
|
"unable to read disk info: %d",
|
||||||
errno);
|
errno);
|
||||||
}
|
}
|
||||||
disk_info.dki_lbsize = DEV_BSIZE;
|
return (VT_EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disk_info.dki_lbsize = lbsize;
|
||||||
|
disk_info.dki_capacity = capacity;
|
||||||
|
|
||||||
if (disk_info.dki_lbsize == 0) {
|
if (disk_info.dki_lbsize == 0) {
|
||||||
if (efi_debug) {
|
if (efi_debug) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
@ -374,9 +637,11 @@ efi_read(int fd, struct dk_gpt *vtoc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((dk_ioc.dki_data = calloc(label_len, 1)) == NULL)
|
if (posix_memalign((void **)&dk_ioc.dki_data,
|
||||||
|
disk_info.dki_lbsize, label_len))
|
||||||
return (VT_ERROR);
|
return (VT_ERROR);
|
||||||
|
|
||||||
|
memset(dk_ioc.dki_data, 0, label_len);
|
||||||
dk_ioc.dki_length = disk_info.dki_lbsize;
|
dk_ioc.dki_length = disk_info.dki_lbsize;
|
||||||
user_length = vtoc->efi_nparts;
|
user_length = vtoc->efi_nparts;
|
||||||
efi = dk_ioc.dki_data;
|
efi = dk_ioc.dki_data;
|
||||||
@ -572,12 +837,14 @@ write_pmbr(int fd, struct dk_gpt *vtoc)
|
|||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = (vtoc->efi_lbasize == 0) ? sizeof (mb) : vtoc->efi_lbasize;
|
len = (vtoc->efi_lbasize == 0) ? sizeof (mb) : vtoc->efi_lbasize;
|
||||||
buf = calloc(len, 1);
|
if (posix_memalign((void **)&buf, len, len))
|
||||||
|
return (VT_ERROR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Preserve any boot code and disk signature if the first block is
|
* Preserve any boot code and disk signature if the first block is
|
||||||
* already an MBR.
|
* already an MBR.
|
||||||
*/
|
*/
|
||||||
|
memset(buf, 0, len);
|
||||||
dk_ioc.dki_lba = 0;
|
dk_ioc.dki_lba = 0;
|
||||||
dk_ioc.dki_length = len;
|
dk_ioc.dki_length = len;
|
||||||
/* LINTED -- always longlong aligned */
|
/* LINTED -- always longlong aligned */
|
||||||
@ -663,10 +930,9 @@ check_input(struct dk_gpt *vtoc)
|
|||||||
if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) &&
|
if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) &&
|
||||||
(vtoc->efi_parts[i].p_size != 0)) {
|
(vtoc->efi_parts[i].p_size != 0)) {
|
||||||
if (efi_debug) {
|
if (efi_debug) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr, "partition %d is "
|
||||||
"partition %d is \"unassigned\" but has a size of %llu",
|
"\"unassigned\" but has a size of %llu",
|
||||||
i,
|
i, vtoc->efi_parts[i].p_size);
|
||||||
vtoc->efi_parts[i].p_size);
|
|
||||||
}
|
}
|
||||||
return (VT_EINVAL);
|
return (VT_EINVAL);
|
||||||
}
|
}
|
||||||
@ -679,9 +945,9 @@ check_input(struct dk_gpt *vtoc)
|
|||||||
if (vtoc->efi_parts[i].p_tag == V_RESERVED) {
|
if (vtoc->efi_parts[i].p_tag == V_RESERVED) {
|
||||||
if (resv_part != -1) {
|
if (resv_part != -1) {
|
||||||
if (efi_debug) {
|
if (efi_debug) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr, "found "
|
||||||
"found duplicate reserved partition at %d\n",
|
"duplicate reserved partition "
|
||||||
i);
|
"at %d\n", i);
|
||||||
}
|
}
|
||||||
return (VT_EINVAL);
|
return (VT_EINVAL);
|
||||||
}
|
}
|
||||||
@ -732,8 +998,8 @@ check_input(struct dk_gpt *vtoc)
|
|||||||
(istart <= endsect)) {
|
(istart <= endsect)) {
|
||||||
if (efi_debug) {
|
if (efi_debug) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
"Partition %d overlaps partition %d.",
|
"Partition %d overlaps "
|
||||||
i, j);
|
"partition %d.", i, j);
|
||||||
}
|
}
|
||||||
return (VT_EINVAL);
|
return (VT_EINVAL);
|
||||||
}
|
}
|
||||||
@ -839,22 +1105,13 @@ efi_write(int fd, struct dk_gpt *vtoc)
|
|||||||
efi_gpe_t *efi_parts;
|
efi_gpe_t *efi_parts;
|
||||||
int i, j;
|
int i, j;
|
||||||
struct dk_cinfo dki_info;
|
struct dk_cinfo dki_info;
|
||||||
|
int rval;
|
||||||
int md_flag = 0;
|
int md_flag = 0;
|
||||||
int nblocks;
|
int nblocks;
|
||||||
diskaddr_t lba_backup_gpt_hdr;
|
diskaddr_t lba_backup_gpt_hdr;
|
||||||
|
|
||||||
if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
|
if ((rval = efi_get_info(fd, &dki_info)) != 0)
|
||||||
if (efi_debug)
|
return rval;
|
||||||
(void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno);
|
|
||||||
switch (errno) {
|
|
||||||
case EIO:
|
|
||||||
return (VT_EIO);
|
|
||||||
case EINVAL:
|
|
||||||
return (VT_EINVAL);
|
|
||||||
default:
|
|
||||||
return (VT_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if we are dealing wih a metadevice */
|
/* check if we are dealing wih a metadevice */
|
||||||
if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
|
if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
|
||||||
@ -892,9 +1149,11 @@ efi_write(int fd, struct dk_gpt *vtoc)
|
|||||||
* for backup GPT header.
|
* for backup GPT header.
|
||||||
*/
|
*/
|
||||||
lba_backup_gpt_hdr = vtoc->efi_last_u_lba + 1 + nblocks;
|
lba_backup_gpt_hdr = vtoc->efi_last_u_lba + 1 + nblocks;
|
||||||
if ((dk_ioc.dki_data = calloc(dk_ioc.dki_length, 1)) == NULL)
|
if (posix_memalign((void **)&dk_ioc.dki_data,
|
||||||
|
vtoc->efi_lbasize, dk_ioc.dki_length))
|
||||||
return (VT_ERROR);
|
return (VT_ERROR);
|
||||||
|
|
||||||
|
memset(dk_ioc.dki_data, 0, dk_ioc.dki_length);
|
||||||
efi = dk_ioc.dki_data;
|
efi = dk_ioc.dki_data;
|
||||||
|
|
||||||
/* stuff user's input into EFI struct */
|
/* stuff user's input into EFI struct */
|
||||||
@ -941,6 +1200,10 @@ efi_write(int fd, struct dk_gpt *vtoc)
|
|||||||
return (VT_EINVAL);
|
return (VT_EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Zero's should be written for empty partitions */
|
||||||
|
if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED)
|
||||||
|
continue;
|
||||||
|
|
||||||
efi_parts[i].efi_gpe_StartingLBA =
|
efi_parts[i].efi_gpe_StartingLBA =
|
||||||
LE_64(vtoc->efi_parts[i].p_start);
|
LE_64(vtoc->efi_parts[i].p_start);
|
||||||
efi_parts[i].efi_gpe_EndingLBA =
|
efi_parts[i].efi_gpe_EndingLBA =
|
||||||
@ -1032,6 +1295,13 @@ efi_write(int fd, struct dk_gpt *vtoc)
|
|||||||
/* write the PMBR */
|
/* write the PMBR */
|
||||||
(void) write_pmbr(fd, vtoc);
|
(void) write_pmbr(fd, vtoc);
|
||||||
free(dk_ioc.dki_data);
|
free(dk_ioc.dki_data);
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
rval = efi_rescan(fd);
|
||||||
|
if (rval)
|
||||||
|
return (VT_ERROR);
|
||||||
|
#endif
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1049,6 +1319,7 @@ efi_free(struct dk_gpt *ptr)
|
|||||||
int
|
int
|
||||||
efi_type(int fd)
|
efi_type(int fd)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
struct vtoc vtoc;
|
struct vtoc vtoc;
|
||||||
struct extvtoc extvtoc;
|
struct extvtoc extvtoc;
|
||||||
|
|
||||||
@ -1062,6 +1333,9 @@ efi_type(int fd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
|
#else
|
||||||
|
return (ENOSYS);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1175,7 +1449,7 @@ efi_auto_sense(int fd, struct dk_gpt **vtoc)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < min((*vtoc)->efi_nparts, V_NUMPAR); i++) {
|
for (i = 0; i < MIN((*vtoc)->efi_nparts, V_NUMPAR); i++) {
|
||||||
(*vtoc)->efi_parts[i].p_tag = default_vtoc_map[i].p_tag;
|
(*vtoc)->efi_parts[i].p_tag = default_vtoc_map[i].p_tag;
|
||||||
(*vtoc)->efi_parts[i].p_flag = default_vtoc_map[i].p_flag;
|
(*vtoc)->efi_parts[i].p_flag = default_vtoc_map[i].p_flag;
|
||||||
(*vtoc)->efi_parts[i].p_start = 0;
|
(*vtoc)->efi_parts[i].p_start = 0;
|
||||||
|
@ -48,6 +48,26 @@ extern "C" {
|
|||||||
#define ZFS_MAXPROPLEN MAXPATHLEN
|
#define ZFS_MAXPROPLEN MAXPATHLEN
|
||||||
#define ZPOOL_MAXPROPLEN MAXPATHLEN
|
#define ZPOOL_MAXPROPLEN MAXPATHLEN
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default device paths
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__sun__) || defined(__sun)
|
||||||
|
#define DISK_ROOT "/dev/dsk"
|
||||||
|
#define RDISK_ROOT "/dev/rdsk"
|
||||||
|
#define UDISK_ROOT RDISK_ROOT
|
||||||
|
#define FIRST_SLICE "s0"
|
||||||
|
#define BACKUP_SLICE "s2"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#define DISK_ROOT "/dev"
|
||||||
|
#define RDISK_ROOT DISK_ROOT
|
||||||
|
#define UDISK_ROOT "/dev/disk"
|
||||||
|
#define FIRST_SLICE "1"
|
||||||
|
#define BACKUP_SLICE ""
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* libzfs errors
|
* libzfs errors
|
||||||
*/
|
*/
|
||||||
@ -248,6 +268,7 @@ extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
|
|||||||
boolean_t *, boolean_t *);
|
boolean_t *, boolean_t *);
|
||||||
extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
|
extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
|
||||||
boolean_t *, boolean_t *, boolean_t *);
|
boolean_t *, boolean_t *, boolean_t *);
|
||||||
|
extern int zpool_label_disk_wait(char *, int);
|
||||||
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
|
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -661,9 +682,6 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
|
|||||||
extern int zpool_read_label(int, nvlist_t **);
|
extern int zpool_read_label(int, nvlist_t **);
|
||||||
extern int zpool_clear_label(int);
|
extern int zpool_clear_label(int);
|
||||||
|
|
||||||
/* is this zvol valid for use as a dump device? */
|
|
||||||
extern int zvol_check_dump_config(char *);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Management interfaces for SMB ACL files
|
* Management interfaces for SMB ACL files
|
||||||
*/
|
*/
|
||||||
|
@ -191,6 +191,8 @@ zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
|
|||||||
|
|
||||||
int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
|
int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
|
||||||
|
|
||||||
|
int zvol_create_link(libzfs_handle_t *, const char *);
|
||||||
|
int zvol_remove_link(libzfs_handle_t *, const char *);
|
||||||
boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
|
boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
|
||||||
|
|
||||||
int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
|
int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
|
||||||
|
@ -93,6 +93,7 @@ struct prop_changelist {
|
|||||||
int
|
int
|
||||||
changelist_prefix(prop_changelist_t *clp)
|
changelist_prefix(prop_changelist_t *clp)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
prop_changenode_t *cn;
|
prop_changenode_t *cn;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -141,6 +142,9 @@ changelist_prefix(prop_changelist_t *clp)
|
|||||||
(void) changelist_postfix(clp);
|
(void) changelist_postfix(clp);
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -155,6 +159,7 @@ changelist_prefix(prop_changelist_t *clp)
|
|||||||
int
|
int
|
||||||
changelist_postfix(prop_changelist_t *clp)
|
changelist_postfix(prop_changelist_t *clp)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
prop_changenode_t *cn;
|
prop_changenode_t *cn;
|
||||||
char shareopts[ZFS_MAXPROPLEN];
|
char shareopts[ZFS_MAXPROPLEN];
|
||||||
int errors = 0;
|
int errors = 0;
|
||||||
@ -255,6 +260,9 @@ changelist_postfix(prop_changelist_t *clp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (errors ? -1 : 0);
|
return (errors ? -1 : 0);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -317,6 +325,7 @@ changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
|
|||||||
int
|
int
|
||||||
changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
|
changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
prop_changenode_t *cn;
|
prop_changenode_t *cn;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -331,6 +340,9 @@ changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
#include "libzfs_impl.h"
|
#include "libzfs_impl.h"
|
||||||
#include "zfs_deleg.h"
|
#include "zfs_deleg.h"
|
||||||
|
|
||||||
|
static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
|
||||||
static int userquota_propname_decode(const char *propname, boolean_t zoned,
|
static int userquota_propname_decode(const char *propname, boolean_t zoned,
|
||||||
zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
|
zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
|
||||||
|
|
||||||
@ -994,6 +995,7 @@ badlabel:
|
|||||||
|
|
||||||
/*FALLTHRU*/
|
/*FALLTHRU*/
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
case ZFS_PROP_SHARESMB:
|
case ZFS_PROP_SHARESMB:
|
||||||
case ZFS_PROP_SHARENFS:
|
case ZFS_PROP_SHARENFS:
|
||||||
/*
|
/*
|
||||||
@ -1104,6 +1106,7 @@ badlabel:
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
case ZFS_PROP_UTF8ONLY:
|
case ZFS_PROP_UTF8ONLY:
|
||||||
chosen_utf = (int)intval;
|
chosen_utf = (int)intval;
|
||||||
break;
|
break;
|
||||||
@ -2742,6 +2745,7 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
|
|||||||
goto ancestorerr;
|
goto ancestorerr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
if (zfs_mount(h, NULL, 0) != 0) {
|
if (zfs_mount(h, NULL, 0) != 0) {
|
||||||
opname = dgettext(TEXT_DOMAIN, "mount");
|
opname = dgettext(TEXT_DOMAIN, "mount");
|
||||||
goto ancestorerr;
|
goto ancestorerr;
|
||||||
@ -2751,6 +2755,7 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
|
|||||||
opname = dgettext(TEXT_DOMAIN, "share");
|
opname = dgettext(TEXT_DOMAIN, "share");
|
||||||
goto ancestorerr;
|
goto ancestorerr;
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
zfs_close(h);
|
zfs_close(h);
|
||||||
}
|
}
|
||||||
@ -2887,6 +2892,18 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
|
|||||||
/* create the dataset */
|
/* create the dataset */
|
||||||
ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
|
ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
|
||||||
|
|
||||||
|
if (ret == 0 && type == ZFS_TYPE_VOLUME) {
|
||||||
|
ret = zvol_create_link(hdl, path);
|
||||||
|
if (ret) {
|
||||||
|
(void) zfs_standard_error(hdl, errno,
|
||||||
|
dgettext(TEXT_DOMAIN,
|
||||||
|
"Volume successfully created, but device links "
|
||||||
|
"were not created"));
|
||||||
|
zcmd_free_nvlists(&zc);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
zcmd_free_nvlists(&zc);
|
zcmd_free_nvlists(&zc);
|
||||||
|
|
||||||
/* check for failure */
|
/* check for failure */
|
||||||
@ -2949,6 +2966,9 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
|
|||||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||||
|
|
||||||
if (ZFS_IS_VOLUME(zhp)) {
|
if (ZFS_IS_VOLUME(zhp)) {
|
||||||
|
if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
zc.zc_objset_type = DMU_OST_ZVOL;
|
zc.zc_objset_type = DMU_OST_ZVOL;
|
||||||
} else {
|
} else {
|
||||||
zc.zc_objset_type = DMU_OST_ZFS;
|
zc.zc_objset_type = DMU_OST_ZFS;
|
||||||
@ -2991,9 +3011,17 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
|
|||||||
zfs_close(szhp);
|
zfs_close(szhp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
|
||||||
|
(void) zvol_remove_link(zhp->zfs_hdl, name);
|
||||||
|
/*
|
||||||
|
* NB: this is simply a best-effort. We don't want to
|
||||||
|
* return an error, because then we wouldn't visit all
|
||||||
|
* the volumes.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
dd->closezhp = B_TRUE;
|
dd->closezhp = B_TRUE;
|
||||||
if (!dd->gotone)
|
rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
|
||||||
rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
|
|
||||||
if (closezhp)
|
if (closezhp)
|
||||||
zfs_close(zhp);
|
zfs_close(zhp);
|
||||||
return (rv);
|
return (rv);
|
||||||
@ -3128,11 +3156,70 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
|
|||||||
return (zfs_standard_error(zhp->zfs_hdl, errno,
|
return (zfs_standard_error(zhp->zfs_hdl, errno,
|
||||||
errbuf));
|
errbuf));
|
||||||
}
|
}
|
||||||
|
} else if (ZFS_IS_VOLUME(zhp)) {
|
||||||
|
ret = zvol_create_link(zhp->zfs_hdl, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct promote_data {
|
||||||
|
char cb_mountpoint[MAXPATHLEN];
|
||||||
|
const char *cb_target;
|
||||||
|
const char *cb_errbuf;
|
||||||
|
uint64_t cb_pivot_txg;
|
||||||
|
} promote_data_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
promote_snap_cb(zfs_handle_t *zhp, void *data)
|
||||||
|
{
|
||||||
|
promote_data_t *pd = data;
|
||||||
|
zfs_handle_t *szhp;
|
||||||
|
char snapname[MAXPATHLEN];
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
/* We don't care about snapshots after the pivot point */
|
||||||
|
if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) {
|
||||||
|
zfs_close(zhp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the device link if it's a zvol. */
|
||||||
|
if (ZFS_IS_VOLUME(zhp))
|
||||||
|
(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
|
||||||
|
|
||||||
|
/* Check for conflicting names */
|
||||||
|
(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
|
||||||
|
(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
|
||||||
|
szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
|
||||||
|
if (szhp != NULL) {
|
||||||
|
zfs_close(szhp);
|
||||||
|
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
"snapshot name '%s' from origin \n"
|
||||||
|
"conflicts with '%s' from target"),
|
||||||
|
zhp->zfs_name, snapname);
|
||||||
|
rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf);
|
||||||
|
}
|
||||||
|
zfs_close(zhp);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
promote_snap_done_cb(zfs_handle_t *zhp, void *data)
|
||||||
|
{
|
||||||
|
promote_data_t *pd = data;
|
||||||
|
|
||||||
|
/* We don't care about snapshots after the pivot point */
|
||||||
|
if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) {
|
||||||
|
/* Create the device link if it's a zvol. */
|
||||||
|
if (ZFS_IS_VOLUME(zhp))
|
||||||
|
(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
zfs_close(zhp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Promotes the given clone fs to be the clone parent.
|
* Promotes the given clone fs to be the clone parent.
|
||||||
*/
|
*/
|
||||||
@ -3142,7 +3229,10 @@ zfs_promote(zfs_handle_t *zhp)
|
|||||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||||
char parent[MAXPATHLEN];
|
char parent[MAXPATHLEN];
|
||||||
|
char *cp;
|
||||||
int ret;
|
int ret;
|
||||||
|
zfs_handle_t *pzhp;
|
||||||
|
promote_data_t pd;
|
||||||
char errbuf[1024];
|
char errbuf[1024];
|
||||||
|
|
||||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||||
@ -3160,7 +3250,29 @@ zfs_promote(zfs_handle_t *zhp)
|
|||||||
"not a cloned filesystem"));
|
"not a cloned filesystem"));
|
||||||
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
|
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
|
||||||
}
|
}
|
||||||
|
cp = strchr(parent, '@');
|
||||||
|
*cp = '\0';
|
||||||
|
|
||||||
|
/* Walk the snapshots we will be moving */
|
||||||
|
pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
|
||||||
|
if (pzhp == NULL)
|
||||||
|
return (-1);
|
||||||
|
pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
|
||||||
|
zfs_close(pzhp);
|
||||||
|
pd.cb_target = zhp->zfs_name;
|
||||||
|
pd.cb_errbuf = errbuf;
|
||||||
|
pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET);
|
||||||
|
if (pzhp == NULL)
|
||||||
|
return (-1);
|
||||||
|
(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
|
||||||
|
sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
|
||||||
|
ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
|
||||||
|
if (ret != 0) {
|
||||||
|
zfs_close(pzhp);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* issue the ioctl */
|
||||||
(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
|
(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
|
||||||
sizeof (zc.zc_value));
|
sizeof (zc.zc_value));
|
||||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||||
@ -3169,9 +3281,16 @@ zfs_promote(zfs_handle_t *zhp)
|
|||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
|
|
||||||
|
(void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
|
||||||
|
zfs_close(pzhp);
|
||||||
|
|
||||||
switch (save_errno) {
|
switch (save_errno) {
|
||||||
case EEXIST:
|
case EEXIST:
|
||||||
/* There is a conflicting snapshot name. */
|
/*
|
||||||
|
* There is a conflicting snapshot name. We
|
||||||
|
* should have caught this above, but they could
|
||||||
|
* have renamed something in the mean time.
|
||||||
|
*/
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
"conflicting snapshot '%s' from parent '%s'"),
|
"conflicting snapshot '%s' from parent '%s'"),
|
||||||
zc.zc_string, parent);
|
zc.zc_string, parent);
|
||||||
@ -3180,7 +3299,44 @@ zfs_promote(zfs_handle_t *zhp)
|
|||||||
default:
|
default:
|
||||||
return (zfs_standard_error(hdl, save_errno, errbuf));
|
return (zfs_standard_error(hdl, save_errno, errbuf));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
(void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zfs_close(pzhp);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct createdata {
|
||||||
|
const char *cd_snapname;
|
||||||
|
int cd_ifexists;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
|
||||||
|
{
|
||||||
|
struct createdata *cd = arg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
|
||||||
|
char name[MAXPATHLEN];
|
||||||
|
|
||||||
|
(void) strlcpy(name, zhp->zfs_name, sizeof (name));
|
||||||
|
(void) strlcat(name, "@", sizeof (name));
|
||||||
|
(void) strlcat(name, cd->cd_snapname, sizeof (name));
|
||||||
|
(void) zvol_create_link_common(zhp->zfs_hdl, name,
|
||||||
|
cd->cd_ifexists);
|
||||||
|
/*
|
||||||
|
* NB: this is simply a best-effort. We don't want to
|
||||||
|
* return an error, because then we wouldn't visit all
|
||||||
|
* the volumes.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd);
|
||||||
|
|
||||||
|
zfs_close(zhp);
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3244,11 +3400,31 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
|
|||||||
* if it was recursive, the one that actually failed will be in
|
* if it was recursive, the one that actually failed will be in
|
||||||
* zc.zc_name.
|
* zc.zc_name.
|
||||||
*/
|
*/
|
||||||
if (ret != 0) {
|
if (ret != 0)
|
||||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||||
"cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
|
"cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
|
||||||
(void) zfs_standard_error(hdl, errno, errbuf);
|
|
||||||
|
if (ret == 0 && recursive) {
|
||||||
|
struct createdata cd;
|
||||||
|
|
||||||
|
cd.cd_snapname = delim + 1;
|
||||||
|
cd.cd_ifexists = B_FALSE;
|
||||||
|
(void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd);
|
||||||
}
|
}
|
||||||
|
if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
|
||||||
|
ret = zvol_create_link(zhp->zfs_hdl, path);
|
||||||
|
if (ret != 0) {
|
||||||
|
(void) zfs_standard_error(hdl, errno,
|
||||||
|
dgettext(TEXT_DOMAIN,
|
||||||
|
"Volume successfully snapshotted, but device links "
|
||||||
|
"were not created"));
|
||||||
|
zfs_close(zhp);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
(void) zfs_standard_error(hdl, errno, errbuf);
|
||||||
|
|
||||||
zfs_close(zhp);
|
zfs_close(zhp);
|
||||||
|
|
||||||
@ -3351,6 +3527,8 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
|
if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
|
||||||
|
if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
|
||||||
|
return (-1);
|
||||||
if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
|
if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
|
old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
|
||||||
@ -3388,6 +3566,10 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
|||||||
*/
|
*/
|
||||||
if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
|
if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
|
||||||
(zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
|
(zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
|
||||||
|
if ((err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name))) {
|
||||||
|
zfs_close(zhp);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
if (restore_resv) {
|
if (restore_resv) {
|
||||||
new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
|
new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
|
||||||
if (old_volsize != new_volsize)
|
if (old_volsize != new_volsize)
|
||||||
@ -3536,6 +3718,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
|
struct destroydata dd;
|
||||||
|
|
||||||
parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
|
parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
|
||||||
if (parentname == NULL) {
|
if (parentname == NULL) {
|
||||||
@ -3550,6 +3733,15 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dd.snapname = delim + 1;
|
||||||
|
dd.gotone = B_FALSE;
|
||||||
|
dd.closezhp = B_TRUE;
|
||||||
|
|
||||||
|
/* We remove any zvol links prior to renaming them */
|
||||||
|
ret = zfs_iter_filesystems(zhrp, zfs_check_snap_cb, &dd);
|
||||||
|
if (ret) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
|
if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
@ -3598,10 +3790,27 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
|
|||||||
* On failure, we still want to remount any filesystems that
|
* On failure, we still want to remount any filesystems that
|
||||||
* were previously mounted, so we don't alter the system state.
|
* were previously mounted, so we don't alter the system state.
|
||||||
*/
|
*/
|
||||||
if (!recursive)
|
if (recursive) {
|
||||||
|
struct createdata cd;
|
||||||
|
|
||||||
|
/* only create links for datasets that had existed */
|
||||||
|
cd.cd_snapname = delim + 1;
|
||||||
|
cd.cd_ifexists = B_TRUE;
|
||||||
|
(void) zfs_iter_filesystems(zhrp, zfs_create_link_cb,
|
||||||
|
&cd);
|
||||||
|
} else {
|
||||||
(void) changelist_postfix(cl);
|
(void) changelist_postfix(cl);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!recursive) {
|
if (recursive) {
|
||||||
|
struct createdata cd;
|
||||||
|
|
||||||
|
/* only create links for datasets that had existed */
|
||||||
|
cd.cd_snapname = strchr(target, '@') + 1;
|
||||||
|
cd.cd_ifexists = B_TRUE;
|
||||||
|
ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb,
|
||||||
|
&cd);
|
||||||
|
} else {
|
||||||
changelist_rename(cl, zfs_get_name(zhp), target);
|
changelist_rename(cl, zfs_get_name(zhp), target);
|
||||||
ret = changelist_postfix(cl);
|
ret = changelist_postfix(cl);
|
||||||
}
|
}
|
||||||
@ -3620,21 +3829,105 @@ error:
|
|||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a zvol dataset, issue the ioctl to create the appropriate minor node,
|
||||||
|
* and wait briefly for udev to create the /dev link.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
|
||||||
|
{
|
||||||
|
return (zvol_create_link_common(hdl, dataset, B_FALSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
|
||||||
|
{
|
||||||
|
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
int error;
|
||||||
|
|
||||||
|
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Issue the appropriate ioctl.
|
||||||
|
*/
|
||||||
|
if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
|
||||||
|
switch (errno) {
|
||||||
|
case EEXIST:
|
||||||
|
/*
|
||||||
|
* Silently ignore the case where the link already
|
||||||
|
* exists. This allows 'zfs volinit' to be run multiple
|
||||||
|
* times without errors.
|
||||||
|
*/
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
case ENOENT:
|
||||||
|
/*
|
||||||
|
* Dataset does not exist in the kernel. If we
|
||||||
|
* don't care (see zfs_rename), then ignore the
|
||||||
|
* error quietly.
|
||||||
|
*/
|
||||||
|
if (ifexists) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (zfs_standard_error_fmt(hdl, errno,
|
||||||
|
dgettext(TEXT_DOMAIN, "cannot create device links "
|
||||||
|
"for '%s'"), dataset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait up to 10 seconds for udev to create the device.
|
||||||
|
*/
|
||||||
|
(void) snprintf(path, sizeof (path), "%s/%s", ZVOL_DIR, dataset);
|
||||||
|
error = zpool_label_disk_wait(path, 10000);
|
||||||
|
if (error)
|
||||||
|
(void) printf(gettext("%s may not be immediately "
|
||||||
|
"available\n"), path);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a minor node for the given zvol and the associated /dev links.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
|
||||||
|
{
|
||||||
|
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||||
|
|
||||||
|
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||||
|
|
||||||
|
if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
|
||||||
|
switch (errno) {
|
||||||
|
case ENXIO:
|
||||||
|
/*
|
||||||
|
* Silently ignore the case where the link no longer
|
||||||
|
* exists, so that 'zfs volfini' can be run multiple
|
||||||
|
* times without errors.
|
||||||
|
*/
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (zfs_standard_error_fmt(hdl, errno,
|
||||||
|
dgettext(TEXT_DOMAIN, "cannot remove device "
|
||||||
|
"links for '%s'"), dataset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
nvlist_t *
|
nvlist_t *
|
||||||
zfs_get_user_props(zfs_handle_t *zhp)
|
zfs_get_user_props(zfs_handle_t *zhp)
|
||||||
{
|
{
|
||||||
return (zhp->zfs_user_props);
|
return (zhp->zfs_user_props);
|
||||||
}
|
}
|
||||||
|
|
||||||
nvlist_t *
|
|
||||||
zfs_get_recvd_props(zfs_handle_t *zhp)
|
|
||||||
{
|
|
||||||
if (zhp->zfs_recvd_props == NULL)
|
|
||||||
if (get_recvd_props_ioctl(zhp) != 0)
|
|
||||||
return (NULL);
|
|
||||||
return (zhp->zfs_recvd_props);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is used by 'zfs list' to determine the exact set of columns to
|
* This function is used by 'zfs list' to determine the exact set of columns to
|
||||||
* display, and their maximum widths. This does two main things:
|
* display, and their maximum widths. This does two main things:
|
||||||
@ -3744,6 +4037,7 @@ zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
int
|
int
|
||||||
zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
|
zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
|
||||||
char *resource, void *export, void *sharetab,
|
char *resource, void *export, void *sharetab,
|
||||||
@ -3763,6 +4057,7 @@ zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
|
|||||||
error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
|
error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
|
||||||
void
|
void
|
||||||
zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
|
zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
|
||||||
|
@ -52,9 +52,11 @@
|
|||||||
#include <sys/vtoc.h>
|
#include <sys/vtoc.h>
|
||||||
#include <sys/dktp/fdisk.h>
|
#include <sys/dktp/fdisk.h>
|
||||||
#include <sys/efi_partition.h>
|
#include <sys/efi_partition.h>
|
||||||
#include <thread_pool.h>
|
|
||||||
|
|
||||||
#include <sys/vdev_impl.h>
|
#include <sys/vdev_impl.h>
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
#include <blkid/blkid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "libzfs.h"
|
#include "libzfs.h"
|
||||||
#include "libzfs_impl.h"
|
#include "libzfs_impl.h"
|
||||||
@ -904,211 +906,76 @@ zpool_read_label(int fd, nvlist_t **config)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct rdsk_node {
|
#ifdef HAVE_LIBBLKID
|
||||||
char *rn_name;
|
|
||||||
int rn_dfd;
|
|
||||||
libzfs_handle_t *rn_hdl;
|
|
||||||
nvlist_t *rn_config;
|
|
||||||
avl_tree_t *rn_avl;
|
|
||||||
avl_node_t rn_node;
|
|
||||||
boolean_t rn_nozpool;
|
|
||||||
} rdsk_node_t;
|
|
||||||
|
|
||||||
static int
|
|
||||||
slice_cache_compare(const void *arg1, const void *arg2)
|
|
||||||
{
|
|
||||||
const char *nm1 = ((rdsk_node_t *)arg1)->rn_name;
|
|
||||||
const char *nm2 = ((rdsk_node_t *)arg2)->rn_name;
|
|
||||||
char *nm1slice, *nm2slice;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* slices zero and two are the most likely to provide results,
|
|
||||||
* so put those first
|
|
||||||
*/
|
|
||||||
nm1slice = strstr(nm1, "s0");
|
|
||||||
nm2slice = strstr(nm2, "s0");
|
|
||||||
if (nm1slice && !nm2slice) {
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if (!nm1slice && nm2slice) {
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
nm1slice = strstr(nm1, "s2");
|
|
||||||
nm2slice = strstr(nm2, "s2");
|
|
||||||
if (nm1slice && !nm2slice) {
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if (!nm1slice && nm2slice) {
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = strcmp(nm1, nm2);
|
|
||||||
if (rv == 0)
|
|
||||||
return (0);
|
|
||||||
return (rv > 0 ? 1 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
check_one_slice(avl_tree_t *r, char *diskname, uint_t partno,
|
|
||||||
diskaddr_t size, uint_t blksz)
|
|
||||||
{
|
|
||||||
rdsk_node_t tmpnode;
|
|
||||||
rdsk_node_t *node;
|
|
||||||
char sname[MAXNAMELEN];
|
|
||||||
|
|
||||||
tmpnode.rn_name = &sname[0];
|
|
||||||
(void) snprintf(tmpnode.rn_name, MAXNAMELEN, "%s%u",
|
|
||||||
diskname, partno);
|
|
||||||
/*
|
|
||||||
* protect against division by zero for disk labels that
|
|
||||||
* contain a bogus sector size
|
|
||||||
*/
|
|
||||||
if (blksz == 0)
|
|
||||||
blksz = DEV_BSIZE;
|
|
||||||
/* too small to contain a zpool? */
|
|
||||||
if ((size < (SPA_MINDEVSIZE / blksz)) &&
|
|
||||||
(node = avl_find(r, &tmpnode, NULL)))
|
|
||||||
node->rn_nozpool = B_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nozpool_all_slices(avl_tree_t *r, const char *sname)
|
|
||||||
{
|
|
||||||
char diskname[MAXNAMELEN];
|
|
||||||
char *ptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
(void) strncpy(diskname, sname, MAXNAMELEN);
|
|
||||||
if (((ptr = strrchr(diskname, 's')) == NULL) &&
|
|
||||||
((ptr = strrchr(diskname, 'p')) == NULL))
|
|
||||||
return;
|
|
||||||
ptr[0] = 's';
|
|
||||||
ptr[1] = '\0';
|
|
||||||
for (i = 0; i < NDKMAP; i++)
|
|
||||||
check_one_slice(r, diskname, i, 0, 1);
|
|
||||||
ptr[0] = 'p';
|
|
||||||
for (i = 0; i <= FD_NUMPART; i++)
|
|
||||||
check_one_slice(r, diskname, i, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
check_slices(avl_tree_t *r, int fd, const char *sname)
|
|
||||||
{
|
|
||||||
struct extvtoc vtoc;
|
|
||||||
struct dk_gpt *gpt;
|
|
||||||
char diskname[MAXNAMELEN];
|
|
||||||
char *ptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
(void) strncpy(diskname, sname, MAXNAMELEN);
|
|
||||||
if ((ptr = strrchr(diskname, 's')) == NULL || !isdigit(ptr[1]))
|
|
||||||
return;
|
|
||||||
ptr[1] = '\0';
|
|
||||||
|
|
||||||
if (read_extvtoc(fd, &vtoc) >= 0) {
|
|
||||||
for (i = 0; i < NDKMAP; i++)
|
|
||||||
check_one_slice(r, diskname, i,
|
|
||||||
vtoc.v_part[i].p_size, vtoc.v_sectorsz);
|
|
||||||
} else if (efi_alloc_and_read(fd, &gpt) >= 0) {
|
|
||||||
/*
|
|
||||||
* on x86 we'll still have leftover links that point
|
|
||||||
* to slices s[9-15], so use NDKMAP instead
|
|
||||||
*/
|
|
||||||
for (i = 0; i < NDKMAP; i++)
|
|
||||||
check_one_slice(r, diskname, i,
|
|
||||||
gpt->efi_parts[i].p_size, gpt->efi_lbasize);
|
|
||||||
/* nodes p[1-4] are never used with EFI labels */
|
|
||||||
ptr[0] = 'p';
|
|
||||||
for (i = 1; i <= FD_NUMPART; i++)
|
|
||||||
check_one_slice(r, diskname, i, 0, 1);
|
|
||||||
efi_free(gpt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
zpool_open_func(void *arg)
|
|
||||||
{
|
|
||||||
rdsk_node_t *rn = arg;
|
|
||||||
struct stat64 statbuf;
|
|
||||||
nvlist_t *config;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (rn->rn_nozpool)
|
|
||||||
return;
|
|
||||||
if ((fd = openat64(rn->rn_dfd, rn->rn_name, O_RDONLY)) < 0) {
|
|
||||||
/* symlink to a device that's no longer there */
|
|
||||||
if (errno == ENOENT)
|
|
||||||
nozpool_all_slices(rn->rn_avl, rn->rn_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Ignore failed stats. We only want regular
|
|
||||||
* files, character devs and block devs.
|
|
||||||
*/
|
|
||||||
if (fstat64(fd, &statbuf) != 0 ||
|
|
||||||
(!S_ISREG(statbuf.st_mode) &&
|
|
||||||
!S_ISCHR(statbuf.st_mode) &&
|
|
||||||
!S_ISBLK(statbuf.st_mode))) {
|
|
||||||
(void) close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* this file is too small to hold a zpool */
|
|
||||||
if (S_ISREG(statbuf.st_mode) &&
|
|
||||||
statbuf.st_size < SPA_MINDEVSIZE) {
|
|
||||||
(void) close(fd);
|
|
||||||
return;
|
|
||||||
} else if (!S_ISREG(statbuf.st_mode)) {
|
|
||||||
/*
|
|
||||||
* Try to read the disk label first so we don't have to
|
|
||||||
* open a bunch of minor nodes that can't have a zpool.
|
|
||||||
*/
|
|
||||||
check_slices(rn->rn_avl, fd, rn->rn_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zpool_read_label(fd, &config)) != 0) {
|
|
||||||
(void) close(fd);
|
|
||||||
(void) no_memory(rn->rn_hdl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(void) close(fd);
|
|
||||||
|
|
||||||
|
|
||||||
rn->rn_config = config;
|
|
||||||
if (config != NULL) {
|
|
||||||
assert(rn->rn_nozpool == B_FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a file descriptor, clear (zero) the label information. This function
|
* Use libblkid to quickly search for zfs devices
|
||||||
* is currently only used in the appliance stack as part of the ZFS sysevent
|
|
||||||
* module.
|
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
zpool_clear_label(int fd)
|
zpool_find_import_blkid(libzfs_handle_t *hdl, pool_list_t *pools)
|
||||||
{
|
{
|
||||||
struct stat64 statbuf;
|
blkid_cache cache;
|
||||||
int l;
|
blkid_dev_iterate iter;
|
||||||
vdev_label_t *label;
|
blkid_dev dev;
|
||||||
uint64_t size;
|
const char *devname;
|
||||||
|
nvlist_t *config;
|
||||||
|
int fd, err;
|
||||||
|
|
||||||
if (fstat64(fd, &statbuf) == -1)
|
err = blkid_get_cache(&cache, NULL);
|
||||||
return (0);
|
if (err != 0) {
|
||||||
size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
|
(void) zfs_error_fmt(hdl, EZFS_BADCACHE,
|
||||||
|
dgettext(TEXT_DOMAIN, "blkid_get_cache() %d"), err);
|
||||||
if ((label = calloc(sizeof (vdev_label_t), 1)) == NULL)
|
goto err_blkid1;
|
||||||
return (-1);
|
|
||||||
|
|
||||||
for (l = 0; l < VDEV_LABELS; l++) {
|
|
||||||
if (pwrite64(fd, label, sizeof (vdev_label_t),
|
|
||||||
label_offset(size, l)) != sizeof (vdev_label_t))
|
|
||||||
return (-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(label);
|
err = blkid_probe_all(cache);
|
||||||
return (0);
|
if (err != 0) {
|
||||||
|
(void) zfs_error_fmt(hdl, EZFS_BADCACHE,
|
||||||
|
dgettext(TEXT_DOMAIN, "blkid_probe_all() %d"), err);
|
||||||
|
goto err_blkid2;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = blkid_dev_iterate_begin(cache);
|
||||||
|
if (iter == NULL) {
|
||||||
|
(void) zfs_error_fmt(hdl, EZFS_BADCACHE,
|
||||||
|
dgettext(TEXT_DOMAIN, "blkid_dev_iterate_begin()"));
|
||||||
|
goto err_blkid2;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = blkid_dev_set_search(iter, "TYPE", "zfs");
|
||||||
|
if (err != 0) {
|
||||||
|
(void) zfs_error_fmt(hdl, EZFS_BADCACHE,
|
||||||
|
dgettext(TEXT_DOMAIN, "blkid_dev_set_search() %d"), err);
|
||||||
|
goto err_blkid3;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (blkid_dev_next(iter, &dev) == 0) {
|
||||||
|
devname = blkid_dev_devname(dev);
|
||||||
|
if ((fd = open64(devname, O_RDONLY)) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = zpool_read_label(fd, &config);
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
(void) no_memory(hdl);
|
||||||
|
goto err_blkid3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config != NULL) {
|
||||||
|
err = add_config(hdl, pools, devname, config);
|
||||||
|
if (err != 0)
|
||||||
|
goto err_blkid3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err_blkid3:
|
||||||
|
blkid_dev_iterate_end(iter);
|
||||||
|
err_blkid2:
|
||||||
|
blkid_put_cache(cache);
|
||||||
|
err_blkid1:
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_LIBBLKID */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a list of directories to search, find all pools stored on disk. This
|
* Given a list of directories to search, find all pools stored on disk. This
|
||||||
@ -1126,18 +993,28 @@ zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
|
|||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
char *end, **dir = iarg->path;
|
char *end, **dir = iarg->path;
|
||||||
size_t pathleft;
|
size_t pathleft;
|
||||||
nvlist_t *ret = NULL;
|
struct stat64 statbuf;
|
||||||
static char *default_dir = "/dev/dsk";
|
nvlist_t *ret = NULL, *config;
|
||||||
|
static char *default_dir = DISK_ROOT;
|
||||||
|
int fd;
|
||||||
pool_list_t pools = { 0 };
|
pool_list_t pools = { 0 };
|
||||||
pool_entry_t *pe, *penext;
|
pool_entry_t *pe, *penext;
|
||||||
vdev_entry_t *ve, *venext;
|
vdev_entry_t *ve, *venext;
|
||||||
config_entry_t *ce, *cenext;
|
config_entry_t *ce, *cenext;
|
||||||
name_entry_t *ne, *nenext;
|
name_entry_t *ne, *nenext;
|
||||||
avl_tree_t slice_cache;
|
|
||||||
rdsk_node_t *slice;
|
verify(iarg->poolname == NULL || iarg->guid == 0);
|
||||||
void *cookie;
|
|
||||||
|
|
||||||
if (dirs == 0) {
|
if (dirs == 0) {
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
/* Use libblkid to scan all device for their type */
|
||||||
|
if (zpool_find_import_blkid(hdl, &pools) == 0)
|
||||||
|
goto skip_scanning;
|
||||||
|
|
||||||
|
(void) zfs_error_fmt(hdl, EZFS_BADCACHE,
|
||||||
|
dgettext(TEXT_DOMAIN, "blkid failure falling back "
|
||||||
|
"to manual probing"));
|
||||||
|
#endif /* HAVE_LIBBLKID */
|
||||||
dirs = 1;
|
dirs = 1;
|
||||||
dir = &default_dir;
|
dir = &default_dir;
|
||||||
}
|
}
|
||||||
@ -1148,7 +1025,6 @@ zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
|
|||||||
* and toplevel GUID.
|
* and toplevel GUID.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < dirs; i++) {
|
for (i = 0; i < dirs; i++) {
|
||||||
tpool_t *t;
|
|
||||||
char *rdsk;
|
char *rdsk;
|
||||||
int dfd;
|
int dfd;
|
||||||
|
|
||||||
@ -1182,8 +1058,6 @@ zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
avl_create(&slice_cache, slice_cache_compare,
|
|
||||||
sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node));
|
|
||||||
/*
|
/*
|
||||||
* This is not MT-safe, but we have no MT consumers of libzfs
|
* This is not MT-safe, but we have no MT consumers of libzfs
|
||||||
*/
|
*/
|
||||||
@ -1193,37 +1067,51 @@ zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
|
|||||||
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
|
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
|
/*
|
||||||
slice->rn_name = zfs_strdup(hdl, name);
|
* Skip checking devices with well known prefixes:
|
||||||
slice->rn_avl = &slice_cache;
|
* watchdog - A special close is required to avoid
|
||||||
slice->rn_dfd = dfd;
|
* triggering it and resetting the system.
|
||||||
slice->rn_hdl = hdl;
|
* fuse - Fuse control device.
|
||||||
slice->rn_nozpool = B_FALSE;
|
* ppp - Generic PPP driver.
|
||||||
avl_add(&slice_cache, slice);
|
* tty* - Generic serial interface.
|
||||||
}
|
* vcs* - Virtual console memory.
|
||||||
/*
|
* parport* - Parallel port interface.
|
||||||
* create a thread pool to do all of this in parallel;
|
* lp* - Printer interface.
|
||||||
* rn_nozpool is not protected, so this is racy in that
|
* fd* - Floppy interface.
|
||||||
* multiple tasks could decide that the same slice can
|
*/
|
||||||
* not hold a zpool, which is benign. Also choose
|
if ((strncmp(name, "watchdog", 8) == 0) ||
|
||||||
* double the number of processors; we hold a lot of
|
(strncmp(name, "fuse", 4) == 0) ||
|
||||||
* locks in the kernel, so going beyond this doesn't
|
(strncmp(name, "ppp", 3) == 0) ||
|
||||||
* buy us much.
|
(strncmp(name, "tty", 3) == 0) ||
|
||||||
*/
|
(strncmp(name, "vcs", 3) == 0) ||
|
||||||
t = tpool_create(1, 2 * sysconf(_SC_NPROCESSORS_ONLN),
|
(strncmp(name, "parport", 7) == 0) ||
|
||||||
0, NULL);
|
(strncmp(name, "lp", 2) == 0) ||
|
||||||
for (slice = avl_first(&slice_cache); slice;
|
(strncmp(name, "fd", 2) == 0))
|
||||||
(slice = avl_walk(&slice_cache, slice,
|
continue;
|
||||||
AVL_AFTER)))
|
|
||||||
(void) tpool_dispatch(t, zpool_open_func, slice);
|
|
||||||
tpool_wait(t);
|
|
||||||
tpool_destroy(t);
|
|
||||||
|
|
||||||
cookie = NULL;
|
if ((fd = openat64(dfd, name, O_RDONLY)) < 0)
|
||||||
while ((slice = avl_destroy_nodes(&slice_cache,
|
continue;
|
||||||
&cookie)) != NULL) {
|
|
||||||
if (slice->rn_config != NULL) {
|
/*
|
||||||
nvlist_t *config = slice->rn_config;
|
* Ignore failed stats. We only want regular
|
||||||
|
* files and block devs.
|
||||||
|
*/
|
||||||
|
if (fstat64(fd, &statbuf) != 0 ||
|
||||||
|
(!S_ISREG(statbuf.st_mode) &&
|
||||||
|
!S_ISBLK(statbuf.st_mode))) {
|
||||||
|
(void) close(fd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zpool_read_label(fd, &config)) != 0) {
|
||||||
|
(void) close(fd);
|
||||||
|
(void) no_memory(hdl);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) close(fd);
|
||||||
|
|
||||||
|
if (config != NULL) {
|
||||||
boolean_t matched = B_TRUE;
|
boolean_t matched = B_TRUE;
|
||||||
|
|
||||||
if (iarg->poolname != NULL) {
|
if (iarg->poolname != NULL) {
|
||||||
@ -1247,19 +1135,19 @@ zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* use the non-raw path for the config */
|
/* use the non-raw path for the config */
|
||||||
(void) strlcpy(end, slice->rn_name, pathleft);
|
(void) strlcpy(end, name, pathleft);
|
||||||
if (add_config(hdl, &pools, path, config) != 0)
|
if (add_config(hdl, &pools, path, config) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
free(slice->rn_name);
|
|
||||||
free(slice);
|
|
||||||
}
|
}
|
||||||
avl_destroy(&slice_cache);
|
|
||||||
|
|
||||||
(void) closedir(dirp);
|
(void) closedir(dirp);
|
||||||
dirp = NULL;
|
dirp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBBLKID
|
||||||
|
skip_scanning:
|
||||||
|
#endif
|
||||||
ret = get_configs(hdl, &pools, iarg->can_be_active);
|
ret = get_configs(hdl, &pools, iarg->can_be_active);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -81,6 +81,7 @@
|
|||||||
#include <sys/systeminfo.h>
|
#include <sys/systeminfo.h>
|
||||||
#define MAXISALEN 257 /* based on sysinfo(2) man page */
|
#define MAXISALEN 257 /* based on sysinfo(2) man page */
|
||||||
|
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
|
static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
|
||||||
zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
|
zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
|
||||||
zfs_share_proto_t);
|
zfs_share_proto_t);
|
||||||
@ -1268,3 +1269,53 @@ out:
|
|||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else /* HAVE_ZPL */
|
||||||
|
|
||||||
|
int
|
||||||
|
zfs_unshare_iscsi(zfs_handle_t *zhp)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
remove_mountpoint(zfs_handle_t *zhp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_t
|
||||||
|
is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where)
|
||||||
|
{
|
||||||
|
return B_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_t
|
||||||
|
zfs_is_mounted(zfs_handle_t *zhp, char **where)
|
||||||
|
{
|
||||||
|
return is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_t
|
||||||
|
zfs_is_shared(zfs_handle_t *zhp)
|
||||||
|
{
|
||||||
|
return B_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
|
||||||
|
{
|
||||||
|
return B_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
|
||||||
|
{
|
||||||
|
return B_FALSE;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <zone.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/efi_partition.h>
|
#include <sys/efi_partition.h>
|
||||||
#include <sys/vtoc.h>
|
#include <sys/vtoc.h>
|
||||||
#include <sys/zfs_ioctl.h>
|
#include <sys/zfs_ioctl.h>
|
||||||
@ -44,10 +46,6 @@
|
|||||||
|
|
||||||
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
|
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
|
||||||
|
|
||||||
#define DISK_ROOT "/dev/dsk"
|
|
||||||
#define RDISK_ROOT "/dev/rdsk"
|
|
||||||
#define BACKUP_SLICE "s2"
|
|
||||||
|
|
||||||
typedef struct prop_flags {
|
typedef struct prop_flags {
|
||||||
int create:1; /* Validate property on creation */
|
int create:1; /* Validate property on creation */
|
||||||
int import:1; /* Validate property on import */
|
int import:1; /* Validate property on import */
|
||||||
@ -651,9 +649,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't start the slice at the default block of 34; many storage
|
* Don't start the slice at the default block of 34; many storage
|
||||||
* devices will use a stripe width of 128k, so start there instead.
|
* devices will use a stripe width of 128k, other vendors prefer a 1m
|
||||||
|
* alignment. It is best to play it safe and ensure a 1m alignment
|
||||||
|
* give 512b blocks. When the block size is larger by a power of 2
|
||||||
|
* we will still be 1m aligned.
|
||||||
*/
|
*/
|
||||||
#define NEW_START_BLOCK 256
|
#define NEW_START_BLOCK 2048
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Validate the given pool name, optionally putting an extended error message in
|
* Validate the given pool name, optionally putting an extended error message in
|
||||||
@ -948,10 +949,12 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
|
|||||||
* This can happen if the user has specified the same
|
* This can happen if the user has specified the same
|
||||||
* device multiple times. We can't reliably detect this
|
* device multiple times. We can't reliably detect this
|
||||||
* until we try to add it and see we already have a
|
* until we try to add it and see we already have a
|
||||||
* label.
|
* label. This can also happen under if the device is
|
||||||
|
* part of an active md or lvm device.
|
||||||
*/
|
*/
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
"one or more vdevs refer to the same device"));
|
"one or more vdevs refer to the same device, or one of\n"
|
||||||
|
"the devices is part of an active md or lvm device"));
|
||||||
return (zfs_error(hdl, EZFS_BADDEV, msg));
|
return (zfs_error(hdl, EZFS_BADDEV, msg));
|
||||||
|
|
||||||
case EOVERFLOW:
|
case EOVERFLOW:
|
||||||
@ -1928,7 +1931,7 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
|
|||||||
} else if (zpool_vdev_is_interior(path)) {
|
} else if (zpool_vdev_is_interior(path)) {
|
||||||
verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
|
verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
|
||||||
} else if (path[0] != '/') {
|
} else if (path[0] != '/') {
|
||||||
(void) snprintf(buf, sizeof (buf), "%s%s", "/dev/dsk/", path);
|
(void) snprintf(buf, sizeof (buf), "%s/%s", DISK_ROOT, path);
|
||||||
verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0);
|
verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0);
|
||||||
} else {
|
} else {
|
||||||
verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0);
|
verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0);
|
||||||
@ -2101,22 +2104,14 @@ zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t phypath_size)
|
|||||||
* the disk to use the new unallocated space.
|
* the disk to use the new unallocated space.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
zpool_relabel_disk(libzfs_handle_t *hdl, const char *name)
|
zpool_relabel_disk(libzfs_handle_t *hdl, const char *path)
|
||||||
{
|
{
|
||||||
char path[MAXPATHLEN];
|
|
||||||
char errbuf[1024];
|
char errbuf[1024];
|
||||||
int fd, error;
|
int fd, error;
|
||||||
int (*_efi_use_whole_disk)(int);
|
|
||||||
|
|
||||||
if ((_efi_use_whole_disk = (int (*)(int))dlsym(RTLD_DEFAULT,
|
if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
|
||||||
"efi_use_whole_disk")) == NULL)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
(void) snprintf(path, sizeof (path), "%s/%s", RDISK_ROOT, name);
|
|
||||||
|
|
||||||
if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
||||||
"relabel '%s': unable to open device"), name);
|
"relabel '%s': unable to open device"), path);
|
||||||
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
|
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2125,11 +2120,11 @@ zpool_relabel_disk(libzfs_handle_t *hdl, const char *name)
|
|||||||
* does not have any unallocated space left. If so, we simply
|
* does not have any unallocated space left. If so, we simply
|
||||||
* ignore that error and continue on.
|
* ignore that error and continue on.
|
||||||
*/
|
*/
|
||||||
error = _efi_use_whole_disk(fd);
|
error = efi_use_whole_disk(fd);
|
||||||
(void) close(fd);
|
(void) close(fd);
|
||||||
if (error && error != VT_ENOSPC) {
|
if (error && error != VT_ENOSPC) {
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
|
||||||
"relabel '%s': unable to read disk capacity"), name);
|
"relabel '%s': unable to read disk capacity"), path);
|
||||||
return (zfs_error(hdl, EZFS_NOCAP, errbuf));
|
return (zfs_error(hdl, EZFS_NOCAP, errbuf));
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
@ -3071,7 +3066,7 @@ char *
|
|||||||
zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
|
zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
|
||||||
boolean_t verbose)
|
boolean_t verbose)
|
||||||
{
|
{
|
||||||
char *path, *devid;
|
char *path, *devid, *type;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
vdev_stat_t *vs;
|
vdev_stat_t *vs;
|
||||||
@ -3085,7 +3080,6 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
|
|||||||
(u_longlong_t)value);
|
(u_longlong_t)value);
|
||||||
path = buf;
|
path = buf;
|
||||||
} else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
|
} else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the device is dead (faulted, offline, etc) then don't
|
* If the device is dead (faulted, offline, etc) then don't
|
||||||
* bother opening it. Otherwise we may be forcing the user to
|
* bother opening it. Otherwise we may be forcing the user to
|
||||||
@ -3124,9 +3118,19 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
|
|||||||
devid_str_free(newdevid);
|
devid_str_free(newdevid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(path, "/dev/dsk/", 9) == 0)
|
/*
|
||||||
path += 9;
|
* For a block device only use the name.
|
||||||
|
*/
|
||||||
|
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
|
||||||
|
if (strcmp(type, VDEV_TYPE_DISK) == 0) {
|
||||||
|
path = strrchr(path, '/');
|
||||||
|
path++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__sun__) || defined(__sun)
|
||||||
|
/*
|
||||||
|
* The following code strips the slice from the device path.
|
||||||
|
*/
|
||||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
|
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
|
||||||
&value) == 0 && value) {
|
&value) == 0 && value) {
|
||||||
int pathlen = strlen(path);
|
int pathlen = strlen(path);
|
||||||
@ -3148,6 +3152,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
|
|||||||
}
|
}
|
||||||
return (tmp);
|
return (tmp);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
|
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
|
||||||
|
|
||||||
@ -3629,7 +3634,7 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb)
|
|||||||
|
|
||||||
(void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
|
(void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
|
||||||
strrchr(path, '/'));
|
strrchr(path, '/'));
|
||||||
if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
|
if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) {
|
||||||
struct dk_gpt *vtoc;
|
struct dk_gpt *vtoc;
|
||||||
|
|
||||||
if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
|
if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
|
||||||
@ -3675,6 +3680,54 @@ find_start_block(nvlist_t *config)
|
|||||||
return (MAXOFFSET_T);
|
return (MAXOFFSET_T);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
zpool_label_disk_wait(char *path, int timeout)
|
||||||
|
{
|
||||||
|
struct stat64 statbuf;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait timeout miliseconds for a newly created device to be available
|
||||||
|
* from the given path. There is a small window when a /dev/ device
|
||||||
|
* will exist and the udev link will not, so we must wait for the
|
||||||
|
* symlink. Depending on the udev rules this may take a few seconds.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < timeout; i++) {
|
||||||
|
usleep(1000);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if ((stat64(path, &statbuf) == 0) && (errno == 0))
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ENOENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
zpool_label_disk_check(char *path)
|
||||||
|
{
|
||||||
|
struct dk_gpt *vtoc;
|
||||||
|
int fd, err;
|
||||||
|
|
||||||
|
if ((fd = open(path, O_RDWR|O_DIRECT)) < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
|
||||||
|
(void) close(fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
|
||||||
|
efi_free(vtoc);
|
||||||
|
(void) close(fd);
|
||||||
|
return EIDRM;
|
||||||
|
}
|
||||||
|
|
||||||
|
efi_free(vtoc);
|
||||||
|
(void) close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Label an individual disk. The name provided is the short name,
|
* Label an individual disk. The name provided is the short name,
|
||||||
* stripped of any leading /dev path.
|
* stripped of any leading /dev path.
|
||||||
@ -3684,7 +3737,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
|||||||
{
|
{
|
||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
struct dk_gpt *vtoc;
|
struct dk_gpt *vtoc;
|
||||||
int fd;
|
int rval, fd;
|
||||||
size_t resv = EFI_MIN_RESV_SIZE;
|
size_t resv = EFI_MIN_RESV_SIZE;
|
||||||
uint64_t slice_size;
|
uint64_t slice_size;
|
||||||
diskaddr_t start_block;
|
diskaddr_t start_block;
|
||||||
@ -3720,13 +3773,13 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
|||||||
(void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name,
|
(void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name,
|
||||||
BACKUP_SLICE);
|
BACKUP_SLICE);
|
||||||
|
|
||||||
if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
|
if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
|
||||||
/*
|
/*
|
||||||
* This shouldn't happen. We've long since verified that this
|
* This shouldn't happen. We've long since verified that this
|
||||||
* is a valid device.
|
* is a valid device.
|
||||||
*/
|
*/
|
||||||
zfs_error_aux(hdl,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
dgettext(TEXT_DOMAIN, "unable to open device"));
|
"unable to open device '%s': %d"), path, errno);
|
||||||
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
|
return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3769,7 +3822,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
|||||||
vtoc->efi_parts[8].p_size = resv;
|
vtoc->efi_parts[8].p_size = resv;
|
||||||
vtoc->efi_parts[8].p_tag = V_RESERVED;
|
vtoc->efi_parts[8].p_tag = V_RESERVED;
|
||||||
|
|
||||||
if (efi_write(fd, vtoc) != 0) {
|
if ((rval = efi_write(fd, vtoc)) != 0) {
|
||||||
/*
|
/*
|
||||||
* Some block drivers (like pcata) may not support EFI
|
* Some block drivers (like pcata) may not support EFI
|
||||||
* GPT labels. Print out a helpful error message dir-
|
* GPT labels. Print out a helpful error message dir-
|
||||||
@ -3779,123 +3832,34 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
|||||||
(void) close(fd);
|
(void) close(fd);
|
||||||
efi_free(vtoc);
|
efi_free(vtoc);
|
||||||
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "try using "
|
||||||
"try using fdisk(1M) and then provide a specific slice"));
|
"parted(8) and then provide a specific slice: %d"), rval);
|
||||||
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) close(fd);
|
(void) close(fd);
|
||||||
efi_free(vtoc);
|
efi_free(vtoc);
|
||||||
return (0);
|
|
||||||
}
|
/* Wait for the first expected slice to appear. */
|
||||||
|
(void) snprintf(path, sizeof (path), "%s/%s%s%s", DISK_ROOT, name,
|
||||||
static boolean_t
|
isdigit(name[strlen(name)-1]) ? "p" : "", FIRST_SLICE);
|
||||||
supported_dump_vdev_type(libzfs_handle_t *hdl, nvlist_t *config, char *errbuf)
|
rval = zpool_label_disk_wait(path, 3000);
|
||||||
{
|
if (rval) {
|
||||||
char *type;
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "
|
||||||
nvlist_t **child;
|
"detect device partitions on '%s': %d"), path, rval);
|
||||||
uint_t children, c;
|
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
||||||
|
}
|
||||||
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_TYPE, &type) == 0);
|
|
||||||
if (strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
|
/* We can't be to paranoid. Read the label back and verify it. */
|
||||||
strcmp(type, VDEV_TYPE_FILE) == 0 ||
|
(void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
|
||||||
strcmp(type, VDEV_TYPE_LOG) == 0 ||
|
rval = zpool_label_disk_check(path);
|
||||||
strcmp(type, VDEV_TYPE_HOLE) == 0 ||
|
if (rval) {
|
||||||
strcmp(type, VDEV_TYPE_MISSING) == 0) {
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "freshly written "
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
"EFI label on '%s' is damaged. Ensure\nthis device "
|
||||||
"vdev type '%s' is not supported"), type);
|
"is not in in use, and is functioning properly: %d"),
|
||||||
(void) zfs_error(hdl, EZFS_VDEVNOTSUP, errbuf);
|
path, rval);
|
||||||
return (B_FALSE);
|
return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
|
||||||
}
|
}
|
||||||
if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
|
|
||||||
&child, &children) == 0) {
|
return 0;
|
||||||
for (c = 0; c < children; c++) {
|
|
||||||
if (!supported_dump_vdev_type(hdl, child[c], errbuf))
|
|
||||||
return (B_FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (B_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check if this zvol is allowable for use as a dump device; zero if
|
|
||||||
* it is, > 0 if it isn't, < 0 if it isn't a zvol
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
zvol_check_dump_config(char *arg)
|
|
||||||
{
|
|
||||||
zpool_handle_t *zhp = NULL;
|
|
||||||
nvlist_t *config, *nvroot;
|
|
||||||
char *p, *volname;
|
|
||||||
nvlist_t **top;
|
|
||||||
uint_t toplevels;
|
|
||||||
libzfs_handle_t *hdl;
|
|
||||||
char errbuf[1024];
|
|
||||||
char poolname[ZPOOL_MAXNAMELEN];
|
|
||||||
int pathlen = strlen(ZVOL_FULL_DEV_DIR);
|
|
||||||
int ret = 1;
|
|
||||||
|
|
||||||
if (strncmp(arg, ZVOL_FULL_DEV_DIR, pathlen)) {
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
||||||
"dump is not supported on device '%s'"), arg);
|
|
||||||
|
|
||||||
if ((hdl = libzfs_init()) == NULL)
|
|
||||||
return (1);
|
|
||||||
libzfs_print_on_error(hdl, B_TRUE);
|
|
||||||
|
|
||||||
volname = arg + pathlen;
|
|
||||||
|
|
||||||
/* check the configuration of the pool */
|
|
||||||
if ((p = strchr(volname, '/')) == NULL) {
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"malformed dataset name"));
|
|
||||||
(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
|
||||||
return (1);
|
|
||||||
} else if (p - volname >= ZFS_MAXNAMELEN) {
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"dataset name is too long"));
|
|
||||||
(void) zfs_error(hdl, EZFS_NAMETOOLONG, errbuf);
|
|
||||||
return (1);
|
|
||||||
} else {
|
|
||||||
(void) strncpy(poolname, volname, p - volname);
|
|
||||||
poolname[p - volname] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zhp = zpool_open(hdl, poolname)) == NULL) {
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"could not open pool '%s'"), poolname);
|
|
||||||
(void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
config = zpool_get_config(zhp, NULL);
|
|
||||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
|
||||||
&nvroot) != 0) {
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"could not obtain vdev configuration for '%s'"), poolname);
|
|
||||||
(void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
|
|
||||||
&top, &toplevels) == 0);
|
|
||||||
if (toplevels != 1) {
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"'%s' has multiple top level vdevs"), poolname);
|
|
||||||
(void) zfs_error(hdl, EZFS_DEVOVERFLOW, errbuf);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!supported_dump_vdev_type(hdl, top[0], errbuf)) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (zhp)
|
|
||||||
zpool_close(zhp);
|
|
||||||
libzfs_fini(hdl);
|
|
||||||
return (ret);
|
|
||||||
}
|
}
|
||||||
|
@ -2608,6 +2608,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_VOLUME &&
|
||||||
|
zvol_remove_link(hdl, zhp->zfs_name) != 0) {
|
||||||
|
zfs_close(zhp);
|
||||||
|
zcmd_free_nvlists(&zc);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
zfs_close(zhp);
|
zfs_close(zhp);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@ -2813,6 +2819,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||||||
if (h != NULL) {
|
if (h != NULL) {
|
||||||
if (h->zfs_type == ZFS_TYPE_VOLUME) {
|
if (h->zfs_type == ZFS_TYPE_VOLUME) {
|
||||||
*cp = '@';
|
*cp = '@';
|
||||||
|
err = zvol_create_link(hdl, h->zfs_name);
|
||||||
|
if (err == 0 && ioctl_err == 0)
|
||||||
|
err = zvol_create_link(hdl,
|
||||||
|
zc.zc_value);
|
||||||
} else if (newfs || stream_avl) {
|
} else if (newfs || stream_avl) {
|
||||||
/*
|
/*
|
||||||
* Track the first/top of hierarchy fs,
|
* Track the first/top of hierarchy fs,
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/mnttab.h>
|
#include <sys/mnttab.h>
|
||||||
#include <sys/mntent.h>
|
#include <sys/mntent.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -648,7 +649,9 @@ libzfs_fini(libzfs_handle_t *hdl)
|
|||||||
#endif
|
#endif
|
||||||
if (hdl->libzfs_sharetab)
|
if (hdl->libzfs_sharetab)
|
||||||
(void) fclose(hdl->libzfs_sharetab);
|
(void) fclose(hdl->libzfs_sharetab);
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
zfs_uninit_libshare(hdl);
|
zfs_uninit_libshare(hdl);
|
||||||
|
#endif
|
||||||
if (hdl->libzfs_log_str)
|
if (hdl->libzfs_log_str)
|
||||||
(void) free(hdl->libzfs_log_str);
|
(void) free(hdl->libzfs_log_str);
|
||||||
zpool_free_handles(hdl);
|
zpool_free_handles(hdl);
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include <sys/processor.h>
|
#include <sys/processor.h>
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/mount.h> /* for BLKGETSIZE64 */
|
||||||
#include <sys/systeminfo.h>
|
#include <sys/systeminfo.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -533,7 +535,11 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
|
|||||||
* for its size. So -- gag -- we open the block device to get
|
* for its size. So -- gag -- we open the block device to get
|
||||||
* its size, and remember it for subsequent VOP_GETATTR().
|
* its size, and remember it for subsequent VOP_GETATTR().
|
||||||
*/
|
*/
|
||||||
|
#if defined(__sun__) || defined(__sun)
|
||||||
if (strncmp(path, "/dev/", 5) == 0) {
|
if (strncmp(path, "/dev/", 5) == 0) {
|
||||||
|
#else
|
||||||
|
if (0) {
|
||||||
|
#endif
|
||||||
char *dsk;
|
char *dsk;
|
||||||
fd = open64(path, O_RDONLY);
|
fd = open64(path, O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
@ -562,6 +568,14 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(flags & FCREAT) && S_ISBLK(st.st_mode)) {
|
||||||
|
#ifdef __linux__
|
||||||
|
flags |= O_DIRECT;
|
||||||
|
#endif
|
||||||
|
/* We shouldn't be writing to block devices in userspace */
|
||||||
|
VERIFY(!(flags & FWRITE));
|
||||||
|
}
|
||||||
|
|
||||||
if (flags & FCREAT)
|
if (flags & FCREAT)
|
||||||
old_umask = umask(0);
|
old_umask = umask(0);
|
||||||
|
|
||||||
@ -584,6 +598,16 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
|
|||||||
return (err);
|
return (err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/* In Linux, use an ioctl to get the size of a block device. */
|
||||||
|
if (S_ISBLK(st.st_mode)) {
|
||||||
|
if (ioctl(fd, BLKGETSIZE64, &st.st_size) != 0) {
|
||||||
|
err = errno;
|
||||||
|
close(fd);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
|
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
*vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
|
*vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
|
||||||
@ -637,6 +661,16 @@ vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
if (rc == -1 && errno == EINVAL) {
|
||||||
|
/*
|
||||||
|
* Under Linux, this most likely means an alignment issue
|
||||||
|
* (memory or disk) due to O_DIRECT, so we abort() in order to
|
||||||
|
* catch the offender.
|
||||||
|
*/
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (rc == -1)
|
if (rc == -1)
|
||||||
return (errno);
|
return (errno);
|
||||||
|
|
||||||
|
@ -1436,6 +1436,7 @@ sa_lookup(sa_handle_t *hdl, sa_attr_type_t attr, void *buf, uint32_t buflen)
|
|||||||
int
|
int
|
||||||
sa_lookup_uio(sa_handle_t *hdl, sa_attr_type_t attr, uio_t *uio)
|
sa_lookup_uio(sa_handle_t *hdl, sa_attr_type_t attr, uio_t *uio)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
int error;
|
int error;
|
||||||
sa_bulk_attr_t bulk;
|
sa_bulk_attr_t bulk;
|
||||||
|
|
||||||
@ -1452,7 +1453,9 @@ sa_lookup_uio(sa_handle_t *hdl, sa_attr_type_t attr, uio_t *uio)
|
|||||||
}
|
}
|
||||||
mutex_exit(&hdl->sa_lock);
|
mutex_exit(&hdl->sa_lock);
|
||||||
return (error);
|
return (error);
|
||||||
|
#else
|
||||||
|
return ENOSYS;
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ sa_attr_reg_t zfs_attr_table[ZPL_END+1] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
|
#ifdef HAVE_ZPL
|
||||||
int
|
int
|
||||||
zfs_sa_readlink(znode_t *zp, uio_t *uio)
|
zfs_sa_readlink(znode_t *zp, uio_t *uio)
|
||||||
{
|
{
|
||||||
@ -331,4 +331,5 @@ zfs_sa_upgrade_txholds(dmu_tx_t *tx, znode_t *zp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_ZPL */
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user