Import Opensolaris source code used with zfs port. Zfs code si from date
200811.
This commit is contained in:
parent
0997da05f2
commit
c1cb2cd89c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* Print intent log header and statistics.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/zil.h>
|
||||
#include <sys/zil_impl.h>
|
||||
|
||||
extern uint8_t dump_opt[256];
|
||||
|
||||
static void
|
||||
print_log_bp(const blkptr_t *bp, const char *prefix)
|
||||
{
|
||||
char blkbuf[BP_SPRINTF_LEN];
|
||||
|
||||
sprintf_blkptr(blkbuf, BP_SPRINTF_LEN, bp);
|
||||
(void) printf("%s%s\n", prefix, blkbuf);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_create(zilog_t *zilog, int txtype, lr_create_t *lr)
|
||||
{
|
||||
time_t crtime = lr->lr_crtime[0];
|
||||
char *name = (char *)(lr + 1);
|
||||
char *link = name + strlen(name) + 1;
|
||||
|
||||
if (txtype == TX_SYMLINK)
|
||||
(void) printf("\t\t\t%s -> %s\n", name, link);
|
||||
else
|
||||
(void) printf("\t\t\t%s\n", name);
|
||||
|
||||
(void) printf("\t\t\t%s", ctime(&crtime));
|
||||
(void) printf("\t\t\tdoid %llu, foid %llu, mode %llo\n",
|
||||
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_foid,
|
||||
(longlong_t)lr->lr_mode);
|
||||
(void) printf("\t\t\tuid %llu, gid %llu, gen %llu, rdev 0x%llx\n",
|
||||
(u_longlong_t)lr->lr_uid, (u_longlong_t)lr->lr_gid,
|
||||
(u_longlong_t)lr->lr_gen, (u_longlong_t)lr->lr_rdev);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_remove(zilog_t *zilog, int txtype, lr_remove_t *lr)
|
||||
{
|
||||
(void) printf("\t\t\tdoid %llu, name %s\n",
|
||||
(u_longlong_t)lr->lr_doid, (char *)(lr + 1));
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_link(zilog_t *zilog, int txtype, lr_link_t *lr)
|
||||
{
|
||||
(void) printf("\t\t\tdoid %llu, link_obj %llu, name %s\n",
|
||||
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj,
|
||||
(char *)(lr + 1));
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_rename(zilog_t *zilog, int txtype, lr_rename_t *lr)
|
||||
{
|
||||
char *snm = (char *)(lr + 1);
|
||||
char *tnm = snm + strlen(snm) + 1;
|
||||
|
||||
(void) printf("\t\t\tsdoid %llu, tdoid %llu\n",
|
||||
(u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid);
|
||||
(void) printf("\t\t\tsrc %s tgt %s\n", snm, tnm);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_write(zilog_t *zilog, int txtype, lr_write_t *lr)
|
||||
{
|
||||
char *data, *dlimit;
|
||||
blkptr_t *bp = &lr->lr_blkptr;
|
||||
char buf[SPA_MAXBLOCKSIZE];
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
int error;
|
||||
|
||||
(void) printf("\t\t\tfoid %llu, offset 0x%llx,"
|
||||
" length 0x%llx, blkoff 0x%llx\n",
|
||||
(u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
|
||||
(u_longlong_t)lr->lr_length, (u_longlong_t)lr->lr_blkoff);
|
||||
|
||||
if (verbose < 5)
|
||||
return;
|
||||
|
||||
if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
|
||||
(void) printf("\t\t\thas blkptr, %s\n",
|
||||
bp->blk_birth >= spa_first_txg(zilog->zl_spa) ?
|
||||
"will claim" : "won't claim");
|
||||
print_log_bp(bp, "\t\t\t");
|
||||
if (bp->blk_birth == 0) {
|
||||
bzero(buf, sizeof (buf));
|
||||
} else {
|
||||
zbookmark_t zb;
|
||||
|
||||
ASSERT3U(bp->blk_cksum.zc_word[ZIL_ZC_OBJSET], ==,
|
||||
dmu_objset_id(zilog->zl_os));
|
||||
|
||||
zb.zb_objset = bp->blk_cksum.zc_word[ZIL_ZC_OBJSET];
|
||||
zb.zb_object = 0;
|
||||
zb.zb_level = -1;
|
||||
zb.zb_blkid = bp->blk_cksum.zc_word[ZIL_ZC_SEQ];
|
||||
|
||||
error = zio_wait(zio_read(NULL, zilog->zl_spa,
|
||||
bp, buf, BP_GET_LSIZE(bp), NULL, NULL,
|
||||
ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL, &zb));
|
||||
if (error)
|
||||
return;
|
||||
}
|
||||
data = buf + lr->lr_blkoff;
|
||||
} else {
|
||||
data = (char *)(lr + 1);
|
||||
}
|
||||
|
||||
dlimit = data + MIN(lr->lr_length,
|
||||
(verbose < 6 ? 20 : SPA_MAXBLOCKSIZE));
|
||||
|
||||
(void) printf("\t\t\t");
|
||||
while (data < dlimit) {
|
||||
if (isprint(*data))
|
||||
(void) printf("%c ", *data);
|
||||
else
|
||||
(void) printf("%2X", *data);
|
||||
data++;
|
||||
}
|
||||
(void) printf("\n");
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_truncate(zilog_t *zilog, int txtype, lr_truncate_t *lr)
|
||||
{
|
||||
(void) printf("\t\t\tfoid %llu, offset 0x%llx, length 0x%llx\n",
|
||||
(u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
|
||||
(u_longlong_t)lr->lr_length);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_setattr(zilog_t *zilog, int txtype, lr_setattr_t *lr)
|
||||
{
|
||||
time_t atime = (time_t)lr->lr_atime[0];
|
||||
time_t mtime = (time_t)lr->lr_mtime[0];
|
||||
|
||||
(void) printf("\t\t\tfoid %llu, mask 0x%llx\n",
|
||||
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_mask);
|
||||
|
||||
if (lr->lr_mask & AT_MODE) {
|
||||
(void) printf("\t\t\tAT_MODE %llo\n",
|
||||
(longlong_t)lr->lr_mode);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_UID) {
|
||||
(void) printf("\t\t\tAT_UID %llu\n",
|
||||
(u_longlong_t)lr->lr_uid);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_GID) {
|
||||
(void) printf("\t\t\tAT_GID %llu\n",
|
||||
(u_longlong_t)lr->lr_gid);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_SIZE) {
|
||||
(void) printf("\t\t\tAT_SIZE %llu\n",
|
||||
(u_longlong_t)lr->lr_size);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_ATIME) {
|
||||
(void) printf("\t\t\tAT_ATIME %llu.%09llu %s",
|
||||
(u_longlong_t)lr->lr_atime[0],
|
||||
(u_longlong_t)lr->lr_atime[1],
|
||||
ctime(&atime));
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_MTIME) {
|
||||
(void) printf("\t\t\tAT_MTIME %llu.%09llu %s",
|
||||
(u_longlong_t)lr->lr_mtime[0],
|
||||
(u_longlong_t)lr->lr_mtime[1],
|
||||
ctime(&mtime));
|
||||
}
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_acl(zilog_t *zilog, int txtype, lr_acl_t *lr)
|
||||
{
|
||||
(void) printf("\t\t\tfoid %llu, aclcnt %llu\n",
|
||||
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt);
|
||||
}
|
||||
|
||||
typedef void (*zil_prt_rec_func_t)();
|
||||
typedef struct zil_rec_info {
|
||||
zil_prt_rec_func_t zri_print;
|
||||
char *zri_name;
|
||||
uint64_t zri_count;
|
||||
} zil_rec_info_t;
|
||||
|
||||
static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
|
||||
{ NULL, "Total " },
|
||||
{ zil_prt_rec_create, "TX_CREATE " },
|
||||
{ zil_prt_rec_create, "TX_MKDIR " },
|
||||
{ zil_prt_rec_create, "TX_MKXATTR " },
|
||||
{ zil_prt_rec_create, "TX_SYMLINK " },
|
||||
{ zil_prt_rec_remove, "TX_REMOVE " },
|
||||
{ zil_prt_rec_remove, "TX_RMDIR " },
|
||||
{ zil_prt_rec_link, "TX_LINK " },
|
||||
{ zil_prt_rec_rename, "TX_RENAME " },
|
||||
{ zil_prt_rec_write, "TX_WRITE " },
|
||||
{ zil_prt_rec_truncate, "TX_TRUNCATE " },
|
||||
{ zil_prt_rec_setattr, "TX_SETATTR " },
|
||||
{ zil_prt_rec_acl, "TX_ACL_V0 " },
|
||||
{ zil_prt_rec_acl, "TX_ACL_ACL " },
|
||||
{ zil_prt_rec_create, "TX_CREATE_ACL " },
|
||||
{ zil_prt_rec_create, "TX_CREATE_ATTR " },
|
||||
{ zil_prt_rec_create, "TX_CREATE_ACL_ATTR " },
|
||||
{ zil_prt_rec_create, "TX_MKDIR_ACL " },
|
||||
{ zil_prt_rec_create, "TX_MKDIR_ATTR " },
|
||||
{ zil_prt_rec_create, "TX_MKDIR_ACL_ATTR " },
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg)
|
||||
{
|
||||
int txtype;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
|
||||
/* reduce size of txtype to strip off TX_CI bit */
|
||||
txtype = lr->lrc_txtype;
|
||||
|
||||
ASSERT(txtype != 0 && (uint_t)txtype < TX_MAX_TYPE);
|
||||
ASSERT(lr->lrc_txg);
|
||||
|
||||
(void) printf("\t\t%s%s len %6llu, txg %llu, seq %llu\n",
|
||||
(lr->lrc_txtype & TX_CI) ? "CI-" : "",
|
||||
zil_rec_info[txtype].zri_name,
|
||||
(u_longlong_t)lr->lrc_reclen,
|
||||
(u_longlong_t)lr->lrc_txg,
|
||||
(u_longlong_t)lr->lrc_seq);
|
||||
|
||||
if (txtype && verbose >= 3)
|
||||
zil_rec_info[txtype].zri_print(zilog, txtype, lr);
|
||||
|
||||
zil_rec_info[txtype].zri_count++;
|
||||
zil_rec_info[0].zri_count++;
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
|
||||
{
|
||||
char blkbuf[BP_SPRINTF_LEN];
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
char *claim;
|
||||
|
||||
if (verbose <= 3)
|
||||
return;
|
||||
|
||||
if (verbose >= 5) {
|
||||
(void) strcpy(blkbuf, ", ");
|
||||
sprintf_blkptr(blkbuf + strlen(blkbuf),
|
||||
BP_SPRINTF_LEN - strlen(blkbuf), bp);
|
||||
} else {
|
||||
blkbuf[0] = '\0';
|
||||
}
|
||||
|
||||
if (claim_txg != 0)
|
||||
claim = "already claimed";
|
||||
else if (bp->blk_birth >= spa_first_txg(zilog->zl_spa))
|
||||
claim = "will claim";
|
||||
else
|
||||
claim = "won't claim";
|
||||
|
||||
(void) printf("\tBlock seqno %llu, %s%s\n",
|
||||
(u_longlong_t)bp->blk_cksum.zc_word[ZIL_ZC_SEQ], claim, blkbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_stats(int verbose)
|
||||
{
|
||||
int i, w, p10;
|
||||
|
||||
if (verbose > 3)
|
||||
(void) printf("\n");
|
||||
|
||||
if (zil_rec_info[0].zri_count == 0)
|
||||
return;
|
||||
|
||||
for (w = 1, p10 = 10; zil_rec_info[0].zri_count >= p10; p10 *= 10)
|
||||
w++;
|
||||
|
||||
for (i = 0; i < TX_MAX_TYPE; i++)
|
||||
if (zil_rec_info[i].zri_count || verbose >= 3)
|
||||
(void) printf("\t\t%s %*llu\n",
|
||||
zil_rec_info[i].zri_name, w,
|
||||
(u_longlong_t)zil_rec_info[i].zri_count);
|
||||
(void) printf("\n");
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
void
|
||||
dump_intent_log(zilog_t *zilog)
|
||||
{
|
||||
const zil_header_t *zh = zilog->zl_header;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
int i;
|
||||
|
||||
if (zh->zh_log.blk_birth == 0 || verbose < 2)
|
||||
return;
|
||||
|
||||
(void) printf("\n ZIL header: claim_txg %llu, seq %llu\n",
|
||||
(u_longlong_t)zh->zh_claim_txg, (u_longlong_t)zh->zh_replay_seq);
|
||||
|
||||
if (verbose >= 4)
|
||||
print_log_bp(&zh->zh_log, "\n\tfirst block: ");
|
||||
|
||||
for (i = 0; i < TX_MAX_TYPE; i++)
|
||||
zil_rec_info[i].zri_count = 0;
|
||||
|
||||
if (verbose >= 2) {
|
||||
(void) printf("\n");
|
||||
(void) zil_parse(zilog, print_log_block, print_log_record, NULL,
|
||||
zh->zh_claim_txg);
|
||||
print_log_stats(verbose);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <libintl.h>
|
||||
#include <libuutil.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "zfs_util.h"
|
||||
#include "zfs_iter.h"
|
||||
|
||||
/*
|
||||
* This is a private interface used to gather up all the datasets specified on
|
||||
* the command line so that we can iterate over them in order.
|
||||
*
|
||||
* First, we iterate over all filesystems, gathering them together into an
|
||||
* AVL tree. We report errors for any explicitly specified datasets
|
||||
* that we couldn't open.
|
||||
*
|
||||
* When finished, we have an AVL tree of ZFS handles. We go through and execute
|
||||
* the provided callback for each one, passing whatever data the user supplied.
|
||||
*/
|
||||
|
||||
typedef struct zfs_node {
|
||||
zfs_handle_t *zn_handle;
|
||||
uu_avl_node_t zn_avlnode;
|
||||
} zfs_node_t;
|
||||
|
||||
typedef struct callback_data {
|
||||
uu_avl_t *cb_avl;
|
||||
int cb_flags;
|
||||
zfs_type_t cb_types;
|
||||
zfs_sort_column_t *cb_sortcol;
|
||||
zprop_list_t **cb_proplist;
|
||||
} callback_data_t;
|
||||
|
||||
uu_avl_pool_t *avl_pool;
|
||||
|
||||
/*
|
||||
* Include snaps if they were requested or if this a zfs list where types
|
||||
* were not specified and the "listsnapshots" property is set on this pool.
|
||||
*/
|
||||
static int
|
||||
zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)
|
||||
{
|
||||
zpool_handle_t *zph;
|
||||
|
||||
if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0)
|
||||
return (cb->cb_types & ZFS_TYPE_SNAPSHOT);
|
||||
|
||||
zph = zfs_get_pool_handle(zhp);
|
||||
return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for each dataset. If the object is of an appropriate type,
|
||||
* add it to the avl tree and recurse over any children as necessary.
|
||||
*/
|
||||
static int
|
||||
zfs_callback(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
callback_data_t *cb = data;
|
||||
int dontclose = 0;
|
||||
int include_snaps = zfs_include_snapshots(zhp, cb);
|
||||
|
||||
if ((zfs_get_type(zhp) & cb->cb_types) ||
|
||||
((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
|
||||
uu_avl_index_t idx;
|
||||
zfs_node_t *node = safe_malloc(sizeof (zfs_node_t));
|
||||
|
||||
node->zn_handle = zhp;
|
||||
uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
|
||||
if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
|
||||
&idx) == NULL) {
|
||||
if (cb->cb_proplist &&
|
||||
zfs_expand_proplist(zhp, cb->cb_proplist) != 0) {
|
||||
free(node);
|
||||
return (-1);
|
||||
}
|
||||
uu_avl_insert(cb->cb_avl, node, idx);
|
||||
dontclose = 1;
|
||||
} else {
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Recurse if necessary.
|
||||
*/
|
||||
if (cb->cb_flags & ZFS_ITER_RECURSE) {
|
||||
if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
|
||||
(void) zfs_iter_filesystems(zhp, zfs_callback, data);
|
||||
if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps)
|
||||
(void) zfs_iter_snapshots(zhp, zfs_callback, data);
|
||||
}
|
||||
|
||||
if (!dontclose)
|
||||
zfs_close(zhp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,
|
||||
boolean_t reverse)
|
||||
{
|
||||
zfs_sort_column_t *col;
|
||||
zfs_prop_t prop;
|
||||
|
||||
if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL &&
|
||||
!zfs_prop_user(name))
|
||||
return (-1);
|
||||
|
||||
col = safe_malloc(sizeof (zfs_sort_column_t));
|
||||
|
||||
col->sc_prop = prop;
|
||||
col->sc_reverse = reverse;
|
||||
if (prop == ZPROP_INVAL) {
|
||||
col->sc_user_prop = safe_malloc(strlen(name) + 1);
|
||||
(void) strcpy(col->sc_user_prop, name);
|
||||
}
|
||||
|
||||
if (*sc == NULL) {
|
||||
col->sc_last = col;
|
||||
*sc = col;
|
||||
} else {
|
||||
(*sc)->sc_last->sc_next = col;
|
||||
(*sc)->sc_last = col;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
zfs_free_sort_columns(zfs_sort_column_t *sc)
|
||||
{
|
||||
zfs_sort_column_t *col;
|
||||
|
||||
while (sc != NULL) {
|
||||
col = sc->sc_next;
|
||||
free(sc->sc_user_prop);
|
||||
free(sc);
|
||||
sc = col;
|
||||
}
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_compare(const void *larg, const void *rarg, void *unused)
|
||||
{
|
||||
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
|
||||
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
|
||||
const char *lname = zfs_get_name(l);
|
||||
const char *rname = zfs_get_name(r);
|
||||
char *lat, *rat;
|
||||
uint64_t lcreate, rcreate;
|
||||
int ret;
|
||||
|
||||
lat = (char *)strchr(lname, '@');
|
||||
rat = (char *)strchr(rname, '@');
|
||||
|
||||
if (lat != NULL)
|
||||
*lat = '\0';
|
||||
if (rat != NULL)
|
||||
*rat = '\0';
|
||||
|
||||
ret = strcmp(lname, rname);
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* If we're comparing a dataset to one of its snapshots, we
|
||||
* always make the full dataset first.
|
||||
*/
|
||||
if (lat == NULL) {
|
||||
ret = -1;
|
||||
} else if (rat == NULL) {
|
||||
ret = 1;
|
||||
} else {
|
||||
/*
|
||||
* If we have two snapshots from the same dataset, then
|
||||
* we want to sort them according to creation time. We
|
||||
* use the hidden CREATETXG property to get an absolute
|
||||
* ordering of snapshots.
|
||||
*/
|
||||
lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
|
||||
rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
|
||||
|
||||
if (lcreate < rcreate)
|
||||
ret = -1;
|
||||
else if (lcreate > rcreate)
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lat != NULL)
|
||||
*lat = '@';
|
||||
if (rat != NULL)
|
||||
*rat = '@';
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort datasets by specified columns.
|
||||
*
|
||||
* o Numeric types sort in ascending order.
|
||||
* o String types sort in alphabetical order.
|
||||
* o Types inappropriate for a row sort that row to the literal
|
||||
* bottom, regardless of the specified ordering.
|
||||
*
|
||||
* If no sort columns are specified, or two datasets compare equally
|
||||
* across all specified columns, they are sorted alphabetically by name
|
||||
* with snapshots grouped under their parents.
|
||||
*/
|
||||
static int
|
||||
zfs_sort(const void *larg, const void *rarg, void *data)
|
||||
{
|
||||
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
|
||||
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
|
||||
zfs_sort_column_t *sc = (zfs_sort_column_t *)data;
|
||||
zfs_sort_column_t *psc;
|
||||
|
||||
for (psc = sc; psc != NULL; psc = psc->sc_next) {
|
||||
char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN];
|
||||
char *lstr, *rstr;
|
||||
uint64_t lnum, rnum;
|
||||
boolean_t lvalid, rvalid;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* We group the checks below the generic code. If 'lstr' and
|
||||
* 'rstr' are non-NULL, then we do a string based comparison.
|
||||
* Otherwise, we compare 'lnum' and 'rnum'.
|
||||
*/
|
||||
lstr = rstr = NULL;
|
||||
if (psc->sc_prop == ZPROP_INVAL) {
|
||||
nvlist_t *luser, *ruser;
|
||||
nvlist_t *lval, *rval;
|
||||
|
||||
luser = zfs_get_user_props(l);
|
||||
ruser = zfs_get_user_props(r);
|
||||
|
||||
lvalid = (nvlist_lookup_nvlist(luser,
|
||||
psc->sc_user_prop, &lval) == 0);
|
||||
rvalid = (nvlist_lookup_nvlist(ruser,
|
||||
psc->sc_user_prop, &rval) == 0);
|
||||
|
||||
if (lvalid)
|
||||
verify(nvlist_lookup_string(lval,
|
||||
ZPROP_VALUE, &lstr) == 0);
|
||||
if (rvalid)
|
||||
verify(nvlist_lookup_string(rval,
|
||||
ZPROP_VALUE, &rstr) == 0);
|
||||
|
||||
} else if (zfs_prop_is_string(psc->sc_prop)) {
|
||||
lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
|
||||
sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);
|
||||
rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf,
|
||||
sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0);
|
||||
|
||||
lstr = lbuf;
|
||||
rstr = rbuf;
|
||||
} else {
|
||||
lvalid = zfs_prop_valid_for_type(psc->sc_prop,
|
||||
zfs_get_type(l));
|
||||
rvalid = zfs_prop_valid_for_type(psc->sc_prop,
|
||||
zfs_get_type(r));
|
||||
|
||||
if (lvalid)
|
||||
(void) zfs_prop_get_numeric(l, psc->sc_prop,
|
||||
&lnum, NULL, NULL, 0);
|
||||
if (rvalid)
|
||||
(void) zfs_prop_get_numeric(r, psc->sc_prop,
|
||||
&rnum, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
if (!lvalid && !rvalid)
|
||||
continue;
|
||||
else if (!lvalid)
|
||||
return (1);
|
||||
else if (!rvalid)
|
||||
return (-1);
|
||||
|
||||
if (lstr)
|
||||
ret = strcmp(lstr, rstr);
|
||||
else if (lnum < rnum)
|
||||
ret = -1;
|
||||
else if (lnum > rnum)
|
||||
ret = 1;
|
||||
|
||||
if (ret != 0) {
|
||||
if (psc->sc_reverse == B_TRUE)
|
||||
ret = (ret < 0) ? 1 : -1;
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
return (zfs_compare(larg, rarg, NULL));
|
||||
}
|
||||
|
||||
int
|
||||
zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
|
||||
zfs_sort_column_t *sortcol, zprop_list_t **proplist,
|
||||
zfs_iter_f callback, void *data)
|
||||
{
|
||||
callback_data_t cb;
|
||||
int ret = 0;
|
||||
zfs_node_t *node;
|
||||
uu_avl_walk_t *walk;
|
||||
|
||||
avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t),
|
||||
offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT);
|
||||
|
||||
if (avl_pool == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("internal error: out of memory\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cb.cb_sortcol = sortcol;
|
||||
cb.cb_flags = flags;
|
||||
cb.cb_proplist = proplist;
|
||||
cb.cb_types = types;
|
||||
if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("internal error: out of memory\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (argc == 0) {
|
||||
/*
|
||||
* If given no arguments, iterate over all datasets.
|
||||
*/
|
||||
cb.cb_flags |= ZFS_ITER_RECURSE;
|
||||
ret = zfs_iter_root(g_zfs, zfs_callback, &cb);
|
||||
} else {
|
||||
int i;
|
||||
zfs_handle_t *zhp;
|
||||
zfs_type_t argtype;
|
||||
|
||||
/*
|
||||
* If we're recursive, then we always allow filesystems as
|
||||
* arguments. If we also are interested in snapshots, then we
|
||||
* can take volumes as well.
|
||||
*/
|
||||
argtype = types;
|
||||
if (flags & ZFS_ITER_RECURSE) {
|
||||
argtype |= ZFS_TYPE_FILESYSTEM;
|
||||
if (types & ZFS_TYPE_SNAPSHOT)
|
||||
argtype |= ZFS_TYPE_VOLUME;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) {
|
||||
zhp = zfs_path_to_zhandle(g_zfs, argv[i],
|
||||
argtype);
|
||||
} else {
|
||||
zhp = zfs_open(g_zfs, argv[i], argtype);
|
||||
}
|
||||
if (zhp != NULL)
|
||||
ret |= zfs_callback(zhp, &cb);
|
||||
else
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we've got our AVL tree full of zfs handles, so iterate
|
||||
* over each one and execute the real user callback.
|
||||
*/
|
||||
for (node = uu_avl_first(cb.cb_avl); node != NULL;
|
||||
node = uu_avl_next(cb.cb_avl, node))
|
||||
ret |= callback(node->zn_handle, data);
|
||||
|
||||
/*
|
||||
* Finally, clean up the AVL tree.
|
||||
*/
|
||||
if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("internal error: out of memory"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((node = uu_avl_walk_next(walk)) != NULL) {
|
||||
uu_avl_remove(cb.cb_avl, node);
|
||||
zfs_close(node->zn_handle);
|
||||
free(node);
|
||||
}
|
||||
|
||||
uu_avl_walk_end(walk);
|
||||
uu_avl_destroy(cb.cb_avl);
|
||||
uu_avl_pool_destroy(avl_pool);
|
||||
|
||||
return (ret);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef ZFS_ITER_H
|
||||
#define ZFS_ITER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct zfs_sort_column {
|
||||
struct zfs_sort_column *sc_next;
|
||||
struct zfs_sort_column *sc_last;
|
||||
zfs_prop_t sc_prop;
|
||||
char *sc_user_prop;
|
||||
boolean_t sc_reverse;
|
||||
} zfs_sort_column_t;
|
||||
|
||||
#define ZFS_ITER_RECURSE (1 << 0)
|
||||
#define ZFS_ITER_ARGS_CAN_BE_PATHS (1 << 1)
|
||||
#define ZFS_ITER_PROP_LISTSNAPS (1 << 2)
|
||||
|
||||
int zfs_for_each(int, char **, int options, zfs_type_t,
|
||||
zfs_sort_column_t *, zprop_list_t **, zfs_iter_f, void *);
|
||||
int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t);
|
||||
void zfs_free_sort_columns(zfs_sort_column_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZFS_ITER_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_UTIL_H
|
||||
#define _ZFS_UTIL_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void * safe_malloc(size_t size);
|
||||
libzfs_handle_t *g_zfs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZFS_UTIL_H */
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <libintl.h>
|
||||
#include <libuutil.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "zpool_util.h"
|
||||
|
||||
/*
|
||||
* Private interface for iterating over pools specified on the command line.
|
||||
* Most consumers will call for_each_pool, but in order to support iostat, we
|
||||
* allow fined grained control through the zpool_list_t interface.
|
||||
*/
|
||||
|
||||
typedef struct zpool_node {
|
||||
zpool_handle_t *zn_handle;
|
||||
uu_avl_node_t zn_avlnode;
|
||||
int zn_mark;
|
||||
} zpool_node_t;
|
||||
|
||||
struct zpool_list {
|
||||
boolean_t zl_findall;
|
||||
uu_avl_t *zl_avl;
|
||||
uu_avl_pool_t *zl_pool;
|
||||
zprop_list_t **zl_proplist;
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zpool_compare(const void *larg, const void *rarg, void *unused)
|
||||
{
|
||||
zpool_handle_t *l = ((zpool_node_t *)larg)->zn_handle;
|
||||
zpool_handle_t *r = ((zpool_node_t *)rarg)->zn_handle;
|
||||
const char *lname = zpool_get_name(l);
|
||||
const char *rname = zpool_get_name(r);
|
||||
|
||||
return (strcmp(lname, rname));
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback function for pool_list_get(). Adds the given pool to the AVL tree
|
||||
* of known pools.
|
||||
*/
|
||||
static int
|
||||
add_pool(zpool_handle_t *zhp, void *data)
|
||||
{
|
||||
zpool_list_t *zlp = data;
|
||||
zpool_node_t *node = safe_malloc(sizeof (zpool_node_t));
|
||||
uu_avl_index_t idx;
|
||||
|
||||
node->zn_handle = zhp;
|
||||
uu_avl_node_init(node, &node->zn_avlnode, zlp->zl_pool);
|
||||
if (uu_avl_find(zlp->zl_avl, node, NULL, &idx) == NULL) {
|
||||
if (zlp->zl_proplist &&
|
||||
zpool_expand_proplist(zhp, zlp->zl_proplist) != 0) {
|
||||
zpool_close(zhp);
|
||||
free(node);
|
||||
return (-1);
|
||||
}
|
||||
uu_avl_insert(zlp->zl_avl, node, idx);
|
||||
} else {
|
||||
zpool_close(zhp);
|
||||
free(node);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a list of pools based on the given arguments. If we're given no
|
||||
* arguments, then iterate over all pools in the system and add them to the AVL
|
||||
* tree. Otherwise, add only those pool explicitly specified on the command
|
||||
* line.
|
||||
*/
|
||||
zpool_list_t *
|
||||
pool_list_get(int argc, char **argv, zprop_list_t **proplist, int *err)
|
||||
{
|
||||
zpool_list_t *zlp;
|
||||
|
||||
zlp = safe_malloc(sizeof (zpool_list_t));
|
||||
|
||||
zlp->zl_pool = uu_avl_pool_create("zfs_pool", sizeof (zpool_node_t),
|
||||
offsetof(zpool_node_t, zn_avlnode), zpool_compare, UU_DEFAULT);
|
||||
|
||||
if (zlp->zl_pool == NULL)
|
||||
zpool_no_memory();
|
||||
|
||||
if ((zlp->zl_avl = uu_avl_create(zlp->zl_pool, NULL,
|
||||
UU_DEFAULT)) == NULL)
|
||||
zpool_no_memory();
|
||||
|
||||
zlp->zl_proplist = proplist;
|
||||
|
||||
if (argc == 0) {
|
||||
(void) zpool_iter(g_zfs, add_pool, zlp);
|
||||
zlp->zl_findall = B_TRUE;
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
zpool_handle_t *zhp;
|
||||
|
||||
if (zhp = zpool_open_canfail(g_zfs, argv[i])) {
|
||||
if (add_pool(zhp, zlp) != 0)
|
||||
*err = B_TRUE;
|
||||
} else {
|
||||
*err = B_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (zlp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for any new pools, adding them to the list. We only add pools when no
|
||||
* options were given on the command line. Otherwise, we keep the list fixed as
|
||||
* those that were explicitly specified.
|
||||
*/
|
||||
void
|
||||
pool_list_update(zpool_list_t *zlp)
|
||||
{
|
||||
if (zlp->zl_findall)
|
||||
(void) zpool_iter(g_zfs, add_pool, zlp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all pools in the list, executing the callback for each
|
||||
*/
|
||||
int
|
||||
pool_list_iter(zpool_list_t *zlp, int unavail, zpool_iter_f func,
|
||||
void *data)
|
||||
{
|
||||
zpool_node_t *node, *next_node;
|
||||
int ret = 0;
|
||||
|
||||
for (node = uu_avl_first(zlp->zl_avl); node != NULL; node = next_node) {
|
||||
next_node = uu_avl_next(zlp->zl_avl, node);
|
||||
if (zpool_get_state(node->zn_handle) != POOL_STATE_UNAVAIL ||
|
||||
unavail)
|
||||
ret |= func(node->zn_handle, data);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the given pool from the list. When running iostat, we want to remove
|
||||
* those pools that no longer exist.
|
||||
*/
|
||||
void
|
||||
pool_list_remove(zpool_list_t *zlp, zpool_handle_t *zhp)
|
||||
{
|
||||
zpool_node_t search, *node;
|
||||
|
||||
search.zn_handle = zhp;
|
||||
if ((node = uu_avl_find(zlp->zl_avl, &search, NULL, NULL)) != NULL) {
|
||||
uu_avl_remove(zlp->zl_avl, node);
|
||||
zpool_close(node->zn_handle);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all the handles associated with this list.
|
||||
*/
|
||||
void
|
||||
pool_list_free(zpool_list_t *zlp)
|
||||
{
|
||||
uu_avl_walk_t *walk;
|
||||
zpool_node_t *node;
|
||||
|
||||
if ((walk = uu_avl_walk_start(zlp->zl_avl, UU_WALK_ROBUST)) == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("internal error: out of memory"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((node = uu_avl_walk_next(walk)) != NULL) {
|
||||
uu_avl_remove(zlp->zl_avl, node);
|
||||
zpool_close(node->zn_handle);
|
||||
free(node);
|
||||
}
|
||||
|
||||
uu_avl_walk_end(walk);
|
||||
uu_avl_destroy(zlp->zl_avl);
|
||||
uu_avl_pool_destroy(zlp->zl_pool);
|
||||
|
||||
free(zlp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of elements in the pool list.
|
||||
*/
|
||||
int
|
||||
pool_list_count(zpool_list_t *zlp)
|
||||
{
|
||||
return (uu_avl_numnodes(zlp->zl_avl));
|
||||
}
|
||||
|
||||
/*
|
||||
* High level function which iterates over all pools given on the command line,
|
||||
* using the pool_list_* interfaces.
|
||||
*/
|
||||
int
|
||||
for_each_pool(int argc, char **argv, boolean_t unavail,
|
||||
zprop_list_t **proplist, zpool_iter_f func, void *data)
|
||||
{
|
||||
zpool_list_t *list;
|
||||
int ret = 0;
|
||||
|
||||
if ((list = pool_list_get(argc, argv, proplist, &ret)) == NULL)
|
||||
return (1);
|
||||
|
||||
if (pool_list_iter(list, unavail, func, data) != 0)
|
||||
ret = 1;
|
||||
|
||||
pool_list_free(list);
|
||||
|
||||
return (ret);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <libintl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "zpool_util.h"
|
||||
|
||||
/*
|
||||
* Utility function to guarantee malloc() success.
|
||||
*/
|
||||
void *
|
||||
safe_malloc(size_t size)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if ((data = calloc(1, size)) == NULL) {
|
||||
(void) fprintf(stderr, "internal error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return (data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as above, but for strdup()
|
||||
*/
|
||||
char *
|
||||
safe_strdup(const char *str)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if ((ret = strdup(str)) == NULL) {
|
||||
(void) fprintf(stderr, "internal error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display an out of memory error message and abort the current program.
|
||||
*/
|
||||
void
|
||||
zpool_no_memory(void)
|
||||
{
|
||||
assert(errno == ENOMEM);
|
||||
(void) fprintf(stderr,
|
||||
gettext("internal error: out of memory\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of logs in supplied nvlist
|
||||
*/
|
||||
uint_t
|
||||
num_logs(nvlist_t *nv)
|
||||
{
|
||||
uint_t nlogs = 0;
|
||||
uint_t c, children;
|
||||
nvlist_t **child;
|
||||
|
||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
||||
&child, &children) != 0)
|
||||
return (0);
|
||||
|
||||
for (c = 0; c < children; c++) {
|
||||
uint64_t is_log = B_FALSE;
|
||||
|
||||
(void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
|
||||
&is_log);
|
||||
if (is_log)
|
||||
nlogs++;
|
||||
}
|
||||
return (nlogs);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef ZPOOL_UTIL_H
|
||||
#define ZPOOL_UTIL_H
|
||||
|
||||
#include <libnvpair.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Basic utility functions
|
||||
*/
|
||||
void *safe_malloc(size_t);
|
||||
char *safe_strdup(const char *);
|
||||
void zpool_no_memory(void);
|
||||
uint_t num_logs(nvlist_t *nv);
|
||||
|
||||
/*
|
||||
* Virtual device functions
|
||||
*/
|
||||
|
||||
nvlist_t *make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
|
||||
boolean_t isreplace, boolean_t dryrun, int argc, char **argv);
|
||||
|
||||
/*
|
||||
* Pool list functions
|
||||
*/
|
||||
int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **,
|
||||
zpool_iter_f, void *);
|
||||
|
||||
typedef struct zpool_list zpool_list_t;
|
||||
|
||||
zpool_list_t *pool_list_get(int, char **, zprop_list_t **, int *);
|
||||
void pool_list_update(zpool_list_t *);
|
||||
int pool_list_iter(zpool_list_t *, int unavail, zpool_iter_f, void *);
|
||||
void pool_list_free(zpool_list_t *);
|
||||
int pool_list_count(zpool_list_t *);
|
||||
void pool_list_remove(zpool_list_t *, zpool_handle_t *);
|
||||
|
||||
libzfs_handle_t *g_zfs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZPOOL_UTIL_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _ACL_COMMON_H
|
||||
#define _ACL_COMMON_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/acl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern ace_t trivial_acl[6];
|
||||
|
||||
extern int acltrivial(const char *);
|
||||
extern void adjust_ace_pair(ace_t *pair, mode_t mode);
|
||||
extern void adjust_ace_pair_common(void *, size_t, size_t, mode_t);
|
||||
extern int ace_trivial(ace_t *acep, int aclcnt);
|
||||
extern int ace_trivial_common(void *, int,
|
||||
uint64_t (*walk)(void *, uint64_t, int aclcnt, uint16_t *, uint16_t *,
|
||||
uint32_t *mask));
|
||||
extern acl_t *acl_alloc(acl_type_t);
|
||||
extern void acl_free(acl_t *aclp);
|
||||
extern int acl_translate(acl_t *aclp, int target_flavor,
|
||||
int isdir, uid_t owner, gid_t group);
|
||||
void ksort(caddr_t v, int n, int s, int (*f)());
|
||||
int cmp2acls(void *a, void *b);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ACL_COMMON_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/stropts.h>
|
||||
#include <sys/isa_defs.h>
|
||||
#include <sys/nvpair.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#if defined(_KERNEL) && !defined(_BOOT)
|
||||
#include <sys/varargs.h>
|
||||
#else
|
||||
#include <stdarg.h>
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This allocator is very simple.
|
||||
* - it uses a pre-allocated buffer for memory allocations.
|
||||
* - it does _not_ free memory in the pre-allocated buffer.
|
||||
*
|
||||
* The reason for the selected implemention is simplicity.
|
||||
* This allocator is designed for the usage in interrupt context when
|
||||
* the caller may not wait for free memory.
|
||||
*/
|
||||
|
||||
/* pre-allocated buffer for memory allocations */
|
||||
typedef struct nvbuf {
|
||||
uintptr_t nvb_buf; /* address of pre-allocated buffer */
|
||||
uintptr_t nvb_lim; /* limit address in the buffer */
|
||||
uintptr_t nvb_cur; /* current address in the buffer */
|
||||
} nvbuf_t;
|
||||
|
||||
/*
|
||||
* Initialize the pre-allocated buffer allocator. The caller needs to supply
|
||||
*
|
||||
* buf address of pre-allocated buffer
|
||||
* bufsz size of pre-allocated buffer
|
||||
*
|
||||
* nv_fixed_init() calculates the remaining members of nvbuf_t.
|
||||
*/
|
||||
static int
|
||||
nv_fixed_init(nv_alloc_t *nva, va_list valist)
|
||||
{
|
||||
uintptr_t base = va_arg(valist, uintptr_t);
|
||||
uintptr_t lim = base + va_arg(valist, size_t);
|
||||
nvbuf_t *nvb = (nvbuf_t *)P2ROUNDUP(base, sizeof (uintptr_t));
|
||||
|
||||
if (base == 0 || (uintptr_t)&nvb[1] > lim)
|
||||
return (EINVAL);
|
||||
|
||||
nvb->nvb_buf = (uintptr_t)&nvb[0];
|
||||
nvb->nvb_cur = (uintptr_t)&nvb[1];
|
||||
nvb->nvb_lim = lim;
|
||||
nva->nva_arg = nvb;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void *
|
||||
nv_fixed_alloc(nv_alloc_t *nva, size_t size)
|
||||
{
|
||||
nvbuf_t *nvb = nva->nva_arg;
|
||||
uintptr_t new = nvb->nvb_cur;
|
||||
|
||||
if (size == 0 || new + size > nvb->nvb_lim)
|
||||
return (NULL);
|
||||
|
||||
nvb->nvb_cur = P2ROUNDUP(new + size, sizeof (uintptr_t));
|
||||
|
||||
return ((void *)new);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
nv_fixed_free(nv_alloc_t *nva, void *buf, size_t size)
|
||||
{
|
||||
/* don't free memory in the pre-allocated buffer */
|
||||
}
|
||||
|
||||
static void
|
||||
nv_fixed_reset(nv_alloc_t *nva)
|
||||
{
|
||||
nvbuf_t *nvb = nva->nva_arg;
|
||||
|
||||
nvb->nvb_cur = (uintptr_t)&nvb[1];
|
||||
}
|
||||
|
||||
const nv_alloc_ops_t nv_fixed_ops_def = {
|
||||
nv_fixed_init, /* nv_ao_init() */
|
||||
NULL, /* nv_ao_fini() */
|
||||
nv_fixed_alloc, /* nv_ao_alloc() */
|
||||
nv_fixed_free, /* nv_ao_free() */
|
||||
nv_fixed_reset /* nv_ao_reset() */
|
||||
};
|
||||
|
||||
const nv_alloc_ops_t *nv_fixed_ops = &nv_fixed_ops_def;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* This file is intended for functions that ought to be common between user
|
||||
* land (libzfs) and the kernel. When many common routines need to be shared
|
||||
* then a separate file should to be created.
|
||||
*/
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/nvpair.h>
|
||||
|
||||
/*
|
||||
* Are there allocatable vdevs?
|
||||
*/
|
||||
boolean_t
|
||||
zfs_allocatable_devs(nvlist_t *nv)
|
||||
{
|
||||
uint64_t is_log;
|
||||
uint_t c;
|
||||
nvlist_t **child;
|
||||
uint_t children;
|
||||
|
||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
||||
&child, &children) != 0) {
|
||||
return (B_FALSE);
|
||||
}
|
||||
for (c = 0; c < children; c++) {
|
||||
is_log = 0;
|
||||
(void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
|
||||
&is_log);
|
||||
if (!is_log)
|
||||
return (B_TRUE);
|
||||
}
|
||||
return (B_FALSE);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_COMUTIL_H
|
||||
#define _ZFS_COMUTIL_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern boolean_t zfs_allocatable_devs(nvlist_t *nv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZFS_COMUTIL_H */
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sunddi.h>
|
||||
#include <sys/ctype.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <libnvpair.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
/* XXX includes zfs_context.h, so why bother with the above? */
|
||||
#include <sys/dsl_deleg.h>
|
||||
#include "zfs_prop.h"
|
||||
#include "zfs_deleg.h"
|
||||
#include "zfs_namecheck.h"
|
||||
|
||||
/*
|
||||
* permission table
|
||||
*
|
||||
* Keep this table in sorted order
|
||||
*
|
||||
* This table is used for displaying all permissions for
|
||||
* zfs allow
|
||||
*/
|
||||
|
||||
zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
|
||||
{ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW},
|
||||
{ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
|
||||
{ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
|
||||
{ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
|
||||
{ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
|
||||
{ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
|
||||
{ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
|
||||
{ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
|
||||
{ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
|
||||
{ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
|
||||
{ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
|
||||
{ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_NONE },
|
||||
{ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
|
||||
{NULL, ZFS_DELEG_NOTE_NONE }
|
||||
};
|
||||
|
||||
static int
|
||||
zfs_valid_permission_name(const char *perm)
|
||||
{
|
||||
if (zfs_deleg_canonicalize_perm(perm))
|
||||
return (0);
|
||||
|
||||
return (permset_namecheck(perm, NULL, NULL));
|
||||
}
|
||||
|
||||
const char *
|
||||
zfs_deleg_canonicalize_perm(const char *perm)
|
||||
{
|
||||
int i;
|
||||
zfs_prop_t prop;
|
||||
|
||||
for (i = 0; zfs_deleg_perm_tab[i].z_perm != NULL; i++) {
|
||||
if (strcmp(perm, zfs_deleg_perm_tab[i].z_perm) == 0)
|
||||
return (perm);
|
||||
}
|
||||
|
||||
prop = zfs_name_to_prop(perm);
|
||||
if (prop != ZPROP_INVAL && zfs_prop_delegatable(prop))
|
||||
return (zfs_prop_to_name(prop));
|
||||
return (NULL);
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_validate_who(char *who)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (who[2] != ZFS_DELEG_FIELD_SEP_CHR)
|
||||
return (-1);
|
||||
|
||||
switch (who[0]) {
|
||||
case ZFS_DELEG_USER:
|
||||
case ZFS_DELEG_GROUP:
|
||||
case ZFS_DELEG_USER_SETS:
|
||||
case ZFS_DELEG_GROUP_SETS:
|
||||
if (who[1] != ZFS_DELEG_LOCAL && who[1] != ZFS_DELEG_DESCENDENT)
|
||||
return (-1);
|
||||
for (p = &who[3]; *p; p++)
|
||||
if (!isdigit(*p))
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case ZFS_DELEG_NAMED_SET:
|
||||
case ZFS_DELEG_NAMED_SET_SETS:
|
||||
if (who[1] != ZFS_DELEG_NA)
|
||||
return (-1);
|
||||
return (permset_namecheck(&who[3], NULL, NULL));
|
||||
|
||||
case ZFS_DELEG_CREATE:
|
||||
case ZFS_DELEG_CREATE_SETS:
|
||||
if (who[1] != ZFS_DELEG_NA)
|
||||
return (-1);
|
||||
if (who[3] != '\0')
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case ZFS_DELEG_EVERYONE:
|
||||
case ZFS_DELEG_EVERYONE_SETS:
|
||||
if (who[1] != ZFS_DELEG_LOCAL && who[1] != ZFS_DELEG_DESCENDENT)
|
||||
return (-1);
|
||||
if (who[3] != '\0')
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_deleg_verify_nvlist(nvlist_t *nvp)
|
||||
{
|
||||
nvpair_t *who, *perm_name;
|
||||
nvlist_t *perms;
|
||||
int error;
|
||||
|
||||
if (nvp == NULL)
|
||||
return (-1);
|
||||
|
||||
who = nvlist_next_nvpair(nvp, NULL);
|
||||
if (who == NULL)
|
||||
return (-1);
|
||||
|
||||
do {
|
||||
if (zfs_validate_who(nvpair_name(who)))
|
||||
return (-1);
|
||||
|
||||
error = nvlist_lookup_nvlist(nvp, nvpair_name(who), &perms);
|
||||
|
||||
if (error && error != ENOENT)
|
||||
return (-1);
|
||||
if (error == ENOENT)
|
||||
continue;
|
||||
|
||||
perm_name = nvlist_next_nvpair(perms, NULL);
|
||||
if (perm_name == NULL) {
|
||||
return (-1);
|
||||
}
|
||||
do {
|
||||
error = zfs_valid_permission_name(
|
||||
nvpair_name(perm_name));
|
||||
if (error)
|
||||
return (-1);
|
||||
} while (perm_name = nvlist_next_nvpair(perms, perm_name));
|
||||
} while (who = nvlist_next_nvpair(nvp, who));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct the base attribute name. The base attribute names
|
||||
* are the "key" to locate the jump objects which contain the actual
|
||||
* permissions. The base attribute names are encoded based on
|
||||
* type of entry and whether it is a local or descendent permission.
|
||||
*
|
||||
* Arguments:
|
||||
* attr - attribute name return string, attribute is assumed to be
|
||||
* ZFS_MAX_DELEG_NAME long.
|
||||
* type - type of entry to construct
|
||||
* inheritchr - inheritance type (local,descendent, or NA for create and
|
||||
* permission set definitions
|
||||
* data - is either a permission set name or a 64 bit uid/gid.
|
||||
*/
|
||||
void
|
||||
zfs_deleg_whokey(char *attr, zfs_deleg_who_type_t type,
|
||||
char inheritchr, void *data)
|
||||
{
|
||||
int len = ZFS_MAX_DELEG_NAME;
|
||||
uint64_t *id = data;
|
||||
|
||||
switch (type) {
|
||||
case ZFS_DELEG_USER:
|
||||
case ZFS_DELEG_GROUP:
|
||||
case ZFS_DELEG_USER_SETS:
|
||||
case ZFS_DELEG_GROUP_SETS:
|
||||
(void) snprintf(attr, len, "%c%c%c%lld", type, inheritchr,
|
||||
ZFS_DELEG_FIELD_SEP_CHR, (longlong_t)*id);
|
||||
break;
|
||||
case ZFS_DELEG_NAMED_SET_SETS:
|
||||
case ZFS_DELEG_NAMED_SET:
|
||||
(void) snprintf(attr, len, "%c-%c%s", type,
|
||||
ZFS_DELEG_FIELD_SEP_CHR, (char *)data);
|
||||
break;
|
||||
case ZFS_DELEG_CREATE:
|
||||
case ZFS_DELEG_CREATE_SETS:
|
||||
(void) snprintf(attr, len, "%c-%c", type,
|
||||
ZFS_DELEG_FIELD_SEP_CHR);
|
||||
break;
|
||||
case ZFS_DELEG_EVERYONE:
|
||||
case ZFS_DELEG_EVERYONE_SETS:
|
||||
(void) snprintf(attr, len, "%c%c%c", type, inheritchr,
|
||||
ZFS_DELEG_FIELD_SEP_CHR);
|
||||
break;
|
||||
default:
|
||||
ASSERT(!"bad zfs_deleg_who_type_t");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_DELEG_H
|
||||
#define _ZFS_DELEG_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ZFS_DELEG_SET_NAME_CHR '@' /* set name lead char */
|
||||
#define ZFS_DELEG_FIELD_SEP_CHR '$' /* field separator */
|
||||
|
||||
/*
|
||||
* Max name length for a delegation attribute
|
||||
*/
|
||||
#define ZFS_MAX_DELEG_NAME 128
|
||||
|
||||
#define ZFS_DELEG_LOCAL 'l'
|
||||
#define ZFS_DELEG_DESCENDENT 'd'
|
||||
#define ZFS_DELEG_NA '-'
|
||||
|
||||
typedef enum {
|
||||
ZFS_DELEG_NOTE_CREATE,
|
||||
ZFS_DELEG_NOTE_DESTROY,
|
||||
ZFS_DELEG_NOTE_SNAPSHOT,
|
||||
ZFS_DELEG_NOTE_ROLLBACK,
|
||||
ZFS_DELEG_NOTE_CLONE,
|
||||
ZFS_DELEG_NOTE_PROMOTE,
|
||||
ZFS_DELEG_NOTE_RENAME,
|
||||
ZFS_DELEG_NOTE_RECEIVE,
|
||||
ZFS_DELEG_NOTE_ALLOW,
|
||||
ZFS_DELEG_NOTE_USERPROP,
|
||||
ZFS_DELEG_NOTE_MOUNT,
|
||||
ZFS_DELEG_NOTE_SHARE,
|
||||
ZFS_DELEG_NOTE_NONE
|
||||
} zfs_deleg_note_t;
|
||||
|
||||
typedef struct zfs_deleg_perm_tab {
|
||||
char *z_perm;
|
||||
zfs_deleg_note_t z_note;
|
||||
} zfs_deleg_perm_tab_t;
|
||||
|
||||
extern zfs_deleg_perm_tab_t zfs_deleg_perm_tab[];
|
||||
|
||||
int zfs_deleg_verify_nvlist(nvlist_t *nvlist);
|
||||
void zfs_deleg_whokey(char *attr, zfs_deleg_who_type_t type,
|
||||
char checkflag, void *data);
|
||||
const char *zfs_deleg_canonicalize_perm(const char *perm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZFS_DELEG_H */
|
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* Common name validation routines for ZFS. These routines are shared by the
|
||||
* userland code as well as the ioctl() layer to ensure that we don't
|
||||
* inadvertently expose a hole through direct ioctl()s that never gets tested.
|
||||
* In userland, however, we want significantly more information about _why_ the
|
||||
* name is invalid. In the kernel, we only care whether it's valid or not.
|
||||
* Each routine therefore takes a 'namecheck_err_t' which describes exactly why
|
||||
* the name failed to validate.
|
||||
*
|
||||
* Each function returns 0 on success, -1 on error.
|
||||
*/
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#else
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/nvpair.h>
|
||||
#include "zfs_namecheck.h"
|
||||
#include "zfs_deleg.h"
|
||||
|
||||
static int
|
||||
valid_char(char c)
|
||||
{
|
||||
return ((c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '_' || c == '.' || c == ':' || c == ' ');
|
||||
}
|
||||
|
||||
/*
|
||||
* Snapshot names must be made up of alphanumeric characters plus the following
|
||||
* characters:
|
||||
*
|
||||
* [-_.:]
|
||||
*/
|
||||
int
|
||||
snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||
{
|
||||
const char *loc;
|
||||
|
||||
if (strlen(path) >= MAXNAMELEN) {
|
||||
if (why)
|
||||
*why = NAME_ERR_TOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (path[0] == '\0') {
|
||||
if (why)
|
||||
*why = NAME_ERR_EMPTY_COMPONENT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (loc = path; *loc; loc++) {
|
||||
if (!valid_char(*loc)) {
|
||||
if (why) {
|
||||
*why = NAME_ERR_INVALCHAR;
|
||||
*what = *loc;
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Permissions set name must start with the letter '@' followed by the
|
||||
* same character restrictions as snapshot names, except that the name
|
||||
* cannot exceed 64 characters.
|
||||
*/
|
||||
int
|
||||
permset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||
{
|
||||
if (strlen(path) >= ZFS_PERMSET_MAXLEN) {
|
||||
if (why)
|
||||
*why = NAME_ERR_TOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (path[0] != '@') {
|
||||
if (why) {
|
||||
*why = NAME_ERR_NO_AT;
|
||||
*what = path[0];
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (snapshot_namecheck(&path[1], why, what));
|
||||
}
|
||||
|
||||
/*
|
||||
* Dataset names must be of the following form:
|
||||
*
|
||||
* [component][/]*[component][@component]
|
||||
*
|
||||
* Where each component is made up of alphanumeric characters plus the following
|
||||
* characters:
|
||||
*
|
||||
* [-_.:%]
|
||||
*
|
||||
* We allow '%' here as we use that character internally to create unique
|
||||
* names for temporary clones (for online recv).
|
||||
*/
|
||||
int
|
||||
dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||
{
|
||||
const char *loc, *end;
|
||||
int found_snapshot;
|
||||
|
||||
/*
|
||||
* Make sure the name is not too long.
|
||||
*
|
||||
* ZFS_MAXNAMELEN is the maximum dataset length used in the userland
|
||||
* which is the same as MAXNAMELEN used in the kernel.
|
||||
* If ZFS_MAXNAMELEN value is changed, make sure to cleanup all
|
||||
* places using MAXNAMELEN.
|
||||
*/
|
||||
|
||||
if (strlen(path) >= MAXNAMELEN) {
|
||||
if (why)
|
||||
*why = NAME_ERR_TOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Explicitly check for a leading slash. */
|
||||
if (path[0] == '/') {
|
||||
if (why)
|
||||
*why = NAME_ERR_LEADING_SLASH;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (path[0] == '\0') {
|
||||
if (why)
|
||||
*why = NAME_ERR_EMPTY_COMPONENT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
loc = path;
|
||||
found_snapshot = 0;
|
||||
for (;;) {
|
||||
/* Find the end of this component */
|
||||
end = loc;
|
||||
while (*end != '/' && *end != '@' && *end != '\0')
|
||||
end++;
|
||||
|
||||
if (*end == '\0' && end[-1] == '/') {
|
||||
/* trailing slashes are not allowed */
|
||||
if (why)
|
||||
*why = NAME_ERR_TRAILING_SLASH;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Zero-length components are not allowed */
|
||||
if (loc == end) {
|
||||
if (why) {
|
||||
/*
|
||||
* Make sure this is really a zero-length
|
||||
* component and not a '@@'.
|
||||
*/
|
||||
if (*end == '@' && found_snapshot) {
|
||||
*why = NAME_ERR_MULTIPLE_AT;
|
||||
} else {
|
||||
*why = NAME_ERR_EMPTY_COMPONENT;
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Validate the contents of this component */
|
||||
while (loc != end) {
|
||||
if (!valid_char(*loc) && *loc != '%') {
|
||||
if (why) {
|
||||
*why = NAME_ERR_INVALCHAR;
|
||||
*what = *loc;
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
loc++;
|
||||
}
|
||||
|
||||
/* If we've reached the end of the string, we're OK */
|
||||
if (*end == '\0')
|
||||
return (0);
|
||||
|
||||
if (*end == '@') {
|
||||
/*
|
||||
* If we've found an @ symbol, indicate that we're in
|
||||
* the snapshot component, and report a second '@'
|
||||
* character as an error.
|
||||
*/
|
||||
if (found_snapshot) {
|
||||
if (why)
|
||||
*why = NAME_ERR_MULTIPLE_AT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
found_snapshot = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a '/' in a snapshot name
|
||||
* then report an error
|
||||
*/
|
||||
if (*end == '/' && found_snapshot) {
|
||||
if (why)
|
||||
*why = NAME_ERR_TRAILING_SLASH;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Update to the next component */
|
||||
loc = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* mountpoint names must be of the following form:
|
||||
*
|
||||
* /[component][/]*[component][/]
|
||||
*/
|
||||
int
|
||||
mountpoint_namecheck(const char *path, namecheck_err_t *why)
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
/*
|
||||
* Make sure none of the mountpoint component names are too long.
|
||||
* If a component name is too long then the mkdir of the mountpoint
|
||||
* will fail but then the mountpoint property will be set to a value
|
||||
* that can never be mounted. Better to fail before setting the prop.
|
||||
* Extra slashes are OK, they will be tossed by the mountpoint mkdir.
|
||||
*/
|
||||
|
||||
if (path == NULL || *path != '/') {
|
||||
if (why)
|
||||
*why = NAME_ERR_LEADING_SLASH;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Skip leading slash */
|
||||
start = &path[1];
|
||||
do {
|
||||
end = start;
|
||||
while (*end != '/' && *end != '\0')
|
||||
end++;
|
||||
|
||||
if (end - start >= MAXNAMELEN) {
|
||||
if (why)
|
||||
*why = NAME_ERR_TOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
start = end + 1;
|
||||
|
||||
} while (*end != '\0');
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* For pool names, we have the same set of valid characters as described in
|
||||
* dataset names, with the additional restriction that the pool name must begin
|
||||
* with a letter. The pool names 'raidz' and 'mirror' are also reserved names
|
||||
* that cannot be used.
|
||||
*/
|
||||
int
|
||||
pool_namecheck(const char *pool, namecheck_err_t *why, char *what)
|
||||
{
|
||||
const char *c;
|
||||
|
||||
/*
|
||||
* Make sure the name is not too long.
|
||||
*
|
||||
* ZPOOL_MAXNAMELEN is the maximum pool length used in the userland
|
||||
* which is the same as MAXNAMELEN used in the kernel.
|
||||
* If ZPOOL_MAXNAMELEN value is changed, make sure to cleanup all
|
||||
* places using MAXNAMELEN.
|
||||
*/
|
||||
if (strlen(pool) >= MAXNAMELEN) {
|
||||
if (why)
|
||||
*why = NAME_ERR_TOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
c = pool;
|
||||
while (*c != '\0') {
|
||||
if (!valid_char(*c)) {
|
||||
if (why) {
|
||||
*why = NAME_ERR_INVALCHAR;
|
||||
*what = *c;
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
if (!(*pool >= 'a' && *pool <= 'z') &&
|
||||
!(*pool >= 'A' && *pool <= 'Z')) {
|
||||
if (why)
|
||||
*why = NAME_ERR_NOLETTER;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (strcmp(pool, "mirror") == 0 || strcmp(pool, "raidz") == 0) {
|
||||
if (why)
|
||||
*why = NAME_ERR_RESERVED;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (pool[0] == 'c' && (pool[1] >= '0' && pool[1] <= '9')) {
|
||||
if (why)
|
||||
*why = NAME_ERR_DISKLIKE;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the dataset name is private for internal usage.
|
||||
* '$' is reserved for internal dataset names. e.g. "$MOS"
|
||||
*
|
||||
* Return 1 if the given name is used internally.
|
||||
* Return 0 if it is not.
|
||||
*/
|
||||
int
|
||||
dataset_name_hidden(const char *name)
|
||||
{
|
||||
if (strchr(name, '$') != NULL)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_NAMECHECK_H
|
||||
#define _ZFS_NAMECHECK_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
NAME_ERR_LEADING_SLASH, /* name begins with leading slash */
|
||||
NAME_ERR_EMPTY_COMPONENT, /* name contains an empty component */
|
||||
NAME_ERR_TRAILING_SLASH, /* name ends with a slash */
|
||||
NAME_ERR_INVALCHAR, /* invalid character found */
|
||||
NAME_ERR_MULTIPLE_AT, /* multiple '@' characters found */
|
||||
NAME_ERR_NOLETTER, /* pool doesn't begin with a letter */
|
||||
NAME_ERR_RESERVED, /* entire name is reserved */
|
||||
NAME_ERR_DISKLIKE, /* reserved disk name (c[0-9].*) */
|
||||
NAME_ERR_TOOLONG, /* name is too long */
|
||||
NAME_ERR_NO_AT, /* permission set is missing '@' */
|
||||
} namecheck_err_t;
|
||||
|
||||
#define ZFS_PERMSET_MAXLEN 64
|
||||
|
||||
int pool_namecheck(const char *, namecheck_err_t *, char *);
|
||||
int dataset_namecheck(const char *, namecheck_err_t *, char *);
|
||||
int mountpoint_namecheck(const char *, namecheck_err_t *);
|
||||
int dataset_name_hidden(const char *);
|
||||
int snapshot_namecheck(const char *, namecheck_err_t *, char *);
|
||||
int permset_namecheck(const char *, namecheck_err_t *, char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZFS_NAMECHECK_H */
|
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/zio.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/u8_textprep.h>
|
||||
#include <sys/zfs_acl.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/zfs_znode.h>
|
||||
|
||||
#include "zfs_prop.h"
|
||||
#include "zfs_deleg.h"
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
static zprop_desc_t zfs_prop_table[ZFS_NUM_PROPS];
|
||||
|
||||
zprop_desc_t *
|
||||
zfs_prop_get_table(void)
|
||||
{
|
||||
return (zfs_prop_table);
|
||||
}
|
||||
|
||||
void
|
||||
zfs_prop_init(void)
|
||||
{
|
||||
static zprop_index_t checksum_table[] = {
|
||||
{ "on", ZIO_CHECKSUM_ON },
|
||||
{ "off", ZIO_CHECKSUM_OFF },
|
||||
{ "fletcher2", ZIO_CHECKSUM_FLETCHER_2 },
|
||||
{ "fletcher4", ZIO_CHECKSUM_FLETCHER_4 },
|
||||
{ "sha256", ZIO_CHECKSUM_SHA256 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t compress_table[] = {
|
||||
{ "on", ZIO_COMPRESS_ON },
|
||||
{ "off", ZIO_COMPRESS_OFF },
|
||||
{ "lzjb", ZIO_COMPRESS_LZJB },
|
||||
{ "gzip", ZIO_COMPRESS_GZIP_6 }, /* gzip default */
|
||||
{ "gzip-1", ZIO_COMPRESS_GZIP_1 },
|
||||
{ "gzip-2", ZIO_COMPRESS_GZIP_2 },
|
||||
{ "gzip-3", ZIO_COMPRESS_GZIP_3 },
|
||||
{ "gzip-4", ZIO_COMPRESS_GZIP_4 },
|
||||
{ "gzip-5", ZIO_COMPRESS_GZIP_5 },
|
||||
{ "gzip-6", ZIO_COMPRESS_GZIP_6 },
|
||||
{ "gzip-7", ZIO_COMPRESS_GZIP_7 },
|
||||
{ "gzip-8", ZIO_COMPRESS_GZIP_8 },
|
||||
{ "gzip-9", ZIO_COMPRESS_GZIP_9 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t snapdir_table[] = {
|
||||
{ "hidden", ZFS_SNAPDIR_HIDDEN },
|
||||
{ "visible", ZFS_SNAPDIR_VISIBLE },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t acl_mode_table[] = {
|
||||
{ "discard", ZFS_ACL_DISCARD },
|
||||
{ "groupmask", ZFS_ACL_GROUPMASK },
|
||||
{ "passthrough", ZFS_ACL_PASSTHROUGH },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t acl_inherit_table[] = {
|
||||
{ "discard", ZFS_ACL_DISCARD },
|
||||
{ "noallow", ZFS_ACL_NOALLOW },
|
||||
{ "restricted", ZFS_ACL_RESTRICTED },
|
||||
{ "passthrough", ZFS_ACL_PASSTHROUGH },
|
||||
{ "secure", ZFS_ACL_RESTRICTED }, /* bkwrd compatability */
|
||||
{ "passthrough-x", ZFS_ACL_PASSTHROUGH_X },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t case_table[] = {
|
||||
{ "sensitive", ZFS_CASE_SENSITIVE },
|
||||
{ "insensitive", ZFS_CASE_INSENSITIVE },
|
||||
{ "mixed", ZFS_CASE_MIXED },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t copies_table[] = {
|
||||
{ "1", 1 },
|
||||
{ "2", 2 },
|
||||
{ "3", 3 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
* Use the unique flags we have to send to u8_strcmp() and/or
|
||||
* u8_textprep() to represent the various normalization property
|
||||
* values.
|
||||
*/
|
||||
static zprop_index_t normalize_table[] = {
|
||||
{ "none", 0 },
|
||||
{ "formD", U8_TEXTPREP_NFD },
|
||||
{ "formKC", U8_TEXTPREP_NFKC },
|
||||
{ "formC", U8_TEXTPREP_NFC },
|
||||
{ "formKD", U8_TEXTPREP_NFKD },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t version_table[] = {
|
||||
{ "1", 1 },
|
||||
{ "2", 2 },
|
||||
{ "3", 3 },
|
||||
{ "current", ZPL_VERSION },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t boolean_table[] = {
|
||||
{ "off", 0 },
|
||||
{ "on", 1 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t canmount_table[] = {
|
||||
{ "off", ZFS_CANMOUNT_OFF },
|
||||
{ "on", ZFS_CANMOUNT_ON },
|
||||
{ "noauto", ZFS_CANMOUNT_NOAUTO },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t cache_table[] = {
|
||||
{ "none", ZFS_CACHE_NONE },
|
||||
{ "metadata", ZFS_CACHE_METADATA },
|
||||
{ "all", ZFS_CACHE_ALL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/* inherit index properties */
|
||||
register_index(ZFS_PROP_CHECKSUM, "checksum", ZIO_CHECKSUM_DEFAULT,
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
|
||||
"on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM",
|
||||
checksum_table);
|
||||
register_index(ZFS_PROP_COMPRESSION, "compression",
|
||||
ZIO_COMPRESS_DEFAULT, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
|
||||
"on | off | lzjb | gzip | gzip-[1-9]", "COMPRESS", compress_table);
|
||||
register_index(ZFS_PROP_SNAPDIR, "snapdir", ZFS_SNAPDIR_HIDDEN,
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
|
||||
"hidden | visible", "SNAPDIR", snapdir_table);
|
||||
register_index(ZFS_PROP_ACLMODE, "aclmode", ZFS_ACL_GROUPMASK,
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
|
||||
"discard | groupmask | passthrough", "ACLMODE", acl_mode_table);
|
||||
register_index(ZFS_PROP_ACLINHERIT, "aclinherit", ZFS_ACL_RESTRICTED,
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
|
||||
"discard | noallow | restricted | passthrough | passthrough-x",
|
||||
"ACLINHERIT", acl_inherit_table);
|
||||
register_index(ZFS_PROP_COPIES, "copies", 1,
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
|
||||
"1 | 2 | 3", "COPIES", copies_table);
|
||||
register_index(ZFS_PROP_PRIMARYCACHE, "primarycache",
|
||||
ZFS_CACHE_ALL, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT | ZFS_TYPE_VOLUME,
|
||||
"all | none | metadata", "PRIMARYCACHE", cache_table);
|
||||
register_index(ZFS_PROP_SECONDARYCACHE, "secondarycache",
|
||||
ZFS_CACHE_ALL, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT | ZFS_TYPE_VOLUME,
|
||||
"all | none | metadata", "SECONDARYCACHE", cache_table);
|
||||
|
||||
/* inherit index (boolean) properties */
|
||||
register_index(ZFS_PROP_ATIME, "atime", 1, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "on | off", "ATIME", boolean_table);
|
||||
register_index(ZFS_PROP_DEVICES, "devices", 1, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "DEVICES",
|
||||
boolean_table);
|
||||
register_index(ZFS_PROP_EXEC, "exec", 1, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "EXEC",
|
||||
boolean_table);
|
||||
register_index(ZFS_PROP_SETUID, "setuid", 1, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "SETUID",
|
||||
boolean_table);
|
||||
register_index(ZFS_PROP_READONLY, "readonly", 0, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "on | off", "RDONLY",
|
||||
boolean_table);
|
||||
register_index(ZFS_PROP_ZONED, "zoned", 0, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "on | off", "ZONED", boolean_table);
|
||||
register_index(ZFS_PROP_XATTR, "xattr", 1, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "XATTR",
|
||||
boolean_table);
|
||||
register_index(ZFS_PROP_VSCAN, "vscan", 0, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "on | off", "VSCAN",
|
||||
boolean_table);
|
||||
register_index(ZFS_PROP_NBMAND, "nbmand", 0, PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "NBMAND",
|
||||
boolean_table);
|
||||
|
||||
/* default index properties */
|
||||
register_index(ZFS_PROP_VERSION, "version", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
|
||||
"1 | 2 | 3 | current", "VERSION", version_table);
|
||||
register_index(ZFS_PROP_CANMOUNT, "canmount", ZFS_CANMOUNT_ON,
|
||||
PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "on | off | noauto",
|
||||
"CANMOUNT", canmount_table);
|
||||
|
||||
/* readonly index (boolean) properties */
|
||||
register_index(ZFS_PROP_MOUNTED, "mounted", 0, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM, "yes | no", "MOUNTED", boolean_table);
|
||||
|
||||
/* set once index properties */
|
||||
register_index(ZFS_PROP_NORMALIZE, "normalization", 0,
|
||||
PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
|
||||
"none | formC | formD | formKC | formKD", "NORMALIZATION",
|
||||
normalize_table);
|
||||
register_index(ZFS_PROP_CASE, "casesensitivity", ZFS_CASE_SENSITIVE,
|
||||
PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
|
||||
"sensitive | insensitive | mixed", "CASE", case_table);
|
||||
|
||||
/* set once index (boolean) properties */
|
||||
register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
|
||||
"on | off", "UTF8ONLY", boolean_table);
|
||||
|
||||
/* string properties */
|
||||
register_string(ZFS_PROP_ORIGIN, "origin", NULL, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<snapshot>", "ORIGIN");
|
||||
register_string(ZFS_PROP_MOUNTPOINT, "mountpoint", "/", PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "<path> | legacy | none", "MOUNTPOINT");
|
||||
register_string(ZFS_PROP_SHARENFS, "sharenfs", "off", PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "on | off | share(1M) options", "SHARENFS");
|
||||
register_string(ZFS_PROP_SHAREISCSI, "shareiscsi", "off", PROP_INHERIT,
|
||||
ZFS_TYPE_DATASET, "on | off | type=<type>", "SHAREISCSI");
|
||||
register_string(ZFS_PROP_TYPE, "type", NULL, PROP_READONLY,
|
||||
ZFS_TYPE_DATASET, "filesystem | volume | snapshot", "TYPE");
|
||||
register_string(ZFS_PROP_SHARESMB, "sharesmb", "off", PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "on | off | sharemgr(1M) options", "SHARESMB");
|
||||
|
||||
/* readonly number properties */
|
||||
register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY,
|
||||
ZFS_TYPE_DATASET, "<size>", "USED");
|
||||
register_number(ZFS_PROP_AVAILABLE, "available", 0, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL");
|
||||
register_number(ZFS_PROP_REFERENCED, "referenced", 0, PROP_READONLY,
|
||||
ZFS_TYPE_DATASET, "<size>", "REFER");
|
||||
register_number(ZFS_PROP_COMPRESSRATIO, "compressratio", 0,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET,
|
||||
"<1.00x or higher if compressed>", "RATIO");
|
||||
register_number(ZFS_PROP_VOLBLOCKSIZE, "volblocksize", 8192,
|
||||
PROP_ONETIME,
|
||||
ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK");
|
||||
register_number(ZFS_PROP_USEDSNAP, "usedbysnapshots", 0, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDSNAP");
|
||||
register_number(ZFS_PROP_USEDDS, "usedbydataset", 0, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDDS");
|
||||
register_number(ZFS_PROP_USEDCHILD, "usedbychildren", 0, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDCHILD");
|
||||
register_number(ZFS_PROP_USEDREFRESERV, "usedbyrefreservation", 0,
|
||||
PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDREFRESERV");
|
||||
|
||||
/* default number properties */
|
||||
register_number(ZFS_PROP_QUOTA, "quota", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA");
|
||||
register_number(ZFS_PROP_RESERVATION, "reservation", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size> | none", "RESERV");
|
||||
register_number(ZFS_PROP_VOLSIZE, "volsize", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_VOLUME, "<size>", "VOLSIZE");
|
||||
register_number(ZFS_PROP_REFQUOTA, "refquota", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_FILESYSTEM, "<size> | none", "REFQUOTA");
|
||||
register_number(ZFS_PROP_REFRESERVATION, "refreservation", 0,
|
||||
PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
|
||||
"<size> | none", "REFRESERV");
|
||||
|
||||
/* inherit number properties */
|
||||
register_number(ZFS_PROP_RECORDSIZE, "recordsize", SPA_MAXBLOCKSIZE,
|
||||
PROP_INHERIT,
|
||||
ZFS_TYPE_FILESYSTEM, "512 to 128k, power of 2", "RECSIZE");
|
||||
|
||||
/* hidden properties */
|
||||
register_hidden(ZFS_PROP_CREATETXG, "createtxg", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, NULL);
|
||||
register_hidden(ZFS_PROP_NUMCLONES, "numclones", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_SNAPSHOT, NULL);
|
||||
register_hidden(ZFS_PROP_NAME, "name", PROP_TYPE_STRING,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "NAME");
|
||||
register_hidden(ZFS_PROP_ISCSIOPTIONS, "iscsioptions", PROP_TYPE_STRING,
|
||||
PROP_INHERIT, ZFS_TYPE_VOLUME, "ISCSIOPTIONS");
|
||||
register_hidden(ZFS_PROP_GUID, "guid", PROP_TYPE_NUMBER, PROP_READONLY,
|
||||
ZFS_TYPE_DATASET, "GUID");
|
||||
|
||||
/* oddball properties */
|
||||
register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0, NULL,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET,
|
||||
"<date>", "CREATION", B_FALSE, B_TRUE, NULL);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
zfs_prop_delegatable(zfs_prop_t prop)
|
||||
{
|
||||
zprop_desc_t *pd = &zfs_prop_table[prop];
|
||||
return (pd->pd_attr != PROP_READONLY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a zfs dataset property name, returns the corresponding property ID.
|
||||
*/
|
||||
zfs_prop_t
|
||||
zfs_name_to_prop(const char *propname)
|
||||
{
|
||||
return (zprop_name_to_prop(propname, ZFS_TYPE_DATASET));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For user property names, we allow all lowercase alphanumeric characters, plus
|
||||
* a few useful punctuation characters.
|
||||
*/
|
||||
static int
|
||||
valid_char(char c)
|
||||
{
|
||||
return ((c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '_' || c == '.' || c == ':');
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a valid user-defined property (one with a ':').
|
||||
*/
|
||||
boolean_t
|
||||
zfs_prop_user(const char *name)
|
||||
{
|
||||
int i;
|
||||
char c;
|
||||
boolean_t foundsep = B_FALSE;
|
||||
|
||||
for (i = 0; i < strlen(name); i++) {
|
||||
c = name[i];
|
||||
if (!valid_char(c))
|
||||
return (B_FALSE);
|
||||
if (c == ':')
|
||||
foundsep = B_TRUE;
|
||||
}
|
||||
|
||||
if (!foundsep)
|
||||
return (B_FALSE);
|
||||
|
||||
return (B_TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tables of index types, plus functions to convert between the user view
|
||||
* (strings) and internal representation (uint64_t).
|
||||
*/
|
||||
int
|
||||
zfs_prop_string_to_index(zfs_prop_t prop, const char *string, uint64_t *index)
|
||||
{
|
||||
return (zprop_string_to_index(prop, string, index, ZFS_TYPE_DATASET));
|
||||
}
|
||||
|
||||
int
|
||||
zfs_prop_index_to_string(zfs_prop_t prop, uint64_t index, const char **string)
|
||||
{
|
||||
return (zprop_index_to_string(prop, index, string, ZFS_TYPE_DATASET));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the property applies to any of the given dataset types.
|
||||
*/
|
||||
boolean_t
|
||||
zfs_prop_valid_for_type(int prop, zfs_type_t types)
|
||||
{
|
||||
return (zprop_valid_for_type(prop, types));
|
||||
}
|
||||
|
||||
zprop_type_t
|
||||
zfs_prop_get_type(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_proptype);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the property is readonly.
|
||||
*/
|
||||
boolean_t
|
||||
zfs_prop_readonly(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_attr == PROP_READONLY ||
|
||||
zfs_prop_table[prop].pd_attr == PROP_ONETIME);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the property is only allowed to be set once.
|
||||
*/
|
||||
boolean_t
|
||||
zfs_prop_setonce(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_attr == PROP_ONETIME);
|
||||
}
|
||||
|
||||
const char *
|
||||
zfs_prop_default_string(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_strdefault);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
zfs_prop_default_numeric(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_numdefault);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a dataset property ID, returns the corresponding name.
|
||||
* Assuming the zfs dataset property ID is valid.
|
||||
*/
|
||||
const char *
|
||||
zfs_prop_to_name(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the property is inheritable.
|
||||
*/
|
||||
boolean_t
|
||||
zfs_prop_inheritable(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_attr == PROP_INHERIT ||
|
||||
zfs_prop_table[prop].pd_attr == PROP_ONETIME);
|
||||
}
|
||||
|
||||
#ifndef _KERNEL
|
||||
|
||||
/*
|
||||
* Returns a string describing the set of acceptable values for the given
|
||||
* zfs property, or NULL if it cannot be set.
|
||||
*/
|
||||
const char *
|
||||
zfs_prop_values(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_values);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if this property is a string type. Note that index types
|
||||
* (compression, checksum) are treated as strings in userland, even though they
|
||||
* are stored numerically on disk.
|
||||
*/
|
||||
int
|
||||
zfs_prop_is_string(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_proptype == PROP_TYPE_STRING ||
|
||||
zfs_prop_table[prop].pd_proptype == PROP_TYPE_INDEX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the column header for the given property. Used only in
|
||||
* 'zfs list -o', but centralized here with the other property information.
|
||||
*/
|
||||
const char *
|
||||
zfs_prop_column_name(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_colname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the given property should be displayed right-justified for
|
||||
* 'zfs list'.
|
||||
*/
|
||||
boolean_t
|
||||
zfs_prop_align_right(zfs_prop_t prop)
|
||||
{
|
||||
return (zfs_prop_table[prop].pd_rightalign);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_PROP_H
|
||||
#define _ZFS_PROP_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For index types (e.g. compression and checksum), we want the numeric value
|
||||
* in the kernel, but the string value in userland.
|
||||
*/
|
||||
typedef enum {
|
||||
PROP_TYPE_NUMBER, /* numeric value */
|
||||
PROP_TYPE_STRING, /* string value */
|
||||
PROP_TYPE_INDEX /* numeric value indexed by string */
|
||||
} zprop_type_t;
|
||||
|
||||
typedef enum {
|
||||
PROP_DEFAULT,
|
||||
PROP_READONLY,
|
||||
PROP_INHERIT,
|
||||
/*
|
||||
* ONETIME properties are a sort of conglomeration of READONLY
|
||||
* and INHERIT. They can be set only during object creation,
|
||||
* after that they are READONLY. If not explicitly set during
|
||||
* creation, they can be inherited.
|
||||
*/
|
||||
PROP_ONETIME
|
||||
} zprop_attr_t;
|
||||
|
||||
typedef struct zfs_index {
|
||||
const char *pi_name;
|
||||
uint64_t pi_value;
|
||||
} zprop_index_t;
|
||||
|
||||
typedef struct {
|
||||
const char *pd_name; /* human-readable property name */
|
||||
int pd_propnum; /* property number */
|
||||
zprop_type_t pd_proptype; /* string, boolean, index, number */
|
||||
const char *pd_strdefault; /* default for strings */
|
||||
uint64_t pd_numdefault; /* for boolean / index / number */
|
||||
zprop_attr_t pd_attr; /* default, readonly, inherit */
|
||||
int pd_types; /* bitfield of valid dataset types */
|
||||
/* fs | vol | snap; or pool */
|
||||
const char *pd_values; /* string telling acceptable values */
|
||||
const char *pd_colname; /* column header for "zfs list" */
|
||||
boolean_t pd_rightalign; /* column alignment for "zfs list" */
|
||||
boolean_t pd_visible; /* do we list this property with the */
|
||||
/* "zfs get" help message */
|
||||
const zprop_index_t *pd_table; /* for index properties, a table */
|
||||
/* defining the possible values */
|
||||
} zprop_desc_t;
|
||||
|
||||
/*
|
||||
* zfs dataset property functions
|
||||
*/
|
||||
void zfs_prop_init(void);
|
||||
zprop_type_t zfs_prop_get_type(zfs_prop_t);
|
||||
boolean_t zfs_prop_delegatable(zfs_prop_t prop);
|
||||
zprop_desc_t *zfs_prop_get_table(void);
|
||||
|
||||
/*
|
||||
* zpool property functions
|
||||
*/
|
||||
void zpool_prop_init(void);
|
||||
zprop_type_t zpool_prop_get_type(zpool_prop_t);
|
||||
zprop_desc_t *zpool_prop_get_table(void);
|
||||
|
||||
/*
|
||||
* Common routines to initialize property tables
|
||||
*/
|
||||
void register_impl(int, const char *, zprop_type_t, uint64_t,
|
||||
const char *, zprop_attr_t, int, const char *, const char *,
|
||||
boolean_t, boolean_t, const zprop_index_t *);
|
||||
void register_string(int, const char *, const char *, zprop_attr_t attr,
|
||||
int, const char *, const char *);
|
||||
void register_number(int, const char *, uint64_t, zprop_attr_t, int,
|
||||
const char *, const char *);
|
||||
void register_index(int, const char *, uint64_t, zprop_attr_t, int,
|
||||
const char *, const char *, const zprop_index_t *);
|
||||
void register_hidden(int, const char *, zprop_type_t, zprop_attr_t,
|
||||
int, const char *);
|
||||
|
||||
/*
|
||||
* Common routines for zfs and zpool property management
|
||||
*/
|
||||
int zprop_iter_common(zprop_func, void *, boolean_t, boolean_t, zfs_type_t);
|
||||
int zprop_name_to_prop(const char *, zfs_type_t);
|
||||
int zprop_string_to_index(int, const char *, uint64_t *, zfs_type_t);
|
||||
int zprop_index_to_string(int, uint64_t, const char **, zfs_type_t);
|
||||
const char *zprop_values(int, zfs_type_t);
|
||||
size_t zprop_width(int, boolean_t *, zfs_type_t);
|
||||
boolean_t zprop_valid_for_type(int, zfs_type_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZFS_PROP_H */
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/zio.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/zfs_acl.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include "zfs_prop.h"
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
static zprop_desc_t zpool_prop_table[ZPOOL_NUM_PROPS];
|
||||
|
||||
zprop_desc_t *
|
||||
zpool_prop_get_table(void)
|
||||
{
|
||||
return (zpool_prop_table);
|
||||
}
|
||||
|
||||
void
|
||||
zpool_prop_init(void)
|
||||
{
|
||||
static zprop_index_t boolean_table[] = {
|
||||
{ "off", 0},
|
||||
{ "on", 1},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static zprop_index_t failuremode_table[] = {
|
||||
{ "wait", ZIO_FAILURE_MODE_WAIT },
|
||||
{ "continue", ZIO_FAILURE_MODE_CONTINUE },
|
||||
{ "panic", ZIO_FAILURE_MODE_PANIC },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/* string properties */
|
||||
register_string(ZPOOL_PROP_ALTROOT, "altroot", NULL, PROP_DEFAULT,
|
||||
ZFS_TYPE_POOL, "<path>", "ALTROOT");
|
||||
register_string(ZPOOL_PROP_BOOTFS, "bootfs", NULL, PROP_DEFAULT,
|
||||
ZFS_TYPE_POOL, "<filesystem>", "BOOTFS");
|
||||
register_string(ZPOOL_PROP_CACHEFILE, "cachefile", NULL, PROP_DEFAULT,
|
||||
ZFS_TYPE_POOL, "<file> | none", "CACHEFILE");
|
||||
|
||||
/* readonly number properties */
|
||||
register_number(ZPOOL_PROP_SIZE, "size", 0, PROP_READONLY,
|
||||
ZFS_TYPE_POOL, "<size>", "SIZE");
|
||||
register_number(ZPOOL_PROP_USED, "used", 0, PROP_READONLY,
|
||||
ZFS_TYPE_POOL, "<size>", "USED");
|
||||
register_number(ZPOOL_PROP_AVAILABLE, "available", 0, PROP_READONLY,
|
||||
ZFS_TYPE_POOL, "<size>", "AVAIL");
|
||||
register_number(ZPOOL_PROP_CAPACITY, "capacity", 0, PROP_READONLY,
|
||||
ZFS_TYPE_POOL, "<size>", "CAP");
|
||||
register_number(ZPOOL_PROP_GUID, "guid", 0, PROP_READONLY,
|
||||
ZFS_TYPE_POOL, "<guid>", "GUID");
|
||||
register_number(ZPOOL_PROP_HEALTH, "health", 0, PROP_READONLY,
|
||||
ZFS_TYPE_POOL, "<state>", "HEALTH");
|
||||
|
||||
/* default number properties */
|
||||
register_number(ZPOOL_PROP_VERSION, "version", SPA_VERSION,
|
||||
PROP_DEFAULT, ZFS_TYPE_POOL, "<version>", "VERSION");
|
||||
|
||||
/* default index (boolean) properties */
|
||||
register_index(ZPOOL_PROP_DELEGATION, "delegation", 1, PROP_DEFAULT,
|
||||
ZFS_TYPE_POOL, "on | off", "DELEGATION", boolean_table);
|
||||
register_index(ZPOOL_PROP_AUTOREPLACE, "autoreplace", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_POOL, "on | off", "REPLACE", boolean_table);
|
||||
register_index(ZPOOL_PROP_LISTSNAPS, "listsnapshots", 0, PROP_DEFAULT,
|
||||
ZFS_TYPE_POOL, "on | off", "LISTSNAPS", boolean_table);
|
||||
|
||||
/* default index properties */
|
||||
register_index(ZPOOL_PROP_FAILUREMODE, "failmode",
|
||||
ZIO_FAILURE_MODE_WAIT, PROP_DEFAULT, ZFS_TYPE_POOL,
|
||||
"wait | continue | panic", "FAILMODE", failuremode_table);
|
||||
|
||||
/* hidden properties */
|
||||
register_hidden(ZPOOL_PROP_NAME, "name", PROP_TYPE_STRING,
|
||||
PROP_READONLY, ZFS_TYPE_POOL, "NAME");
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a property name and its type, returns the corresponding property ID.
|
||||
*/
|
||||
zpool_prop_t
|
||||
zpool_name_to_prop(const char *propname)
|
||||
{
|
||||
return (zprop_name_to_prop(propname, ZFS_TYPE_POOL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a pool property ID, returns the corresponding name.
|
||||
* Assuming the pool propety ID is valid.
|
||||
*/
|
||||
const char *
|
||||
zpool_prop_to_name(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_name);
|
||||
}
|
||||
|
||||
zprop_type_t
|
||||
zpool_prop_get_type(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_proptype);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
zpool_prop_readonly(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_attr == PROP_READONLY);
|
||||
}
|
||||
|
||||
const char *
|
||||
zpool_prop_default_string(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_strdefault);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
zpool_prop_default_numeric(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_numdefault);
|
||||
}
|
||||
|
||||
int
|
||||
zpool_prop_string_to_index(zpool_prop_t prop, const char *string,
|
||||
uint64_t *index)
|
||||
{
|
||||
return (zprop_string_to_index(prop, string, index, ZFS_TYPE_POOL));
|
||||
}
|
||||
|
||||
int
|
||||
zpool_prop_index_to_string(zpool_prop_t prop, uint64_t index,
|
||||
const char **string)
|
||||
{
|
||||
return (zprop_index_to_string(prop, index, string, ZFS_TYPE_POOL));
|
||||
}
|
||||
|
||||
#ifndef _KERNEL
|
||||
|
||||
const char *
|
||||
zpool_prop_values(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_values);
|
||||
}
|
||||
|
||||
const char *
|
||||
zpool_prop_column_name(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_colname);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
zpool_prop_align_right(zpool_prop_t prop)
|
||||
{
|
||||
return (zpool_prop_table[prop].pd_rightalign);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* Common routines used by zfs and zpool property management.
|
||||
*/
|
||||
|
||||
#include <sys/zio.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/zfs_acl.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/zfs_znode.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include "zfs_prop.h"
|
||||
#include "zfs_deleg.h"
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#include <util/qsort.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
static zprop_desc_t *
|
||||
zprop_get_proptable(zfs_type_t type)
|
||||
{
|
||||
if (type == ZFS_TYPE_POOL)
|
||||
return (zpool_prop_get_table());
|
||||
else
|
||||
return (zfs_prop_get_table());
|
||||
}
|
||||
|
||||
static int
|
||||
zprop_get_numprops(zfs_type_t type)
|
||||
{
|
||||
if (type == ZFS_TYPE_POOL)
|
||||
return (ZPOOL_NUM_PROPS);
|
||||
else
|
||||
return (ZFS_NUM_PROPS);
|
||||
}
|
||||
|
||||
void
|
||||
register_impl(int prop, const char *name, zprop_type_t type,
|
||||
uint64_t numdefault, const char *strdefault, zprop_attr_t attr,
|
||||
int objset_types, const char *values, const char *colname,
|
||||
boolean_t rightalign, boolean_t visible, const zprop_index_t *idx_tbl)
|
||||
{
|
||||
zprop_desc_t *prop_tbl = zprop_get_proptable(objset_types);
|
||||
zprop_desc_t *pd;
|
||||
|
||||
pd = &prop_tbl[prop];
|
||||
|
||||
ASSERT(pd->pd_name == NULL || pd->pd_name == name);
|
||||
|
||||
pd->pd_name = name;
|
||||
pd->pd_propnum = prop;
|
||||
pd->pd_proptype = type;
|
||||
pd->pd_numdefault = numdefault;
|
||||
pd->pd_strdefault = strdefault;
|
||||
pd->pd_attr = attr;
|
||||
pd->pd_types = objset_types;
|
||||
pd->pd_values = values;
|
||||
pd->pd_colname = colname;
|
||||
pd->pd_rightalign = rightalign;
|
||||
pd->pd_visible = visible;
|
||||
pd->pd_table = idx_tbl;
|
||||
}
|
||||
|
||||
void
|
||||
register_string(int prop, const char *name, const char *def,
|
||||
zprop_attr_t attr, int objset_types, const char *values,
|
||||
const char *colname)
|
||||
{
|
||||
register_impl(prop, name, PROP_TYPE_STRING, 0, def, attr,
|
||||
objset_types, values, colname, B_FALSE, B_TRUE, NULL);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
register_number(int prop, const char *name, uint64_t def, zprop_attr_t attr,
|
||||
int objset_types, const char *values, const char *colname)
|
||||
{
|
||||
register_impl(prop, name, PROP_TYPE_NUMBER, def, NULL, attr,
|
||||
objset_types, values, colname, B_TRUE, B_TRUE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
register_index(int prop, const char *name, uint64_t def, zprop_attr_t attr,
|
||||
int objset_types, const char *values, const char *colname,
|
||||
const zprop_index_t *idx_tbl)
|
||||
{
|
||||
register_impl(prop, name, PROP_TYPE_INDEX, def, NULL, attr,
|
||||
objset_types, values, colname, B_TRUE, B_TRUE, idx_tbl);
|
||||
}
|
||||
|
||||
void
|
||||
register_hidden(int prop, const char *name, zprop_type_t type,
|
||||
zprop_attr_t attr, int objset_types, const char *colname)
|
||||
{
|
||||
register_impl(prop, name, type, 0, NULL, attr,
|
||||
objset_types, NULL, colname, B_FALSE, B_FALSE, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* A comparison function we can use to order indexes into property tables.
|
||||
*/
|
||||
static int
|
||||
zprop_compare(const void *arg1, const void *arg2)
|
||||
{
|
||||
const zprop_desc_t *p1 = *((zprop_desc_t **)arg1);
|
||||
const zprop_desc_t *p2 = *((zprop_desc_t **)arg2);
|
||||
boolean_t p1ro, p2ro;
|
||||
|
||||
p1ro = (p1->pd_attr == PROP_READONLY);
|
||||
p2ro = (p2->pd_attr == PROP_READONLY);
|
||||
|
||||
if (p1ro == p2ro)
|
||||
return (strcmp(p1->pd_name, p2->pd_name));
|
||||
|
||||
return (p1ro ? -1 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all properties in the given property table, calling back
|
||||
* into the specified function for each property. We will continue to
|
||||
* iterate until we either reach the end or the callback function returns
|
||||
* something other than ZPROP_CONT.
|
||||
*/
|
||||
int
|
||||
zprop_iter_common(zprop_func func, void *cb, boolean_t show_all,
|
||||
boolean_t ordered, zfs_type_t type)
|
||||
{
|
||||
int i, num_props, size, prop;
|
||||
zprop_desc_t *prop_tbl;
|
||||
zprop_desc_t **order;
|
||||
|
||||
prop_tbl = zprop_get_proptable(type);
|
||||
num_props = zprop_get_numprops(type);
|
||||
size = num_props * sizeof (zprop_desc_t *);
|
||||
|
||||
#if defined(_KERNEL)
|
||||
order = kmem_alloc(size, KM_SLEEP);
|
||||
#else
|
||||
if ((order = malloc(size)) == NULL)
|
||||
return (ZPROP_CONT);
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < num_props; j++)
|
||||
order[j] = &prop_tbl[j];
|
||||
|
||||
if (ordered) {
|
||||
qsort((void *)order, num_props, sizeof (zprop_desc_t *),
|
||||
zprop_compare);
|
||||
}
|
||||
|
||||
prop = ZPROP_CONT;
|
||||
for (i = 0; i < num_props; i++) {
|
||||
if ((order[i]->pd_visible || show_all) &&
|
||||
(func(order[i]->pd_propnum, cb) != ZPROP_CONT)) {
|
||||
prop = order[i]->pd_propnum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_KERNEL)
|
||||
kmem_free(order, size);
|
||||
#else
|
||||
free(order);
|
||||
#endif
|
||||
return (prop);
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
propname_match(const char *p, size_t len, zprop_desc_t *prop_entry)
|
||||
{
|
||||
const char *propname = prop_entry->pd_name;
|
||||
#ifndef _KERNEL
|
||||
const char *colname = prop_entry->pd_colname;
|
||||
int c;
|
||||
|
||||
if (colname == NULL)
|
||||
return (B_FALSE);
|
||||
#endif
|
||||
|
||||
if (len == strlen(propname) &&
|
||||
strncmp(p, propname, len) == 0)
|
||||
return (B_TRUE);
|
||||
|
||||
#ifndef _KERNEL
|
||||
if (len != strlen(colname))
|
||||
return (B_FALSE);
|
||||
|
||||
for (c = 0; c < len; c++)
|
||||
if (p[c] != tolower(colname[c]))
|
||||
break;
|
||||
|
||||
return (colname[c] == '\0');
|
||||
#else
|
||||
return (B_FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct name_to_prop_cb {
|
||||
const char *propname;
|
||||
zprop_desc_t *prop_tbl;
|
||||
} name_to_prop_cb_t;
|
||||
|
||||
static int
|
||||
zprop_name_to_prop_cb(int prop, void *cb_data)
|
||||
{
|
||||
name_to_prop_cb_t *data = cb_data;
|
||||
|
||||
if (propname_match(data->propname, strlen(data->propname),
|
||||
&data->prop_tbl[prop]))
|
||||
return (prop);
|
||||
|
||||
return (ZPROP_CONT);
|
||||
}
|
||||
|
||||
int
|
||||
zprop_name_to_prop(const char *propname, zfs_type_t type)
|
||||
{
|
||||
int prop;
|
||||
name_to_prop_cb_t cb_data;
|
||||
|
||||
cb_data.propname = propname;
|
||||
cb_data.prop_tbl = zprop_get_proptable(type);
|
||||
|
||||
prop = zprop_iter_common(zprop_name_to_prop_cb, &cb_data,
|
||||
B_TRUE, B_FALSE, type);
|
||||
|
||||
return (prop == ZPROP_CONT ? ZPROP_INVAL : prop);
|
||||
}
|
||||
|
||||
int
|
||||
zprop_string_to_index(int prop, const char *string, uint64_t *index,
|
||||
zfs_type_t type)
|
||||
{
|
||||
zprop_desc_t *prop_tbl;
|
||||
const zprop_index_t *idx_tbl;
|
||||
int i;
|
||||
|
||||
if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
|
||||
return (-1);
|
||||
|
||||
ASSERT(prop < zprop_get_numprops(type));
|
||||
prop_tbl = zprop_get_proptable(type);
|
||||
if ((idx_tbl = prop_tbl[prop].pd_table) == NULL)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; idx_tbl[i].pi_name != NULL; i++) {
|
||||
if (strcmp(string, idx_tbl[i].pi_name) == 0) {
|
||||
*index = idx_tbl[i].pi_value;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
zprop_index_to_string(int prop, uint64_t index, const char **string,
|
||||
zfs_type_t type)
|
||||
{
|
||||
zprop_desc_t *prop_tbl;
|
||||
const zprop_index_t *idx_tbl;
|
||||
int i;
|
||||
|
||||
if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
|
||||
return (-1);
|
||||
|
||||
ASSERT(prop < zprop_get_numprops(type));
|
||||
prop_tbl = zprop_get_proptable(type);
|
||||
if ((idx_tbl = prop_tbl[prop].pd_table) == NULL)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; idx_tbl[i].pi_name != NULL; i++) {
|
||||
if (idx_tbl[i].pi_value == index) {
|
||||
*string = idx_tbl[i].pi_name;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
const char *
|
||||
zprop_values(int prop, zfs_type_t type)
|
||||
{
|
||||
zprop_desc_t *prop_tbl;
|
||||
|
||||
ASSERT(prop != ZPROP_INVAL && prop != ZPROP_CONT);
|
||||
ASSERT(prop < zprop_get_numprops(type));
|
||||
|
||||
prop_tbl = zprop_get_proptable(type);
|
||||
|
||||
return (prop_tbl[prop].pd_values);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the property applies to any of the given dataset types.
|
||||
*/
|
||||
boolean_t
|
||||
zprop_valid_for_type(int prop, zfs_type_t type)
|
||||
{
|
||||
zprop_desc_t *prop_tbl;
|
||||
|
||||
if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
|
||||
return (B_FALSE);
|
||||
|
||||
ASSERT(prop < zprop_get_numprops(type));
|
||||
prop_tbl = zprop_get_proptable(type);
|
||||
return ((prop_tbl[prop].pd_types & type) != 0);
|
||||
}
|
||||
|
||||
#ifndef _KERNEL
|
||||
|
||||
/*
|
||||
* Determines the minimum width for the column, and indicates whether it's fixed
|
||||
* or not. Only string columns are non-fixed.
|
||||
*/
|
||||
size_t
|
||||
zprop_width(int prop, boolean_t *fixed, zfs_type_t type)
|
||||
{
|
||||
zprop_desc_t *prop_tbl, *pd;
|
||||
const zprop_index_t *idx;
|
||||
size_t ret;
|
||||
int i;
|
||||
|
||||
ASSERT(prop != ZPROP_INVAL && prop != ZPROP_CONT);
|
||||
ASSERT(prop < zprop_get_numprops(type));
|
||||
|
||||
prop_tbl = zprop_get_proptable(type);
|
||||
pd = &prop_tbl[prop];
|
||||
|
||||
*fixed = B_TRUE;
|
||||
|
||||
/*
|
||||
* Start with the width of the column name.
|
||||
*/
|
||||
ret = strlen(pd->pd_colname);
|
||||
|
||||
/*
|
||||
* For fixed-width values, make sure the width is large enough to hold
|
||||
* any possible value.
|
||||
*/
|
||||
switch (pd->pd_proptype) {
|
||||
case PROP_TYPE_NUMBER:
|
||||
/*
|
||||
* The maximum length of a human-readable number is 5 characters
|
||||
* ("20.4M", for example).
|
||||
*/
|
||||
if (ret < 5)
|
||||
ret = 5;
|
||||
/*
|
||||
* 'creation' is handled specially because it's a number
|
||||
* internally, but displayed as a date string.
|
||||
*/
|
||||
if (prop == ZFS_PROP_CREATION)
|
||||
*fixed = B_FALSE;
|
||||
break;
|
||||
case PROP_TYPE_INDEX:
|
||||
idx = prop_tbl[prop].pd_table;
|
||||
for (i = 0; idx[i].pi_name != NULL; i++) {
|
||||
if (strlen(idx[i].pi_name) > ret)
|
||||
ret = strlen(idx[i].pi_name);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_TYPE_STRING:
|
||||
*fixed = B_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _UCRED_H_
|
||||
#define _UCRED_H_
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/tsol/label.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ucred_s ucred_t;
|
||||
|
||||
/*
|
||||
* library functions prototype.
|
||||
*/
|
||||
#if defined(__STDC__)
|
||||
|
||||
extern ucred_t *ucred_get(pid_t pid);
|
||||
|
||||
extern void ucred_free(ucred_t *);
|
||||
|
||||
extern uid_t ucred_geteuid(const ucred_t *);
|
||||
extern uid_t ucred_getruid(const ucred_t *);
|
||||
extern uid_t ucred_getsuid(const ucred_t *);
|
||||
extern gid_t ucred_getegid(const ucred_t *);
|
||||
extern gid_t ucred_getrgid(const ucred_t *);
|
||||
extern gid_t ucred_getsgid(const ucred_t *);
|
||||
extern int ucred_getgroups(const ucred_t *, const gid_t **);
|
||||
|
||||
extern const priv_set_t *ucred_getprivset(const ucred_t *, priv_ptype_t);
|
||||
extern uint_t ucred_getpflags(const ucred_t *, uint_t);
|
||||
|
||||
extern pid_t ucred_getpid(const ucred_t *); /* for door_cred compatibility */
|
||||
|
||||
extern size_t ucred_size(void);
|
||||
|
||||
extern int getpeerucred(int, ucred_t **);
|
||||
|
||||
extern zoneid_t ucred_getzoneid(const ucred_t *);
|
||||
|
||||
extern bslabel_t *ucred_getlabel(const ucred_t *);
|
||||
|
||||
extern projid_t ucred_getprojid(const ucred_t *);
|
||||
|
||||
#else /* Non ANSI */
|
||||
|
||||
extern ucred_t *ucred_get(/* pid_t pid */);
|
||||
|
||||
extern void ucred_free(/* ucred_t * */);
|
||||
|
||||
extern uid_t ucred_geteuid(/* ucred_t * */);
|
||||
extern uid_t ucred_getruid(/* ucred_t * */);
|
||||
extern uid_t ucred_getsuid(/* ucred_t * */);
|
||||
extern gid_t ucred_getegid(/* ucred_t * */);
|
||||
extern gid_t ucred_getrgid(/* ucred_t * */);
|
||||
extern gid_t ucred_getsgid(/* ucred_t * */);
|
||||
extern int ucred_getgroups(/* ucred_t *, gid_t ** */);
|
||||
|
||||
extern priv_set_t *ucred_getprivset(/* ucred_t *, priv_ptype_t */);
|
||||
extern uint_t ucred_getpflags(/* ucred_t *, uint_t */);
|
||||
|
||||
extern pid_t ucred_getpid(/* ucred_t * */);
|
||||
|
||||
extern size_t ucred_size(/* void */);
|
||||
|
||||
extern int getpeerucred(/* int, ucred_t ** */);
|
||||
|
||||
extern zoneid_t ucred_getzoneid(/* ucred_t * */);
|
||||
|
||||
extern bslabel_t *ucred_getlabel(/* const ucred_t * */);
|
||||
|
||||
extern projid_t ucred_getprojid(/* ucred_t * */);
|
||||
|
||||
#endif /* __STDC__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _UCRED_H_ */
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2002 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/crc32.h>
|
||||
|
||||
static unsigned int crc32_tab[] = { CRC32_TABLE };
|
||||
|
||||
/*
|
||||
* Return a 32-bit CRC of the contents of the buffer.
|
||||
*
|
||||
* The seed is 0xffffffff and the result is XORed with 0xffffffff
|
||||
* because this is what the Itanium firmware expects.
|
||||
*/
|
||||
unsigned int
|
||||
efi_crc32(const unsigned char *s, unsigned int len)
|
||||
{
|
||||
unsigned int crc32val;
|
||||
|
||||
CRC32(crc32val, s, len, -1U, crc32_tab);
|
||||
|
||||
return (crc32val ^ -1U);
|
||||
}
|
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/inttypes.h>
|
||||
#include "libnvpair.h"
|
||||
|
||||
/*
|
||||
* libnvpair - A tools library for manipulating <name, value> pairs.
|
||||
*
|
||||
* This library provides routines packing an unpacking nv pairs
|
||||
* for transporting data across process boundaries, transporting
|
||||
* between kernel and userland, and possibly saving onto disk files.
|
||||
*/
|
||||
|
||||
static void
|
||||
indent(FILE *fp, int depth)
|
||||
{
|
||||
while (depth-- > 0)
|
||||
(void) fprintf(fp, "\t");
|
||||
}
|
||||
|
||||
/*
|
||||
* nvlist_print - Prints elements in an event buffer
|
||||
*/
|
||||
static
|
||||
void
|
||||
nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
|
||||
{
|
||||
int i;
|
||||
char *name;
|
||||
uint_t nelem;
|
||||
nvpair_t *nvp;
|
||||
|
||||
if (nvl == NULL)
|
||||
return;
|
||||
|
||||
indent(fp, depth);
|
||||
(void) fprintf(fp, "nvlist version: %d\n", NVL_VERSION(nvl));
|
||||
|
||||
nvp = nvlist_next_nvpair(nvl, NULL);
|
||||
|
||||
while (nvp) {
|
||||
data_type_t type = nvpair_type(nvp);
|
||||
|
||||
indent(fp, depth);
|
||||
name = nvpair_name(nvp);
|
||||
(void) fprintf(fp, "\t%s =", name);
|
||||
nelem = 0;
|
||||
switch (type) {
|
||||
case DATA_TYPE_BOOLEAN: {
|
||||
(void) fprintf(fp, " 1");
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BOOLEAN_VALUE: {
|
||||
boolean_t val;
|
||||
(void) nvpair_value_boolean_value(nvp, &val);
|
||||
(void) fprintf(fp, " %d", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BYTE: {
|
||||
uchar_t val;
|
||||
(void) nvpair_value_byte(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%2.2x", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT8: {
|
||||
int8_t val;
|
||||
(void) nvpair_value_int8(nvp, &val);
|
||||
(void) fprintf(fp, " %d", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT8: {
|
||||
uint8_t val;
|
||||
(void) nvpair_value_uint8(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%x", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT16: {
|
||||
int16_t val;
|
||||
(void) nvpair_value_int16(nvp, &val);
|
||||
(void) fprintf(fp, " %d", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT16: {
|
||||
uint16_t val;
|
||||
(void) nvpair_value_uint16(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%x", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT32: {
|
||||
int32_t val;
|
||||
(void) nvpair_value_int32(nvp, &val);
|
||||
(void) fprintf(fp, " %d", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT32: {
|
||||
uint32_t val;
|
||||
(void) nvpair_value_uint32(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%x", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT64: {
|
||||
int64_t val;
|
||||
(void) nvpair_value_int64(nvp, &val);
|
||||
(void) fprintf(fp, " %lld", (longlong_t)val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT64: {
|
||||
uint64_t val;
|
||||
(void) nvpair_value_uint64(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%llx", (u_longlong_t)val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_DOUBLE: {
|
||||
double val;
|
||||
(void) nvpair_value_double(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%llf", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_STRING: {
|
||||
char *val;
|
||||
(void) nvpair_value_string(nvp, &val);
|
||||
(void) fprintf(fp, " %s", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BOOLEAN_ARRAY: {
|
||||
boolean_t *val;
|
||||
(void) nvpair_value_boolean_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " %d", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BYTE_ARRAY: {
|
||||
uchar_t *val;
|
||||
(void) nvpair_value_byte_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " 0x%2.2x", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT8_ARRAY: {
|
||||
int8_t *val;
|
||||
(void) nvpair_value_int8_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " %d", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT8_ARRAY: {
|
||||
uint8_t *val;
|
||||
(void) nvpair_value_uint8_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " 0x%x", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT16_ARRAY: {
|
||||
int16_t *val;
|
||||
(void) nvpair_value_int16_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " %d", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT16_ARRAY: {
|
||||
uint16_t *val;
|
||||
(void) nvpair_value_uint16_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " 0x%x", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT32_ARRAY: {
|
||||
int32_t *val;
|
||||
(void) nvpair_value_int32_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " %d", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT32_ARRAY: {
|
||||
uint32_t *val;
|
||||
(void) nvpair_value_uint32_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " 0x%x", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT64_ARRAY: {
|
||||
int64_t *val;
|
||||
(void) nvpair_value_int64_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " %lld", (longlong_t)val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT64_ARRAY: {
|
||||
uint64_t *val;
|
||||
(void) nvpair_value_uint64_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " 0x%llx",
|
||||
(u_longlong_t)val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_STRING_ARRAY: {
|
||||
char **val;
|
||||
(void) nvpair_value_string_array(nvp, &val, &nelem);
|
||||
for (i = 0; i < nelem; i++)
|
||||
(void) fprintf(fp, " %s", val[i]);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_HRTIME: {
|
||||
hrtime_t val;
|
||||
(void) nvpair_value_hrtime(nvp, &val);
|
||||
(void) fprintf(fp, " 0x%llx", val);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_NVLIST: {
|
||||
nvlist_t *val;
|
||||
(void) nvpair_value_nvlist(nvp, &val);
|
||||
(void) fprintf(fp, " (embedded nvlist)\n");
|
||||
nvlist_print_with_indent(fp, val, depth + 1);
|
||||
indent(fp, depth + 1);
|
||||
(void) fprintf(fp, "(end %s)\n", name);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_NVLIST_ARRAY: {
|
||||
nvlist_t **val;
|
||||
(void) nvpair_value_nvlist_array(nvp, &val, &nelem);
|
||||
(void) fprintf(fp, " (array of embedded nvlists)\n");
|
||||
for (i = 0; i < nelem; i++) {
|
||||
indent(fp, depth + 1);
|
||||
(void) fprintf(fp,
|
||||
"(start %s[%d])\n", name, i);
|
||||
nvlist_print_with_indent(fp, val[i], depth + 1);
|
||||
indent(fp, depth + 1);
|
||||
(void) fprintf(fp, "(end %s[%d])\n", name, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
(void) fprintf(fp, " unknown data type (%d)", type);
|
||||
break;
|
||||
}
|
||||
(void) fprintf(fp, "\n");
|
||||
nvp = nvlist_next_nvpair(nvl, nvp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nvlist_print(FILE *fp, nvlist_t *nvl)
|
||||
{
|
||||
nvlist_print_with_indent(fp, nvl, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if string 'value' matches 'nvp' value. The 'value' string is
|
||||
* converted, depending on the type of 'nvp', prior to match. For numeric
|
||||
* types, a radix independent sscanf conversion of 'value' is used. If 'nvp'
|
||||
* is an array type, 'ai' is the index into the array against which we are
|
||||
* checking for match. If nvp is of DATA_TYPE_STRING*, the caller can pass
|
||||
* in a regex_t compilation of value in 'value_regex' to trigger regular
|
||||
* expression string match instead of simple strcmp().
|
||||
*
|
||||
* Return 1 on match, 0 on no-match, and -1 on error. If the error is
|
||||
* related to value syntax error and 'ep' is non-NULL, *ep will point into
|
||||
* the 'value' string at the location where the error exists.
|
||||
*
|
||||
* NOTE: It may be possible to move the non-regex_t version of this into
|
||||
* common code used by library/kernel/boot.
|
||||
*/
|
||||
int
|
||||
nvpair_value_match_regex(nvpair_t *nvp, int ai,
|
||||
char *value, regex_t *value_regex, char **ep)
|
||||
{
|
||||
char *evalue;
|
||||
uint_t a_len;
|
||||
int sr;
|
||||
|
||||
if (ep)
|
||||
*ep = NULL;
|
||||
|
||||
if ((nvp == NULL) || (value == NULL))
|
||||
return (-1); /* error fail match - invalid args */
|
||||
|
||||
/* make sure array and index combination make sense */
|
||||
if ((nvpair_type_is_array(nvp) && (ai < 0)) ||
|
||||
(!nvpair_type_is_array(nvp) && (ai >= 0)))
|
||||
return (-1); /* error fail match - bad index */
|
||||
|
||||
/* non-string values should be single 'chunk' */
|
||||
if ((nvpair_type(nvp) != DATA_TYPE_STRING) &&
|
||||
(nvpair_type(nvp) != DATA_TYPE_STRING_ARRAY)) {
|
||||
value += strspn(value, " \t");
|
||||
evalue = value + strcspn(value, " \t");
|
||||
if (*evalue) {
|
||||
if (ep)
|
||||
*ep = evalue;
|
||||
return (-1); /* error fail match - syntax */
|
||||
}
|
||||
}
|
||||
|
||||
sr = EOF;
|
||||
switch (nvpair_type(nvp)) {
|
||||
case DATA_TYPE_STRING: {
|
||||
char *val;
|
||||
|
||||
/* check string value for match */
|
||||
if (nvpair_value_string(nvp, &val) == 0) {
|
||||
if (value_regex) {
|
||||
if (regexec(value_regex, val,
|
||||
(size_t)0, NULL, 0) == 0)
|
||||
return (1); /* match */
|
||||
} else {
|
||||
if (strcmp(value, val) == 0)
|
||||
return (1); /* match */
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_STRING_ARRAY: {
|
||||
char **val_array;
|
||||
|
||||
/* check indexed string value of array for match */
|
||||
if ((nvpair_value_string_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len)) {
|
||||
if (value_regex) {
|
||||
if (regexec(value_regex, val_array[ai],
|
||||
(size_t)0, NULL, 0) == 0)
|
||||
return (1);
|
||||
} else {
|
||||
if (strcmp(value, val_array[ai]) == 0)
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BYTE: {
|
||||
uchar_t val, val_arg;
|
||||
|
||||
/* scanf uchar_t from value and check for match */
|
||||
sr = sscanf(value, "%c", &val_arg);
|
||||
if ((sr == 1) && (nvpair_value_byte(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BYTE_ARRAY: {
|
||||
uchar_t *val_array, val_arg;
|
||||
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%c", &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_byte_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT8: {
|
||||
int8_t val, val_arg;
|
||||
|
||||
/* scanf int8_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi8, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int8(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT8_ARRAY: {
|
||||
int8_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi8, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int8_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT8: {
|
||||
uint8_t val, val_arg;
|
||||
|
||||
/* scanf uint8_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint8(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT8_ARRAY: {
|
||||
uint8_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint8_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT16: {
|
||||
int16_t val, val_arg;
|
||||
|
||||
/* scanf int16_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi16, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int16(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT16_ARRAY: {
|
||||
int16_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi16, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int16_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT16: {
|
||||
uint16_t val, val_arg;
|
||||
|
||||
/* scanf uint16_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint16(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT16_ARRAY: {
|
||||
uint16_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint16_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT32: {
|
||||
int32_t val, val_arg;
|
||||
|
||||
/* scanf int32_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi32, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int32(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT32_ARRAY: {
|
||||
int32_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi32, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int32_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT32: {
|
||||
uint32_t val, val_arg;
|
||||
|
||||
/* scanf uint32_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint32(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT32_ARRAY: {
|
||||
uint32_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint32_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT64: {
|
||||
int64_t val, val_arg;
|
||||
|
||||
/* scanf int64_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi64, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int64(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_INT64_ARRAY: {
|
||||
int64_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi64, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_int64_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT64: {
|
||||
uint64_t val_arg, val;
|
||||
|
||||
/* scanf uint64_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint64(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_UINT64_ARRAY: {
|
||||
uint64_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_uint64_array(nvp, &val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BOOLEAN_VALUE: {
|
||||
boolean_t val, val_arg;
|
||||
|
||||
/* scanf boolean_t from value and check for match */
|
||||
sr = sscanf(value, "%"SCNi32, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_boolean_value(nvp, &val) == 0) &&
|
||||
(val == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_BOOLEAN_ARRAY: {
|
||||
boolean_t *val_array, val_arg;
|
||||
|
||||
/* check indexed value of array for match */
|
||||
sr = sscanf(value, "%"SCNi32, &val_arg);
|
||||
if ((sr == 1) &&
|
||||
(nvpair_value_boolean_array(nvp,
|
||||
&val_array, &a_len) == 0) &&
|
||||
(ai < a_len) &&
|
||||
(val_array[ai] == val_arg))
|
||||
return (1);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_HRTIME:
|
||||
case DATA_TYPE_NVLIST:
|
||||
case DATA_TYPE_NVLIST_ARRAY:
|
||||
case DATA_TYPE_BOOLEAN:
|
||||
case DATA_TYPE_DOUBLE:
|
||||
case DATA_TYPE_UNKNOWN:
|
||||
default:
|
||||
/*
|
||||
* unknown/unsupported data type
|
||||
*/
|
||||
return (-1); /* error fail match */
|
||||
}
|
||||
|
||||
/*
|
||||
* check to see if sscanf failed conversion, return approximate
|
||||
* pointer to problem
|
||||
*/
|
||||
if (sr != 1) {
|
||||
if (ep)
|
||||
*ep = value;
|
||||
return (-1); /* error fail match - syntax */
|
||||
}
|
||||
|
||||
return (0); /* fail match */
|
||||
}
|
||||
|
||||
int
|
||||
nvpair_value_match(nvpair_t *nvp, int ai, char *value, char **ep)
|
||||
{
|
||||
return (nvpair_value_match_regex(nvp, ai, value, NULL, ep));
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _LIBNVPAIR_H
|
||||
#define _LIBNVPAIR_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/nvpair.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <regex.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void nvlist_print(FILE *, nvlist_t *);
|
||||
int nvpair_value_match(nvpair_t *, int, char *, char **);
|
||||
int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *, char **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBNVPAIR_H */
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/nvpair.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void *
|
||||
nv_alloc_sys(nv_alloc_t *nva, size_t size)
|
||||
{
|
||||
return (malloc(size));
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
nv_free_sys(nv_alloc_t *nva, void *buf, size_t size)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
const nv_alloc_ops_t system_ops_def = {
|
||||
NULL, /* nv_ao_init() */
|
||||
NULL, /* nv_ao_fini() */
|
||||
nv_alloc_sys, /* nv_ao_alloc() */
|
||||
nv_free_sys, /* nv_ao_free() */
|
||||
NULL /* nv_ao_reset() */
|
||||
};
|
||||
|
||||
nv_alloc_t nv_alloc_nosleep_def = {
|
||||
&system_ops_def,
|
||||
NULL
|
||||
};
|
||||
|
||||
nv_alloc_t *nv_alloc_nosleep = &nv_alloc_nosleep_def;
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* basic API declarations for share management
|
||||
*/
|
||||
|
||||
#ifndef _LIBSHARE_H
|
||||
#define _LIBSHARE_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* Basic datatypes for most functions
|
||||
*/
|
||||
typedef void *sa_group_t;
|
||||
typedef void *sa_share_t;
|
||||
typedef void *sa_property_t;
|
||||
typedef void *sa_optionset_t;
|
||||
typedef void *sa_security_t;
|
||||
typedef void *sa_protocol_properties_t;
|
||||
typedef void *sa_resource_t;
|
||||
|
||||
typedef void *sa_handle_t; /* opaque handle to access core functions */
|
||||
|
||||
/*
|
||||
* defined error values
|
||||
*/
|
||||
|
||||
#define SA_OK 0
|
||||
#define SA_NO_SUCH_PATH 1 /* provided path doesn't exist */
|
||||
#define SA_NO_MEMORY 2 /* no memory for data structures */
|
||||
#define SA_DUPLICATE_NAME 3 /* object name is already in use */
|
||||
#define SA_BAD_PATH 4 /* not a full path */
|
||||
#define SA_NO_SUCH_GROUP 5 /* group is not defined */
|
||||
#define SA_CONFIG_ERR 6 /* system configuration error */
|
||||
#define SA_SYSTEM_ERR 7 /* system error, use errno */
|
||||
#define SA_SYNTAX_ERR 8 /* syntax error on command line */
|
||||
#define SA_NO_PERMISSION 9 /* no permission for operation */
|
||||
#define SA_BUSY 10 /* resource is busy */
|
||||
#define SA_NO_SUCH_PROP 11 /* property doesn't exist */
|
||||
#define SA_INVALID_NAME 12 /* name of object is invalid */
|
||||
#define SA_INVALID_PROTOCOL 13 /* specified protocol not valid */
|
||||
#define SA_NOT_ALLOWED 14 /* operation not allowed */
|
||||
#define SA_BAD_VALUE 15 /* bad value for property */
|
||||
#define SA_INVALID_SECURITY 16 /* invalid security type */
|
||||
#define SA_NO_SUCH_SECURITY 17 /* security set not found */
|
||||
#define SA_VALUE_CONFLICT 18 /* property value conflict */
|
||||
#define SA_NOT_IMPLEMENTED 19 /* plugin interface not implemented */
|
||||
#define SA_INVALID_PATH 20 /* path is sub-dir of existing share */
|
||||
#define SA_NOT_SUPPORTED 21 /* operation not supported for proto */
|
||||
#define SA_PROP_SHARE_ONLY 22 /* property valid on share only */
|
||||
#define SA_NOT_SHARED 23 /* path is not shared */
|
||||
#define SA_NO_SUCH_RESOURCE 24 /* resource not found */
|
||||
#define SA_RESOURCE_REQUIRED 25 /* resource name is required */
|
||||
#define SA_MULTIPLE_ERROR 26 /* multiple protocols reported error */
|
||||
#define SA_PATH_IS_SUBDIR 27 /* check_path found path is subdir */
|
||||
#define SA_PATH_IS_PARENTDIR 28 /* check_path found path is parent */
|
||||
#define SA_NO_SECTION 29 /* protocol requires section info */
|
||||
#define SA_NO_SUCH_SECTION 30 /* no section found */
|
||||
#define SA_NO_PROPERTIES 31 /* no properties found */
|
||||
#define SA_PASSWORD_ENC 32 /* passwords must be encrypted */
|
||||
|
||||
/* API Initialization */
|
||||
#define SA_INIT_SHARE_API 0x0001 /* init share specific interface */
|
||||
#define SA_INIT_CONTROL_API 0x0002 /* init control specific interface */
|
||||
|
||||
/* not part of API returns */
|
||||
#define SA_LEGACY_ERR 32 /* share/unshare error return */
|
||||
|
||||
/*
|
||||
* other defined values
|
||||
*/
|
||||
|
||||
#define SA_MAX_NAME_LEN 100 /* must fit service instance name */
|
||||
#define SA_MAX_RESOURCE_NAME 255 /* Maximum length of resource name */
|
||||
|
||||
/* Used in calls to sa_add_share() and sa_add_resource() */
|
||||
#define SA_SHARE_TRANSIENT 0 /* shared but not across reboot */
|
||||
#define SA_SHARE_LEGACY 1 /* share is in dfstab only */
|
||||
#define SA_SHARE_PERMANENT 2 /* share goes to repository */
|
||||
|
||||
/* sa_check_path() related */
|
||||
#define SA_CHECK_NORMAL 0 /* only check against active shares */
|
||||
#define SA_CHECK_STRICT 1 /* check against all shares */
|
||||
|
||||
/* RBAC related */
|
||||
#define SA_RBAC_MANAGE "solaris.smf.manage.shares"
|
||||
#define SA_RBAC_VALUE "solaris.smf.value.shares"
|
||||
|
||||
/*
|
||||
* Feature set bit definitions
|
||||
*/
|
||||
|
||||
#define SA_FEATURE_NONE 0x0000 /* no feature flags set */
|
||||
#define SA_FEATURE_RESOURCE 0x0001 /* resource names are required */
|
||||
#define SA_FEATURE_DFSTAB 0x0002 /* need to manage in dfstab */
|
||||
#define SA_FEATURE_ALLOWSUBDIRS 0x0004 /* allow subdirs to be shared */
|
||||
#define SA_FEATURE_ALLOWPARDIRS 0x0008 /* allow parent dirs to be shared */
|
||||
#define SA_FEATURE_HAS_SECTIONS 0x0010 /* protocol supports sections */
|
||||
#define SA_FEATURE_ADD_PROPERTIES 0x0020 /* can add properties */
|
||||
#define SA_FEATURE_SERVER 0x0040 /* protocol supports server mode */
|
||||
|
||||
/*
|
||||
* legacy files
|
||||
*/
|
||||
|
||||
#define SA_LEGACY_DFSTAB "/etc/dfs/dfstab"
|
||||
#define SA_LEGACY_SHARETAB "/etc/dfs/sharetab"
|
||||
|
||||
/*
|
||||
* SMF related
|
||||
*/
|
||||
|
||||
#define SA_SVC_FMRI_BASE "svc:/network/shares/group"
|
||||
|
||||
/* initialization */
|
||||
extern sa_handle_t sa_init(int);
|
||||
extern void sa_fini(sa_handle_t);
|
||||
extern int sa_update_config(sa_handle_t);
|
||||
extern char *sa_errorstr(int);
|
||||
|
||||
/* protocol names */
|
||||
extern int sa_get_protocols(char ***);
|
||||
extern int sa_valid_protocol(char *);
|
||||
|
||||
/* group control (create, remove, etc) */
|
||||
extern sa_group_t sa_create_group(sa_handle_t, char *, int *);
|
||||
extern int sa_remove_group(sa_group_t);
|
||||
extern sa_group_t sa_get_group(sa_handle_t, char *);
|
||||
extern sa_group_t sa_get_next_group(sa_group_t);
|
||||
extern char *sa_get_group_attr(sa_group_t, char *);
|
||||
extern int sa_set_group_attr(sa_group_t, char *, char *);
|
||||
extern sa_group_t sa_get_sub_group(sa_group_t);
|
||||
extern int sa_valid_group_name(char *);
|
||||
|
||||
/* share control */
|
||||
extern sa_share_t sa_add_share(sa_group_t, char *, int, int *);
|
||||
extern int sa_check_path(sa_group_t, char *, int);
|
||||
extern int sa_move_share(sa_group_t, sa_share_t);
|
||||
extern int sa_remove_share(sa_share_t);
|
||||
extern sa_share_t sa_get_share(sa_group_t, char *);
|
||||
extern sa_share_t sa_find_share(sa_handle_t, char *);
|
||||
extern sa_share_t sa_get_next_share(sa_share_t);
|
||||
extern char *sa_get_share_attr(sa_share_t, char *);
|
||||
extern char *sa_get_share_description(sa_share_t);
|
||||
extern sa_group_t sa_get_parent_group(sa_share_t);
|
||||
extern int sa_set_share_attr(sa_share_t, char *, char *);
|
||||
extern int sa_set_share_description(sa_share_t, char *);
|
||||
extern int sa_enable_share(sa_group_t, char *);
|
||||
extern int sa_disable_share(sa_share_t, char *);
|
||||
extern int sa_is_share(void *);
|
||||
|
||||
/* resource name related */
|
||||
extern sa_resource_t sa_find_resource(sa_handle_t, char *);
|
||||
extern sa_resource_t sa_get_resource(sa_group_t, char *);
|
||||
extern sa_resource_t sa_get_next_resource(sa_resource_t);
|
||||
extern sa_share_t sa_get_resource_parent(sa_resource_t);
|
||||
extern sa_resource_t sa_get_share_resource(sa_share_t, char *);
|
||||
extern sa_resource_t sa_add_resource(sa_share_t, char *, int, int *);
|
||||
extern int sa_remove_resource(sa_resource_t);
|
||||
extern char *sa_get_resource_attr(sa_resource_t, char *);
|
||||
extern int sa_set_resource_attr(sa_resource_t, char *, char *);
|
||||
extern int sa_set_resource_description(sa_resource_t, char *);
|
||||
extern char *sa_get_resource_description(sa_resource_t);
|
||||
extern int sa_enable_resource(sa_resource_t, char *);
|
||||
extern int sa_disable_resource(sa_resource_t, char *);
|
||||
extern int sa_rename_resource(sa_resource_t, char *);
|
||||
extern void sa_fix_resource_name(char *);
|
||||
|
||||
/* data structure free calls */
|
||||
extern void sa_free_attr_string(char *);
|
||||
extern void sa_free_share_description(char *);
|
||||
|
||||
/* optionset control */
|
||||
extern sa_optionset_t sa_get_optionset(sa_group_t, char *);
|
||||
extern sa_optionset_t sa_get_next_optionset(sa_group_t);
|
||||
extern char *sa_get_optionset_attr(sa_optionset_t, char *);
|
||||
extern void sa_set_optionset_attr(sa_optionset_t, char *, char *);
|
||||
extern sa_optionset_t sa_create_optionset(sa_group_t, char *);
|
||||
extern int sa_destroy_optionset(sa_optionset_t);
|
||||
extern sa_optionset_t sa_get_derived_optionset(void *, char *, int);
|
||||
extern void sa_free_derived_optionset(sa_optionset_t);
|
||||
|
||||
/* property functions */
|
||||
extern sa_property_t sa_get_property(sa_optionset_t, char *);
|
||||
extern sa_property_t sa_get_next_property(sa_group_t);
|
||||
extern char *sa_get_property_attr(sa_property_t, char *);
|
||||
extern sa_property_t sa_create_section(char *, char *);
|
||||
extern void sa_set_section_attr(sa_property_t, char *, char *);
|
||||
extern sa_property_t sa_create_property(char *, char *);
|
||||
extern int sa_add_property(void *, sa_property_t);
|
||||
extern int sa_update_property(sa_property_t, char *);
|
||||
extern int sa_remove_property(sa_property_t);
|
||||
extern int sa_commit_properties(sa_optionset_t, int);
|
||||
extern int sa_valid_property(sa_handle_t, void *, char *, sa_property_t);
|
||||
extern int sa_is_persistent(void *);
|
||||
|
||||
/* security control */
|
||||
extern sa_security_t sa_get_security(sa_group_t, char *, char *);
|
||||
extern sa_security_t sa_get_next_security(sa_security_t);
|
||||
extern char *sa_get_security_attr(sa_optionset_t, char *);
|
||||
extern sa_security_t sa_create_security(sa_group_t, char *, char *);
|
||||
extern int sa_destroy_security(sa_security_t);
|
||||
extern void sa_set_security_attr(sa_security_t, char *, char *);
|
||||
extern sa_optionset_t sa_get_all_security_types(void *, char *, int);
|
||||
extern sa_security_t sa_get_derived_security(void *, char *, char *, int);
|
||||
extern void sa_free_derived_security(sa_security_t);
|
||||
|
||||
/* protocol specific interfaces */
|
||||
extern int sa_parse_legacy_options(sa_group_t, char *, char *);
|
||||
extern char *sa_proto_legacy_format(char *, sa_group_t, int);
|
||||
extern int sa_is_security(char *, char *);
|
||||
extern sa_protocol_properties_t sa_proto_get_properties(char *);
|
||||
extern uint64_t sa_proto_get_featureset(char *);
|
||||
extern sa_property_t sa_get_protocol_section(sa_protocol_properties_t, char *);
|
||||
extern sa_property_t sa_get_next_protocol_section(sa_property_t, char *);
|
||||
extern sa_property_t sa_get_protocol_property(sa_protocol_properties_t, char *);
|
||||
extern sa_property_t sa_get_next_protocol_property(sa_property_t, char *);
|
||||
extern int sa_set_protocol_property(sa_property_t, char *, char *);
|
||||
extern char *sa_get_protocol_status(char *);
|
||||
extern void sa_format_free(char *);
|
||||
extern sa_protocol_properties_t sa_create_protocol_properties(char *);
|
||||
extern int sa_add_protocol_property(sa_protocol_properties_t, sa_property_t);
|
||||
extern int sa_proto_valid_prop(sa_handle_t, char *, sa_property_t,
|
||||
sa_optionset_t);
|
||||
extern int sa_proto_valid_space(char *, char *);
|
||||
extern char *sa_proto_space_alias(char *, char *);
|
||||
extern int sa_proto_get_transients(sa_handle_t, char *);
|
||||
extern int sa_proto_notify_resource(sa_resource_t, char *);
|
||||
extern int sa_proto_change_notify(sa_share_t, char *);
|
||||
extern int sa_proto_delete_section(char *, char *);
|
||||
|
||||
/* handle legacy (dfstab/sharetab) files */
|
||||
extern int sa_delete_legacy(sa_share_t, char *);
|
||||
extern int sa_update_legacy(sa_share_t, char *);
|
||||
extern int sa_update_sharetab(sa_share_t, char *);
|
||||
extern int sa_delete_sharetab(sa_handle_t, char *, char *);
|
||||
|
||||
/* ZFS functions */
|
||||
extern int sa_zfs_is_shared(sa_handle_t, char *);
|
||||
extern int sa_group_is_zfs(sa_group_t);
|
||||
extern int sa_path_is_zfs(char *);
|
||||
|
||||
/* SA Handle specific functions */
|
||||
extern sa_handle_t sa_find_group_handle(sa_group_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBSHARE_H */
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUUTIL_H
|
||||
#define _LIBUUTIL_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Standard flags codes.
|
||||
*/
|
||||
#define UU_DEFAULT 0
|
||||
|
||||
/*
|
||||
* Standard error codes.
|
||||
*/
|
||||
#define UU_ERROR_NONE 0 /* no error */
|
||||
#define UU_ERROR_INVALID_ARGUMENT 1 /* invalid argument */
|
||||
#define UU_ERROR_UNKNOWN_FLAG 2 /* passed flag invalid */
|
||||
#define UU_ERROR_NO_MEMORY 3 /* out of memory */
|
||||
#define UU_ERROR_CALLBACK_FAILED 4 /* callback-initiated error */
|
||||
#define UU_ERROR_NOT_SUPPORTED 5 /* operation not supported */
|
||||
#define UU_ERROR_EMPTY 6 /* no value provided */
|
||||
#define UU_ERROR_UNDERFLOW 7 /* value is too small */
|
||||
#define UU_ERROR_OVERFLOW 8 /* value is too value */
|
||||
#define UU_ERROR_INVALID_CHAR 9 /* value contains unexpected char */
|
||||
#define UU_ERROR_INVALID_DIGIT 10 /* value contains digit not in base */
|
||||
|
||||
#define UU_ERROR_SYSTEM 99 /* underlying system error */
|
||||
#define UU_ERROR_UNKNOWN 100 /* error status not known */
|
||||
|
||||
/*
|
||||
* Standard program exit codes.
|
||||
*/
|
||||
#define UU_EXIT_OK (*(uu_exit_ok()))
|
||||
#define UU_EXIT_FATAL (*(uu_exit_fatal()))
|
||||
#define UU_EXIT_USAGE (*(uu_exit_usage()))
|
||||
|
||||
/*
|
||||
* Exit status profiles.
|
||||
*/
|
||||
#define UU_PROFILE_DEFAULT 0
|
||||
#define UU_PROFILE_LAUNCHER 1
|
||||
|
||||
/*
|
||||
* Error reporting functions.
|
||||
*/
|
||||
uint32_t uu_error(void);
|
||||
const char *uu_strerror(uint32_t);
|
||||
|
||||
/*
|
||||
* Program notification functions.
|
||||
*/
|
||||
extern void uu_alt_exit(int);
|
||||
extern const char *uu_setpname(char *);
|
||||
extern const char *uu_getpname(void);
|
||||
/*PRINTFLIKE1*/
|
||||
extern void uu_warn(const char *, ...);
|
||||
extern void uu_vwarn(const char *, va_list);
|
||||
/*PRINTFLIKE1*/
|
||||
extern void uu_die(const char *, ...) __NORETURN;
|
||||
extern void uu_vdie(const char *, va_list) __NORETURN;
|
||||
/*PRINTFLIKE2*/
|
||||
extern void uu_xdie(int, const char *, ...) __NORETURN;
|
||||
extern void uu_vxdie(int, const char *, va_list) __NORETURN;
|
||||
|
||||
/*
|
||||
* Exit status functions (not to be used directly)
|
||||
*/
|
||||
extern int *uu_exit_ok(void);
|
||||
extern int *uu_exit_fatal(void);
|
||||
extern int *uu_exit_usage(void);
|
||||
|
||||
/*
|
||||
* string->number conversions
|
||||
*/
|
||||
extern int uu_strtoint(const char *, void *, size_t, int, int64_t, int64_t);
|
||||
extern int uu_strtouint(const char *, void *, size_t, int, uint64_t, uint64_t);
|
||||
|
||||
/*
|
||||
* Debug print facility functions.
|
||||
*/
|
||||
typedef struct uu_dprintf uu_dprintf_t;
|
||||
|
||||
typedef enum {
|
||||
UU_DPRINTF_SILENT,
|
||||
UU_DPRINTF_FATAL,
|
||||
UU_DPRINTF_WARNING,
|
||||
UU_DPRINTF_NOTICE,
|
||||
UU_DPRINTF_INFO,
|
||||
UU_DPRINTF_DEBUG
|
||||
} uu_dprintf_severity_t;
|
||||
|
||||
extern uu_dprintf_t *uu_dprintf_create(const char *, uu_dprintf_severity_t,
|
||||
uint_t);
|
||||
/*PRINTFLIKE3*/
|
||||
extern void uu_dprintf(uu_dprintf_t *, uu_dprintf_severity_t,
|
||||
const char *, ...);
|
||||
extern void uu_dprintf_destroy(uu_dprintf_t *);
|
||||
extern const char *uu_dprintf_getname(uu_dprintf_t *);
|
||||
|
||||
/*
|
||||
* Identifier test flags and function.
|
||||
*/
|
||||
#define UU_NAME_DOMAIN 0x1 /* allow SUNW, or com.sun, prefix */
|
||||
#define UU_NAME_PATH 0x2 /* allow '/'-delimited paths */
|
||||
|
||||
int uu_check_name(const char *, uint_t);
|
||||
|
||||
/*
|
||||
* File creation functions.
|
||||
*/
|
||||
extern int uu_open_tmp(const char *dir, uint_t uflags);
|
||||
|
||||
/*
|
||||
* Convenience functions.
|
||||
*/
|
||||
/*PRINTFLIKE1*/
|
||||
extern char *uu_msprintf(const char *format, ...);
|
||||
extern void *uu_zalloc(size_t);
|
||||
extern char *uu_strdup(const char *);
|
||||
extern void uu_free(void *);
|
||||
|
||||
/*
|
||||
* Comparison function type definition.
|
||||
* Developers should be careful in their use of the _private argument. If you
|
||||
* break interface guarantees, you get undefined behavior.
|
||||
*/
|
||||
typedef int uu_compare_fn_t(const void *__left, const void *__right,
|
||||
void *__private);
|
||||
|
||||
/*
|
||||
* Walk variant flags.
|
||||
* A data structure need not provide support for all variants and
|
||||
* combinations. Refer to the appropriate documentation.
|
||||
*/
|
||||
#define UU_WALK_ROBUST 0x00000001 /* walk can survive removes */
|
||||
#define UU_WALK_REVERSE 0x00000002 /* reverse walk order */
|
||||
|
||||
#define UU_WALK_PREORDER 0x00000010 /* walk tree in pre-order */
|
||||
#define UU_WALK_POSTORDER 0x00000020 /* walk tree in post-order */
|
||||
|
||||
/*
|
||||
* Walk callback function return codes.
|
||||
*/
|
||||
#define UU_WALK_ERROR -1
|
||||
#define UU_WALK_NEXT 0
|
||||
#define UU_WALK_DONE 1
|
||||
|
||||
/*
|
||||
* Walk callback function type definition.
|
||||
*/
|
||||
typedef int uu_walk_fn_t(void *_elem, void *_private);
|
||||
|
||||
/*
|
||||
* lists: opaque structures
|
||||
*/
|
||||
typedef struct uu_list_pool uu_list_pool_t;
|
||||
typedef struct uu_list uu_list_t;
|
||||
|
||||
typedef struct uu_list_node {
|
||||
uintptr_t uln_opaque[2];
|
||||
} uu_list_node_t;
|
||||
|
||||
typedef struct uu_list_walk uu_list_walk_t;
|
||||
|
||||
typedef uintptr_t uu_list_index_t;
|
||||
|
||||
/*
|
||||
* lists: interface
|
||||
*
|
||||
* basic usage:
|
||||
* typedef struct foo {
|
||||
* ...
|
||||
* uu_list_node_t foo_node;
|
||||
* ...
|
||||
* } foo_t;
|
||||
*
|
||||
* static int
|
||||
* foo_compare(void *l_arg, void *r_arg, void *private)
|
||||
* {
|
||||
* foo_t *l = l_arg;
|
||||
* foo_t *r = r_arg;
|
||||
*
|
||||
* if (... l greater than r ...)
|
||||
* return (1);
|
||||
* if (... l less than r ...)
|
||||
* return (-1);
|
||||
* return (0);
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* // at initialization time
|
||||
* foo_pool = uu_list_pool_create("foo_pool",
|
||||
* sizeof (foo_t), offsetof(foo_t, foo_node), foo_compare,
|
||||
* debugging? 0 : UU_AVL_POOL_DEBUG);
|
||||
* ...
|
||||
*/
|
||||
uu_list_pool_t *uu_list_pool_create(const char *, size_t, size_t,
|
||||
uu_compare_fn_t *, uint32_t);
|
||||
#define UU_LIST_POOL_DEBUG 0x00000001
|
||||
|
||||
void uu_list_pool_destroy(uu_list_pool_t *);
|
||||
|
||||
/*
|
||||
* usage:
|
||||
*
|
||||
* foo_t *a;
|
||||
* a = malloc(sizeof(*a));
|
||||
* uu_list_node_init(a, &a->foo_list, pool);
|
||||
* ...
|
||||
* uu_list_node_fini(a, &a->foo_list, pool);
|
||||
* free(a);
|
||||
*/
|
||||
void uu_list_node_init(void *, uu_list_node_t *, uu_list_pool_t *);
|
||||
void uu_list_node_fini(void *, uu_list_node_t *, uu_list_pool_t *);
|
||||
|
||||
uu_list_t *uu_list_create(uu_list_pool_t *, void *_parent, uint32_t);
|
||||
#define UU_LIST_DEBUG 0x00000001
|
||||
#define UU_LIST_SORTED 0x00000002 /* list is sorted */
|
||||
|
||||
void uu_list_destroy(uu_list_t *); /* list must be empty */
|
||||
|
||||
size_t uu_list_numnodes(uu_list_t *);
|
||||
|
||||
void *uu_list_first(uu_list_t *);
|
||||
void *uu_list_last(uu_list_t *);
|
||||
|
||||
void *uu_list_next(uu_list_t *, void *);
|
||||
void *uu_list_prev(uu_list_t *, void *);
|
||||
|
||||
int uu_list_walk(uu_list_t *, uu_walk_fn_t *, void *, uint32_t);
|
||||
|
||||
uu_list_walk_t *uu_list_walk_start(uu_list_t *, uint32_t);
|
||||
void *uu_list_walk_next(uu_list_walk_t *);
|
||||
void uu_list_walk_end(uu_list_walk_t *);
|
||||
|
||||
void *uu_list_find(uu_list_t *, void *, void *, uu_list_index_t *);
|
||||
void uu_list_insert(uu_list_t *, void *, uu_list_index_t);
|
||||
|
||||
void *uu_list_nearest_next(uu_list_t *, uu_list_index_t);
|
||||
void *uu_list_nearest_prev(uu_list_t *, uu_list_index_t);
|
||||
|
||||
void *uu_list_teardown(uu_list_t *, void **);
|
||||
|
||||
void uu_list_remove(uu_list_t *, void *);
|
||||
|
||||
/*
|
||||
* lists: interfaces for non-sorted lists only
|
||||
*/
|
||||
int uu_list_insert_before(uu_list_t *, void *_target, void *_elem);
|
||||
int uu_list_insert_after(uu_list_t *, void *_target, void *_elem);
|
||||
|
||||
/*
|
||||
* avl trees: opaque structures
|
||||
*/
|
||||
typedef struct uu_avl_pool uu_avl_pool_t;
|
||||
typedef struct uu_avl uu_avl_t;
|
||||
|
||||
typedef struct uu_avl_node {
|
||||
#ifdef _LP64
|
||||
uintptr_t uan_opaque[3];
|
||||
#else
|
||||
uintptr_t uan_opaque[4];
|
||||
#endif
|
||||
} uu_avl_node_t;
|
||||
|
||||
typedef struct uu_avl_walk uu_avl_walk_t;
|
||||
|
||||
typedef uintptr_t uu_avl_index_t;
|
||||
|
||||
/*
|
||||
* avl trees: interface
|
||||
*
|
||||
* basic usage:
|
||||
* typedef struct foo {
|
||||
* ...
|
||||
* uu_avl_node_t foo_node;
|
||||
* ...
|
||||
* } foo_t;
|
||||
*
|
||||
* static int
|
||||
* foo_compare(void *l_arg, void *r_arg, void *private)
|
||||
* {
|
||||
* foo_t *l = l_arg;
|
||||
* foo_t *r = r_arg;
|
||||
*
|
||||
* if (... l greater than r ...)
|
||||
* return (1);
|
||||
* if (... l less than r ...)
|
||||
* return (-1);
|
||||
* return (0);
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* // at initialization time
|
||||
* foo_pool = uu_avl_pool_create("foo_pool",
|
||||
* sizeof (foo_t), offsetof(foo_t, foo_node), foo_compare,
|
||||
* debugging? 0 : UU_AVL_POOL_DEBUG);
|
||||
* ...
|
||||
*/
|
||||
uu_avl_pool_t *uu_avl_pool_create(const char *, size_t, size_t,
|
||||
uu_compare_fn_t *, uint32_t);
|
||||
#define UU_AVL_POOL_DEBUG 0x00000001
|
||||
|
||||
void uu_avl_pool_destroy(uu_avl_pool_t *);
|
||||
|
||||
/*
|
||||
* usage:
|
||||
*
|
||||
* foo_t *a;
|
||||
* a = malloc(sizeof(*a));
|
||||
* uu_avl_node_init(a, &a->foo_avl, pool);
|
||||
* ...
|
||||
* uu_avl_node_fini(a, &a->foo_avl, pool);
|
||||
* free(a);
|
||||
*/
|
||||
void uu_avl_node_init(void *, uu_avl_node_t *, uu_avl_pool_t *);
|
||||
void uu_avl_node_fini(void *, uu_avl_node_t *, uu_avl_pool_t *);
|
||||
|
||||
uu_avl_t *uu_avl_create(uu_avl_pool_t *, void *_parent, uint32_t);
|
||||
#define UU_AVL_DEBUG 0x00000001
|
||||
|
||||
void uu_avl_destroy(uu_avl_t *); /* list must be empty */
|
||||
|
||||
size_t uu_avl_numnodes(uu_avl_t *);
|
||||
|
||||
void *uu_avl_first(uu_avl_t *);
|
||||
void *uu_avl_last(uu_avl_t *);
|
||||
|
||||
void *uu_avl_next(uu_avl_t *, void *);
|
||||
void *uu_avl_prev(uu_avl_t *, void *);
|
||||
|
||||
int uu_avl_walk(uu_avl_t *, uu_walk_fn_t *, void *, uint32_t);
|
||||
|
||||
uu_avl_walk_t *uu_avl_walk_start(uu_avl_t *, uint32_t);
|
||||
void *uu_avl_walk_next(uu_avl_walk_t *);
|
||||
void uu_avl_walk_end(uu_avl_walk_t *);
|
||||
|
||||
void *uu_avl_find(uu_avl_t *, void *, void *, uu_avl_index_t *);
|
||||
void uu_avl_insert(uu_avl_t *, void *, uu_avl_index_t);
|
||||
|
||||
void *uu_avl_nearest_next(uu_avl_t *, uu_avl_index_t);
|
||||
void *uu_avl_nearest_prev(uu_avl_t *, uu_avl_index_t);
|
||||
|
||||
void *uu_avl_teardown(uu_avl_t *, void **);
|
||||
|
||||
void uu_avl_remove(uu_avl_t *, void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBUUTIL_H */
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUUTIL_COMMON_H
|
||||
#define _LIBUUTIL_COMMON_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <libuutil.h>
|
||||
#include <libuutil_impl.h>
|
||||
|
||||
#endif /* _LIBUUTIL_COMMON_H */
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUUTIL_IMPL_H
|
||||
#define _LIBUUTIL_IMPL_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <libuutil.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/avl_impl.h>
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void uu_set_error(uint_t);
|
||||
#pragma rarely_called(uu_set_error)
|
||||
|
||||
/*PRINTFLIKE1*/
|
||||
void uu_panic(const char *format, ...);
|
||||
#pragma rarely_called(uu_panic)
|
||||
|
||||
struct uu_dprintf {
|
||||
char *uud_name;
|
||||
uu_dprintf_severity_t uud_severity;
|
||||
uint_t uud_flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* For debugging purposes, libuutil keeps around linked lists of all uu_lists
|
||||
* and uu_avls, along with pointers to their parents. These can cause false
|
||||
* negatives when looking for memory leaks, so we encode the pointers by
|
||||
* storing them with swapped endianness; this is not perfect, but it's about
|
||||
* the best we can do without wasting a lot of space.
|
||||
*/
|
||||
#ifdef _LP64
|
||||
#define UU_PTR_ENCODE(ptr) BSWAP_64((uintptr_t)(void *)(ptr))
|
||||
#else
|
||||
#define UU_PTR_ENCODE(ptr) BSWAP_32((uintptr_t)(void *)(ptr))
|
||||
#endif
|
||||
|
||||
#define UU_PTR_DECODE(ptr) ((void *)UU_PTR_ENCODE(ptr))
|
||||
|
||||
/*
|
||||
* uu_list structures
|
||||
*/
|
||||
typedef struct uu_list_node_impl {
|
||||
struct uu_list_node_impl *uln_next;
|
||||
struct uu_list_node_impl *uln_prev;
|
||||
} uu_list_node_impl_t;
|
||||
|
||||
struct uu_list_walk {
|
||||
uu_list_walk_t *ulw_next;
|
||||
uu_list_walk_t *ulw_prev;
|
||||
|
||||
uu_list_t *ulw_list;
|
||||
int8_t ulw_dir;
|
||||
uint8_t ulw_robust;
|
||||
uu_list_node_impl_t *ulw_next_result;
|
||||
};
|
||||
|
||||
struct uu_list {
|
||||
uintptr_t ul_next_enc;
|
||||
uintptr_t ul_prev_enc;
|
||||
|
||||
uu_list_pool_t *ul_pool;
|
||||
uintptr_t ul_parent_enc; /* encoded parent pointer */
|
||||
size_t ul_offset;
|
||||
size_t ul_numnodes;
|
||||
uint8_t ul_debug;
|
||||
uint8_t ul_sorted;
|
||||
uint8_t ul_index; /* mark for uu_list_index_ts */
|
||||
|
||||
uu_list_node_impl_t ul_null_node;
|
||||
uu_list_walk_t ul_null_walk; /* for robust walkers */
|
||||
};
|
||||
|
||||
#define UU_LIST_PTR(ptr) ((uu_list_t *)UU_PTR_DECODE(ptr))
|
||||
|
||||
#define UU_LIST_POOL_MAXNAME 64
|
||||
|
||||
struct uu_list_pool {
|
||||
uu_list_pool_t *ulp_next;
|
||||
uu_list_pool_t *ulp_prev;
|
||||
|
||||
char ulp_name[UU_LIST_POOL_MAXNAME];
|
||||
size_t ulp_nodeoffset;
|
||||
size_t ulp_objsize;
|
||||
uu_compare_fn_t *ulp_cmp;
|
||||
uint8_t ulp_debug;
|
||||
uint8_t ulp_last_index;
|
||||
pthread_mutex_t ulp_lock; /* protects null_list */
|
||||
uu_list_t ulp_null_list;
|
||||
};
|
||||
|
||||
/*
|
||||
* uu_avl structures
|
||||
*/
|
||||
typedef struct avl_node uu_avl_node_impl_t;
|
||||
|
||||
struct uu_avl_walk {
|
||||
uu_avl_walk_t *uaw_next;
|
||||
uu_avl_walk_t *uaw_prev;
|
||||
|
||||
uu_avl_t *uaw_avl;
|
||||
void *uaw_next_result;
|
||||
int8_t uaw_dir;
|
||||
uint8_t uaw_robust;
|
||||
};
|
||||
|
||||
struct uu_avl {
|
||||
uintptr_t ua_next_enc;
|
||||
uintptr_t ua_prev_enc;
|
||||
|
||||
uu_avl_pool_t *ua_pool;
|
||||
uintptr_t ua_parent_enc;
|
||||
uint8_t ua_debug;
|
||||
uint8_t ua_index; /* mark for uu_avl_index_ts */
|
||||
|
||||
struct avl_tree ua_tree;
|
||||
uu_avl_walk_t ua_null_walk;
|
||||
};
|
||||
|
||||
#define UU_AVL_PTR(x) ((uu_avl_t *)UU_PTR_DECODE(x))
|
||||
|
||||
#define UU_AVL_POOL_MAXNAME 64
|
||||
|
||||
struct uu_avl_pool {
|
||||
uu_avl_pool_t *uap_next;
|
||||
uu_avl_pool_t *uap_prev;
|
||||
|
||||
char uap_name[UU_AVL_POOL_MAXNAME];
|
||||
size_t uap_nodeoffset;
|
||||
size_t uap_objsize;
|
||||
uu_compare_fn_t *uap_cmp;
|
||||
uint8_t uap_debug;
|
||||
uint8_t uap_last_index;
|
||||
pthread_mutex_t uap_lock; /* protects null_avl */
|
||||
uu_avl_t uap_null_avl;
|
||||
};
|
||||
|
||||
/*
|
||||
* atfork() handlers
|
||||
*/
|
||||
void uu_avl_lockup(void);
|
||||
void uu_avl_release(void);
|
||||
|
||||
void uu_list_lockup(void);
|
||||
void uu_list_release(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBUUTIL_IMPL_H */
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void *
|
||||
uu_zalloc(size_t n)
|
||||
{
|
||||
void *p = malloc(n);
|
||||
|
||||
if (p == NULL) {
|
||||
uu_set_error(UU_ERROR_SYSTEM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
(void) memset(p, 0, n);
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
void
|
||||
uu_free(void *p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
char *
|
||||
uu_strdup(const char *str)
|
||||
{
|
||||
char *buf = NULL;
|
||||
|
||||
if (str != NULL) {
|
||||
size_t sz;
|
||||
|
||||
sz = strlen(str) + 1;
|
||||
buf = uu_zalloc(sz);
|
||||
if (buf != NULL)
|
||||
(void) memcpy(buf, str, sz);
|
||||
}
|
||||
return (buf);
|
||||
}
|
||||
|
||||
char *
|
||||
uu_msprintf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char attic[1];
|
||||
uint_t M, m;
|
||||
char *b;
|
||||
|
||||
va_start(args, format);
|
||||
M = vsnprintf(attic, 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
for (;;) {
|
||||
m = M;
|
||||
if ((b = uu_zalloc(m + 1)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
va_start(args, format);
|
||||
M = vsnprintf(b, m + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
if (M == m)
|
||||
break; /* sizes match */
|
||||
|
||||
uu_free(b);
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/avl.h>
|
||||
|
||||
static uu_avl_pool_t uu_null_apool = { &uu_null_apool, &uu_null_apool };
|
||||
static pthread_mutex_t uu_apool_list_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* The index mark change on every insert and delete, to catch stale
|
||||
* references.
|
||||
*
|
||||
* We leave the low bit alone, since the avl code uses it.
|
||||
*/
|
||||
#define INDEX_MAX (sizeof (uintptr_t) - 2)
|
||||
#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 2 : ((m) + 2) & INDEX_MAX)
|
||||
|
||||
#define INDEX_DECODE(i) ((i) & ~INDEX_MAX)
|
||||
#define INDEX_ENCODE(p, n) (((n) & ~INDEX_MAX) | (p)->ua_index)
|
||||
#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ua_index)
|
||||
#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)
|
||||
|
||||
/*
|
||||
* When an element is inactive (not in a tree), we keep a marked pointer to
|
||||
* its containing pool in its first word, and a NULL pointer in its second.
|
||||
*
|
||||
* On insert, we use these to verify that it comes from the correct pool.
|
||||
*/
|
||||
#define NODE_ARRAY(p, n) ((uintptr_t *)((uintptr_t)(n) + \
|
||||
(pp)->uap_nodeoffset))
|
||||
|
||||
#define POOL_TO_MARKER(pp) (((uintptr_t)(pp) | 1))
|
||||
|
||||
#define DEAD_MARKER 0xc4
|
||||
|
||||
uu_avl_pool_t *
|
||||
uu_avl_pool_create(const char *name, size_t objsize, size_t nodeoffset,
|
||||
uu_compare_fn_t *compare_func, uint32_t flags)
|
||||
{
|
||||
uu_avl_pool_t *pp, *next, *prev;
|
||||
|
||||
if (name == NULL ||
|
||||
uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
|
||||
nodeoffset + sizeof (uu_avl_node_t) > objsize ||
|
||||
compare_func == NULL) {
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (flags & ~UU_AVL_POOL_DEBUG) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pp = uu_zalloc(sizeof (uu_avl_pool_t));
|
||||
if (pp == NULL) {
|
||||
uu_set_error(UU_ERROR_NO_MEMORY);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
(void) strlcpy(pp->uap_name, name, sizeof (pp->uap_name));
|
||||
pp->uap_nodeoffset = nodeoffset;
|
||||
pp->uap_objsize = objsize;
|
||||
pp->uap_cmp = compare_func;
|
||||
if (flags & UU_AVL_POOL_DEBUG)
|
||||
pp->uap_debug = 1;
|
||||
pp->uap_last_index = 0;
|
||||
|
||||
(void) pthread_mutex_init(&pp->uap_lock, NULL);
|
||||
|
||||
pp->uap_null_avl.ua_next_enc = UU_PTR_ENCODE(&pp->uap_null_avl);
|
||||
pp->uap_null_avl.ua_prev_enc = UU_PTR_ENCODE(&pp->uap_null_avl);
|
||||
|
||||
(void) pthread_mutex_lock(&uu_apool_list_lock);
|
||||
pp->uap_next = next = &uu_null_apool;
|
||||
pp->uap_prev = prev = next->uap_prev;
|
||||
next->uap_prev = pp;
|
||||
prev->uap_next = pp;
|
||||
(void) pthread_mutex_unlock(&uu_apool_list_lock);
|
||||
|
||||
return (pp);
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_pool_destroy(uu_avl_pool_t *pp)
|
||||
{
|
||||
if (pp->uap_debug) {
|
||||
if (pp->uap_null_avl.ua_next_enc !=
|
||||
UU_PTR_ENCODE(&pp->uap_null_avl) ||
|
||||
pp->uap_null_avl.ua_prev_enc !=
|
||||
UU_PTR_ENCODE(&pp->uap_null_avl)) {
|
||||
uu_panic("uu_avl_pool_destroy: Pool \"%.*s\" (%p) has "
|
||||
"outstanding avls, or is corrupt.\n",
|
||||
(int)sizeof (pp->uap_name), pp->uap_name,
|
||||
(void *)pp);
|
||||
}
|
||||
}
|
||||
(void) pthread_mutex_lock(&uu_apool_list_lock);
|
||||
pp->uap_next->uap_prev = pp->uap_prev;
|
||||
pp->uap_prev->uap_next = pp->uap_next;
|
||||
(void) pthread_mutex_unlock(&uu_apool_list_lock);
|
||||
pp->uap_prev = NULL;
|
||||
pp->uap_next = NULL;
|
||||
uu_free(pp);
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_node_init(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
|
||||
{
|
||||
uintptr_t *na = (uintptr_t *)np;
|
||||
|
||||
if (pp->uap_debug) {
|
||||
uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
|
||||
if (offset + sizeof (*np) > pp->uap_objsize) {
|
||||
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
|
||||
"offset %ld doesn't fit in object (size %ld)\n",
|
||||
base, (void *)np, (void *)pp, pp->uap_name,
|
||||
(long)offset, (long)pp->uap_objsize);
|
||||
}
|
||||
if (offset != pp->uap_nodeoffset) {
|
||||
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
|
||||
"offset %ld doesn't match pool's offset (%ld)\n",
|
||||
base, (void *)np, (void *)pp, pp->uap_name,
|
||||
(long)offset, (long)pp->uap_objsize);
|
||||
}
|
||||
}
|
||||
|
||||
na[0] = POOL_TO_MARKER(pp);
|
||||
na[1] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_node_fini(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
|
||||
{
|
||||
uintptr_t *na = (uintptr_t *)np;
|
||||
|
||||
if (pp->uap_debug) {
|
||||
if (na[0] == DEAD_MARKER && na[1] == DEAD_MARKER) {
|
||||
uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
|
||||
"node already finied\n",
|
||||
base, (void *)np, (void *)pp, pp->uap_name);
|
||||
}
|
||||
if (na[0] != POOL_TO_MARKER(pp) || na[1] != 0) {
|
||||
uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
|
||||
"node corrupt, in tree, or in different pool\n",
|
||||
base, (void *)np, (void *)pp, pp->uap_name);
|
||||
}
|
||||
}
|
||||
|
||||
na[0] = DEAD_MARKER;
|
||||
na[1] = DEAD_MARKER;
|
||||
na[2] = DEAD_MARKER;
|
||||
}
|
||||
|
||||
struct uu_avl_node_compare_info {
|
||||
uu_compare_fn_t *ac_compare;
|
||||
void *ac_private;
|
||||
void *ac_right;
|
||||
void *ac_found;
|
||||
};
|
||||
|
||||
static int
|
||||
uu_avl_node_compare(const void *l, const void *r)
|
||||
{
|
||||
struct uu_avl_node_compare_info *info =
|
||||
(struct uu_avl_node_compare_info *)l;
|
||||
|
||||
int res = info->ac_compare(r, info->ac_right, info->ac_private);
|
||||
|
||||
if (res == 0) {
|
||||
if (info->ac_found == NULL)
|
||||
info->ac_found = (void *)r;
|
||||
return (-1);
|
||||
}
|
||||
if (res < 0)
|
||||
return (1);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
uu_avl_t *
|
||||
uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags)
|
||||
{
|
||||
uu_avl_t *ap, *next, *prev;
|
||||
|
||||
if (flags & ~UU_AVL_DEBUG) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
ap = uu_zalloc(sizeof (*ap));
|
||||
if (ap == NULL) {
|
||||
uu_set_error(UU_ERROR_NO_MEMORY);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
ap->ua_pool = pp;
|
||||
ap->ua_parent_enc = UU_PTR_ENCODE(parent);
|
||||
ap->ua_debug = pp->uap_debug || (flags & UU_AVL_DEBUG);
|
||||
ap->ua_index = (pp->uap_last_index = INDEX_NEXT(pp->uap_last_index));
|
||||
|
||||
avl_create(&ap->ua_tree, &uu_avl_node_compare, pp->uap_objsize,
|
||||
pp->uap_nodeoffset);
|
||||
|
||||
ap->ua_null_walk.uaw_next = &ap->ua_null_walk;
|
||||
ap->ua_null_walk.uaw_prev = &ap->ua_null_walk;
|
||||
|
||||
(void) pthread_mutex_lock(&pp->uap_lock);
|
||||
next = &pp->uap_null_avl;
|
||||
prev = UU_PTR_DECODE(next->ua_prev_enc);
|
||||
ap->ua_next_enc = UU_PTR_ENCODE(next);
|
||||
ap->ua_prev_enc = UU_PTR_ENCODE(prev);
|
||||
next->ua_prev_enc = UU_PTR_ENCODE(ap);
|
||||
prev->ua_next_enc = UU_PTR_ENCODE(ap);
|
||||
(void) pthread_mutex_unlock(&pp->uap_lock);
|
||||
|
||||
return (ap);
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_destroy(uu_avl_t *ap)
|
||||
{
|
||||
uu_avl_pool_t *pp = ap->ua_pool;
|
||||
|
||||
if (ap->ua_debug) {
|
||||
if (avl_numnodes(&ap->ua_tree) != 0) {
|
||||
uu_panic("uu_avl_destroy(%p): tree not empty\n",
|
||||
(void *)ap);
|
||||
}
|
||||
if (ap->ua_null_walk.uaw_next != &ap->ua_null_walk ||
|
||||
ap->ua_null_walk.uaw_prev != &ap->ua_null_walk) {
|
||||
uu_panic("uu_avl_destroy(%p): outstanding walkers\n",
|
||||
(void *)ap);
|
||||
}
|
||||
}
|
||||
(void) pthread_mutex_lock(&pp->uap_lock);
|
||||
UU_AVL_PTR(ap->ua_next_enc)->ua_prev_enc = ap->ua_prev_enc;
|
||||
UU_AVL_PTR(ap->ua_prev_enc)->ua_next_enc = ap->ua_next_enc;
|
||||
(void) pthread_mutex_unlock(&pp->uap_lock);
|
||||
ap->ua_prev_enc = UU_PTR_ENCODE(NULL);
|
||||
ap->ua_next_enc = UU_PTR_ENCODE(NULL);
|
||||
|
||||
ap->ua_pool = NULL;
|
||||
avl_destroy(&ap->ua_tree);
|
||||
|
||||
uu_free(ap);
|
||||
}
|
||||
|
||||
size_t
|
||||
uu_avl_numnodes(uu_avl_t *ap)
|
||||
{
|
||||
return (avl_numnodes(&ap->ua_tree));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_first(uu_avl_t *ap)
|
||||
{
|
||||
return (avl_first(&ap->ua_tree));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_last(uu_avl_t *ap)
|
||||
{
|
||||
return (avl_last(&ap->ua_tree));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_next(uu_avl_t *ap, void *node)
|
||||
{
|
||||
return (AVL_NEXT(&ap->ua_tree, node));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_prev(uu_avl_t *ap, void *node)
|
||||
{
|
||||
return (AVL_PREV(&ap->ua_tree, node));
|
||||
}
|
||||
|
||||
static void
|
||||
_avl_walk_init(uu_avl_walk_t *wp, uu_avl_t *ap, uint32_t flags)
|
||||
{
|
||||
uu_avl_walk_t *next, *prev;
|
||||
|
||||
int robust = (flags & UU_WALK_ROBUST);
|
||||
int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
|
||||
|
||||
(void) memset(wp, 0, sizeof (*wp));
|
||||
wp->uaw_avl = ap;
|
||||
wp->uaw_robust = robust;
|
||||
wp->uaw_dir = direction;
|
||||
|
||||
if (direction > 0)
|
||||
wp->uaw_next_result = avl_first(&ap->ua_tree);
|
||||
else
|
||||
wp->uaw_next_result = avl_last(&ap->ua_tree);
|
||||
|
||||
if (ap->ua_debug || robust) {
|
||||
wp->uaw_next = next = &ap->ua_null_walk;
|
||||
wp->uaw_prev = prev = next->uaw_prev;
|
||||
next->uaw_prev = wp;
|
||||
prev->uaw_next = wp;
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
_avl_walk_advance(uu_avl_walk_t *wp, uu_avl_t *ap)
|
||||
{
|
||||
void *np = wp->uaw_next_result;
|
||||
|
||||
avl_tree_t *t = &ap->ua_tree;
|
||||
|
||||
if (np == NULL)
|
||||
return (NULL);
|
||||
|
||||
wp->uaw_next_result = (wp->uaw_dir > 0)? AVL_NEXT(t, np) :
|
||||
AVL_PREV(t, np);
|
||||
|
||||
return (np);
|
||||
}
|
||||
|
||||
static void
|
||||
_avl_walk_fini(uu_avl_walk_t *wp)
|
||||
{
|
||||
if (wp->uaw_next != NULL) {
|
||||
wp->uaw_next->uaw_prev = wp->uaw_prev;
|
||||
wp->uaw_prev->uaw_next = wp->uaw_next;
|
||||
wp->uaw_next = NULL;
|
||||
wp->uaw_prev = NULL;
|
||||
}
|
||||
wp->uaw_avl = NULL;
|
||||
wp->uaw_next_result = NULL;
|
||||
}
|
||||
|
||||
uu_avl_walk_t *
|
||||
uu_avl_walk_start(uu_avl_t *ap, uint32_t flags)
|
||||
{
|
||||
uu_avl_walk_t *wp;
|
||||
|
||||
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
wp = uu_zalloc(sizeof (*wp));
|
||||
if (wp == NULL) {
|
||||
uu_set_error(UU_ERROR_NO_MEMORY);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
_avl_walk_init(wp, ap, flags);
|
||||
return (wp);
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_walk_next(uu_avl_walk_t *wp)
|
||||
{
|
||||
return (_avl_walk_advance(wp, wp->uaw_avl));
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_walk_end(uu_avl_walk_t *wp)
|
||||
{
|
||||
_avl_walk_fini(wp);
|
||||
uu_free(wp);
|
||||
}
|
||||
|
||||
int
|
||||
uu_avl_walk(uu_avl_t *ap, uu_walk_fn_t *func, void *private, uint32_t flags)
|
||||
{
|
||||
void *e;
|
||||
uu_avl_walk_t my_walk;
|
||||
|
||||
int status = UU_WALK_NEXT;
|
||||
|
||||
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
_avl_walk_init(&my_walk, ap, flags);
|
||||
while (status == UU_WALK_NEXT &&
|
||||
(e = _avl_walk_advance(&my_walk, ap)) != NULL)
|
||||
status = (*func)(e, private);
|
||||
_avl_walk_fini(&my_walk);
|
||||
|
||||
if (status >= 0)
|
||||
return (0);
|
||||
uu_set_error(UU_ERROR_CALLBACK_FAILED);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_remove(uu_avl_t *ap, void *elem)
|
||||
{
|
||||
uu_avl_walk_t *wp;
|
||||
uu_avl_pool_t *pp = ap->ua_pool;
|
||||
uintptr_t *na = NODE_ARRAY(pp, elem);
|
||||
|
||||
if (ap->ua_debug) {
|
||||
/*
|
||||
* invalidate outstanding uu_avl_index_ts.
|
||||
*/
|
||||
ap->ua_index = INDEX_NEXT(ap->ua_index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Robust walkers most be advanced, if we are removing the node
|
||||
* they are currently using. In debug mode, non-robust walkers
|
||||
* are also on the walker list.
|
||||
*/
|
||||
for (wp = ap->ua_null_walk.uaw_next; wp != &ap->ua_null_walk;
|
||||
wp = wp->uaw_next) {
|
||||
if (wp->uaw_robust) {
|
||||
if (elem == wp->uaw_next_result)
|
||||
(void) _avl_walk_advance(wp, ap);
|
||||
} else if (wp->uaw_next_result != NULL) {
|
||||
uu_panic("uu_avl_remove(%p, %p): active non-robust "
|
||||
"walker\n", (void *)ap, elem);
|
||||
}
|
||||
}
|
||||
|
||||
avl_remove(&ap->ua_tree, elem);
|
||||
|
||||
na[0] = POOL_TO_MARKER(pp);
|
||||
na[1] = 0;
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_teardown(uu_avl_t *ap, void **cookie)
|
||||
{
|
||||
void *elem = avl_destroy_nodes(&ap->ua_tree, cookie);
|
||||
|
||||
if (elem != NULL) {
|
||||
uu_avl_pool_t *pp = ap->ua_pool;
|
||||
uintptr_t *na = NODE_ARRAY(pp, elem);
|
||||
|
||||
na[0] = POOL_TO_MARKER(pp);
|
||||
na[1] = 0;
|
||||
}
|
||||
return (elem);
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_find(uu_avl_t *ap, void *elem, void *private, uu_avl_index_t *out)
|
||||
{
|
||||
struct uu_avl_node_compare_info info;
|
||||
void *result;
|
||||
|
||||
info.ac_compare = ap->ua_pool->uap_cmp;
|
||||
info.ac_private = private;
|
||||
info.ac_right = elem;
|
||||
info.ac_found = NULL;
|
||||
|
||||
result = avl_find(&ap->ua_tree, &info, out);
|
||||
if (out != NULL)
|
||||
*out = INDEX_ENCODE(ap, *out);
|
||||
|
||||
if (ap->ua_debug && result != NULL)
|
||||
uu_panic("uu_avl_find: internal error: avl_find succeeded\n");
|
||||
|
||||
return (info.ac_found);
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_insert(uu_avl_t *ap, void *elem, uu_avl_index_t idx)
|
||||
{
|
||||
if (ap->ua_debug) {
|
||||
uu_avl_pool_t *pp = ap->ua_pool;
|
||||
uintptr_t *na = NODE_ARRAY(pp, elem);
|
||||
|
||||
if (na[1] != 0)
|
||||
uu_panic("uu_avl_insert(%p, %p, %p): node already "
|
||||
"in tree, or corrupt\n",
|
||||
(void *)ap, elem, (void *)idx);
|
||||
if (na[0] == 0)
|
||||
uu_panic("uu_avl_insert(%p, %p, %p): node not "
|
||||
"initialized\n",
|
||||
(void *)ap, elem, (void *)idx);
|
||||
if (na[0] != POOL_TO_MARKER(pp))
|
||||
uu_panic("uu_avl_insert(%p, %p, %p): node from "
|
||||
"other pool, or corrupt\n",
|
||||
(void *)ap, elem, (void *)idx);
|
||||
|
||||
if (!INDEX_VALID(ap, idx))
|
||||
uu_panic("uu_avl_insert(%p, %p, %p): %s\n",
|
||||
(void *)ap, elem, (void *)idx,
|
||||
INDEX_CHECK(idx)? "outdated index" :
|
||||
"invalid index");
|
||||
|
||||
/*
|
||||
* invalidate outstanding uu_avl_index_ts.
|
||||
*/
|
||||
ap->ua_index = INDEX_NEXT(ap->ua_index);
|
||||
}
|
||||
avl_insert(&ap->ua_tree, elem, INDEX_DECODE(idx));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_nearest_next(uu_avl_t *ap, uu_avl_index_t idx)
|
||||
{
|
||||
if (ap->ua_debug && !INDEX_VALID(ap, idx))
|
||||
uu_panic("uu_avl_nearest_next(%p, %p): %s\n",
|
||||
(void *)ap, (void *)idx, INDEX_CHECK(idx)?
|
||||
"outdated index" : "invalid index");
|
||||
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_AFTER));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_avl_nearest_prev(uu_avl_t *ap, uu_avl_index_t idx)
|
||||
{
|
||||
if (ap->ua_debug && !INDEX_VALID(ap, idx))
|
||||
uu_panic("uu_avl_nearest_prev(%p, %p): %s\n",
|
||||
(void *)ap, (void *)idx, INDEX_CHECK(idx)?
|
||||
"outdated index" : "invalid index");
|
||||
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_BEFORE));
|
||||
}
|
||||
|
||||
/*
|
||||
* called from uu_lockup() and uu_release(), as part of our fork1()-safety.
|
||||
*/
|
||||
void
|
||||
uu_avl_lockup(void)
|
||||
{
|
||||
uu_avl_pool_t *pp;
|
||||
|
||||
(void) pthread_mutex_lock(&uu_apool_list_lock);
|
||||
for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
|
||||
pp = pp->uap_next)
|
||||
(void) pthread_mutex_lock(&pp->uap_lock);
|
||||
}
|
||||
|
||||
void
|
||||
uu_avl_release(void)
|
||||
{
|
||||
uu_avl_pool_t *pp;
|
||||
|
||||
for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
|
||||
pp = pp->uap_next)
|
||||
(void) pthread_mutex_unlock(&pp->uap_lock);
|
||||
(void) pthread_mutex_unlock(&uu_apool_list_lock);
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <libintl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define FACILITY_FMT "%s (%s): "
|
||||
|
||||
#if !defined(TEXT_DOMAIN)
|
||||
#define TEXT_DOMAIN "SYS_TEST"
|
||||
#endif
|
||||
|
||||
static const char *
|
||||
strseverity(uu_dprintf_severity_t severity)
|
||||
{
|
||||
switch (severity) {
|
||||
case UU_DPRINTF_SILENT:
|
||||
return (dgettext(TEXT_DOMAIN, "silent"));
|
||||
case UU_DPRINTF_FATAL:
|
||||
return (dgettext(TEXT_DOMAIN, "FATAL"));
|
||||
case UU_DPRINTF_WARNING:
|
||||
return (dgettext(TEXT_DOMAIN, "WARNING"));
|
||||
case UU_DPRINTF_NOTICE:
|
||||
return (dgettext(TEXT_DOMAIN, "note"));
|
||||
case UU_DPRINTF_INFO:
|
||||
return (dgettext(TEXT_DOMAIN, "info"));
|
||||
case UU_DPRINTF_DEBUG:
|
||||
return (dgettext(TEXT_DOMAIN, "debug"));
|
||||
default:
|
||||
return (dgettext(TEXT_DOMAIN, "unspecified"));
|
||||
}
|
||||
}
|
||||
|
||||
uu_dprintf_t *
|
||||
uu_dprintf_create(const char *name, uu_dprintf_severity_t severity,
|
||||
uint_t flags)
|
||||
{
|
||||
uu_dprintf_t *D;
|
||||
|
||||
if (uu_check_name(name, UU_NAME_DOMAIN) == -1) {
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((D = uu_zalloc(sizeof (uu_dprintf_t))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (name != NULL) {
|
||||
D->uud_name = strdup(name);
|
||||
if (D->uud_name == NULL) {
|
||||
uu_free(D);
|
||||
return (NULL);
|
||||
}
|
||||
} else {
|
||||
D->uud_name = NULL;
|
||||
}
|
||||
|
||||
D->uud_severity = severity;
|
||||
D->uud_flags = flags;
|
||||
|
||||
return (D);
|
||||
}
|
||||
|
||||
/*PRINTFLIKE3*/
|
||||
void
|
||||
uu_dprintf(uu_dprintf_t *D, uu_dprintf_severity_t severity,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list alist;
|
||||
|
||||
/* XXX Assert that severity is not UU_DPRINTF_SILENT. */
|
||||
|
||||
if (severity > D->uud_severity)
|
||||
return;
|
||||
|
||||
(void) fprintf(stderr, FACILITY_FMT, D->uud_name,
|
||||
strseverity(severity));
|
||||
|
||||
va_start(alist, format);
|
||||
(void) vfprintf(stderr, format, alist);
|
||||
va_end(alist);
|
||||
}
|
||||
|
||||
void
|
||||
uu_dprintf_destroy(uu_dprintf_t *D)
|
||||
{
|
||||
if (D->uud_name)
|
||||
free(D->uud_name);
|
||||
|
||||
uu_free(D);
|
||||
}
|
||||
|
||||
const char *
|
||||
uu_dprintf_getname(uu_dprintf_t *D)
|
||||
{
|
||||
return (D->uud_name);
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* We require names of the form:
|
||||
* [provider,]identifier[/[provider,]identifier]...
|
||||
*
|
||||
* Where provider is either a stock symbol (SUNW) or a java-style reversed
|
||||
* domain name (com.sun).
|
||||
*
|
||||
* Both providers and identifiers must start with a letter, and may
|
||||
* only contain alphanumerics, dashes, and underlines. Providers
|
||||
* may also contain periods.
|
||||
*
|
||||
* Note that we do _not_ use the macros in <ctype.h>, since they are affected
|
||||
* by the current locale settings.
|
||||
*/
|
||||
|
||||
#define IS_ALPHA(c) \
|
||||
(((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
|
||||
|
||||
#define IS_DIGIT(c) \
|
||||
((c) >= '0' && (c) <= '9')
|
||||
|
||||
static int
|
||||
is_valid_ident(const char *s, const char *e, int allowdot)
|
||||
{
|
||||
char c;
|
||||
|
||||
if (s >= e)
|
||||
return (0); /* name is empty */
|
||||
|
||||
c = *s++;
|
||||
if (!IS_ALPHA(c))
|
||||
return (0); /* does not start with letter */
|
||||
|
||||
while (s < e && (c = *s++) != 0) {
|
||||
if (IS_ALPHA(c) || IS_DIGIT(c) || c == '-' || c == '_' ||
|
||||
(allowdot && c == '.'))
|
||||
continue;
|
||||
return (0); /* invalid character */
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
is_valid_component(const char *b, const char *e, uint_t flags)
|
||||
{
|
||||
char *sp;
|
||||
|
||||
if (flags & UU_NAME_DOMAIN) {
|
||||
sp = strchr(b, ',');
|
||||
if (sp != NULL && sp < e) {
|
||||
if (!is_valid_ident(b, sp, 1))
|
||||
return (0);
|
||||
b = sp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (is_valid_ident(b, e, 0));
|
||||
}
|
||||
|
||||
int
|
||||
uu_check_name(const char *name, uint_t flags)
|
||||
{
|
||||
const char *end = name + strlen(name);
|
||||
const char *p;
|
||||
|
||||
if (flags & ~(UU_NAME_DOMAIN | UU_NAME_PATH)) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (!(flags & UU_NAME_PATH)) {
|
||||
if (!is_valid_component(name, end, flags))
|
||||
goto bad;
|
||||
return (0);
|
||||
}
|
||||
|
||||
while ((p = strchr(name, '/')) != NULL) {
|
||||
if (!is_valid_component(name, p - 1, flags))
|
||||
goto bad;
|
||||
name = p + 1;
|
||||
}
|
||||
if (!is_valid_component(name, end, flags))
|
||||
goto bad;
|
||||
|
||||
return (0);
|
||||
|
||||
bad:
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (-1);
|
||||
}
|
|
@ -0,0 +1,718 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define ELEM_TO_NODE(lp, e) \
|
||||
((uu_list_node_impl_t *)((uintptr_t)(e) + (lp)->ul_offset))
|
||||
|
||||
#define NODE_TO_ELEM(lp, n) \
|
||||
((void *)((uintptr_t)(n) - (lp)->ul_offset))
|
||||
|
||||
/*
|
||||
* uu_list_index_ts define a location for insertion. They are simply a
|
||||
* pointer to the object after the insertion point. We store a mark
|
||||
* in the low-bits of the index, to help prevent mistakes.
|
||||
*
|
||||
* When debugging, the index mark changes on every insert and delete, to
|
||||
* catch stale references.
|
||||
*/
|
||||
#define INDEX_MAX (sizeof (uintptr_t) - 1)
|
||||
#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 1 : ((m) + 1) & INDEX_MAX)
|
||||
|
||||
#define INDEX_TO_NODE(i) ((uu_list_node_impl_t *)((i) & ~INDEX_MAX))
|
||||
#define NODE_TO_INDEX(p, n) (((uintptr_t)(n) & ~INDEX_MAX) | (p)->ul_index)
|
||||
#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ul_index)
|
||||
#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)
|
||||
|
||||
#define POOL_TO_MARKER(pp) ((void *)((uintptr_t)(pp) | 1))
|
||||
|
||||
static uu_list_pool_t uu_null_lpool = { &uu_null_lpool, &uu_null_lpool };
|
||||
static pthread_mutex_t uu_lpool_list_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
uu_list_pool_t *
|
||||
uu_list_pool_create(const char *name, size_t objsize,
|
||||
size_t nodeoffset, uu_compare_fn_t *compare_func, uint32_t flags)
|
||||
{
|
||||
uu_list_pool_t *pp, *next, *prev;
|
||||
|
||||
if (name == NULL ||
|
||||
uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
|
||||
nodeoffset + sizeof (uu_list_node_t) > objsize) {
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (flags & ~UU_LIST_POOL_DEBUG) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pp = uu_zalloc(sizeof (uu_list_pool_t));
|
||||
if (pp == NULL) {
|
||||
uu_set_error(UU_ERROR_NO_MEMORY);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
(void) strlcpy(pp->ulp_name, name, sizeof (pp->ulp_name));
|
||||
pp->ulp_nodeoffset = nodeoffset;
|
||||
pp->ulp_objsize = objsize;
|
||||
pp->ulp_cmp = compare_func;
|
||||
if (flags & UU_LIST_POOL_DEBUG)
|
||||
pp->ulp_debug = 1;
|
||||
pp->ulp_last_index = 0;
|
||||
|
||||
(void) pthread_mutex_init(&pp->ulp_lock, NULL);
|
||||
|
||||
pp->ulp_null_list.ul_next_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
|
||||
pp->ulp_null_list.ul_prev_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
|
||||
|
||||
(void) pthread_mutex_lock(&uu_lpool_list_lock);
|
||||
pp->ulp_next = next = &uu_null_lpool;
|
||||
pp->ulp_prev = prev = next->ulp_prev;
|
||||
next->ulp_prev = pp;
|
||||
prev->ulp_next = pp;
|
||||
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
|
||||
|
||||
return (pp);
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_pool_destroy(uu_list_pool_t *pp)
|
||||
{
|
||||
if (pp->ulp_debug) {
|
||||
if (pp->ulp_null_list.ul_next_enc !=
|
||||
UU_PTR_ENCODE(&pp->ulp_null_list) ||
|
||||
pp->ulp_null_list.ul_prev_enc !=
|
||||
UU_PTR_ENCODE(&pp->ulp_null_list)) {
|
||||
uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has "
|
||||
"outstanding lists, or is corrupt.\n",
|
||||
(int)sizeof (pp->ulp_name), pp->ulp_name,
|
||||
(void *)pp);
|
||||
}
|
||||
}
|
||||
(void) pthread_mutex_lock(&uu_lpool_list_lock);
|
||||
pp->ulp_next->ulp_prev = pp->ulp_prev;
|
||||
pp->ulp_prev->ulp_next = pp->ulp_next;
|
||||
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
|
||||
pp->ulp_prev = NULL;
|
||||
pp->ulp_next = NULL;
|
||||
uu_free(pp);
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
|
||||
{
|
||||
uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
|
||||
|
||||
if (pp->ulp_debug) {
|
||||
uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
|
||||
if (offset + sizeof (*np) > pp->ulp_objsize) {
|
||||
uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
|
||||
"offset %ld doesn't fit in object (size %ld)\n",
|
||||
base, (void *)np, (void *)pp, pp->ulp_name,
|
||||
(long)offset, (long)pp->ulp_objsize);
|
||||
}
|
||||
if (offset != pp->ulp_nodeoffset) {
|
||||
uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
|
||||
"offset %ld doesn't match pool's offset (%ld)\n",
|
||||
base, (void *)np, (void *)pp, pp->ulp_name,
|
||||
(long)offset, (long)pp->ulp_objsize);
|
||||
}
|
||||
}
|
||||
np->uln_next = POOL_TO_MARKER(pp);
|
||||
np->uln_prev = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
|
||||
{
|
||||
uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
|
||||
|
||||
if (pp->ulp_debug) {
|
||||
if (np->uln_next == NULL &&
|
||||
np->uln_prev == NULL) {
|
||||
uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
|
||||
"node already finied\n",
|
||||
base, (void *)np_arg, (void *)pp, pp->ulp_name);
|
||||
}
|
||||
if (np->uln_next != POOL_TO_MARKER(pp) ||
|
||||
np->uln_prev != NULL) {
|
||||
uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
|
||||
"node corrupt or on list\n",
|
||||
base, (void *)np_arg, (void *)pp, pp->ulp_name);
|
||||
}
|
||||
}
|
||||
np->uln_next = NULL;
|
||||
np->uln_prev = NULL;
|
||||
}
|
||||
|
||||
uu_list_t *
|
||||
uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)
|
||||
{
|
||||
uu_list_t *lp, *next, *prev;
|
||||
|
||||
if (flags & ~(UU_LIST_DEBUG | UU_LIST_SORTED)) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((flags & UU_LIST_SORTED) && pp->ulp_cmp == NULL) {
|
||||
if (pp->ulp_debug)
|
||||
uu_panic("uu_list_create(%p, ...): requested "
|
||||
"UU_LIST_SORTED, but pool has no comparison func\n",
|
||||
(void *)pp);
|
||||
uu_set_error(UU_ERROR_NOT_SUPPORTED);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
lp = uu_zalloc(sizeof (*lp));
|
||||
if (lp == NULL) {
|
||||
uu_set_error(UU_ERROR_NO_MEMORY);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
lp->ul_pool = pp;
|
||||
lp->ul_parent_enc = UU_PTR_ENCODE(parent);
|
||||
lp->ul_offset = pp->ulp_nodeoffset;
|
||||
lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG);
|
||||
lp->ul_sorted = (flags & UU_LIST_SORTED);
|
||||
lp->ul_numnodes = 0;
|
||||
lp->ul_index = (pp->ulp_last_index = INDEX_NEXT(pp->ulp_last_index));
|
||||
|
||||
lp->ul_null_node.uln_next = &lp->ul_null_node;
|
||||
lp->ul_null_node.uln_prev = &lp->ul_null_node;
|
||||
|
||||
lp->ul_null_walk.ulw_next = &lp->ul_null_walk;
|
||||
lp->ul_null_walk.ulw_prev = &lp->ul_null_walk;
|
||||
|
||||
(void) pthread_mutex_lock(&pp->ulp_lock);
|
||||
next = &pp->ulp_null_list;
|
||||
prev = UU_PTR_DECODE(next->ul_prev_enc);
|
||||
lp->ul_next_enc = UU_PTR_ENCODE(next);
|
||||
lp->ul_prev_enc = UU_PTR_ENCODE(prev);
|
||||
next->ul_prev_enc = UU_PTR_ENCODE(lp);
|
||||
prev->ul_next_enc = UU_PTR_ENCODE(lp);
|
||||
(void) pthread_mutex_unlock(&pp->ulp_lock);
|
||||
|
||||
return (lp);
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_destroy(uu_list_t *lp)
|
||||
{
|
||||
uu_list_pool_t *pp = lp->ul_pool;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (lp->ul_null_node.uln_next != &lp->ul_null_node ||
|
||||
lp->ul_null_node.uln_prev != &lp->ul_null_node) {
|
||||
uu_panic("uu_list_destroy(%p): list not empty\n",
|
||||
(void *)lp);
|
||||
}
|
||||
if (lp->ul_numnodes != 0) {
|
||||
uu_panic("uu_list_destroy(%p): numnodes is nonzero, "
|
||||
"but list is empty\n", (void *)lp);
|
||||
}
|
||||
if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||
|
||||
lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {
|
||||
uu_panic("uu_list_destroy(%p): outstanding walkers\n",
|
||||
(void *)lp);
|
||||
}
|
||||
}
|
||||
|
||||
(void) pthread_mutex_lock(&pp->ulp_lock);
|
||||
UU_LIST_PTR(lp->ul_next_enc)->ul_prev_enc = lp->ul_prev_enc;
|
||||
UU_LIST_PTR(lp->ul_prev_enc)->ul_next_enc = lp->ul_next_enc;
|
||||
(void) pthread_mutex_unlock(&pp->ulp_lock);
|
||||
lp->ul_prev_enc = UU_PTR_ENCODE(NULL);
|
||||
lp->ul_next_enc = UU_PTR_ENCODE(NULL);
|
||||
lp->ul_pool = NULL;
|
||||
uu_free(lp);
|
||||
}
|
||||
|
||||
static void
|
||||
list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,
|
||||
uu_list_node_impl_t *next)
|
||||
{
|
||||
if (lp->ul_debug) {
|
||||
if (next->uln_prev != prev || prev->uln_next != next)
|
||||
uu_panic("insert(%p): internal error: %p and %p not "
|
||||
"neighbors\n", (void *)lp, (void *)next,
|
||||
(void *)prev);
|
||||
|
||||
if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||
|
||||
np->uln_prev != NULL) {
|
||||
uu_panic("insert(%p): elem %p node %p corrupt, "
|
||||
"not initialized, or already in a list.\n",
|
||||
(void *)lp, NODE_TO_ELEM(lp, np), (void *)np);
|
||||
}
|
||||
/*
|
||||
* invalidate outstanding uu_list_index_ts.
|
||||
*/
|
||||
lp->ul_index = INDEX_NEXT(lp->ul_index);
|
||||
}
|
||||
np->uln_next = next;
|
||||
np->uln_prev = prev;
|
||||
next->uln_prev = np;
|
||||
prev->uln_next = np;
|
||||
|
||||
lp->ul_numnodes++;
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)
|
||||
{
|
||||
uu_list_node_impl_t *np;
|
||||
|
||||
np = INDEX_TO_NODE(idx);
|
||||
if (np == NULL)
|
||||
np = &lp->ul_null_node;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (!INDEX_VALID(lp, idx))
|
||||
uu_panic("uu_list_insert(%p, %p, %p): %s\n",
|
||||
(void *)lp, elem, (void *)idx,
|
||||
INDEX_CHECK(idx)? "outdated index" :
|
||||
"invalid index");
|
||||
if (np->uln_prev == NULL)
|
||||
uu_panic("uu_list_insert(%p, %p, %p): out-of-date "
|
||||
"index\n", (void *)lp, elem, (void *)idx);
|
||||
}
|
||||
|
||||
list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
|
||||
{
|
||||
int sorted = lp->ul_sorted;
|
||||
uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
|
||||
uu_list_node_impl_t *np;
|
||||
|
||||
if (func == NULL) {
|
||||
if (out != NULL)
|
||||
*out = 0;
|
||||
uu_set_error(UU_ERROR_NOT_SUPPORTED);
|
||||
return (NULL);
|
||||
}
|
||||
for (np = lp->ul_null_node.uln_next; np != &lp->ul_null_node;
|
||||
np = np->uln_next) {
|
||||
void *ep = NODE_TO_ELEM(lp, np);
|
||||
int cmp = func(ep, elem, private);
|
||||
if (cmp == 0) {
|
||||
if (out != NULL)
|
||||
*out = NODE_TO_INDEX(lp, np);
|
||||
return (ep);
|
||||
}
|
||||
if (sorted && cmp > 0) {
|
||||
if (out != NULL)
|
||||
*out = NODE_TO_INDEX(lp, np);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if (out != NULL)
|
||||
*out = NODE_TO_INDEX(lp, 0);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)
|
||||
{
|
||||
uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
|
||||
|
||||
if (np == NULL)
|
||||
np = &lp->ul_null_node;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (!INDEX_VALID(lp, idx))
|
||||
uu_panic("uu_list_nearest_next(%p, %p): %s\n",
|
||||
(void *)lp, (void *)idx,
|
||||
INDEX_CHECK(idx)? "outdated index" :
|
||||
"invalid index");
|
||||
if (np->uln_prev == NULL)
|
||||
uu_panic("uu_list_nearest_next(%p, %p): out-of-date "
|
||||
"index\n", (void *)lp, (void *)idx);
|
||||
}
|
||||
|
||||
if (np == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
else
|
||||
return (NODE_TO_ELEM(lp, np));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)
|
||||
{
|
||||
uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
|
||||
|
||||
if (np == NULL)
|
||||
np = &lp->ul_null_node;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (!INDEX_VALID(lp, idx))
|
||||
uu_panic("uu_list_nearest_prev(%p, %p): %s\n",
|
||||
(void *)lp, (void *)idx, INDEX_CHECK(idx)?
|
||||
"outdated index" : "invalid index");
|
||||
if (np->uln_prev == NULL)
|
||||
uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "
|
||||
"index\n", (void *)lp, (void *)idx);
|
||||
}
|
||||
|
||||
if ((np = np->uln_prev) == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
else
|
||||
return (NODE_TO_ELEM(lp, np));
|
||||
}
|
||||
|
||||
static void
|
||||
list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)
|
||||
{
|
||||
uu_list_walk_t *next, *prev;
|
||||
|
||||
int robust = (flags & UU_WALK_ROBUST);
|
||||
int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
|
||||
|
||||
(void) memset(wp, 0, sizeof (*wp));
|
||||
wp->ulw_list = lp;
|
||||
wp->ulw_robust = robust;
|
||||
wp->ulw_dir = direction;
|
||||
if (direction > 0)
|
||||
wp->ulw_next_result = lp->ul_null_node.uln_next;
|
||||
else
|
||||
wp->ulw_next_result = lp->ul_null_node.uln_prev;
|
||||
|
||||
if (lp->ul_debug || robust) {
|
||||
/*
|
||||
* Add this walker to the list's list of walkers so
|
||||
* uu_list_remove() can advance us if somebody tries to
|
||||
* remove ulw_next_result.
|
||||
*/
|
||||
wp->ulw_next = next = &lp->ul_null_walk;
|
||||
wp->ulw_prev = prev = next->ulw_prev;
|
||||
next->ulw_prev = wp;
|
||||
prev->ulw_next = wp;
|
||||
}
|
||||
}
|
||||
|
||||
static uu_list_node_impl_t *
|
||||
list_walk_advance(uu_list_walk_t *wp, uu_list_t *lp)
|
||||
{
|
||||
uu_list_node_impl_t *np = wp->ulw_next_result;
|
||||
uu_list_node_impl_t *next;
|
||||
|
||||
if (np == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
|
||||
next = (wp->ulw_dir > 0)? np->uln_next : np->uln_prev;
|
||||
|
||||
wp->ulw_next_result = next;
|
||||
return (np);
|
||||
}
|
||||
|
||||
static void
|
||||
list_walk_fini(uu_list_walk_t *wp)
|
||||
{
|
||||
/* GLXXX debugging? */
|
||||
if (wp->ulw_next != NULL) {
|
||||
wp->ulw_next->ulw_prev = wp->ulw_prev;
|
||||
wp->ulw_prev->ulw_next = wp->ulw_next;
|
||||
wp->ulw_next = NULL;
|
||||
wp->ulw_prev = NULL;
|
||||
}
|
||||
wp->ulw_list = NULL;
|
||||
wp->ulw_next_result = NULL;
|
||||
}
|
||||
|
||||
uu_list_walk_t *
|
||||
uu_list_walk_start(uu_list_t *lp, uint32_t flags)
|
||||
{
|
||||
uu_list_walk_t *wp;
|
||||
|
||||
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
wp = uu_zalloc(sizeof (*wp));
|
||||
if (wp == NULL) {
|
||||
uu_set_error(UU_ERROR_NO_MEMORY);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
list_walk_init(wp, lp, flags);
|
||||
return (wp);
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_walk_next(uu_list_walk_t *wp)
|
||||
{
|
||||
uu_list_t *lp = wp->ulw_list;
|
||||
uu_list_node_impl_t *np = list_walk_advance(wp, lp);
|
||||
|
||||
if (np == NULL)
|
||||
return (NULL);
|
||||
|
||||
return (NODE_TO_ELEM(lp, np));
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_walk_end(uu_list_walk_t *wp)
|
||||
{
|
||||
list_walk_fini(wp);
|
||||
uu_free(wp);
|
||||
}
|
||||
|
||||
int
|
||||
uu_list_walk(uu_list_t *lp, uu_walk_fn_t *func, void *private, uint32_t flags)
|
||||
{
|
||||
uu_list_node_impl_t *np;
|
||||
|
||||
int status = UU_WALK_NEXT;
|
||||
|
||||
int robust = (flags & UU_WALK_ROBUST);
|
||||
int reverse = (flags & UU_WALK_REVERSE);
|
||||
|
||||
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
|
||||
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (lp->ul_debug || robust) {
|
||||
uu_list_walk_t my_walk;
|
||||
void *e;
|
||||
|
||||
list_walk_init(&my_walk, lp, flags);
|
||||
while (status == UU_WALK_NEXT &&
|
||||
(e = uu_list_walk_next(&my_walk)) != NULL)
|
||||
status = (*func)(e, private);
|
||||
list_walk_fini(&my_walk);
|
||||
} else {
|
||||
if (!reverse) {
|
||||
for (np = lp->ul_null_node.uln_next;
|
||||
status == UU_WALK_NEXT && np != &lp->ul_null_node;
|
||||
np = np->uln_next) {
|
||||
status = (*func)(NODE_TO_ELEM(lp, np), private);
|
||||
}
|
||||
} else {
|
||||
for (np = lp->ul_null_node.uln_prev;
|
||||
status == UU_WALK_NEXT && np != &lp->ul_null_node;
|
||||
np = np->uln_prev) {
|
||||
status = (*func)(NODE_TO_ELEM(lp, np), private);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status >= 0)
|
||||
return (0);
|
||||
uu_set_error(UU_ERROR_CALLBACK_FAILED);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_remove(uu_list_t *lp, void *elem)
|
||||
{
|
||||
uu_list_node_impl_t *np = ELEM_TO_NODE(lp, elem);
|
||||
uu_list_walk_t *wp;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (np->uln_prev == NULL)
|
||||
uu_panic("uu_list_remove(%p, %p): elem not on list\n",
|
||||
(void *)lp, elem);
|
||||
/*
|
||||
* invalidate outstanding uu_list_index_ts.
|
||||
*/
|
||||
lp->ul_index = INDEX_NEXT(lp->ul_index);
|
||||
}
|
||||
|
||||
/*
|
||||
* robust walkers must be advanced. In debug mode, non-robust
|
||||
* walkers are also on the list. If there are any, it's an error.
|
||||
*/
|
||||
for (wp = lp->ul_null_walk.ulw_next; wp != &lp->ul_null_walk;
|
||||
wp = wp->ulw_next) {
|
||||
if (wp->ulw_robust) {
|
||||
if (np == wp->ulw_next_result)
|
||||
(void) list_walk_advance(wp, lp);
|
||||
} else if (wp->ulw_next_result != NULL) {
|
||||
uu_panic("uu_list_remove(%p, %p): active non-robust "
|
||||
"walker\n", (void *)lp, elem);
|
||||
}
|
||||
}
|
||||
|
||||
np->uln_next->uln_prev = np->uln_prev;
|
||||
np->uln_prev->uln_next = np->uln_next;
|
||||
|
||||
lp->ul_numnodes--;
|
||||
|
||||
np->uln_next = POOL_TO_MARKER(lp->ul_pool);
|
||||
np->uln_prev = NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_teardown(uu_list_t *lp, void **cookie)
|
||||
{
|
||||
void *ep;
|
||||
|
||||
/*
|
||||
* XXX: disable list modification until list is empty
|
||||
*/
|
||||
if (lp->ul_debug && *cookie != NULL)
|
||||
uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n",
|
||||
(void *)lp, (void *)cookie);
|
||||
|
||||
ep = uu_list_first(lp);
|
||||
if (ep)
|
||||
uu_list_remove(lp, ep);
|
||||
return (ep);
|
||||
}
|
||||
|
||||
int
|
||||
uu_list_insert_before(uu_list_t *lp, void *target, void *elem)
|
||||
{
|
||||
uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
|
||||
|
||||
if (target == NULL)
|
||||
np = &lp->ul_null_node;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (np->uln_prev == NULL)
|
||||
uu_panic("uu_list_insert_before(%p, %p, %p): %p is "
|
||||
"not currently on a list\n",
|
||||
(void *)lp, target, elem, target);
|
||||
}
|
||||
if (lp->ul_sorted) {
|
||||
if (lp->ul_debug)
|
||||
uu_panic("uu_list_insert_before(%p, ...): list is "
|
||||
"UU_LIST_SORTED\n", (void *)lp);
|
||||
uu_set_error(UU_ERROR_NOT_SUPPORTED);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
uu_list_insert_after(uu_list_t *lp, void *target, void *elem)
|
||||
{
|
||||
uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
|
||||
|
||||
if (target == NULL)
|
||||
np = &lp->ul_null_node;
|
||||
|
||||
if (lp->ul_debug) {
|
||||
if (np->uln_prev == NULL)
|
||||
uu_panic("uu_list_insert_after(%p, %p, %p): %p is "
|
||||
"not currently on a list\n",
|
||||
(void *)lp, target, elem, target);
|
||||
}
|
||||
if (lp->ul_sorted) {
|
||||
if (lp->ul_debug)
|
||||
uu_panic("uu_list_insert_after(%p, ...): list is "
|
||||
"UU_LIST_SORTED\n", (void *)lp);
|
||||
uu_set_error(UU_ERROR_NOT_SUPPORTED);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);
|
||||
return (0);
|
||||
}
|
||||
|
||||
size_t
|
||||
uu_list_numnodes(uu_list_t *lp)
|
||||
{
|
||||
return (lp->ul_numnodes);
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_first(uu_list_t *lp)
|
||||
{
|
||||
uu_list_node_impl_t *n = lp->ul_null_node.uln_next;
|
||||
if (n == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
return (NODE_TO_ELEM(lp, n));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_last(uu_list_t *lp)
|
||||
{
|
||||
uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;
|
||||
if (n == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
return (NODE_TO_ELEM(lp, n));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_next(uu_list_t *lp, void *elem)
|
||||
{
|
||||
uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
|
||||
|
||||
n = n->uln_next;
|
||||
if (n == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
return (NODE_TO_ELEM(lp, n));
|
||||
}
|
||||
|
||||
void *
|
||||
uu_list_prev(uu_list_t *lp, void *elem)
|
||||
{
|
||||
uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
|
||||
|
||||
n = n->uln_prev;
|
||||
if (n == &lp->ul_null_node)
|
||||
return (NULL);
|
||||
return (NODE_TO_ELEM(lp, n));
|
||||
}
|
||||
|
||||
/*
|
||||
* called from uu_lockup() and uu_release(), as part of our fork1()-safety.
|
||||
*/
|
||||
void
|
||||
uu_list_lockup(void)
|
||||
{
|
||||
uu_list_pool_t *pp;
|
||||
|
||||
(void) pthread_mutex_lock(&uu_lpool_list_lock);
|
||||
for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
|
||||
pp = pp->ulp_next)
|
||||
(void) pthread_mutex_lock(&pp->ulp_lock);
|
||||
}
|
||||
|
||||
void
|
||||
uu_list_release(void)
|
||||
{
|
||||
uu_list_pool_t *pp;
|
||||
|
||||
for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
|
||||
pp = pp->ulp_next)
|
||||
(void) pthread_mutex_unlock(&pp->ulp_lock);
|
||||
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libintl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/debug.h>
|
||||
#include <thread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(TEXT_DOMAIN)
|
||||
#define TEXT_DOMAIN "SYS_TEST"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* All of the old code under !defined(PTHREAD_ONCE_KEY_NP)
|
||||
* is here to enable the building of a native version of
|
||||
* libuutil.so when the build machine has not yet been upgraded
|
||||
* to a version of libc that provides pthread_key_create_once_np().
|
||||
* It should all be deleted when solaris_nevada ships.
|
||||
* The code is not MT-safe in a relaxed memory model.
|
||||
*/
|
||||
|
||||
#if defined(PTHREAD_ONCE_KEY_NP)
|
||||
static pthread_key_t uu_error_key = PTHREAD_ONCE_KEY_NP;
|
||||
#else /* PTHREAD_ONCE_KEY_NP */
|
||||
static pthread_key_t uu_error_key = 0;
|
||||
static pthread_mutex_t uu_key_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
#endif /* PTHREAD_ONCE_KEY_NP */
|
||||
|
||||
static int uu_error_key_setup = 0;
|
||||
|
||||
static pthread_mutex_t uu_panic_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
/* LINTED static unused */
|
||||
static const char *uu_panic_format;
|
||||
/* LINTED static unused */
|
||||
static va_list uu_panic_args;
|
||||
static pthread_t uu_panic_thread;
|
||||
|
||||
static uint32_t _uu_main_error;
|
||||
|
||||
void
|
||||
uu_set_error(uint_t code)
|
||||
{
|
||||
if (thr_main() != 0) {
|
||||
_uu_main_error = code;
|
||||
return;
|
||||
}
|
||||
#if defined(PTHREAD_ONCE_KEY_NP)
|
||||
if (pthread_key_create_once_np(&uu_error_key, NULL) != 0)
|
||||
uu_error_key_setup = -1;
|
||||
else
|
||||
uu_error_key_setup = 1;
|
||||
#else /* PTHREAD_ONCE_KEY_NP */
|
||||
if (uu_error_key_setup == 0) {
|
||||
(void) pthread_mutex_lock(&uu_key_lock);
|
||||
if (uu_error_key_setup == 0) {
|
||||
if (pthread_key_create(&uu_error_key, NULL) != 0)
|
||||
uu_error_key_setup = -1;
|
||||
else
|
||||
uu_error_key_setup = 1;
|
||||
}
|
||||
(void) pthread_mutex_unlock(&uu_key_lock);
|
||||
}
|
||||
#endif /* PTHREAD_ONCE_KEY_NP */
|
||||
if (uu_error_key_setup > 0)
|
||||
(void) pthread_setspecific(uu_error_key,
|
||||
(void *)(uintptr_t)code);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
uu_error(void)
|
||||
{
|
||||
if (thr_main() != 0)
|
||||
return (_uu_main_error);
|
||||
|
||||
if (uu_error_key_setup < 0) /* can't happen? */
|
||||
return (UU_ERROR_UNKNOWN);
|
||||
|
||||
/*
|
||||
* Because UU_ERROR_NONE == 0, if uu_set_error() was
|
||||
* never called, then this will return UU_ERROR_NONE:
|
||||
*/
|
||||
return ((uint32_t)(uintptr_t)pthread_getspecific(uu_error_key));
|
||||
}
|
||||
|
||||
const char *
|
||||
uu_strerror(uint32_t code)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
switch (code) {
|
||||
case UU_ERROR_NONE:
|
||||
str = dgettext(TEXT_DOMAIN, "No error");
|
||||
break;
|
||||
|
||||
case UU_ERROR_INVALID_ARGUMENT:
|
||||
str = dgettext(TEXT_DOMAIN, "Invalid argument");
|
||||
break;
|
||||
|
||||
case UU_ERROR_UNKNOWN_FLAG:
|
||||
str = dgettext(TEXT_DOMAIN, "Unknown flag passed");
|
||||
break;
|
||||
|
||||
case UU_ERROR_NO_MEMORY:
|
||||
str = dgettext(TEXT_DOMAIN, "Out of memory");
|
||||
break;
|
||||
|
||||
case UU_ERROR_CALLBACK_FAILED:
|
||||
str = dgettext(TEXT_DOMAIN, "Callback-initiated failure");
|
||||
break;
|
||||
|
||||
case UU_ERROR_NOT_SUPPORTED:
|
||||
str = dgettext(TEXT_DOMAIN, "Operation not supported");
|
||||
break;
|
||||
|
||||
case UU_ERROR_EMPTY:
|
||||
str = dgettext(TEXT_DOMAIN, "No value provided");
|
||||
break;
|
||||
|
||||
case UU_ERROR_UNDERFLOW:
|
||||
str = dgettext(TEXT_DOMAIN, "Value too small");
|
||||
break;
|
||||
|
||||
case UU_ERROR_OVERFLOW:
|
||||
str = dgettext(TEXT_DOMAIN, "Value too large");
|
||||
break;
|
||||
|
||||
case UU_ERROR_INVALID_CHAR:
|
||||
str = dgettext(TEXT_DOMAIN,
|
||||
"Value contains unexpected character");
|
||||
break;
|
||||
|
||||
case UU_ERROR_INVALID_DIGIT:
|
||||
str = dgettext(TEXT_DOMAIN,
|
||||
"Value contains digit not in base");
|
||||
break;
|
||||
|
||||
case UU_ERROR_SYSTEM:
|
||||
str = dgettext(TEXT_DOMAIN, "Underlying system error");
|
||||
break;
|
||||
|
||||
case UU_ERROR_UNKNOWN:
|
||||
str = dgettext(TEXT_DOMAIN, "Error status not known");
|
||||
break;
|
||||
|
||||
default:
|
||||
errno = ESRCH;
|
||||
str = NULL;
|
||||
break;
|
||||
}
|
||||
return (str);
|
||||
}
|
||||
|
||||
void
|
||||
uu_panic(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
|
||||
(void) pthread_mutex_lock(&uu_panic_lock);
|
||||
if (uu_panic_thread == 0) {
|
||||
uu_panic_thread = pthread_self();
|
||||
uu_panic_format = format;
|
||||
va_copy(uu_panic_args, args);
|
||||
}
|
||||
(void) pthread_mutex_unlock(&uu_panic_lock);
|
||||
|
||||
(void) vfprintf(stderr, format, args);
|
||||
|
||||
if (uu_panic_thread == pthread_self())
|
||||
abort();
|
||||
else
|
||||
for (;;)
|
||||
(void) pause();
|
||||
}
|
||||
|
||||
int
|
||||
assfail(const char *astring, const char *file, int line)
|
||||
{
|
||||
__assert(astring, file, line);
|
||||
/*NOTREACHED*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
uu_lockup(void)
|
||||
{
|
||||
(void) pthread_mutex_lock(&uu_panic_lock);
|
||||
#if !defined(PTHREAD_ONCE_KEY_NP)
|
||||
(void) pthread_mutex_lock(&uu_key_lock);
|
||||
#endif
|
||||
uu_avl_lockup();
|
||||
uu_list_lockup();
|
||||
}
|
||||
|
||||
static void
|
||||
uu_release(void)
|
||||
{
|
||||
(void) pthread_mutex_unlock(&uu_panic_lock);
|
||||
#if !defined(PTHREAD_ONCE_KEY_NP)
|
||||
(void) pthread_mutex_unlock(&uu_key_lock);
|
||||
#endif
|
||||
uu_avl_release();
|
||||
uu_list_release();
|
||||
}
|
||||
|
||||
static void
|
||||
uu_release_child(void)
|
||||
{
|
||||
uu_panic_format = NULL;
|
||||
uu_panic_thread = 0;
|
||||
|
||||
uu_release();
|
||||
}
|
||||
|
||||
#pragma init(uu_init)
|
||||
static void
|
||||
uu_init(void)
|
||||
{
|
||||
(void) pthread_atfork(uu_lockup, uu_release, uu_release_child);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _LP64
|
||||
#define TMPPATHFMT "%s/uu%ld"
|
||||
#else /* _LP64 */
|
||||
#define TMPPATHFMT "%s/uu%lld"
|
||||
#endif /* _LP64 */
|
||||
|
||||
/*ARGSUSED*/
|
||||
int
|
||||
uu_open_tmp(const char *dir, uint_t uflags)
|
||||
{
|
||||
int f;
|
||||
char *fname = uu_zalloc(PATH_MAX);
|
||||
|
||||
if (fname == NULL)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
(void) snprintf(fname, PATH_MAX, "%s/uu%lld", dir, gethrtime());
|
||||
|
||||
f = open(fname, O_CREAT | O_EXCL | O_RDWR, 0600);
|
||||
|
||||
if (f >= 0 || errno != EEXIST)
|
||||
break;
|
||||
}
|
||||
|
||||
if (f >= 0)
|
||||
(void) unlink(fname);
|
||||
|
||||
uu_free(fname);
|
||||
|
||||
return (f);
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <libintl.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char PNAME_FMT[] = "%s: ";
|
||||
static const char ERRNO_FMT[] = ": %s\n";
|
||||
|
||||
static const char *pname;
|
||||
|
||||
static void
|
||||
uu_die_internal(int status, const char *format, va_list alist) __NORETURN;
|
||||
|
||||
int uu_exit_ok_value = EXIT_SUCCESS;
|
||||
int uu_exit_fatal_value = EXIT_FAILURE;
|
||||
int uu_exit_usage_value = 2;
|
||||
|
||||
int *
|
||||
uu_exit_ok(void)
|
||||
{
|
||||
return (&uu_exit_ok_value);
|
||||
}
|
||||
|
||||
int *
|
||||
uu_exit_fatal(void)
|
||||
{
|
||||
return (&uu_exit_fatal_value);
|
||||
}
|
||||
|
||||
int *
|
||||
uu_exit_usage(void)
|
||||
{
|
||||
return (&uu_exit_usage_value);
|
||||
}
|
||||
|
||||
void
|
||||
uu_alt_exit(int profile)
|
||||
{
|
||||
switch (profile) {
|
||||
case UU_PROFILE_DEFAULT:
|
||||
uu_exit_ok_value = EXIT_SUCCESS;
|
||||
uu_exit_fatal_value = EXIT_FAILURE;
|
||||
uu_exit_usage_value = 2;
|
||||
break;
|
||||
case UU_PROFILE_LAUNCHER:
|
||||
uu_exit_ok_value = EXIT_SUCCESS;
|
||||
uu_exit_fatal_value = 124;
|
||||
uu_exit_usage_value = 125;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
uu_warn_internal(int err, const char *format, va_list alist)
|
||||
{
|
||||
if (pname != NULL)
|
||||
(void) fprintf(stderr, PNAME_FMT, pname);
|
||||
|
||||
(void) vfprintf(stderr, format, alist);
|
||||
|
||||
if (strrchr(format, '\n') == NULL)
|
||||
(void) fprintf(stderr, ERRNO_FMT, strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
uu_vwarn(const char *format, va_list alist)
|
||||
{
|
||||
uu_warn_internal(errno, format, alist);
|
||||
}
|
||||
|
||||
/*PRINTFLIKE1*/
|
||||
void
|
||||
uu_warn(const char *format, ...)
|
||||
{
|
||||
va_list alist;
|
||||
va_start(alist, format);
|
||||
uu_warn_internal(errno, format, alist);
|
||||
va_end(alist);
|
||||
}
|
||||
|
||||
static void
|
||||
uu_die_internal(int status, const char *format, va_list alist)
|
||||
{
|
||||
uu_warn_internal(errno, format, alist);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
char *cp;
|
||||
|
||||
if (!issetugid()) {
|
||||
cp = getenv("UU_DIE_ABORTS");
|
||||
if (cp != NULL && *cp != '\0')
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
exit(status);
|
||||
}
|
||||
|
||||
void
|
||||
uu_vdie(const char *format, va_list alist)
|
||||
{
|
||||
uu_die_internal(UU_EXIT_FATAL, format, alist);
|
||||
}
|
||||
|
||||
/*PRINTFLIKE1*/
|
||||
void
|
||||
uu_die(const char *format, ...)
|
||||
{
|
||||
va_list alist;
|
||||
va_start(alist, format);
|
||||
uu_die_internal(UU_EXIT_FATAL, format, alist);
|
||||
va_end(alist);
|
||||
}
|
||||
|
||||
void
|
||||
uu_vxdie(int status, const char *format, va_list alist)
|
||||
{
|
||||
uu_die_internal(status, format, alist);
|
||||
}
|
||||
|
||||
/*PRINTFLIKE2*/
|
||||
void
|
||||
uu_xdie(int status, const char *format, ...)
|
||||
{
|
||||
va_list alist;
|
||||
va_start(alist, format);
|
||||
uu_die_internal(status, format, alist);
|
||||
va_end(alist);
|
||||
}
|
||||
|
||||
const char *
|
||||
uu_setpname(char *arg0)
|
||||
{
|
||||
/*
|
||||
* Having a NULL argv[0], while uncommon, is possible. It
|
||||
* makes more sense to handle this event in uu_setpname rather
|
||||
* than in each of its consumers.
|
||||
*/
|
||||
if (arg0 == NULL) {
|
||||
pname = getexecname();
|
||||
if (pname == NULL)
|
||||
pname = "unknown_command";
|
||||
return (pname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guard against '/' at end of command invocation.
|
||||
*/
|
||||
for (;;) {
|
||||
char *p = strrchr(arg0, '/');
|
||||
if (p == NULL) {
|
||||
pname = arg0;
|
||||
break;
|
||||
} else {
|
||||
if (*(p + 1) == '\0') {
|
||||
*p = '\0';
|
||||
continue;
|
||||
}
|
||||
|
||||
pname = p + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (pname);
|
||||
}
|
||||
|
||||
const char *
|
||||
uu_getpname(void)
|
||||
{
|
||||
return (pname);
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define MAX_BASE 36
|
||||
|
||||
#define IS_DIGIT(x) ((x) >= '0' && (x) <= '9')
|
||||
|
||||
#define CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \
|
||||
((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A')
|
||||
|
||||
static int
|
||||
strtoint(const char *s_arg, uint64_t *out, uint32_t base, int sign)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char *)s_arg;
|
||||
|
||||
uint64_t val = 0;
|
||||
uint64_t multmax;
|
||||
|
||||
unsigned c, i;
|
||||
|
||||
int neg = 0;
|
||||
|
||||
int bad_digit = 0;
|
||||
int bad_char = 0;
|
||||
int overflow = 0;
|
||||
|
||||
if (s == NULL || base == 1 || base > MAX_BASE) {
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
while ((c = *s) != 0 && isspace(c))
|
||||
s++;
|
||||
|
||||
switch (c) {
|
||||
case '-':
|
||||
if (!sign)
|
||||
overflow = 1; /* becomes underflow below */
|
||||
neg = 1;
|
||||
/*FALLTHRU*/
|
||||
case '+':
|
||||
c = *++s;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '\0') {
|
||||
uu_set_error(UU_ERROR_EMPTY);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (base == 0) {
|
||||
if (c != '0')
|
||||
base = 10;
|
||||
else if (s[1] == 'x' || s[1] == 'X')
|
||||
base = 16;
|
||||
else
|
||||
base = 8;
|
||||
}
|
||||
|
||||
if (base == 16 && c == '0' && (s[1] == 'x' || s[1] == 'X'))
|
||||
c = *(s += 2);
|
||||
|
||||
if ((val = CTOI(c)) >= base) {
|
||||
if (IS_DIGIT(c))
|
||||
bad_digit = 1;
|
||||
else
|
||||
bad_char = 1;
|
||||
val = 0;
|
||||
}
|
||||
|
||||
multmax = (uint64_t)UINT64_MAX / (uint64_t)base;
|
||||
|
||||
for (c = *++s; c != '\0'; c = *++s) {
|
||||
if ((i = CTOI(c)) >= base) {
|
||||
if (isspace(c))
|
||||
break;
|
||||
if (IS_DIGIT(c))
|
||||
bad_digit = 1;
|
||||
else
|
||||
bad_char = 1;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (val > multmax)
|
||||
overflow = 1;
|
||||
|
||||
val *= base;
|
||||
if ((uint64_t)UINT64_MAX - val < (uint64_t)i)
|
||||
overflow = 1;
|
||||
|
||||
val += i;
|
||||
}
|
||||
|
||||
while ((c = *s) != 0) {
|
||||
if (!isspace(c))
|
||||
bad_char = 1;
|
||||
s++;
|
||||
}
|
||||
|
||||
if (sign) {
|
||||
if (neg) {
|
||||
if (val > -(uint64_t)INT64_MIN)
|
||||
overflow = 1;
|
||||
} else {
|
||||
if (val > INT64_MAX)
|
||||
overflow = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (neg)
|
||||
val = -val;
|
||||
|
||||
if (bad_char | bad_digit | overflow) {
|
||||
if (bad_char)
|
||||
uu_set_error(UU_ERROR_INVALID_CHAR);
|
||||
else if (bad_digit)
|
||||
uu_set_error(UU_ERROR_INVALID_DIGIT);
|
||||
else if (overflow) {
|
||||
if (neg)
|
||||
uu_set_error(UU_ERROR_UNDERFLOW);
|
||||
else
|
||||
uu_set_error(UU_ERROR_OVERFLOW);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*out = val;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
uu_strtoint(const char *s, void *v, size_t sz, int base,
|
||||
int64_t min, int64_t max)
|
||||
{
|
||||
uint64_t val_u;
|
||||
int64_t val;
|
||||
|
||||
if (min > max)
|
||||
goto bad_argument;
|
||||
|
||||
switch (sz) {
|
||||
case 1:
|
||||
if (max > INT8_MAX || min < INT8_MIN)
|
||||
goto bad_argument;
|
||||
break;
|
||||
case 2:
|
||||
if (max > INT16_MAX || min < INT16_MIN)
|
||||
goto bad_argument;
|
||||
break;
|
||||
case 4:
|
||||
if (max > INT32_MAX || min < INT32_MIN)
|
||||
goto bad_argument;
|
||||
break;
|
||||
case 8:
|
||||
if (max > INT64_MAX || min < INT64_MIN)
|
||||
goto bad_argument;
|
||||
break;
|
||||
default:
|
||||
goto bad_argument;
|
||||
}
|
||||
|
||||
if (min == 0 && max == 0) {
|
||||
min = -(1ULL << (8 * sz - 1));
|
||||
max = (1ULL << (8 * sz - 1)) - 1;
|
||||
}
|
||||
|
||||
if (strtoint(s, &val_u, base, 1) == -1)
|
||||
return (-1);
|
||||
|
||||
val = (int64_t)val_u;
|
||||
|
||||
if (val < min) {
|
||||
uu_set_error(UU_ERROR_UNDERFLOW);
|
||||
return (-1);
|
||||
} else if (val > max) {
|
||||
uu_set_error(UU_ERROR_OVERFLOW);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
switch (sz) {
|
||||
case 1:
|
||||
*(int8_t *)v = val;
|
||||
return (0);
|
||||
case 2:
|
||||
*(int16_t *)v = val;
|
||||
return (0);
|
||||
case 4:
|
||||
*(int32_t *)v = val;
|
||||
return (0);
|
||||
case 8:
|
||||
*(int64_t *)v = val;
|
||||
return (0);
|
||||
default:
|
||||
break; /* fall through to bad_argument */
|
||||
}
|
||||
|
||||
bad_argument:
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
uu_strtouint(const char *s, void *v, size_t sz, int base,
|
||||
uint64_t min, uint64_t max)
|
||||
{
|
||||
uint64_t val;
|
||||
|
||||
if (min > max)
|
||||
goto bad_argument;
|
||||
|
||||
switch (sz) {
|
||||
case 1:
|
||||
if (max > UINT8_MAX)
|
||||
goto bad_argument;
|
||||
break;
|
||||
case 2:
|
||||
if (max > UINT16_MAX)
|
||||
goto bad_argument;
|
||||
break;
|
||||
case 4:
|
||||
if (max > UINT32_MAX)
|
||||
goto bad_argument;
|
||||
break;
|
||||
case 8:
|
||||
if (max > UINT64_MAX)
|
||||
goto bad_argument;
|
||||
break;
|
||||
default:
|
||||
goto bad_argument;
|
||||
}
|
||||
|
||||
if (min == 0 && max == 0) {
|
||||
/* we have to be careful, since << can overflow */
|
||||
max = (1ULL << (8 * sz - 1)) * 2 - 1;
|
||||
}
|
||||
|
||||
if (strtoint(s, &val, base, 0) == -1)
|
||||
return (-1);
|
||||
|
||||
if (val < min) {
|
||||
uu_set_error(UU_ERROR_UNDERFLOW);
|
||||
return (-1);
|
||||
} else if (val > max) {
|
||||
uu_set_error(UU_ERROR_OVERFLOW);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
switch (sz) {
|
||||
case 1:
|
||||
*(uint8_t *)v = val;
|
||||
return (0);
|
||||
case 2:
|
||||
*(uint16_t *)v = val;
|
||||
return (0);
|
||||
case 4:
|
||||
*(uint32_t *)v = val;
|
||||
return (0);
|
||||
case 8:
|
||||
*(uint64_t *)v = val;
|
||||
return (0);
|
||||
default:
|
||||
break; /* shouldn't happen, fall through */
|
||||
}
|
||||
|
||||
bad_argument:
|
||||
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
|
||||
return (-1);
|
||||
}
|
|
@ -0,0 +1,570 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZFS_H
|
||||
#define _LIBZFS_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <libnvpair.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/varargs.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/avl.h>
|
||||
#include <ucred.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Miscellaneous ZFS constants
|
||||
*/
|
||||
#define ZFS_MAXNAMELEN MAXNAMELEN
|
||||
#define ZPOOL_MAXNAMELEN MAXNAMELEN
|
||||
#define ZFS_MAXPROPLEN MAXPATHLEN
|
||||
#define ZPOOL_MAXPROPLEN MAXPATHLEN
|
||||
|
||||
/*
|
||||
* libzfs errors
|
||||
*/
|
||||
enum {
|
||||
EZFS_NOMEM = 2000, /* out of memory */
|
||||
EZFS_BADPROP, /* invalid property value */
|
||||
EZFS_PROPREADONLY, /* cannot set readonly property */
|
||||
EZFS_PROPTYPE, /* property does not apply to dataset type */
|
||||
EZFS_PROPNONINHERIT, /* property is not inheritable */
|
||||
EZFS_PROPSPACE, /* bad quota or reservation */
|
||||
EZFS_BADTYPE, /* dataset is not of appropriate type */
|
||||
EZFS_BUSY, /* pool or dataset is busy */
|
||||
EZFS_EXISTS, /* pool or dataset already exists */
|
||||
EZFS_NOENT, /* no such pool or dataset */
|
||||
EZFS_BADSTREAM, /* bad backup stream */
|
||||
EZFS_DSREADONLY, /* dataset is readonly */
|
||||
EZFS_VOLTOOBIG, /* volume is too large for 32-bit system */
|
||||
EZFS_VOLHASDATA, /* volume already contains data */
|
||||
EZFS_INVALIDNAME, /* invalid dataset name */
|
||||
EZFS_BADRESTORE, /* unable to restore to destination */
|
||||
EZFS_BADBACKUP, /* backup failed */
|
||||
EZFS_BADTARGET, /* bad attach/detach/replace target */
|
||||
EZFS_NODEVICE, /* no such device in pool */
|
||||
EZFS_BADDEV, /* invalid device to add */
|
||||
EZFS_NOREPLICAS, /* no valid replicas */
|
||||
EZFS_RESILVERING, /* currently resilvering */
|
||||
EZFS_BADVERSION, /* unsupported version */
|
||||
EZFS_POOLUNAVAIL, /* pool is currently unavailable */
|
||||
EZFS_DEVOVERFLOW, /* too many devices in one vdev */
|
||||
EZFS_BADPATH, /* must be an absolute path */
|
||||
EZFS_CROSSTARGET, /* rename or clone across pool or dataset */
|
||||
EZFS_ZONED, /* used improperly in local zone */
|
||||
EZFS_MOUNTFAILED, /* failed to mount dataset */
|
||||
EZFS_UMOUNTFAILED, /* failed to unmount dataset */
|
||||
EZFS_UNSHARENFSFAILED, /* unshare(1M) failed */
|
||||
EZFS_SHARENFSFAILED, /* share(1M) failed */
|
||||
EZFS_DEVLINKS, /* failed to create zvol links */
|
||||
EZFS_PERM, /* permission denied */
|
||||
EZFS_NOSPC, /* out of space */
|
||||
EZFS_IO, /* I/O error */
|
||||
EZFS_INTR, /* signal received */
|
||||
EZFS_ISSPARE, /* device is a hot spare */
|
||||
EZFS_INVALCONFIG, /* invalid vdev configuration */
|
||||
EZFS_RECURSIVE, /* recursive dependency */
|
||||
EZFS_NOHISTORY, /* no history object */
|
||||
EZFS_UNSHAREISCSIFAILED, /* iscsitgtd failed request to unshare */
|
||||
EZFS_SHAREISCSIFAILED, /* iscsitgtd failed request to share */
|
||||
EZFS_POOLPROPS, /* couldn't retrieve pool props */
|
||||
EZFS_POOL_NOTSUP, /* ops not supported for this type of pool */
|
||||
EZFS_POOL_INVALARG, /* invalid argument for this pool operation */
|
||||
EZFS_NAMETOOLONG, /* dataset name is too long */
|
||||
EZFS_OPENFAILED, /* open of device failed */
|
||||
EZFS_NOCAP, /* couldn't get capacity */
|
||||
EZFS_LABELFAILED, /* write of label failed */
|
||||
EZFS_ISCSISVCUNAVAIL, /* iscsi service unavailable */
|
||||
EZFS_BADWHO, /* invalid permission who */
|
||||
EZFS_BADPERM, /* invalid permission */
|
||||
EZFS_BADPERMSET, /* invalid permission set name */
|
||||
EZFS_NODELEGATION, /* delegated administration is disabled */
|
||||
EZFS_PERMRDONLY, /* pemissions are readonly */
|
||||
EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
|
||||
EZFS_SHARESMBFAILED, /* failed to share over smb */
|
||||
EZFS_BADCACHE, /* bad cache file */
|
||||
EZFS_ISL2CACHE, /* device is for the level 2 ARC */
|
||||
EZFS_VDEVNOTSUP, /* unsupported vdev type */
|
||||
EZFS_NOTSUP, /* ops not supported on this dataset */
|
||||
EZFS_ACTIVE_SPARE, /* pool has active shared spare devices */
|
||||
EZFS_UNKNOWN
|
||||
};
|
||||
|
||||
/*
|
||||
* The following data structures are all part
|
||||
* of the zfs_allow_t data structure which is
|
||||
* used for printing 'allow' permissions.
|
||||
* It is a linked list of zfs_allow_t's which
|
||||
* then contain avl tree's for user/group/sets/...
|
||||
* and each one of the entries in those trees have
|
||||
* avl tree's for the permissions they belong to and
|
||||
* whether they are local,descendent or local+descendent
|
||||
* permissions. The AVL trees are used primarily for
|
||||
* sorting purposes, but also so that we can quickly find
|
||||
* a given user and or permission.
|
||||
*/
|
||||
typedef struct zfs_perm_node {
|
||||
avl_node_t z_node;
|
||||
char z_pname[MAXPATHLEN];
|
||||
} zfs_perm_node_t;
|
||||
|
||||
typedef struct zfs_allow_node {
|
||||
avl_node_t z_node;
|
||||
char z_key[MAXPATHLEN]; /* name, such as joe */
|
||||
avl_tree_t z_localdescend; /* local+descendent perms */
|
||||
avl_tree_t z_local; /* local permissions */
|
||||
avl_tree_t z_descend; /* descendent permissions */
|
||||
} zfs_allow_node_t;
|
||||
|
||||
typedef struct zfs_allow {
|
||||
struct zfs_allow *z_next;
|
||||
char z_setpoint[MAXPATHLEN];
|
||||
avl_tree_t z_sets;
|
||||
avl_tree_t z_crperms;
|
||||
avl_tree_t z_user;
|
||||
avl_tree_t z_group;
|
||||
avl_tree_t z_everyone;
|
||||
} zfs_allow_t;
|
||||
|
||||
/*
|
||||
* Basic handle types
|
||||
*/
|
||||
typedef struct zfs_handle zfs_handle_t;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
typedef struct libzfs_handle libzfs_handle_t;
|
||||
|
||||
/*
|
||||
* Library initialization
|
||||
*/
|
||||
extern libzfs_handle_t *libzfs_init(void);
|
||||
extern void libzfs_fini(libzfs_handle_t *);
|
||||
|
||||
extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *);
|
||||
extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
|
||||
|
||||
extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
|
||||
|
||||
extern int libzfs_errno(libzfs_handle_t *);
|
||||
extern const char *libzfs_error_action(libzfs_handle_t *);
|
||||
extern const char *libzfs_error_description(libzfs_handle_t *);
|
||||
|
||||
/*
|
||||
* Basic handle functions
|
||||
*/
|
||||
extern zpool_handle_t *zpool_open(libzfs_handle_t *, const char *);
|
||||
extern zpool_handle_t *zpool_open_canfail(libzfs_handle_t *, const char *);
|
||||
extern void zpool_close(zpool_handle_t *);
|
||||
extern const char *zpool_get_name(zpool_handle_t *);
|
||||
extern int zpool_get_state(zpool_handle_t *);
|
||||
extern char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
|
||||
extern void zpool_free_handles(libzfs_handle_t *);
|
||||
|
||||
/*
|
||||
* Iterate over all active pools in the system.
|
||||
*/
|
||||
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
|
||||
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
|
||||
|
||||
/*
|
||||
* Functions to create and destroy pools
|
||||
*/
|
||||
extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
|
||||
nvlist_t *, nvlist_t *);
|
||||
extern int zpool_destroy(zpool_handle_t *);
|
||||
extern int zpool_add(zpool_handle_t *, nvlist_t *);
|
||||
|
||||
/*
|
||||
* Functions to manipulate pool and vdev state
|
||||
*/
|
||||
extern int zpool_scrub(zpool_handle_t *, pool_scrub_type_t);
|
||||
extern int zpool_clear(zpool_handle_t *, const char *);
|
||||
|
||||
extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
|
||||
vdev_state_t *);
|
||||
extern int zpool_vdev_offline(zpool_handle_t *, const char *, boolean_t);
|
||||
extern int zpool_vdev_attach(zpool_handle_t *, const char *,
|
||||
const char *, nvlist_t *, int);
|
||||
extern int zpool_vdev_detach(zpool_handle_t *, const char *);
|
||||
extern int zpool_vdev_remove(zpool_handle_t *, const char *);
|
||||
|
||||
extern int zpool_vdev_fault(zpool_handle_t *, uint64_t);
|
||||
extern int zpool_vdev_degrade(zpool_handle_t *, uint64_t);
|
||||
extern int zpool_vdev_clear(zpool_handle_t *, uint64_t);
|
||||
|
||||
extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
|
||||
boolean_t *, boolean_t *);
|
||||
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
|
||||
|
||||
/*
|
||||
* Functions to manage pool properties
|
||||
*/
|
||||
extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
|
||||
extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
|
||||
size_t proplen, zprop_source_t *);
|
||||
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
|
||||
zprop_source_t *);
|
||||
|
||||
extern const char *zpool_prop_to_name(zpool_prop_t);
|
||||
extern const char *zpool_prop_values(zpool_prop_t);
|
||||
|
||||
/*
|
||||
* Pool health statistics.
|
||||
*/
|
||||
typedef enum {
|
||||
/*
|
||||
* The following correspond to faults as defined in the (fault.fs.zfs.*)
|
||||
* event namespace. Each is associated with a corresponding message ID.
|
||||
*/
|
||||
ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */
|
||||
ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */
|
||||
ZPOOL_STATUS_MISSING_DEV_NR, /* missing device with no replicas */
|
||||
ZPOOL_STATUS_CORRUPT_LABEL_R, /* bad device label with replicas */
|
||||
ZPOOL_STATUS_CORRUPT_LABEL_NR, /* bad device label with no replicas */
|
||||
ZPOOL_STATUS_BAD_GUID_SUM, /* sum of device guids didn't match */
|
||||
ZPOOL_STATUS_CORRUPT_POOL, /* pool metadata is corrupted */
|
||||
ZPOOL_STATUS_CORRUPT_DATA, /* data errors in user (meta)data */
|
||||
ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
|
||||
ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */
|
||||
ZPOOL_STATUS_HOSTID_MISMATCH, /* last accessed by another system */
|
||||
ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */
|
||||
ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
|
||||
ZPOOL_STATUS_FAULTED_DEV_R, /* faulted device with replicas */
|
||||
ZPOOL_STATUS_FAULTED_DEV_NR, /* faulted device with no replicas */
|
||||
ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
|
||||
|
||||
/*
|
||||
* The following are not faults per se, but still an error possibly
|
||||
* requiring administrative attention. There is no corresponding
|
||||
* message ID.
|
||||
*/
|
||||
ZPOOL_STATUS_VERSION_OLDER, /* older on-disk version */
|
||||
ZPOOL_STATUS_RESILVERING, /* device being resilvered */
|
||||
ZPOOL_STATUS_OFFLINE_DEV, /* device online */
|
||||
|
||||
/*
|
||||
* Finally, the following indicates a healthy pool.
|
||||
*/
|
||||
ZPOOL_STATUS_OK
|
||||
} zpool_status_t;
|
||||
|
||||
extern zpool_status_t zpool_get_status(zpool_handle_t *, char **);
|
||||
extern zpool_status_t zpool_import_status(nvlist_t *, char **);
|
||||
|
||||
/*
|
||||
* Statistics and configuration functions.
|
||||
*/
|
||||
extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
|
||||
extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
|
||||
extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
|
||||
|
||||
/*
|
||||
* Import and export functions
|
||||
*/
|
||||
extern int zpool_export(zpool_handle_t *, boolean_t);
|
||||
extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
|
||||
char *altroot);
|
||||
extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
|
||||
nvlist_t *, boolean_t);
|
||||
|
||||
/*
|
||||
* Search for pools to import
|
||||
*/
|
||||
extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
|
||||
extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *,
|
||||
char *, uint64_t);
|
||||
extern nvlist_t *zpool_find_import_byname(libzfs_handle_t *, int, char **,
|
||||
char *);
|
||||
extern nvlist_t *zpool_find_import_byguid(libzfs_handle_t *, int, char **,
|
||||
uint64_t);
|
||||
extern nvlist_t *zpool_find_import_activeok(libzfs_handle_t *, int, char **);
|
||||
|
||||
/*
|
||||
* Miscellaneous pool functions
|
||||
*/
|
||||
struct zfs_cmd;
|
||||
|
||||
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *);
|
||||
extern int zpool_upgrade(zpool_handle_t *, uint64_t);
|
||||
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
|
||||
extern void zpool_set_history_str(const char *subcommand, int argc,
|
||||
char **argv, char *history_str);
|
||||
extern int zpool_stage_history(libzfs_handle_t *, const char *);
|
||||
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
|
||||
size_t len);
|
||||
extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
|
||||
extern int zpool_get_physpath(zpool_handle_t *, char *);
|
||||
/*
|
||||
* Basic handle manipulations. These functions do not create or destroy the
|
||||
* underlying datasets, only the references to them.
|
||||
*/
|
||||
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
|
||||
extern void zfs_close(zfs_handle_t *);
|
||||
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
|
||||
extern const char *zfs_get_name(const zfs_handle_t *);
|
||||
extern zpool_handle_t *zfs_get_pool_handle(const zfs_handle_t *);
|
||||
|
||||
/*
|
||||
* Property management functions. Some functions are shared with the kernel,
|
||||
* and are found in sys/fs/zfs.h.
|
||||
*/
|
||||
|
||||
/*
|
||||
* zfs dataset property management
|
||||
*/
|
||||
extern const char *zfs_prop_default_string(zfs_prop_t);
|
||||
extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
|
||||
extern const char *zfs_prop_column_name(zfs_prop_t);
|
||||
extern boolean_t zfs_prop_align_right(zfs_prop_t);
|
||||
|
||||
extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t,
|
||||
nvlist_t *, uint64_t, zfs_handle_t *, const char *);
|
||||
|
||||
extern const char *zfs_prop_to_name(zfs_prop_t);
|
||||
extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
|
||||
extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
|
||||
zprop_source_t *, char *, size_t, boolean_t);
|
||||
extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
|
||||
zprop_source_t *, char *, size_t);
|
||||
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
|
||||
extern int zfs_prop_inherit(zfs_handle_t *, const char *);
|
||||
extern const char *zfs_prop_values(zfs_prop_t);
|
||||
extern int zfs_prop_is_string(zfs_prop_t prop);
|
||||
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
|
||||
|
||||
typedef struct zprop_list {
|
||||
int pl_prop;
|
||||
char *pl_user_prop;
|
||||
struct zprop_list *pl_next;
|
||||
boolean_t pl_all;
|
||||
size_t pl_width;
|
||||
boolean_t pl_fixed;
|
||||
} zprop_list_t;
|
||||
|
||||
extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **);
|
||||
|
||||
#define ZFS_MOUNTPOINT_NONE "none"
|
||||
#define ZFS_MOUNTPOINT_LEGACY "legacy"
|
||||
|
||||
/*
|
||||
* zpool property management
|
||||
*/
|
||||
extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
|
||||
extern const char *zpool_prop_default_string(zpool_prop_t);
|
||||
extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
|
||||
extern const char *zpool_prop_column_name(zpool_prop_t);
|
||||
extern boolean_t zpool_prop_align_right(zpool_prop_t);
|
||||
|
||||
/*
|
||||
* Functions shared by zfs and zpool property management.
|
||||
*/
|
||||
extern int zprop_iter(zprop_func func, void *cb, boolean_t show_all,
|
||||
boolean_t ordered, zfs_type_t type);
|
||||
extern int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **,
|
||||
zfs_type_t);
|
||||
extern void zprop_free_list(zprop_list_t *);
|
||||
|
||||
/*
|
||||
* Functions for printing zfs or zpool properties
|
||||
*/
|
||||
typedef struct zprop_get_cbdata {
|
||||
int cb_sources;
|
||||
int cb_columns[4];
|
||||
int cb_colwidths[5];
|
||||
boolean_t cb_scripted;
|
||||
boolean_t cb_literal;
|
||||
boolean_t cb_first;
|
||||
zprop_list_t *cb_proplist;
|
||||
zfs_type_t cb_type;
|
||||
} zprop_get_cbdata_t;
|
||||
|
||||
void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
|
||||
const char *, const char *, zprop_source_t, const char *);
|
||||
|
||||
#define GET_COL_NAME 1
|
||||
#define GET_COL_PROPERTY 2
|
||||
#define GET_COL_VALUE 3
|
||||
#define GET_COL_SOURCE 4
|
||||
|
||||
/*
|
||||
* Iterator functions.
|
||||
*/
|
||||
typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
|
||||
extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
|
||||
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
|
||||
|
||||
/*
|
||||
* Functions to create and destroy datasets.
|
||||
*/
|
||||
extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
|
||||
nvlist_t *);
|
||||
extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
|
||||
extern int zfs_destroy(zfs_handle_t *);
|
||||
extern int zfs_destroy_snaps(zfs_handle_t *, char *);
|
||||
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
|
||||
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
|
||||
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
|
||||
extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
|
||||
extern int zfs_send(zfs_handle_t *, const char *, const char *,
|
||||
boolean_t, boolean_t, boolean_t, boolean_t, int);
|
||||
extern int zfs_promote(zfs_handle_t *);
|
||||
|
||||
typedef struct recvflags {
|
||||
/* print informational messages (ie, -v was specified) */
|
||||
int verbose : 1;
|
||||
|
||||
/* the destination is a prefix, not the exact fs (ie, -d) */
|
||||
int isprefix : 1;
|
||||
|
||||
/* do not actually do the recv, just check if it would work (ie, -n) */
|
||||
int dryrun : 1;
|
||||
|
||||
/* rollback/destroy filesystems as necessary (eg, -F) */
|
||||
int force : 1;
|
||||
|
||||
/* set "canmount=off" on all modified filesystems */
|
||||
int canmountoff : 1;
|
||||
|
||||
/* byteswap flag is used internally; callers need not specify */
|
||||
int byteswap : 1;
|
||||
} recvflags_t;
|
||||
|
||||
extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
|
||||
int, avl_tree_t *);
|
||||
|
||||
/*
|
||||
* Miscellaneous functions.
|
||||
*/
|
||||
extern const char *zfs_type_to_name(zfs_type_t);
|
||||
extern void zfs_refresh_properties(zfs_handle_t *);
|
||||
extern int zfs_name_valid(const char *, zfs_type_t);
|
||||
extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
|
||||
extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
|
||||
zfs_type_t);
|
||||
extern int zfs_spa_version(zfs_handle_t *, int *);
|
||||
|
||||
/*
|
||||
* dataset permission functions.
|
||||
*/
|
||||
extern int zfs_perm_set(zfs_handle_t *, nvlist_t *);
|
||||
extern int zfs_perm_remove(zfs_handle_t *, nvlist_t *);
|
||||
extern int zfs_build_perms(zfs_handle_t *, char *, char *,
|
||||
zfs_deleg_who_type_t, zfs_deleg_inherit_t, nvlist_t **nvlist_t);
|
||||
extern int zfs_perm_get(zfs_handle_t *, zfs_allow_t **);
|
||||
extern void zfs_free_allows(zfs_allow_t *);
|
||||
extern void zfs_deleg_permissions(void);
|
||||
|
||||
/*
|
||||
* Mount support functions.
|
||||
*/
|
||||
extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
|
||||
extern boolean_t zfs_is_mounted(zfs_handle_t *, char **);
|
||||
extern int zfs_mount(zfs_handle_t *, const char *, int);
|
||||
extern int zfs_unmount(zfs_handle_t *, const char *, int);
|
||||
extern int zfs_unmountall(zfs_handle_t *, int);
|
||||
|
||||
/*
|
||||
* Share support functions.
|
||||
*/
|
||||
extern boolean_t zfs_is_shared(zfs_handle_t *);
|
||||
extern int zfs_share(zfs_handle_t *);
|
||||
extern int zfs_unshare(zfs_handle_t *);
|
||||
|
||||
/*
|
||||
* Protocol-specific share support functions.
|
||||
*/
|
||||
extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
|
||||
extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **);
|
||||
extern int zfs_share_nfs(zfs_handle_t *);
|
||||
extern int zfs_share_smb(zfs_handle_t *);
|
||||
extern int zfs_shareall(zfs_handle_t *);
|
||||
extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
|
||||
extern int zfs_unshare_smb(zfs_handle_t *, const char *);
|
||||
extern int zfs_unshareall_nfs(zfs_handle_t *);
|
||||
extern int zfs_unshareall_smb(zfs_handle_t *);
|
||||
extern int zfs_unshareall_bypath(zfs_handle_t *, const char *);
|
||||
extern int zfs_unshareall(zfs_handle_t *);
|
||||
extern boolean_t zfs_is_shared_iscsi(zfs_handle_t *);
|
||||
extern int zfs_share_iscsi(zfs_handle_t *);
|
||||
extern int zfs_unshare_iscsi(zfs_handle_t *);
|
||||
extern int zfs_iscsi_perm_check(libzfs_handle_t *, char *, ucred_t *);
|
||||
extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *,
|
||||
void *, void *, int, zfs_share_op_t);
|
||||
|
||||
/*
|
||||
* When dealing with nvlists, verify() is extremely useful
|
||||
*/
|
||||
#ifdef NDEBUG
|
||||
#define verify(EX) ((void)(EX))
|
||||
#else
|
||||
#define verify(EX) assert(EX)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Utility function to convert a number to a human-readable form.
|
||||
*/
|
||||
extern void zfs_nicenum(uint64_t, char *, size_t);
|
||||
extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
|
||||
|
||||
/*
|
||||
* Given a device or file, determine if it is part of a pool.
|
||||
*/
|
||||
extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
|
||||
boolean_t *);
|
||||
|
||||
/*
|
||||
* ftyp special. Read the label from a given device.
|
||||
*/
|
||||
extern int zpool_read_label(int, nvlist_t **);
|
||||
|
||||
/*
|
||||
* Create and remove zvol /dev links.
|
||||
*/
|
||||
extern int zpool_create_zvol_links(zpool_handle_t *);
|
||||
extern int zpool_remove_zvol_links(zpool_handle_t *);
|
||||
|
||||
/* is this zvol valid for use as a dump device? */
|
||||
extern int zvol_check_dump_config(char *);
|
||||
|
||||
/*
|
||||
* Enable and disable datasets within a pool by mounting/unmounting and
|
||||
* sharing/unsharing them.
|
||||
*/
|
||||
extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
|
||||
extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBZFS_H */
|
|
@ -0,0 +1,713 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*
|
||||
* Portions Copyright 2007 Ramprakash Jelari
|
||||
*/
|
||||
|
||||
#include <libintl.h>
|
||||
#include <libuutil.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <zone.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
/*
|
||||
* Structure to keep track of dataset state. Before changing the 'sharenfs' or
|
||||
* 'mountpoint' property, we record whether the filesystem was previously
|
||||
* mounted/shared. This prior state dictates whether we remount/reshare the
|
||||
* dataset after the property has been changed.
|
||||
*
|
||||
* The interface consists of the following sequence of functions:
|
||||
*
|
||||
* changelist_gather()
|
||||
* changelist_prefix()
|
||||
* < change property >
|
||||
* changelist_postfix()
|
||||
* changelist_free()
|
||||
*
|
||||
* Other interfaces:
|
||||
*
|
||||
* changelist_remove() - remove a node from a gathered list
|
||||
* changelist_rename() - renames all datasets appropriately when doing a rename
|
||||
* changelist_unshare() - unshares all the nodes in a given changelist
|
||||
* changelist_haszonedchild() - check if there is any child exported to
|
||||
* a local zone
|
||||
*/
|
||||
typedef struct prop_changenode {
|
||||
zfs_handle_t *cn_handle;
|
||||
int cn_shared;
|
||||
int cn_mounted;
|
||||
int cn_zoned;
|
||||
boolean_t cn_needpost; /* is postfix() needed? */
|
||||
uu_list_node_t cn_listnode;
|
||||
} prop_changenode_t;
|
||||
|
||||
struct prop_changelist {
|
||||
zfs_prop_t cl_prop;
|
||||
zfs_prop_t cl_realprop;
|
||||
zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
|
||||
uu_list_pool_t *cl_pool;
|
||||
uu_list_t *cl_list;
|
||||
boolean_t cl_waslegacy;
|
||||
boolean_t cl_allchildren;
|
||||
boolean_t cl_alldependents;
|
||||
int cl_mflags; /* Mount flags */
|
||||
int cl_gflags; /* Gather request flags */
|
||||
boolean_t cl_haszonedchild;
|
||||
boolean_t cl_sorted;
|
||||
};
|
||||
|
||||
/*
|
||||
* If the property is 'mountpoint', go through and unmount filesystems as
|
||||
* necessary. We don't do the same for 'sharenfs', because we can just re-share
|
||||
* with different options without interrupting service. We do handle 'sharesmb'
|
||||
* since there may be old resource names that need to be removed.
|
||||
*/
|
||||
int
|
||||
changelist_prefix(prop_changelist_t *clp)
|
||||
{
|
||||
prop_changenode_t *cn;
|
||||
int ret = 0;
|
||||
|
||||
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
|
||||
clp->cl_prop != ZFS_PROP_SHARESMB)
|
||||
return (0);
|
||||
|
||||
for (cn = uu_list_first(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_next(clp->cl_list, cn)) {
|
||||
|
||||
/* if a previous loop failed, set the remaining to false */
|
||||
if (ret == -1) {
|
||||
cn->cn_needpost = B_FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are in the global zone, but this dataset is exported
|
||||
* to a local zone, do nothing.
|
||||
*/
|
||||
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
||||
continue;
|
||||
|
||||
if (ZFS_IS_VOLUME(cn->cn_handle)) {
|
||||
switch (clp->cl_realprop) {
|
||||
case ZFS_PROP_NAME:
|
||||
/*
|
||||
* If this was a rename, unshare the zvol, and
|
||||
* remove the /dev/zvol links.
|
||||
*/
|
||||
(void) zfs_unshare_iscsi(cn->cn_handle);
|
||||
|
||||
if (zvol_remove_link(cn->cn_handle->zfs_hdl,
|
||||
cn->cn_handle->zfs_name) != 0) {
|
||||
ret = -1;
|
||||
cn->cn_needpost = B_FALSE;
|
||||
(void) zfs_share_iscsi(cn->cn_handle);
|
||||
}
|
||||
break;
|
||||
|
||||
case ZFS_PROP_VOLSIZE:
|
||||
/*
|
||||
* If this was a change to the volume size, we
|
||||
* need to unshare and reshare the volume.
|
||||
*/
|
||||
(void) zfs_unshare_iscsi(cn->cn_handle);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Do the property specific processing.
|
||||
*/
|
||||
switch (clp->cl_prop) {
|
||||
case ZFS_PROP_MOUNTPOINT:
|
||||
if (zfs_unmount(cn->cn_handle, NULL,
|
||||
clp->cl_mflags) != 0) {
|
||||
ret = -1;
|
||||
cn->cn_needpost = B_FALSE;
|
||||
}
|
||||
break;
|
||||
case ZFS_PROP_SHARESMB:
|
||||
(void) zfs_unshare_smb(cn->cn_handle, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == -1)
|
||||
(void) changelist_postfix(clp);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
|
||||
* reshare the filesystems as necessary. In changelist_gather() we recorded
|
||||
* whether the filesystem was previously shared or mounted. The action we take
|
||||
* depends on the previous state, and whether the value was previously 'legacy'.
|
||||
* For non-legacy properties, we only remount/reshare the filesystem if it was
|
||||
* previously mounted/shared. Otherwise, we always remount/reshare the
|
||||
* filesystem.
|
||||
*/
|
||||
int
|
||||
changelist_postfix(prop_changelist_t *clp)
|
||||
{
|
||||
prop_changenode_t *cn;
|
||||
char shareopts[ZFS_MAXPROPLEN];
|
||||
int errors = 0;
|
||||
libzfs_handle_t *hdl;
|
||||
|
||||
/*
|
||||
* If we're changing the mountpoint, attempt to destroy the underlying
|
||||
* mountpoint. All other datasets will have inherited from this dataset
|
||||
* (in which case their mountpoints exist in the filesystem in the new
|
||||
* location), or have explicit mountpoints set (in which case they won't
|
||||
* be in the changelist).
|
||||
*/
|
||||
if ((cn = uu_list_last(clp->cl_list)) == NULL)
|
||||
return (0);
|
||||
|
||||
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT)
|
||||
remove_mountpoint(cn->cn_handle);
|
||||
|
||||
/*
|
||||
* It is possible that the changelist_prefix() used libshare
|
||||
* to unshare some entries. Since libshare caches data, an
|
||||
* attempt to reshare during postfix can fail unless libshare
|
||||
* is uninitialized here so that it will reinitialize later.
|
||||
*/
|
||||
if (cn->cn_handle != NULL) {
|
||||
hdl = cn->cn_handle->zfs_hdl;
|
||||
assert(hdl != NULL);
|
||||
zfs_uninit_libshare(hdl);
|
||||
}
|
||||
|
||||
/*
|
||||
* We walk the datasets in reverse, because we want to mount any parent
|
||||
* datasets before mounting the children. We walk all datasets even if
|
||||
* there are errors.
|
||||
*/
|
||||
for (cn = uu_list_last(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_prev(clp->cl_list, cn)) {
|
||||
|
||||
boolean_t sharenfs;
|
||||
boolean_t sharesmb;
|
||||
|
||||
/*
|
||||
* If we are in the global zone, but this dataset is exported
|
||||
* to a local zone, do nothing.
|
||||
*/
|
||||
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
||||
continue;
|
||||
|
||||
/* Only do post-processing if it's required */
|
||||
if (!cn->cn_needpost)
|
||||
continue;
|
||||
cn->cn_needpost = B_FALSE;
|
||||
|
||||
zfs_refresh_properties(cn->cn_handle);
|
||||
|
||||
if (ZFS_IS_VOLUME(cn->cn_handle)) {
|
||||
/*
|
||||
* If we're doing a rename, recreate the /dev/zvol
|
||||
* links.
|
||||
*/
|
||||
if (clp->cl_realprop == ZFS_PROP_NAME &&
|
||||
zvol_create_link(cn->cn_handle->zfs_hdl,
|
||||
cn->cn_handle->zfs_name) != 0) {
|
||||
errors++;
|
||||
} else if (cn->cn_shared ||
|
||||
clp->cl_prop == ZFS_PROP_SHAREISCSI) {
|
||||
if (zfs_prop_get(cn->cn_handle,
|
||||
ZFS_PROP_SHAREISCSI, shareopts,
|
||||
sizeof (shareopts), NULL, NULL, 0,
|
||||
B_FALSE) == 0 &&
|
||||
strcmp(shareopts, "off") == 0) {
|
||||
errors +=
|
||||
zfs_unshare_iscsi(cn->cn_handle);
|
||||
} else {
|
||||
errors +=
|
||||
zfs_share_iscsi(cn->cn_handle);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remount if previously mounted or mountpoint was legacy,
|
||||
* or sharenfs or sharesmb property is set.
|
||||
*/
|
||||
sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
|
||||
shareopts, sizeof (shareopts), NULL, NULL, 0,
|
||||
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
|
||||
|
||||
sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
|
||||
shareopts, sizeof (shareopts), NULL, NULL, 0,
|
||||
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
|
||||
|
||||
if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs ||
|
||||
sharesmb) && !zfs_is_mounted(cn->cn_handle, NULL) &&
|
||||
zfs_mount(cn->cn_handle, NULL, 0) != 0)
|
||||
errors++;
|
||||
|
||||
/*
|
||||
* We always re-share even if the filesystem is currently
|
||||
* shared, so that we can adopt any new options.
|
||||
*/
|
||||
if (sharenfs)
|
||||
errors += zfs_share_nfs(cn->cn_handle);
|
||||
else if (cn->cn_shared || clp->cl_waslegacy)
|
||||
errors += zfs_unshare_nfs(cn->cn_handle, NULL);
|
||||
if (sharesmb)
|
||||
errors += zfs_share_smb(cn->cn_handle);
|
||||
else if (cn->cn_shared || clp->cl_waslegacy)
|
||||
errors += zfs_unshare_smb(cn->cn_handle, NULL);
|
||||
}
|
||||
|
||||
return (errors ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is this "dataset" a child of "parent"?
|
||||
*/
|
||||
boolean_t
|
||||
isa_child_of(const char *dataset, const char *parent)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(parent);
|
||||
|
||||
if (strncmp(dataset, parent, len) == 0 &&
|
||||
(dataset[len] == '@' || dataset[len] == '/' ||
|
||||
dataset[len] == '\0'))
|
||||
return (B_TRUE);
|
||||
else
|
||||
return (B_FALSE);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If we rename a filesystem, child filesystem handles are no longer valid
|
||||
* since we identify each dataset by its name in the ZFS namespace. As a
|
||||
* result, we have to go through and fix up all the names appropriately. We
|
||||
* could do this automatically if libzfs kept track of all open handles, but
|
||||
* this is a lot less work.
|
||||
*/
|
||||
void
|
||||
changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
|
||||
{
|
||||
prop_changenode_t *cn;
|
||||
char newname[ZFS_MAXNAMELEN];
|
||||
|
||||
for (cn = uu_list_first(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_next(clp->cl_list, cn)) {
|
||||
/*
|
||||
* Do not rename a clone that's not in the source hierarchy.
|
||||
*/
|
||||
if (!isa_child_of(cn->cn_handle->zfs_name, src))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Destroy the previous mountpoint if needed.
|
||||
*/
|
||||
remove_mountpoint(cn->cn_handle);
|
||||
|
||||
(void) strlcpy(newname, dst, sizeof (newname));
|
||||
(void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
|
||||
|
||||
(void) strlcpy(cn->cn_handle->zfs_name, newname,
|
||||
sizeof (cn->cn_handle->zfs_name));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
|
||||
* unshare all the datasets in the list.
|
||||
*/
|
||||
int
|
||||
changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
|
||||
{
|
||||
prop_changenode_t *cn;
|
||||
int ret = 0;
|
||||
|
||||
if (clp->cl_prop != ZFS_PROP_SHARENFS &&
|
||||
clp->cl_prop != ZFS_PROP_SHARESMB)
|
||||
return (0);
|
||||
|
||||
for (cn = uu_list_first(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_next(clp->cl_list, cn)) {
|
||||
if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there is any child exported to a local zone in a given changelist.
|
||||
* This information has already been recorded while gathering the changelist
|
||||
* via changelist_gather().
|
||||
*/
|
||||
int
|
||||
changelist_haszonedchild(prop_changelist_t *clp)
|
||||
{
|
||||
return (clp->cl_haszonedchild);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a node from a gathered list.
|
||||
*/
|
||||
void
|
||||
changelist_remove(prop_changelist_t *clp, const char *name)
|
||||
{
|
||||
prop_changenode_t *cn;
|
||||
|
||||
for (cn = uu_list_first(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_next(clp->cl_list, cn)) {
|
||||
|
||||
if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
|
||||
uu_list_remove(clp->cl_list, cn);
|
||||
zfs_close(cn->cn_handle);
|
||||
free(cn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Release any memory associated with a changelist.
|
||||
*/
|
||||
void
|
||||
changelist_free(prop_changelist_t *clp)
|
||||
{
|
||||
prop_changenode_t *cn;
|
||||
void *cookie;
|
||||
|
||||
if (clp->cl_list) {
|
||||
cookie = NULL;
|
||||
while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) {
|
||||
zfs_close(cn->cn_handle);
|
||||
free(cn);
|
||||
}
|
||||
|
||||
uu_list_destroy(clp->cl_list);
|
||||
}
|
||||
if (clp->cl_pool)
|
||||
uu_list_pool_destroy(clp->cl_pool);
|
||||
|
||||
free(clp);
|
||||
}
|
||||
|
||||
static int
|
||||
change_one(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
prop_changelist_t *clp = data;
|
||||
char property[ZFS_MAXPROPLEN];
|
||||
char where[64];
|
||||
prop_changenode_t *cn;
|
||||
zprop_source_t sourcetype;
|
||||
zprop_source_t share_sourcetype;
|
||||
|
||||
/*
|
||||
* We only want to unmount/unshare those filesystems that may inherit
|
||||
* from the target filesystem. If we find any filesystem with a
|
||||
* locally set mountpoint, we ignore any children since changing the
|
||||
* property will not affect them. If this is a rename, we iterate
|
||||
* over all children regardless, since we need them unmounted in
|
||||
* order to do the rename. Also, if this is a volume and we're doing
|
||||
* a rename, then always add it to the changelist.
|
||||
*/
|
||||
|
||||
if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
|
||||
zfs_prop_get(zhp, clp->cl_prop, property,
|
||||
sizeof (property), &sourcetype, where, sizeof (where),
|
||||
B_FALSE) != 0) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are "watching" sharenfs or sharesmb
|
||||
* then check out the companion property which is tracked
|
||||
* in cl_shareprop
|
||||
*/
|
||||
if (clp->cl_shareprop != ZPROP_INVAL &&
|
||||
zfs_prop_get(zhp, clp->cl_shareprop, property,
|
||||
sizeof (property), &share_sourcetype, where, sizeof (where),
|
||||
B_FALSE) != 0) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (clp->cl_alldependents || clp->cl_allchildren ||
|
||||
sourcetype == ZPROP_SRC_DEFAULT ||
|
||||
sourcetype == ZPROP_SRC_INHERITED ||
|
||||
(clp->cl_shareprop != ZPROP_INVAL &&
|
||||
(share_sourcetype == ZPROP_SRC_DEFAULT ||
|
||||
share_sourcetype == ZPROP_SRC_INHERITED))) {
|
||||
if ((cn = zfs_alloc(zfs_get_handle(zhp),
|
||||
sizeof (prop_changenode_t))) == NULL) {
|
||||
zfs_close(zhp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
cn->cn_handle = zhp;
|
||||
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
|
||||
zfs_is_mounted(zhp, NULL);
|
||||
cn->cn_shared = zfs_is_shared(zhp);
|
||||
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
||||
cn->cn_needpost = B_TRUE;
|
||||
|
||||
/* Indicate if any child is exported to a local zone. */
|
||||
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
|
||||
clp->cl_haszonedchild = B_TRUE;
|
||||
|
||||
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
|
||||
|
||||
if (clp->cl_sorted) {
|
||||
uu_list_index_t idx;
|
||||
|
||||
(void) uu_list_find(clp->cl_list, cn, NULL,
|
||||
&idx);
|
||||
uu_list_insert(clp->cl_list, cn, idx);
|
||||
} else {
|
||||
ASSERT(!clp->cl_alldependents);
|
||||
verify(uu_list_insert_before(clp->cl_list,
|
||||
uu_list_first(clp->cl_list), cn) == 0);
|
||||
}
|
||||
|
||||
if (!clp->cl_alldependents)
|
||||
return (zfs_iter_children(zhp, change_one, data));
|
||||
} else {
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
compare_mountpoints(const void *a, const void *b, void *unused)
|
||||
{
|
||||
const prop_changenode_t *ca = a;
|
||||
const prop_changenode_t *cb = b;
|
||||
|
||||
char mounta[MAXPATHLEN];
|
||||
char mountb[MAXPATHLEN];
|
||||
|
||||
boolean_t hasmounta, hasmountb;
|
||||
|
||||
/*
|
||||
* When unsharing or unmounting filesystems, we need to do it in
|
||||
* mountpoint order. This allows the user to have a mountpoint
|
||||
* hierarchy that is different from the dataset hierarchy, and still
|
||||
* allow it to be changed. However, if either dataset doesn't have a
|
||||
* mountpoint (because it is a volume or a snapshot), we place it at the
|
||||
* end of the list, because it doesn't affect our change at all.
|
||||
*/
|
||||
hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
|
||||
sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
|
||||
hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
|
||||
sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
|
||||
|
||||
if (!hasmounta && hasmountb)
|
||||
return (-1);
|
||||
else if (hasmounta && !hasmountb)
|
||||
return (1);
|
||||
else if (!hasmounta && !hasmountb)
|
||||
return (0);
|
||||
else
|
||||
return (strcmp(mountb, mounta));
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a ZFS handle and a property, construct a complete list of datasets
|
||||
* that need to be modified as part of this process. For anything but the
|
||||
* 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
|
||||
* Otherwise, we iterate over all children and look for any datasets that
|
||||
* inherit the property. For each such dataset, we add it to the list and
|
||||
* mark whether it was shared beforehand.
|
||||
*/
|
||||
prop_changelist_t *
|
||||
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
|
||||
int mnt_flags)
|
||||
{
|
||||
prop_changelist_t *clp;
|
||||
prop_changenode_t *cn;
|
||||
zfs_handle_t *temp;
|
||||
char property[ZFS_MAXPROPLEN];
|
||||
uu_compare_fn_t *compare = NULL;
|
||||
|
||||
if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* For mountpoint-related tasks, we want to sort everything by
|
||||
* mountpoint, so that we mount and unmount them in the appropriate
|
||||
* order, regardless of their position in the hierarchy.
|
||||
*/
|
||||
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
|
||||
prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
|
||||
prop == ZFS_PROP_SHARESMB) {
|
||||
compare = compare_mountpoints;
|
||||
clp->cl_sorted = B_TRUE;
|
||||
}
|
||||
|
||||
clp->cl_pool = uu_list_pool_create("changelist_pool",
|
||||
sizeof (prop_changenode_t),
|
||||
offsetof(prop_changenode_t, cn_listnode),
|
||||
compare, 0);
|
||||
if (clp->cl_pool == NULL) {
|
||||
assert(uu_error() == UU_ERROR_NO_MEMORY);
|
||||
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
|
||||
changelist_free(clp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
clp->cl_list = uu_list_create(clp->cl_pool, NULL,
|
||||
clp->cl_sorted ? UU_LIST_SORTED : 0);
|
||||
clp->cl_gflags = gather_flags;
|
||||
clp->cl_mflags = mnt_flags;
|
||||
|
||||
if (clp->cl_list == NULL) {
|
||||
assert(uu_error() == UU_ERROR_NO_MEMORY);
|
||||
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
|
||||
changelist_free(clp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a rename or the 'zoned' property, we pretend we're
|
||||
* changing the mountpoint and flag it so we can catch all children in
|
||||
* change_one().
|
||||
*
|
||||
* Flag cl_alldependents to catch all children plus the dependents
|
||||
* (clones) that are not in the hierarchy.
|
||||
*/
|
||||
if (prop == ZFS_PROP_NAME) {
|
||||
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
||||
clp->cl_alldependents = B_TRUE;
|
||||
} else if (prop == ZFS_PROP_ZONED) {
|
||||
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
||||
clp->cl_allchildren = B_TRUE;
|
||||
} else if (prop == ZFS_PROP_CANMOUNT) {
|
||||
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
||||
} else if (prop == ZFS_PROP_VOLSIZE) {
|
||||
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
||||
} else if (prop == ZFS_PROP_VERSION) {
|
||||
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
|
||||
} else {
|
||||
clp->cl_prop = prop;
|
||||
}
|
||||
clp->cl_realprop = prop;
|
||||
|
||||
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
|
||||
clp->cl_prop != ZFS_PROP_SHARENFS &&
|
||||
clp->cl_prop != ZFS_PROP_SHARESMB &&
|
||||
clp->cl_prop != ZFS_PROP_SHAREISCSI)
|
||||
return (clp);
|
||||
|
||||
/*
|
||||
* If watching SHARENFS or SHARESMB then
|
||||
* also watch its companion property.
|
||||
*/
|
||||
if (clp->cl_prop == ZFS_PROP_SHARENFS)
|
||||
clp->cl_shareprop = ZFS_PROP_SHARESMB;
|
||||
else if (clp->cl_prop == ZFS_PROP_SHARESMB)
|
||||
clp->cl_shareprop = ZFS_PROP_SHARENFS;
|
||||
|
||||
if (clp->cl_alldependents) {
|
||||
if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
|
||||
changelist_free(clp);
|
||||
return (NULL);
|
||||
}
|
||||
} else if (zfs_iter_children(zhp, change_one, clp) != 0) {
|
||||
changelist_free(clp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to re-open ourselves because we auto-close all the handles
|
||||
* and can't tell the difference.
|
||||
*/
|
||||
if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
|
||||
ZFS_TYPE_DATASET)) == NULL) {
|
||||
changelist_free(clp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Always add ourself to the list. We add ourselves to the end so that
|
||||
* we're the last to be unmounted.
|
||||
*/
|
||||
if ((cn = zfs_alloc(zhp->zfs_hdl,
|
||||
sizeof (prop_changenode_t))) == NULL) {
|
||||
zfs_close(temp);
|
||||
changelist_free(clp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
cn->cn_handle = temp;
|
||||
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
|
||||
zfs_is_mounted(temp, NULL);
|
||||
cn->cn_shared = zfs_is_shared(temp);
|
||||
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
||||
cn->cn_needpost = B_TRUE;
|
||||
|
||||
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
|
||||
if (clp->cl_sorted) {
|
||||
uu_list_index_t idx;
|
||||
(void) uu_list_find(clp->cl_list, cn, NULL, &idx);
|
||||
uu_list_insert(clp->cl_list, cn, idx);
|
||||
} else {
|
||||
verify(uu_list_insert_after(clp->cl_list,
|
||||
uu_list_last(clp->cl_list), cn) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the mountpoint property was previously 'legacy', or 'none',
|
||||
* record it as the behavior of changelist_postfix() will be different.
|
||||
*/
|
||||
if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) &&
|
||||
(zfs_prop_get(zhp, prop, property, sizeof (property),
|
||||
NULL, NULL, 0, B_FALSE) == 0 &&
|
||||
(strcmp(property, "legacy") == 0 ||
|
||||
strcmp(property, "none") == 0))) {
|
||||
/*
|
||||
* do not automatically mount ex-legacy datasets if
|
||||
* we specifically set canmount to noauto
|
||||
*/
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
|
||||
ZFS_CANMOUNT_NOAUTO)
|
||||
clp->cl_waslegacy = B_TRUE;
|
||||
}
|
||||
|
||||
return (clp);
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* The pool configuration repository is stored in /etc/zfs/zpool.cache as a
|
||||
* single packed nvlist. While it would be nice to just read in this
|
||||
* file from userland, this wouldn't work from a local zone. So we have to have
|
||||
* a zpool ioctl to return the complete configuration for all pools. In the
|
||||
* global zone, this will be identical to reading the file and unpacking it in
|
||||
* userland.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libintl.h>
|
||||
#include <libuutil.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
typedef struct config_node {
|
||||
char *cn_name;
|
||||
nvlist_t *cn_config;
|
||||
uu_avl_node_t cn_avl;
|
||||
} config_node_t;
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
config_node_compare(const void *a, const void *b, void *unused)
|
||||
{
|
||||
int ret;
|
||||
|
||||
const config_node_t *ca = (config_node_t *)a;
|
||||
const config_node_t *cb = (config_node_t *)b;
|
||||
|
||||
ret = strcmp(ca->cn_name, cb->cn_name);
|
||||
|
||||
if (ret < 0)
|
||||
return (-1);
|
||||
else if (ret > 0)
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
namespace_clear(libzfs_handle_t *hdl)
|
||||
{
|
||||
if (hdl->libzfs_ns_avl) {
|
||||
config_node_t *cn;
|
||||
void *cookie = NULL;
|
||||
|
||||
while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl,
|
||||
&cookie)) != NULL) {
|
||||
nvlist_free(cn->cn_config);
|
||||
free(cn->cn_name);
|
||||
free(cn);
|
||||
}
|
||||
|
||||
uu_avl_destroy(hdl->libzfs_ns_avl);
|
||||
hdl->libzfs_ns_avl = NULL;
|
||||
}
|
||||
|
||||
if (hdl->libzfs_ns_avlpool) {
|
||||
uu_avl_pool_destroy(hdl->libzfs_ns_avlpool);
|
||||
hdl->libzfs_ns_avlpool = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the pool namespace, or re-loads it if the cache has changed.
|
||||
*/
|
||||
static int
|
||||
namespace_reload(libzfs_handle_t *hdl)
|
||||
{
|
||||
nvlist_t *config;
|
||||
config_node_t *cn;
|
||||
nvpair_t *elem;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
void *cookie;
|
||||
|
||||
if (hdl->libzfs_ns_gen == 0) {
|
||||
/*
|
||||
* This is the first time we've accessed the configuration
|
||||
* cache. Initialize the AVL tree and then fall through to the
|
||||
* common code.
|
||||
*/
|
||||
if ((hdl->libzfs_ns_avlpool = uu_avl_pool_create("config_pool",
|
||||
sizeof (config_node_t),
|
||||
offsetof(config_node_t, cn_avl),
|
||||
config_node_compare, UU_DEFAULT)) == NULL)
|
||||
return (no_memory(hdl));
|
||||
|
||||
if ((hdl->libzfs_ns_avl = uu_avl_create(hdl->libzfs_ns_avlpool,
|
||||
NULL, UU_DEFAULT)) == NULL)
|
||||
return (no_memory(hdl));
|
||||
}
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
zc.zc_cookie = hdl->libzfs_ns_gen;
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
|
||||
switch (errno) {
|
||||
case EEXIST:
|
||||
/*
|
||||
* The namespace hasn't changed.
|
||||
*/
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (0);
|
||||
|
||||
case ENOMEM:
|
||||
if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (zfs_standard_error(hdl, errno,
|
||||
dgettext(TEXT_DOMAIN, "failed to read "
|
||||
"pool configuration")));
|
||||
}
|
||||
} else {
|
||||
hdl->libzfs_ns_gen = zc.zc_cookie;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
zcmd_free_nvlists(&zc);
|
||||
|
||||
/*
|
||||
* Clear out any existing configuration information.
|
||||
*/
|
||||
cookie = NULL;
|
||||
while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl, &cookie)) != NULL) {
|
||||
nvlist_free(cn->cn_config);
|
||||
free(cn->cn_name);
|
||||
free(cn);
|
||||
}
|
||||
|
||||
elem = NULL;
|
||||
while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {
|
||||
nvlist_t *child;
|
||||
uu_avl_index_t where;
|
||||
|
||||
if ((cn = zfs_alloc(hdl, sizeof (config_node_t))) == NULL) {
|
||||
nvlist_free(config);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((cn->cn_name = zfs_strdup(hdl,
|
||||
nvpair_name(elem))) == NULL) {
|
||||
free(cn);
|
||||
nvlist_free(config);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
verify(nvpair_value_nvlist(elem, &child) == 0);
|
||||
if (nvlist_dup(child, &cn->cn_config, 0) != 0) {
|
||||
free(cn->cn_name);
|
||||
free(cn);
|
||||
nvlist_free(config);
|
||||
return (no_memory(hdl));
|
||||
}
|
||||
verify(uu_avl_find(hdl->libzfs_ns_avl, cn, NULL, &where)
|
||||
== NULL);
|
||||
|
||||
uu_avl_insert(hdl->libzfs_ns_avl, cn, where);
|
||||
}
|
||||
|
||||
nvlist_free(config);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the configuration for the given pool. The configuration is a nvlist
|
||||
* describing the vdevs, as well as the statistics associated with each one.
|
||||
*/
|
||||
nvlist_t *
|
||||
zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
|
||||
{
|
||||
if (oldconfig)
|
||||
*oldconfig = zhp->zpool_old_config;
|
||||
return (zhp->zpool_config);
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh the vdev statistics associated with the given pool. This is used in
|
||||
* iostat to show configuration changes and determine the delta from the last
|
||||
* time the function was called. This function can fail, in case the pool has
|
||||
* been destroyed.
|
||||
*/
|
||||
int
|
||||
zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
int error;
|
||||
nvlist_t *config;
|
||||
libzfs_handle_t *hdl = zhp->zpool_hdl;
|
||||
|
||||
*missing = B_FALSE;
|
||||
(void) strcpy(zc.zc_name, zhp->zpool_name);
|
||||
|
||||
if (zhp->zpool_config_size == 0)
|
||||
zhp->zpool_config_size = 1 << 16;
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_STATS,
|
||||
&zc) == 0) {
|
||||
/*
|
||||
* The real error is returned in the zc_cookie field.
|
||||
*/
|
||||
error = zc.zc_cookie;
|
||||
break;
|
||||
}
|
||||
|
||||
if (errno == ENOMEM) {
|
||||
if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
zcmd_free_nvlists(&zc);
|
||||
if (errno == ENOENT || errno == EINVAL)
|
||||
*missing = B_TRUE;
|
||||
zhp->zpool_state = POOL_STATE_UNAVAIL;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
zcmd_free_nvlists(&zc);
|
||||
|
||||
zhp->zpool_config_size = zc.zc_nvlist_dst_size;
|
||||
|
||||
if (zhp->zpool_config != NULL) {
|
||||
uint64_t oldtxg, newtxg;
|
||||
|
||||
verify(nvlist_lookup_uint64(zhp->zpool_config,
|
||||
ZPOOL_CONFIG_POOL_TXG, &oldtxg) == 0);
|
||||
verify(nvlist_lookup_uint64(config,
|
||||
ZPOOL_CONFIG_POOL_TXG, &newtxg) == 0);
|
||||
|
||||
if (zhp->zpool_old_config != NULL)
|
||||
nvlist_free(zhp->zpool_old_config);
|
||||
|
||||
if (oldtxg != newtxg) {
|
||||
nvlist_free(zhp->zpool_config);
|
||||
zhp->zpool_old_config = NULL;
|
||||
} else {
|
||||
zhp->zpool_old_config = zhp->zpool_config;
|
||||
}
|
||||
}
|
||||
|
||||
zhp->zpool_config = config;
|
||||
if (error)
|
||||
zhp->zpool_state = POOL_STATE_UNAVAIL;
|
||||
else
|
||||
zhp->zpool_state = POOL_STATE_ACTIVE;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all pools in the system.
|
||||
*/
|
||||
int
|
||||
zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
|
||||
{
|
||||
config_node_t *cn;
|
||||
zpool_handle_t *zhp;
|
||||
int ret;
|
||||
|
||||
if (namespace_reload(hdl) != 0)
|
||||
return (-1);
|
||||
|
||||
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
|
||||
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
|
||||
|
||||
if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0)
|
||||
return (-1);
|
||||
|
||||
if (zhp == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(zhp, data)) != 0)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over root datasets, calling the given function for each. The zfs
|
||||
* handle passed each time must be explicitly closed by the callback.
|
||||
*/
|
||||
int
|
||||
zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
|
||||
{
|
||||
config_node_t *cn;
|
||||
zfs_handle_t *zhp;
|
||||
int ret;
|
||||
|
||||
if (namespace_reload(hdl) != 0)
|
||||
return (-1);
|
||||
|
||||
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
|
||||
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
|
||||
|
||||
if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(zhp, data)) != 0)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,662 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* Iterate over all children of the current object. This includes the normal
|
||||
* dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
|
||||
* walk all datasets in the pool, and construct a directed graph of the form:
|
||||
*
|
||||
* home
|
||||
* |
|
||||
* +----+----+
|
||||
* | |
|
||||
* v v ws
|
||||
* bar baz |
|
||||
* | |
|
||||
* v v
|
||||
* @yesterday ----> foo
|
||||
*
|
||||
* In order to construct this graph, we have to walk every dataset in the pool,
|
||||
* because the clone parent is stored as a property of the child, not the
|
||||
* parent. The parent only keeps track of the number of clones.
|
||||
*
|
||||
* In the normal case (without clones) this would be rather expensive. To avoid
|
||||
* unnecessary computation, we first try a walk of the subtree hierarchy
|
||||
* starting from the initial node. At each dataset, we construct a node in the
|
||||
* graph and an edge leading from its parent. If we don't see any snapshots
|
||||
* with a non-zero clone count, then we are finished.
|
||||
*
|
||||
* If we do find a cloned snapshot, then we finish the walk of the current
|
||||
* subtree, but indicate that we need to do a complete walk. We then perform a
|
||||
* global walk of all datasets, avoiding the subtree we already processed.
|
||||
*
|
||||
* At the end of this, we'll end up with a directed graph of all relevant (and
|
||||
* possible some irrelevant) datasets in the system. We need to both find our
|
||||
* limiting subgraph and determine a safe ordering in which to destroy the
|
||||
* datasets. We do a topological ordering of our graph starting at our target
|
||||
* dataset, and then walk the results in reverse.
|
||||
*
|
||||
* It's possible for the graph to have cycles if, for example, the user renames
|
||||
* a clone to be the parent of its origin snapshot. The user can request to
|
||||
* generate an error in this case, or ignore the cycle and continue.
|
||||
*
|
||||
* When removing datasets, we want to destroy the snapshots in chronological
|
||||
* order (because this is the most efficient method). In order to accomplish
|
||||
* this, we store the creation transaction group with each vertex and keep each
|
||||
* vertex's edges sorted according to this value. The topological sort will
|
||||
* automatically walk the snapshots in the correct order.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <libintl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
#include "zfs_namecheck.h"
|
||||
|
||||
#define MIN_EDGECOUNT 4
|
||||
|
||||
/*
|
||||
* Vertex structure. Indexed by dataset name, this structure maintains a list
|
||||
* of edges to other vertices.
|
||||
*/
|
||||
struct zfs_edge;
|
||||
typedef struct zfs_vertex {
|
||||
char zv_dataset[ZFS_MAXNAMELEN];
|
||||
struct zfs_vertex *zv_next;
|
||||
int zv_visited;
|
||||
uint64_t zv_txg;
|
||||
struct zfs_edge **zv_edges;
|
||||
int zv_edgecount;
|
||||
int zv_edgealloc;
|
||||
} zfs_vertex_t;
|
||||
|
||||
enum {
|
||||
VISIT_SEEN = 1,
|
||||
VISIT_SORT_PRE,
|
||||
VISIT_SORT_POST
|
||||
};
|
||||
|
||||
/*
|
||||
* Edge structure. Simply maintains a pointer to the destination vertex. There
|
||||
* is no need to store the source vertex, since we only use edges in the context
|
||||
* of the source vertex.
|
||||
*/
|
||||
typedef struct zfs_edge {
|
||||
zfs_vertex_t *ze_dest;
|
||||
struct zfs_edge *ze_next;
|
||||
} zfs_edge_t;
|
||||
|
||||
#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
|
||||
|
||||
/*
|
||||
* Graph structure. Vertices are maintained in a hash indexed by dataset name.
|
||||
*/
|
||||
typedef struct zfs_graph {
|
||||
zfs_vertex_t **zg_hash;
|
||||
size_t zg_size;
|
||||
size_t zg_nvertex;
|
||||
const char *zg_root;
|
||||
int zg_clone_count;
|
||||
} zfs_graph_t;
|
||||
|
||||
/*
|
||||
* Allocate a new edge pointing to the target vertex.
|
||||
*/
|
||||
static zfs_edge_t *
|
||||
zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest)
|
||||
{
|
||||
zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t));
|
||||
|
||||
if (zep == NULL)
|
||||
return (NULL);
|
||||
|
||||
zep->ze_dest = dest;
|
||||
|
||||
return (zep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an edge.
|
||||
*/
|
||||
static void
|
||||
zfs_edge_destroy(zfs_edge_t *zep)
|
||||
{
|
||||
free(zep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new vertex with the given name.
|
||||
*/
|
||||
static zfs_vertex_t *
|
||||
zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset)
|
||||
{
|
||||
zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t));
|
||||
|
||||
if (zvp == NULL)
|
||||
return (NULL);
|
||||
|
||||
assert(strlen(dataset) < ZFS_MAXNAMELEN);
|
||||
|
||||
(void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
|
||||
|
||||
if ((zvp->zv_edges = zfs_alloc(hdl,
|
||||
MIN_EDGECOUNT * sizeof (void *))) == NULL) {
|
||||
free(zvp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
zvp->zv_edgealloc = MIN_EDGECOUNT;
|
||||
|
||||
return (zvp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a vertex. Frees up any associated edges.
|
||||
*/
|
||||
static void
|
||||
zfs_vertex_destroy(zfs_vertex_t *zvp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < zvp->zv_edgecount; i++)
|
||||
zfs_edge_destroy(zvp->zv_edges[i]);
|
||||
|
||||
free(zvp->zv_edges);
|
||||
free(zvp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a vertex, add an edge to the destination vertex.
|
||||
*/
|
||||
static int
|
||||
zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp,
|
||||
zfs_vertex_t *dest)
|
||||
{
|
||||
zfs_edge_t *zep = zfs_edge_create(hdl, dest);
|
||||
|
||||
if (zep == NULL)
|
||||
return (-1);
|
||||
|
||||
if (zvp->zv_edgecount == zvp->zv_edgealloc) {
|
||||
void *ptr;
|
||||
|
||||
if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
|
||||
zvp->zv_edgealloc * sizeof (void *),
|
||||
zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
|
||||
return (-1);
|
||||
|
||||
zvp->zv_edges = ptr;
|
||||
zvp->zv_edgealloc *= 2;
|
||||
}
|
||||
|
||||
zvp->zv_edges[zvp->zv_edgecount++] = zep;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_edge_compare(const void *a, const void *b)
|
||||
{
|
||||
const zfs_edge_t *ea = *((zfs_edge_t **)a);
|
||||
const zfs_edge_t *eb = *((zfs_edge_t **)b);
|
||||
|
||||
if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
|
||||
return (-1);
|
||||
if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort the given vertex edges according to the creation txg of each vertex.
|
||||
*/
|
||||
static void
|
||||
zfs_vertex_sort_edges(zfs_vertex_t *zvp)
|
||||
{
|
||||
if (zvp->zv_edgecount == 0)
|
||||
return;
|
||||
|
||||
qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
|
||||
zfs_edge_compare);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a new graph object. We allow the size to be specified as a
|
||||
* parameter so in the future we can size the hash according to the number of
|
||||
* datasets in the pool.
|
||||
*/
|
||||
static zfs_graph_t *
|
||||
zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
|
||||
{
|
||||
zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
|
||||
|
||||
if (zgp == NULL)
|
||||
return (NULL);
|
||||
|
||||
zgp->zg_size = size;
|
||||
if ((zgp->zg_hash = zfs_alloc(hdl,
|
||||
size * sizeof (zfs_vertex_t *))) == NULL) {
|
||||
free(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
zgp->zg_root = dataset;
|
||||
zgp->zg_clone_count = 0;
|
||||
|
||||
return (zgp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a graph object. We have to iterate over all the hash chains,
|
||||
* destroying each vertex in the process.
|
||||
*/
|
||||
static void
|
||||
zfs_graph_destroy(zfs_graph_t *zgp)
|
||||
{
|
||||
int i;
|
||||
zfs_vertex_t *current, *next;
|
||||
|
||||
for (i = 0; i < zgp->zg_size; i++) {
|
||||
current = zgp->zg_hash[i];
|
||||
while (current != NULL) {
|
||||
next = current->zv_next;
|
||||
zfs_vertex_destroy(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
free(zgp->zg_hash);
|
||||
free(zgp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Graph hash function. Classic bernstein k=33 hash function, taken from
|
||||
* usr/src/cmd/sgs/tools/common/strhash.c
|
||||
*/
|
||||
static size_t
|
||||
zfs_graph_hash(zfs_graph_t *zgp, const char *str)
|
||||
{
|
||||
size_t hash = 5381;
|
||||
int c;
|
||||
|
||||
while ((c = *str++) != 0)
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
|
||||
return (hash % zgp->zg_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a dataset name, finds the associated vertex, creating it if necessary.
|
||||
*/
|
||||
static zfs_vertex_t *
|
||||
zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset,
|
||||
uint64_t txg)
|
||||
{
|
||||
size_t idx = zfs_graph_hash(zgp, dataset);
|
||||
zfs_vertex_t *zvp;
|
||||
|
||||
for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
|
||||
if (strcmp(zvp->zv_dataset, dataset) == 0) {
|
||||
if (zvp->zv_txg == 0)
|
||||
zvp->zv_txg = txg;
|
||||
return (zvp);
|
||||
}
|
||||
}
|
||||
|
||||
if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
zvp->zv_next = zgp->zg_hash[idx];
|
||||
zvp->zv_txg = txg;
|
||||
zgp->zg_hash[idx] = zvp;
|
||||
zgp->zg_nvertex++;
|
||||
|
||||
return (zvp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given two dataset names, create an edge between them. For the source vertex,
|
||||
* mark 'zv_visited' to indicate that we have seen this vertex, and not simply
|
||||
* created it as a destination of another edge. If 'dest' is NULL, then this
|
||||
* is an individual vertex (i.e. the starting vertex), so don't add an edge.
|
||||
*/
|
||||
static int
|
||||
zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
|
||||
const char *dest, uint64_t txg)
|
||||
{
|
||||
zfs_vertex_t *svp, *dvp;
|
||||
|
||||
if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
|
||||
return (-1);
|
||||
svp->zv_visited = VISIT_SEEN;
|
||||
if (dest != NULL) {
|
||||
dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
|
||||
if (dvp == NULL)
|
||||
return (-1);
|
||||
if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children of the given dataset, adding any vertices
|
||||
* as necessary. Returns -1 if there was an error, or 0 otherwise.
|
||||
* This is a simple recursive algorithm - the ZFS namespace typically
|
||||
* is very flat. We manually invoke the necessary ioctl() calls to
|
||||
* avoid the overhead and additional semantics of zfs_open().
|
||||
*/
|
||||
static int
|
||||
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_vertex_t *zvp;
|
||||
|
||||
/*
|
||||
* Look up the source vertex, and avoid it if we've seen it before.
|
||||
*/
|
||||
zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
|
||||
if (zvp == NULL)
|
||||
return (-1);
|
||||
if (zvp->zv_visited == VISIT_SEEN)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Iterate over all children
|
||||
*/
|
||||
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
|
||||
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
|
||||
|
||||
/*
|
||||
* Ignore private dataset names.
|
||||
*/
|
||||
if (dataset_name_hidden(zc.zc_name))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Get statistics for this dataset, to determine the type of the
|
||||
* dataset and clone statistics. If this fails, the dataset has
|
||||
* since been removed, and we're pretty much screwed anyway.
|
||||
*/
|
||||
zc.zc_objset_stats.dds_origin[0] = '\0';
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
|
||||
continue;
|
||||
|
||||
if (zc.zc_objset_stats.dds_origin[0] != '\0') {
|
||||
if (zfs_graph_add(hdl, zgp,
|
||||
zc.zc_objset_stats.dds_origin, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (-1);
|
||||
/*
|
||||
* Count origins only if they are contained in the graph
|
||||
*/
|
||||
if (isa_child_of(zc.zc_objset_stats.dds_origin,
|
||||
zgp->zg_root))
|
||||
zgp->zg_clone_count--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an edge between the parent and the child.
|
||||
*/
|
||||
if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Recursively visit child
|
||||
*/
|
||||
if (iterate_children(hdl, zgp, zc.zc_name))
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now iterate over all snapshots.
|
||||
*/
|
||||
bzero(&zc, sizeof (zc));
|
||||
|
||||
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||
ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
|
||||
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
|
||||
|
||||
/*
|
||||
* Get statistics for this dataset, to determine the type of the
|
||||
* dataset and clone statistics. If this fails, the dataset has
|
||||
* since been removed, and we're pretty much screwed anyway.
|
||||
*/
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Add an edge between the parent and the child.
|
||||
*/
|
||||
if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (-1);
|
||||
|
||||
zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
|
||||
}
|
||||
|
||||
zvp->zv_visited = VISIT_SEEN;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns false if there are no snapshots with dependent clones in this
|
||||
* subtree or if all of those clones are also in this subtree. Returns
|
||||
* true if there is an error or there are external dependents.
|
||||
*/
|
||||
static boolean_t
|
||||
external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
/*
|
||||
* Check whether this dataset is a clone or has clones since
|
||||
* iterate_children() only checks the children.
|
||||
*/
|
||||
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
|
||||
return (B_TRUE);
|
||||
|
||||
if (zc.zc_objset_stats.dds_origin[0] != '\0') {
|
||||
if (zfs_graph_add(hdl, zgp,
|
||||
zc.zc_objset_stats.dds_origin, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (B_TRUE);
|
||||
if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
|
||||
zgp->zg_clone_count--;
|
||||
}
|
||||
|
||||
if ((zc.zc_objset_stats.dds_num_clones) ||
|
||||
iterate_children(hdl, zgp, dataset))
|
||||
return (B_TRUE);
|
||||
|
||||
return (zgp->zg_clone_count != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a complete graph of all necessary vertices. First, iterate over
|
||||
* only our object's children. If no cloned snapshots are found, or all of
|
||||
* the cloned snapshots are in this subtree then return a graph of the subtree.
|
||||
* Otherwise, start at the root of the pool and iterate over all datasets.
|
||||
*/
|
||||
static zfs_graph_t *
|
||||
construct_graph(libzfs_handle_t *hdl, const char *dataset)
|
||||
{
|
||||
zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
|
||||
int ret = 0;
|
||||
|
||||
if (zgp == NULL)
|
||||
return (zgp);
|
||||
|
||||
if ((strchr(dataset, '/') == NULL) ||
|
||||
(external_dependents(hdl, zgp, dataset))) {
|
||||
/*
|
||||
* Determine pool name and try again.
|
||||
*/
|
||||
int len = strcspn(dataset, "/@") + 1;
|
||||
char *pool = zfs_alloc(hdl, len);
|
||||
|
||||
if (pool == NULL) {
|
||||
zfs_graph_destroy(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
(void) strlcpy(pool, dataset, len);
|
||||
|
||||
if (iterate_children(hdl, zgp, pool) == -1 ||
|
||||
zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
|
||||
free(pool);
|
||||
zfs_graph_destroy(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
free(pool);
|
||||
}
|
||||
|
||||
if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
|
||||
zfs_graph_destroy(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (zgp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a graph, do a recursive topological sort into the given array. This is
|
||||
* really just a depth first search, so that the deepest nodes appear first.
|
||||
* hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
|
||||
*/
|
||||
static int
|
||||
topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
|
||||
size_t *idx, zfs_vertex_t *zgv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
|
||||
/*
|
||||
* If we've already seen this vertex as part of our depth-first
|
||||
* search, then we have a cyclic dependency, and we must return
|
||||
* an error.
|
||||
*/
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"recursive dependency at '%s'"),
|
||||
zgv->zv_dataset);
|
||||
return (zfs_error(hdl, EZFS_RECURSIVE,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot determine dependent datasets")));
|
||||
} else if (zgv->zv_visited >= VISIT_SORT_PRE) {
|
||||
/*
|
||||
* If we've already processed this as part of the topological
|
||||
* sort, then don't bother doing so again.
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
zgv->zv_visited = VISIT_SORT_PRE;
|
||||
|
||||
/* avoid doing a search if we don't have to */
|
||||
zfs_vertex_sort_edges(zgv);
|
||||
for (i = 0; i < zgv->zv_edgecount; i++) {
|
||||
if (topo_sort(hdl, allowrecursion, result, idx,
|
||||
zgv->zv_edges[i]->ze_dest) != 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* we may have visited this in the course of the above */
|
||||
if (zgv->zv_visited == VISIT_SORT_POST)
|
||||
return (0);
|
||||
|
||||
if ((result[*idx] = zfs_alloc(hdl,
|
||||
strlen(zgv->zv_dataset) + 1)) == NULL)
|
||||
return (-1);
|
||||
|
||||
(void) strcpy(result[*idx], zgv->zv_dataset);
|
||||
*idx += 1;
|
||||
zgv->zv_visited = VISIT_SORT_POST;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The only public interface for this file. Do the dirty work of constructing a
|
||||
* child list for the given object. Construct the graph, do the toplogical
|
||||
* sort, and then return the array of strings to the caller.
|
||||
*
|
||||
* The 'allowrecursion' parameter controls behavior when cycles are found. If
|
||||
* it is set, the the cycle is ignored and the results returned as if the cycle
|
||||
* did not exist. If it is not set, then the routine will generate an error if
|
||||
* a cycle is found.
|
||||
*/
|
||||
int
|
||||
get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
|
||||
const char *dataset, char ***result, size_t *count)
|
||||
{
|
||||
zfs_graph_t *zgp;
|
||||
zfs_vertex_t *zvp;
|
||||
|
||||
if ((zgp = construct_graph(hdl, dataset)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if ((*result = zfs_alloc(hdl,
|
||||
zgp->zg_nvertex * sizeof (char *))) == NULL) {
|
||||
zfs_graph_destroy(zgp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
|
||||
free(*result);
|
||||
zfs_graph_destroy(zgp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
|
||||
free(*result);
|
||||
zfs_graph_destroy(zgp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get rid of the last entry, which is our starting vertex and not
|
||||
* strictly a dependent.
|
||||
*/
|
||||
assert(*count > 0);
|
||||
free((*result)[*count - 1]);
|
||||
(*count)--;
|
||||
|
||||
zfs_graph_destroy(zgp);
|
||||
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* CDDL HEADER SART
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _LIBFS_IMPL_H
|
||||
#define _LIBFS_IMPL_H
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/zfs_acl.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/nvpair.h>
|
||||
|
||||
#include <libuutil.h>
|
||||
#include <libzfs.h>
|
||||
#include <libshare.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef VERIFY
|
||||
#undef VERIFY
|
||||
#endif
|
||||
#define VERIFY verify
|
||||
|
||||
struct libzfs_handle {
|
||||
int libzfs_error;
|
||||
int libzfs_fd;
|
||||
FILE *libzfs_mnttab;
|
||||
FILE *libzfs_sharetab;
|
||||
zpool_handle_t *libzfs_pool_handles;
|
||||
uu_avl_pool_t *libzfs_ns_avlpool;
|
||||
uu_avl_t *libzfs_ns_avl;
|
||||
uint64_t libzfs_ns_gen;
|
||||
int libzfs_desc_active;
|
||||
char libzfs_action[1024];
|
||||
char libzfs_desc[1024];
|
||||
char *libzfs_log_str;
|
||||
int libzfs_printerr;
|
||||
void *libzfs_sharehdl; /* libshare handle */
|
||||
uint_t libzfs_shareflags;
|
||||
};
|
||||
#define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */
|
||||
|
||||
struct zfs_handle {
|
||||
libzfs_handle_t *zfs_hdl;
|
||||
zpool_handle_t *zpool_hdl;
|
||||
char zfs_name[ZFS_MAXNAMELEN];
|
||||
zfs_type_t zfs_type; /* type including snapshot */
|
||||
zfs_type_t zfs_head_type; /* type excluding snapshot */
|
||||
dmu_objset_stats_t zfs_dmustats;
|
||||
nvlist_t *zfs_props;
|
||||
nvlist_t *zfs_user_props;
|
||||
boolean_t zfs_mntcheck;
|
||||
char *zfs_mntopts;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is different from checking zfs_type, because it will also catch
|
||||
* snapshots of volumes.
|
||||
*/
|
||||
#define ZFS_IS_VOLUME(zhp) ((zhp)->zfs_head_type == ZFS_TYPE_VOLUME)
|
||||
|
||||
struct zpool_handle {
|
||||
libzfs_handle_t *zpool_hdl;
|
||||
zpool_handle_t *zpool_next;
|
||||
char zpool_name[ZPOOL_MAXNAMELEN];
|
||||
int zpool_state;
|
||||
size_t zpool_config_size;
|
||||
nvlist_t *zpool_config;
|
||||
nvlist_t *zpool_old_config;
|
||||
nvlist_t *zpool_props;
|
||||
diskaddr_t zpool_start_block;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PROTO_NFS = 0,
|
||||
PROTO_SMB = 1,
|
||||
PROTO_END = 2
|
||||
} zfs_share_proto_t;
|
||||
|
||||
/*
|
||||
* The following can be used as a bitmask and any new values
|
||||
* added must preserve that capability.
|
||||
*/
|
||||
typedef enum {
|
||||
SHARED_NOT_SHARED = 0x0,
|
||||
SHARED_ISCSI = 0x1,
|
||||
SHARED_NFS = 0x2,
|
||||
SHARED_SMB = 0x4
|
||||
} zfs_share_type_t;
|
||||
|
||||
int zfs_error(libzfs_handle_t *, int, const char *);
|
||||
int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
|
||||
void zfs_error_aux(libzfs_handle_t *, const char *, ...);
|
||||
void *zfs_alloc(libzfs_handle_t *, size_t);
|
||||
void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t);
|
||||
char *zfs_strdup(libzfs_handle_t *, const char *);
|
||||
int no_memory(libzfs_handle_t *);
|
||||
|
||||
int zfs_standard_error(libzfs_handle_t *, int, const char *);
|
||||
int zfs_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
|
||||
int zpool_standard_error(libzfs_handle_t *, int, const char *);
|
||||
int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
|
||||
|
||||
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
|
||||
size_t *);
|
||||
|
||||
|
||||
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
|
||||
nvlist_t *, char **, uint64_t *, const char *);
|
||||
int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
|
||||
zfs_type_t type);
|
||||
|
||||
/*
|
||||
* Use this changelist_gather() flag to force attempting mounts
|
||||
* on each change node regardless of whether or not it is currently
|
||||
* mounted.
|
||||
*/
|
||||
#define CL_GATHER_MOUNT_ALWAYS 1
|
||||
|
||||
typedef struct prop_changelist prop_changelist_t;
|
||||
|
||||
int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t);
|
||||
int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
|
||||
int zcmd_write_conf_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
|
||||
int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *);
|
||||
int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **);
|
||||
void zcmd_free_nvlists(zfs_cmd_t *);
|
||||
|
||||
int changelist_prefix(prop_changelist_t *);
|
||||
int changelist_postfix(prop_changelist_t *);
|
||||
void changelist_rename(prop_changelist_t *, const char *, const char *);
|
||||
void changelist_remove(prop_changelist_t *, const char *);
|
||||
void changelist_free(prop_changelist_t *);
|
||||
prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int, int);
|
||||
int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *);
|
||||
int changelist_haszonedchild(prop_changelist_t *);
|
||||
|
||||
void remove_mountpoint(zfs_handle_t *);
|
||||
int create_parents(libzfs_handle_t *, char *, int);
|
||||
boolean_t isa_child_of(const char *dataset, const char *parent);
|
||||
|
||||
zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
|
||||
|
||||
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 *);
|
||||
int zpool_iter_zvol(zpool_handle_t *, int (*)(const char *, void *), void *);
|
||||
boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
|
||||
|
||||
void namespace_clear(libzfs_handle_t *);
|
||||
|
||||
/*
|
||||
* libshare (sharemgr) interfaces used internally.
|
||||
*/
|
||||
|
||||
extern int zfs_init_libshare(libzfs_handle_t *, int);
|
||||
extern void zfs_uninit_libshare(libzfs_handle_t *);
|
||||
extern int zfs_parse_options(char *, zfs_share_proto_t);
|
||||
|
||||
extern int zfs_unshare_proto(zfs_handle_t *zhp,
|
||||
const char *, zfs_share_proto_t *);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBFS_IMPL_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains the functions which analyze the status of a pool. This
|
||||
* include both the status of an active pool, as well as the status exported
|
||||
* pools. Returns one of the ZPOOL_STATUS_* defines describing the status of
|
||||
* the pool. This status is independent (to a certain degree) from the state of
|
||||
* the pool. A pool's state describes only whether or not it is capable of
|
||||
* providing the necessary fault tolerance for data. The status describes the
|
||||
* overall status of devices. A pool that is online can still have a device
|
||||
* that is experiencing errors.
|
||||
*
|
||||
* Only a subset of the possible faults can be detected using 'zpool status',
|
||||
* and not all possible errors correspond to a FMA message ID. The explanation
|
||||
* is left up to the caller, depending on whether it is a live pool or an
|
||||
* import.
|
||||
*/
|
||||
|
||||
#include <libzfs.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
/*
|
||||
* Message ID table. This must be kept in sync with the ZPOOL_STATUS_* defines
|
||||
* in libzfs.h. Note that there are some status results which go past the end
|
||||
* of this table, and hence have no associated message ID.
|
||||
*/
|
||||
static char *zfs_msgid_table[] = {
|
||||
"ZFS-8000-14",
|
||||
"ZFS-8000-2Q",
|
||||
"ZFS-8000-3C",
|
||||
"ZFS-8000-4J",
|
||||
"ZFS-8000-5E",
|
||||
"ZFS-8000-6X",
|
||||
"ZFS-8000-72",
|
||||
"ZFS-8000-8A",
|
||||
"ZFS-8000-9P",
|
||||
"ZFS-8000-A5",
|
||||
"ZFS-8000-EY",
|
||||
"ZFS-8000-HC",
|
||||
"ZFS-8000-JQ",
|
||||
"ZFS-8000-K4",
|
||||
};
|
||||
|
||||
#define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_missing(uint64_t state, uint64_t aux, uint64_t errs)
|
||||
{
|
||||
return (state == VDEV_STATE_CANT_OPEN &&
|
||||
aux == VDEV_AUX_OPEN_FAILED);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_faulted(uint64_t state, uint64_t aux, uint64_t errs)
|
||||
{
|
||||
return (state == VDEV_STATE_FAULTED);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_errors(uint64_t state, uint64_t aux, uint64_t errs)
|
||||
{
|
||||
return (state == VDEV_STATE_DEGRADED || errs != 0);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_broken(uint64_t state, uint64_t aux, uint64_t errs)
|
||||
{
|
||||
return (state == VDEV_STATE_CANT_OPEN);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs)
|
||||
{
|
||||
return (state == VDEV_STATE_OFFLINE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect if any leaf devices that have seen errors or could not be opened.
|
||||
*/
|
||||
static boolean_t
|
||||
find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t))
|
||||
{
|
||||
nvlist_t **child;
|
||||
vdev_stat_t *vs;
|
||||
uint_t c, children;
|
||||
char *type;
|
||||
|
||||
/*
|
||||
* Ignore problems within a 'replacing' vdev, since we're presumably in
|
||||
* the process of repairing any such errors, and don't want to call them
|
||||
* out again. We'll pick up the fact that a resilver is happening
|
||||
* later.
|
||||
*/
|
||||
verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0);
|
||||
if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
|
||||
return (B_FALSE);
|
||||
|
||||
if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,
|
||||
&children) == 0) {
|
||||
for (c = 0; c < children; c++)
|
||||
if (find_vdev_problem(child[c], func))
|
||||
return (B_TRUE);
|
||||
} else {
|
||||
verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_STATS,
|
||||
(uint64_t **)&vs, &c) == 0);
|
||||
|
||||
if (func(vs->vs_state, vs->vs_aux,
|
||||
vs->vs_read_errors +
|
||||
vs->vs_write_errors +
|
||||
vs->vs_checksum_errors))
|
||||
return (B_TRUE);
|
||||
}
|
||||
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Active pool health status.
|
||||
*
|
||||
* To determine the status for a pool, we make several passes over the config,
|
||||
* picking the most egregious error we find. In order of importance, we do the
|
||||
* following:
|
||||
*
|
||||
* - Check for a complete and valid configuration
|
||||
* - Look for any faulted or missing devices in a non-replicated config
|
||||
* - Check for any data errors
|
||||
* - Check for any faulted or missing devices in a replicated config
|
||||
* - Look for any devices showing errors
|
||||
* - Check for any resilvering devices
|
||||
*
|
||||
* There can obviously be multiple errors within a single pool, so this routine
|
||||
* only picks the most damaging of all the current errors to report.
|
||||
*/
|
||||
static zpool_status_t
|
||||
check_status(nvlist_t *config, boolean_t isimport)
|
||||
{
|
||||
nvlist_t *nvroot;
|
||||
vdev_stat_t *vs;
|
||||
uint_t vsc;
|
||||
uint64_t nerr;
|
||||
uint64_t version;
|
||||
uint64_t stateval;
|
||||
uint64_t suspended;
|
||||
uint64_t hostid = 0;
|
||||
|
||||
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
|
||||
&version) == 0);
|
||||
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
||||
&nvroot) == 0);
|
||||
verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
|
||||
(uint64_t **)&vs, &vsc) == 0);
|
||||
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
|
||||
&stateval) == 0);
|
||||
(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);
|
||||
|
||||
/*
|
||||
* Pool last accessed by another system.
|
||||
*/
|
||||
if (hostid != 0 && (unsigned long)hostid != gethostid() &&
|
||||
stateval == POOL_STATE_ACTIVE)
|
||||
return (ZPOOL_STATUS_HOSTID_MISMATCH);
|
||||
|
||||
/*
|
||||
* Newer on-disk version.
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_VERSION_NEWER)
|
||||
return (ZPOOL_STATUS_VERSION_NEWER);
|
||||
|
||||
/*
|
||||
* Check that the config is complete.
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_BAD_GUID_SUM)
|
||||
return (ZPOOL_STATUS_BAD_GUID_SUM);
|
||||
|
||||
/*
|
||||
* Check whether the pool has suspended due to failed I/O.
|
||||
*/
|
||||
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,
|
||||
&suspended) == 0) {
|
||||
if (suspended == ZIO_FAILURE_MODE_CONTINUE)
|
||||
return (ZPOOL_STATUS_IO_FAILURE_CONTINUE);
|
||||
return (ZPOOL_STATUS_IO_FAILURE_WAIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Could not read a log.
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_BAD_LOG) {
|
||||
return (ZPOOL_STATUS_BAD_LOG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bad devices in non-replicated config.
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
find_vdev_problem(nvroot, vdev_faulted))
|
||||
return (ZPOOL_STATUS_FAULTED_DEV_NR);
|
||||
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
find_vdev_problem(nvroot, vdev_missing))
|
||||
return (ZPOOL_STATUS_MISSING_DEV_NR);
|
||||
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
find_vdev_problem(nvroot, vdev_broken))
|
||||
return (ZPOOL_STATUS_CORRUPT_LABEL_NR);
|
||||
|
||||
/*
|
||||
* Corrupted pool metadata
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
|
||||
return (ZPOOL_STATUS_CORRUPT_POOL);
|
||||
|
||||
/*
|
||||
* Persistent data errors.
|
||||
*/
|
||||
if (!isimport) {
|
||||
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
|
||||
&nerr) == 0 && nerr != 0)
|
||||
return (ZPOOL_STATUS_CORRUPT_DATA);
|
||||
}
|
||||
|
||||
/*
|
||||
* Missing devices in a replicated config.
|
||||
*/
|
||||
if (find_vdev_problem(nvroot, vdev_faulted))
|
||||
return (ZPOOL_STATUS_FAULTED_DEV_R);
|
||||
if (find_vdev_problem(nvroot, vdev_missing))
|
||||
return (ZPOOL_STATUS_MISSING_DEV_R);
|
||||
if (find_vdev_problem(nvroot, vdev_broken))
|
||||
return (ZPOOL_STATUS_CORRUPT_LABEL_R);
|
||||
|
||||
/*
|
||||
* Devices with errors
|
||||
*/
|
||||
if (!isimport && find_vdev_problem(nvroot, vdev_errors))
|
||||
return (ZPOOL_STATUS_FAILING_DEV);
|
||||
|
||||
/*
|
||||
* Offlined devices
|
||||
*/
|
||||
if (find_vdev_problem(nvroot, vdev_offlined))
|
||||
return (ZPOOL_STATUS_OFFLINE_DEV);
|
||||
|
||||
/*
|
||||
* Currently resilvering
|
||||
*/
|
||||
if (!vs->vs_scrub_complete && vs->vs_scrub_type == POOL_SCRUB_RESILVER)
|
||||
return (ZPOOL_STATUS_RESILVERING);
|
||||
|
||||
/*
|
||||
* Outdated, but usable, version
|
||||
*/
|
||||
if (version < SPA_VERSION)
|
||||
return (ZPOOL_STATUS_VERSION_OLDER);
|
||||
|
||||
return (ZPOOL_STATUS_OK);
|
||||
}
|
||||
|
||||
zpool_status_t
|
||||
zpool_get_status(zpool_handle_t *zhp, char **msgid)
|
||||
{
|
||||
zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE);
|
||||
|
||||
if (ret >= NMSGID)
|
||||
*msgid = NULL;
|
||||
else
|
||||
*msgid = zfs_msgid_table[ret];
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
zpool_status_t
|
||||
zpool_import_status(nvlist_t *config, char **msgid)
|
||||
{
|
||||
zpool_status_t ret = check_status(config, B_TRUE);
|
||||
|
||||
if (ret >= NMSGID)
|
||||
*msgid = NULL;
|
||||
else
|
||||
*msgid = zfs_msgid_table[ret];
|
||||
|
||||
return (ret);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
int taskq_now;
|
||||
taskq_t *system_taskq;
|
||||
|
||||
typedef struct task {
|
||||
struct task *task_next;
|
||||
struct task *task_prev;
|
||||
task_func_t *task_func;
|
||||
void *task_arg;
|
||||
} task_t;
|
||||
|
||||
#define TASKQ_ACTIVE 0x00010000
|
||||
|
||||
struct taskq {
|
||||
kmutex_t tq_lock;
|
||||
krwlock_t tq_threadlock;
|
||||
kcondvar_t tq_dispatch_cv;
|
||||
kcondvar_t tq_wait_cv;
|
||||
thread_t *tq_threadlist;
|
||||
int tq_flags;
|
||||
int tq_active;
|
||||
int tq_nthreads;
|
||||
int tq_nalloc;
|
||||
int tq_minalloc;
|
||||
int tq_maxalloc;
|
||||
task_t *tq_freelist;
|
||||
task_t tq_task;
|
||||
};
|
||||
|
||||
static task_t *
|
||||
task_alloc(taskq_t *tq, int tqflags)
|
||||
{
|
||||
task_t *t;
|
||||
|
||||
if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
|
||||
tq->tq_freelist = t->task_next;
|
||||
} else {
|
||||
mutex_exit(&tq->tq_lock);
|
||||
if (tq->tq_nalloc >= tq->tq_maxalloc) {
|
||||
if (!(tqflags & KM_SLEEP)) {
|
||||
mutex_enter(&tq->tq_lock);
|
||||
return (NULL);
|
||||
}
|
||||
/*
|
||||
* We don't want to exceed tq_maxalloc, but we can't
|
||||
* wait for other tasks to complete (and thus free up
|
||||
* task structures) without risking deadlock with
|
||||
* the caller. So, we just delay for one second
|
||||
* to throttle the allocation rate.
|
||||
*/
|
||||
delay(hz);
|
||||
}
|
||||
t = kmem_alloc(sizeof (task_t), tqflags);
|
||||
mutex_enter(&tq->tq_lock);
|
||||
if (t != NULL)
|
||||
tq->tq_nalloc++;
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
static void
|
||||
task_free(taskq_t *tq, task_t *t)
|
||||
{
|
||||
if (tq->tq_nalloc <= tq->tq_minalloc) {
|
||||
t->task_next = tq->tq_freelist;
|
||||
tq->tq_freelist = t;
|
||||
} else {
|
||||
tq->tq_nalloc--;
|
||||
mutex_exit(&tq->tq_lock);
|
||||
kmem_free(t, sizeof (task_t));
|
||||
mutex_enter(&tq->tq_lock);
|
||||
}
|
||||
}
|
||||
|
||||
taskqid_t
|
||||
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
|
||||
{
|
||||
task_t *t;
|
||||
|
||||
if (taskq_now) {
|
||||
func(arg);
|
||||
return (1);
|
||||
}
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
ASSERT(tq->tq_flags & TASKQ_ACTIVE);
|
||||
if ((t = task_alloc(tq, tqflags)) == NULL) {
|
||||
mutex_exit(&tq->tq_lock);
|
||||
return (0);
|
||||
}
|
||||
t->task_next = &tq->tq_task;
|
||||
t->task_prev = tq->tq_task.task_prev;
|
||||
t->task_next->task_prev = t;
|
||||
t->task_prev->task_next = t;
|
||||
t->task_func = func;
|
||||
t->task_arg = arg;
|
||||
cv_signal(&tq->tq_dispatch_cv);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_wait(taskq_t *tq)
|
||||
{
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
|
||||
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
}
|
||||
|
||||
static void *
|
||||
taskq_thread(void *arg)
|
||||
{
|
||||
taskq_t *tq = arg;
|
||||
task_t *t;
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (tq->tq_flags & TASKQ_ACTIVE) {
|
||||
if ((t = tq->tq_task.task_next) == &tq->tq_task) {
|
||||
if (--tq->tq_active == 0)
|
||||
cv_broadcast(&tq->tq_wait_cv);
|
||||
cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
|
||||
tq->tq_active++;
|
||||
continue;
|
||||
}
|
||||
t->task_prev->task_next = t->task_next;
|
||||
t->task_next->task_prev = t->task_prev;
|
||||
mutex_exit(&tq->tq_lock);
|
||||
|
||||
rw_enter(&tq->tq_threadlock, RW_READER);
|
||||
t->task_func(t->task_arg);
|
||||
rw_exit(&tq->tq_threadlock);
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
task_free(tq, t);
|
||||
}
|
||||
tq->tq_nthreads--;
|
||||
cv_broadcast(&tq->tq_wait_cv);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
taskq_t *
|
||||
taskq_create(const char *name, int nthreads, pri_t pri,
|
||||
int minalloc, int maxalloc, uint_t flags)
|
||||
{
|
||||
taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
|
||||
int t;
|
||||
|
||||
rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
|
||||
mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL);
|
||||
cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL);
|
||||
tq->tq_flags = flags | TASKQ_ACTIVE;
|
||||
tq->tq_active = nthreads;
|
||||
tq->tq_nthreads = nthreads;
|
||||
tq->tq_minalloc = minalloc;
|
||||
tq->tq_maxalloc = maxalloc;
|
||||
tq->tq_task.task_next = &tq->tq_task;
|
||||
tq->tq_task.task_prev = &tq->tq_task;
|
||||
tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
|
||||
|
||||
if (flags & TASKQ_PREPOPULATE) {
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (minalloc-- > 0)
|
||||
task_free(tq, task_alloc(tq, KM_SLEEP));
|
||||
mutex_exit(&tq->tq_lock);
|
||||
}
|
||||
|
||||
for (t = 0; t < nthreads; t++)
|
||||
(void) thr_create(0, 0, taskq_thread,
|
||||
tq, THR_BOUND, &tq->tq_threadlist[t]);
|
||||
|
||||
return (tq);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_destroy(taskq_t *tq)
|
||||
{
|
||||
int t;
|
||||
int nthreads = tq->tq_nthreads;
|
||||
|
||||
taskq_wait(tq);
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
|
||||
tq->tq_flags &= ~TASKQ_ACTIVE;
|
||||
cv_broadcast(&tq->tq_dispatch_cv);
|
||||
|
||||
while (tq->tq_nthreads != 0)
|
||||
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
|
||||
|
||||
tq->tq_minalloc = 0;
|
||||
while (tq->tq_nalloc != 0) {
|
||||
ASSERT(tq->tq_freelist != NULL);
|
||||
task_free(tq, task_alloc(tq, KM_SLEEP));
|
||||
}
|
||||
|
||||
mutex_exit(&tq->tq_lock);
|
||||
|
||||
for (t = 0; t < nthreads; t++)
|
||||
(void) thr_join(tq->tq_threadlist[t], NULL, NULL);
|
||||
|
||||
kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
|
||||
|
||||
rw_destroy(&tq->tq_threadlock);
|
||||
mutex_destroy(&tq->tq_lock);
|
||||
cv_destroy(&tq->tq_dispatch_cv);
|
||||
cv_destroy(&tq->tq_wait_cv);
|
||||
|
||||
kmem_free(tq, sizeof (taskq_t));
|
||||
}
|
||||
|
||||
int
|
||||
taskq_member(taskq_t *tq, void *t)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (taskq_now)
|
||||
return (1);
|
||||
|
||||
for (i = 0; i < tq->tq_nthreads; i++)
|
||||
if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
system_taskq_init(void)
|
||||
{
|
||||
system_taskq = taskq_create("system_taskq", 64, minclsyspri, 4, 512,
|
||||
TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/avl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/refcount.h>
|
||||
|
||||
/*
|
||||
* Routines needed by more than one client of libzpool.
|
||||
*/
|
||||
|
||||
void
|
||||
nicenum(uint64_t num, char *buf)
|
||||
{
|
||||
uint64_t n = num;
|
||||
int index = 0;
|
||||
char u;
|
||||
|
||||
while (n >= 1024) {
|
||||
n = (n + (1024 / 2)) / 1024; /* Round up or down */
|
||||
index++;
|
||||
}
|
||||
|
||||
u = " KMGTPE"[index];
|
||||
|
||||
if (index == 0) {
|
||||
(void) sprintf(buf, "%llu", (u_longlong_t)n);
|
||||
} else if (n < 10 && (num & (num - 1)) != 0) {
|
||||
(void) sprintf(buf, "%.2f%c",
|
||||
(double)num / (1ULL << 10 * index), u);
|
||||
} else if (n < 100 && (num & (num - 1)) != 0) {
|
||||
(void) sprintf(buf, "%.1f%c",
|
||||
(double)num / (1ULL << 10 * index), u);
|
||||
} else {
|
||||
(void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent)
|
||||
{
|
||||
vdev_stat_t *vs;
|
||||
vdev_stat_t v0 = { 0 };
|
||||
uint64_t sec;
|
||||
uint64_t is_log = 0;
|
||||
nvlist_t **child;
|
||||
uint_t c, children;
|
||||
char used[6], avail[6];
|
||||
char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6];
|
||||
char *prefix = "";
|
||||
|
||||
if (indent == 0 && desc != NULL) {
|
||||
(void) printf(" "
|
||||
" capacity operations bandwidth ---- errors ----\n");
|
||||
(void) printf("description "
|
||||
"used avail read write read write read write cksum\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
|
||||
|
||||
if (is_log)
|
||||
prefix = "log ";
|
||||
|
||||
if (nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
|
||||
(uint64_t **)&vs, &c) != 0)
|
||||
vs = &v0;
|
||||
|
||||
sec = MAX(1, vs->vs_timestamp / NANOSEC);
|
||||
|
||||
nicenum(vs->vs_alloc, used);
|
||||
nicenum(vs->vs_space - vs->vs_alloc, avail);
|
||||
nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops);
|
||||
nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops);
|
||||
nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes);
|
||||
nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes);
|
||||
nicenum(vs->vs_read_errors, rerr);
|
||||
nicenum(vs->vs_write_errors, werr);
|
||||
nicenum(vs->vs_checksum_errors, cerr);
|
||||
|
||||
(void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
|
||||
indent, "",
|
||||
prefix,
|
||||
indent + strlen(prefix) - 25 - (vs->vs_space ? 0 : 12),
|
||||
desc,
|
||||
vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
|
||||
vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
|
||||
rops, wops, rbytes, wbytes, rerr, werr, cerr);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_nvlist_array(nv, ctype, &child, &children) != 0)
|
||||
return;
|
||||
|
||||
for (c = 0; c < children; c++) {
|
||||
nvlist_t *cnv = child[c];
|
||||
char *cname, *tname;
|
||||
uint64_t np;
|
||||
if (nvlist_lookup_string(cnv, ZPOOL_CONFIG_PATH, &cname) &&
|
||||
nvlist_lookup_string(cnv, ZPOOL_CONFIG_TYPE, &cname))
|
||||
cname = "<unknown>";
|
||||
tname = calloc(1, strlen(cname) + 2);
|
||||
(void) strcpy(tname, cname);
|
||||
if (nvlist_lookup_uint64(cnv, ZPOOL_CONFIG_NPARITY, &np) == 0)
|
||||
tname[strlen(tname)] = '0' + np;
|
||||
show_vdev_stats(tname, ctype, cnv, indent + 2);
|
||||
free(tname);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
show_pool_stats(spa_t *spa)
|
||||
{
|
||||
nvlist_t *config, *nvroot;
|
||||
char *name;
|
||||
|
||||
VERIFY(spa_get_stats(spa_name(spa), &config, NULL, 0) == 0);
|
||||
|
||||
VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
||||
&nvroot) == 0);
|
||||
VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
|
||||
&name) == 0);
|
||||
|
||||
show_vdev_stats(name, ZPOOL_CONFIG_CHILDREN, nvroot, 0);
|
||||
show_vdev_stats(NULL, ZPOOL_CONFIG_L2CACHE, nvroot, 0);
|
||||
show_vdev_stats(NULL, ZPOOL_CONFIG_SPARES, nvroot, 0);
|
||||
|
||||
nvlist_free(config);
|
||||
}
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/bplist.h>
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
static int
|
||||
bplist_hold(bplist_t *bpl)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&bpl->bpl_lock));
|
||||
if (bpl->bpl_dbuf == NULL) {
|
||||
int err = dmu_bonus_hold(bpl->bpl_mos,
|
||||
bpl->bpl_object, bpl, &bpl->bpl_dbuf);
|
||||
if (err)
|
||||
return (err);
|
||||
bpl->bpl_phys = bpl->bpl_dbuf->db_data;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
bplist_create(objset_t *mos, int blocksize, dmu_tx_t *tx)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = spa_version(dmu_objset_spa(mos)) < SPA_VERSION_BPLIST_ACCOUNT ?
|
||||
BPLIST_SIZE_V0 : sizeof (bplist_phys_t);
|
||||
|
||||
return (dmu_object_alloc(mos, DMU_OT_BPLIST, blocksize,
|
||||
DMU_OT_BPLIST_HDR, size, tx));
|
||||
}
|
||||
|
||||
void
|
||||
bplist_destroy(objset_t *mos, uint64_t object, dmu_tx_t *tx)
|
||||
{
|
||||
VERIFY(dmu_object_free(mos, object, tx) == 0);
|
||||
}
|
||||
|
||||
int
|
||||
bplist_open(bplist_t *bpl, objset_t *mos, uint64_t object)
|
||||
{
|
||||
dmu_object_info_t doi;
|
||||
int err;
|
||||
|
||||
err = dmu_object_info(mos, object, &doi);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
|
||||
ASSERT(bpl->bpl_dbuf == NULL);
|
||||
ASSERT(bpl->bpl_phys == NULL);
|
||||
ASSERT(bpl->bpl_cached_dbuf == NULL);
|
||||
ASSERT(bpl->bpl_queue == NULL);
|
||||
ASSERT(object != 0);
|
||||
ASSERT3U(doi.doi_type, ==, DMU_OT_BPLIST);
|
||||
ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_BPLIST_HDR);
|
||||
|
||||
bpl->bpl_mos = mos;
|
||||
bpl->bpl_object = object;
|
||||
bpl->bpl_blockshift = highbit(doi.doi_data_block_size - 1);
|
||||
bpl->bpl_bpshift = bpl->bpl_blockshift - SPA_BLKPTRSHIFT;
|
||||
bpl->bpl_havecomp = (doi.doi_bonus_size == sizeof (bplist_phys_t));
|
||||
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
bplist_close(bplist_t *bpl)
|
||||
{
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
|
||||
ASSERT(bpl->bpl_queue == NULL);
|
||||
|
||||
if (bpl->bpl_cached_dbuf) {
|
||||
dmu_buf_rele(bpl->bpl_cached_dbuf, bpl);
|
||||
bpl->bpl_cached_dbuf = NULL;
|
||||
}
|
||||
if (bpl->bpl_dbuf) {
|
||||
dmu_buf_rele(bpl->bpl_dbuf, bpl);
|
||||
bpl->bpl_dbuf = NULL;
|
||||
bpl->bpl_phys = NULL;
|
||||
}
|
||||
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
bplist_empty(bplist_t *bpl)
|
||||
{
|
||||
boolean_t rv;
|
||||
|
||||
if (bpl->bpl_object == 0)
|
||||
return (B_TRUE);
|
||||
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
VERIFY(0 == bplist_hold(bpl)); /* XXX */
|
||||
rv = (bpl->bpl_phys->bpl_entries == 0);
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static int
|
||||
bplist_cache(bplist_t *bpl, uint64_t blkid)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (bpl->bpl_cached_dbuf == NULL ||
|
||||
bpl->bpl_cached_dbuf->db_offset != (blkid << bpl->bpl_blockshift)) {
|
||||
if (bpl->bpl_cached_dbuf != NULL)
|
||||
dmu_buf_rele(bpl->bpl_cached_dbuf, bpl);
|
||||
err = dmu_buf_hold(bpl->bpl_mos,
|
||||
bpl->bpl_object, blkid << bpl->bpl_blockshift,
|
||||
bpl, &bpl->bpl_cached_dbuf);
|
||||
ASSERT(err || bpl->bpl_cached_dbuf->db_size ==
|
||||
1ULL << bpl->bpl_blockshift);
|
||||
}
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
bplist_iterate(bplist_t *bpl, uint64_t *itorp, blkptr_t *bp)
|
||||
{
|
||||
uint64_t blk, off;
|
||||
blkptr_t *bparray;
|
||||
int err;
|
||||
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
|
||||
err = bplist_hold(bpl);
|
||||
if (err) {
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (*itorp >= bpl->bpl_phys->bpl_entries) {
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
blk = *itorp >> bpl->bpl_bpshift;
|
||||
off = P2PHASE(*itorp, 1ULL << bpl->bpl_bpshift);
|
||||
|
||||
err = bplist_cache(bpl, blk);
|
||||
if (err) {
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
bparray = bpl->bpl_cached_dbuf->db_data;
|
||||
*bp = bparray[off];
|
||||
(*itorp)++;
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bplist_enqueue(bplist_t *bpl, const blkptr_t *bp, dmu_tx_t *tx)
|
||||
{
|
||||
uint64_t blk, off;
|
||||
blkptr_t *bparray;
|
||||
int err;
|
||||
|
||||
ASSERT(!BP_IS_HOLE(bp));
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
err = bplist_hold(bpl);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
blk = bpl->bpl_phys->bpl_entries >> bpl->bpl_bpshift;
|
||||
off = P2PHASE(bpl->bpl_phys->bpl_entries, 1ULL << bpl->bpl_bpshift);
|
||||
|
||||
err = bplist_cache(bpl, blk);
|
||||
if (err) {
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
dmu_buf_will_dirty(bpl->bpl_cached_dbuf, tx);
|
||||
bparray = bpl->bpl_cached_dbuf->db_data;
|
||||
bparray[off] = *bp;
|
||||
|
||||
/* We never need the fill count. */
|
||||
bparray[off].blk_fill = 0;
|
||||
|
||||
/* The bplist will compress better if we can leave off the checksum */
|
||||
bzero(&bparray[off].blk_cksum, sizeof (bparray[off].blk_cksum));
|
||||
|
||||
dmu_buf_will_dirty(bpl->bpl_dbuf, tx);
|
||||
bpl->bpl_phys->bpl_entries++;
|
||||
bpl->bpl_phys->bpl_bytes +=
|
||||
bp_get_dasize(dmu_objset_spa(bpl->bpl_mos), bp);
|
||||
if (bpl->bpl_havecomp) {
|
||||
bpl->bpl_phys->bpl_comp += BP_GET_PSIZE(bp);
|
||||
bpl->bpl_phys->bpl_uncomp += BP_GET_UCSIZE(bp);
|
||||
}
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deferred entry; will be written later by bplist_sync().
|
||||
*/
|
||||
void
|
||||
bplist_enqueue_deferred(bplist_t *bpl, const blkptr_t *bp)
|
||||
{
|
||||
bplist_q_t *bpq = kmem_alloc(sizeof (*bpq), KM_SLEEP);
|
||||
|
||||
ASSERT(!BP_IS_HOLE(bp));
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
bpq->bpq_blk = *bp;
|
||||
bpq->bpq_next = bpl->bpl_queue;
|
||||
bpl->bpl_queue = bpq;
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
}
|
||||
|
||||
void
|
||||
bplist_sync(bplist_t *bpl, dmu_tx_t *tx)
|
||||
{
|
||||
bplist_q_t *bpq;
|
||||
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
while ((bpq = bpl->bpl_queue) != NULL) {
|
||||
bpl->bpl_queue = bpq->bpq_next;
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
VERIFY(0 == bplist_enqueue(bpl, &bpq->bpq_blk, tx));
|
||||
kmem_free(bpq, sizeof (*bpq));
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
}
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
}
|
||||
|
||||
void
|
||||
bplist_vacate(bplist_t *bpl, dmu_tx_t *tx)
|
||||
{
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
ASSERT3P(bpl->bpl_queue, ==, NULL);
|
||||
VERIFY(0 == bplist_hold(bpl));
|
||||
dmu_buf_will_dirty(bpl->bpl_dbuf, tx);
|
||||
VERIFY(0 == dmu_free_range(bpl->bpl_mos,
|
||||
bpl->bpl_object, 0, -1ULL, tx));
|
||||
bpl->bpl_phys->bpl_entries = 0;
|
||||
bpl->bpl_phys->bpl_bytes = 0;
|
||||
if (bpl->bpl_havecomp) {
|
||||
bpl->bpl_phys->bpl_comp = 0;
|
||||
bpl->bpl_phys->bpl_uncomp = 0;
|
||||
}
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
}
|
||||
|
||||
int
|
||||
bplist_space(bplist_t *bpl, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
|
||||
err = bplist_hold(bpl);
|
||||
if (err) {
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
*usedp = bpl->bpl_phys->bpl_bytes;
|
||||
if (bpl->bpl_havecomp) {
|
||||
*compp = bpl->bpl_phys->bpl_comp;
|
||||
*uncompp = bpl->bpl_phys->bpl_uncomp;
|
||||
}
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
|
||||
if (!bpl->bpl_havecomp) {
|
||||
uint64_t itor = 0, comp = 0, uncomp = 0;
|
||||
blkptr_t bp;
|
||||
|
||||
while ((err = bplist_iterate(bpl, &itor, &bp)) == 0) {
|
||||
comp += BP_GET_PSIZE(&bp);
|
||||
uncomp += BP_GET_UCSIZE(&bp);
|
||||
}
|
||||
if (err == ENOENT)
|
||||
err = 0;
|
||||
*compp = comp;
|
||||
*uncompp = uncomp;
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return (in *dasizep) the amount of space on the deadlist which is:
|
||||
* mintxg < blk_birth <= maxtxg
|
||||
*/
|
||||
int
|
||||
bplist_space_birthrange(bplist_t *bpl, uint64_t mintxg, uint64_t maxtxg,
|
||||
uint64_t *dasizep)
|
||||
{
|
||||
uint64_t size = 0;
|
||||
uint64_t itor = 0;
|
||||
blkptr_t bp;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* As an optimization, if they want the whole txg range, just
|
||||
* get bpl_bytes rather than iterating over the bps.
|
||||
*/
|
||||
if (mintxg < TXG_INITIAL && maxtxg == UINT64_MAX) {
|
||||
mutex_enter(&bpl->bpl_lock);
|
||||
err = bplist_hold(bpl);
|
||||
if (err == 0)
|
||||
*dasizep = bpl->bpl_phys->bpl_bytes;
|
||||
mutex_exit(&bpl->bpl_lock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
while ((err = bplist_iterate(bpl, &itor, &bp)) == 0) {
|
||||
if (bp.blk_birth > mintxg && bp.blk_birth <= maxtxg) {
|
||||
size +=
|
||||
bp_get_dasize(dmu_objset_spa(bpl->bpl_mos), &bp);
|
||||
}
|
||||
}
|
||||
if (err == ENOENT)
|
||||
err = 0;
|
||||
*dasizep = size;
|
||||
return (err);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dnode.h>
|
||||
|
||||
uint64_t
|
||||
dmu_object_alloc(objset_t *os, dmu_object_type_t ot, int blocksize,
|
||||
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
|
||||
{
|
||||
objset_impl_t *osi = os->os;
|
||||
uint64_t object;
|
||||
uint64_t L2_dnode_count = DNODES_PER_BLOCK <<
|
||||
(osi->os_meta_dnode->dn_indblkshift - SPA_BLKPTRSHIFT);
|
||||
dnode_t *dn = NULL;
|
||||
int restarted = B_FALSE;
|
||||
|
||||
mutex_enter(&osi->os_obj_lock);
|
||||
for (;;) {
|
||||
object = osi->os_obj_next;
|
||||
/*
|
||||
* Each time we polish off an L2 bp worth of dnodes
|
||||
* (2^13 objects), move to another L2 bp that's still
|
||||
* reasonably sparse (at most 1/4 full). Look from the
|
||||
* beginning once, but after that keep looking from here.
|
||||
* If we can't find one, just keep going from here.
|
||||
*/
|
||||
if (P2PHASE(object, L2_dnode_count) == 0) {
|
||||
uint64_t offset = restarted ? object << DNODE_SHIFT : 0;
|
||||
int error = dnode_next_offset(osi->os_meta_dnode,
|
||||
DNODE_FIND_HOLE,
|
||||
&offset, 2, DNODES_PER_BLOCK >> 2, 0);
|
||||
restarted = B_TRUE;
|
||||
if (error == 0)
|
||||
object = offset >> DNODE_SHIFT;
|
||||
}
|
||||
osi->os_obj_next = ++object;
|
||||
|
||||
/*
|
||||
* XXX We should check for an i/o error here and return
|
||||
* up to our caller. Actually we should pre-read it in
|
||||
* dmu_tx_assign(), but there is currently no mechanism
|
||||
* to do so.
|
||||
*/
|
||||
(void) dnode_hold_impl(os->os, object, DNODE_MUST_BE_FREE,
|
||||
FTAG, &dn);
|
||||
if (dn)
|
||||
break;
|
||||
|
||||
if (dmu_object_next(os, &object, B_TRUE, 0) == 0)
|
||||
osi->os_obj_next = object - 1;
|
||||
}
|
||||
|
||||
dnode_allocate(dn, ot, blocksize, 0, bonustype, bonuslen, tx);
|
||||
dnode_rele(dn, FTAG);
|
||||
|
||||
mutex_exit(&osi->os_obj_lock);
|
||||
|
||||
dmu_tx_add_new_object(tx, os, object);
|
||||
return (object);
|
||||
}
|
||||
|
||||
int
|
||||
dmu_object_claim(objset_t *os, uint64_t object, dmu_object_type_t ot,
|
||||
int blocksize, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
|
||||
{
|
||||
dnode_t *dn;
|
||||
int err;
|
||||
|
||||
if (object == DMU_META_DNODE_OBJECT && !dmu_tx_private_ok(tx))
|
||||
return (EBADF);
|
||||
|
||||
err = dnode_hold_impl(os->os, object, DNODE_MUST_BE_FREE, FTAG, &dn);
|
||||
if (err)
|
||||
return (err);
|
||||
dnode_allocate(dn, ot, blocksize, 0, bonustype, bonuslen, tx);
|
||||
dnode_rele(dn, FTAG);
|
||||
|
||||
dmu_tx_add_new_object(tx, os, object);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dmu_object_reclaim(objset_t *os, uint64_t object, dmu_object_type_t ot,
|
||||
int blocksize, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
|
||||
{
|
||||
dnode_t *dn;
|
||||
int err;
|
||||
|
||||
if (object == DMU_META_DNODE_OBJECT && !dmu_tx_private_ok(tx))
|
||||
return (EBADF);
|
||||
|
||||
err = dnode_hold_impl(os->os, object, DNODE_MUST_BE_ALLOCATED,
|
||||
FTAG, &dn);
|
||||
if (err)
|
||||
return (err);
|
||||
dnode_reallocate(dn, ot, blocksize, bonustype, bonuslen, tx);
|
||||
dnode_rele(dn, FTAG);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dmu_object_free(objset_t *os, uint64_t object, dmu_tx_t *tx)
|
||||
{
|
||||
dnode_t *dn;
|
||||
int err;
|
||||
|
||||
ASSERT(object != DMU_META_DNODE_OBJECT || dmu_tx_private_ok(tx));
|
||||
|
||||
err = dnode_hold_impl(os->os, object, DNODE_MUST_BE_ALLOCATED,
|
||||
FTAG, &dn);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
ASSERT(dn->dn_type != DMU_OT_NONE);
|
||||
dnode_free_range(dn, 0, DMU_OBJECT_END, tx);
|
||||
dnode_free(dn, tx);
|
||||
dnode_rele(dn, FTAG);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dmu_object_next(objset_t *os, uint64_t *objectp, boolean_t hole, uint64_t txg)
|
||||
{
|
||||
uint64_t offset = (*objectp + 1) << DNODE_SHIFT;
|
||||
int error;
|
||||
|
||||
error = dnode_next_offset(os->os->os_meta_dnode,
|
||||
(hole ? DNODE_FIND_HOLE : 0), &offset, 0, DNODES_PER_BLOCK, txg);
|
||||
|
||||
*objectp = offset >> DNODE_SHIFT;
|
||||
|
||||
return (error);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_traverse.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/dsl_pool.h>
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/dmu_impl.h>
|
||||
#include <sys/callb.h>
|
||||
|
||||
#define SET_BOOKMARK(zb, objset, object, level, blkid) \
|
||||
{ \
|
||||
(zb)->zb_objset = objset; \
|
||||
(zb)->zb_object = object; \
|
||||
(zb)->zb_level = level; \
|
||||
(zb)->zb_blkid = blkid; \
|
||||
}
|
||||
|
||||
struct prefetch_data {
|
||||
kmutex_t pd_mtx;
|
||||
kcondvar_t pd_cv;
|
||||
int pd_blks_max;
|
||||
int pd_blks_fetched;
|
||||
int pd_flags;
|
||||
boolean_t pd_cancel;
|
||||
boolean_t pd_exited;
|
||||
};
|
||||
|
||||
struct traverse_data {
|
||||
spa_t *td_spa;
|
||||
uint64_t td_objset;
|
||||
blkptr_t *td_rootbp;
|
||||
uint64_t td_min_txg;
|
||||
int td_flags;
|
||||
struct prefetch_data *td_pfd;
|
||||
blkptr_cb_t *td_func;
|
||||
void *td_arg;
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
traverse_zil_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
|
||||
{
|
||||
struct traverse_data *td = arg;
|
||||
zbookmark_t zb;
|
||||
|
||||
if (bp->blk_birth == 0)
|
||||
return;
|
||||
|
||||
if (claim_txg == 0 && bp->blk_birth >= spa_first_txg(td->td_spa))
|
||||
return;
|
||||
|
||||
zb.zb_objset = td->td_objset;
|
||||
zb.zb_object = 0;
|
||||
zb.zb_level = -1;
|
||||
zb.zb_blkid = bp->blk_cksum.zc_word[ZIL_ZC_SEQ];
|
||||
VERIFY(0 == td->td_func(td->td_spa, bp, &zb, NULL, td->td_arg));
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
traverse_zil_record(zilog_t *zilog, lr_t *lrc, void *arg, uint64_t claim_txg)
|
||||
{
|
||||
struct traverse_data *td = arg;
|
||||
|
||||
if (lrc->lrc_txtype == TX_WRITE) {
|
||||
lr_write_t *lr = (lr_write_t *)lrc;
|
||||
blkptr_t *bp = &lr->lr_blkptr;
|
||||
zbookmark_t zb;
|
||||
|
||||
if (bp->blk_birth == 0)
|
||||
return;
|
||||
|
||||
if (claim_txg == 0 || bp->blk_birth < claim_txg)
|
||||
return;
|
||||
|
||||
zb.zb_objset = td->td_objset;
|
||||
zb.zb_object = lr->lr_foid;
|
||||
zb.zb_level = BP_GET_LEVEL(bp);
|
||||
zb.zb_blkid = lr->lr_offset / BP_GET_LSIZE(bp);
|
||||
VERIFY(0 == td->td_func(td->td_spa, bp, &zb, NULL, td->td_arg));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
traverse_zil(struct traverse_data *td, zil_header_t *zh)
|
||||
{
|
||||
uint64_t claim_txg = zh->zh_claim_txg;
|
||||
zilog_t *zilog;
|
||||
|
||||
/*
|
||||
* We only want to visit blocks that have been claimed but not yet
|
||||
* replayed (or, in read-only mode, blocks that *would* be claimed).
|
||||
*/
|
||||
if (claim_txg == 0 && (spa_mode & FWRITE))
|
||||
return;
|
||||
|
||||
zilog = zil_alloc(spa_get_dsl(td->td_spa)->dp_meta_objset, zh);
|
||||
|
||||
(void) zil_parse(zilog, traverse_zil_block, traverse_zil_record, td,
|
||||
claim_txg);
|
||||
|
||||
zil_free(zilog);
|
||||
}
|
||||
|
||||
static int
|
||||
traverse_visitbp(struct traverse_data *td, const dnode_phys_t *dnp,
|
||||
arc_buf_t *pbuf, blkptr_t *bp, const zbookmark_t *zb)
|
||||
{
|
||||
zbookmark_t czb;
|
||||
int err = 0;
|
||||
arc_buf_t *buf = NULL;
|
||||
struct prefetch_data *pd = td->td_pfd;
|
||||
|
||||
if (bp->blk_birth == 0) {
|
||||
err = td->td_func(td->td_spa, NULL, zb, dnp, td->td_arg);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (bp->blk_birth <= td->td_min_txg)
|
||||
return (0);
|
||||
|
||||
if (pd && !pd->pd_exited &&
|
||||
((pd->pd_flags & TRAVERSE_PREFETCH_DATA) ||
|
||||
BP_GET_TYPE(bp) == DMU_OT_DNODE || BP_GET_LEVEL(bp) > 0)) {
|
||||
mutex_enter(&pd->pd_mtx);
|
||||
ASSERT(pd->pd_blks_fetched >= 0);
|
||||
while (pd->pd_blks_fetched == 0 && !pd->pd_exited)
|
||||
cv_wait(&pd->pd_cv, &pd->pd_mtx);
|
||||
pd->pd_blks_fetched--;
|
||||
cv_broadcast(&pd->pd_cv);
|
||||
mutex_exit(&pd->pd_mtx);
|
||||
}
|
||||
|
||||
if (td->td_flags & TRAVERSE_PRE) {
|
||||
err = td->td_func(td->td_spa, bp, zb, dnp, td->td_arg);
|
||||
if (err)
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (BP_GET_LEVEL(bp) > 0) {
|
||||
uint32_t flags = ARC_WAIT;
|
||||
int i;
|
||||
blkptr_t *cbp;
|
||||
int epb = BP_GET_LSIZE(bp) >> SPA_BLKPTRSHIFT;
|
||||
|
||||
err = arc_read(NULL, td->td_spa, bp, pbuf,
|
||||
arc_getbuf_func, &buf,
|
||||
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
/* recursively visitbp() blocks below this */
|
||||
cbp = buf->b_data;
|
||||
for (i = 0; i < epb; i++, cbp++) {
|
||||
SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object,
|
||||
zb->zb_level - 1,
|
||||
zb->zb_blkid * epb + i);
|
||||
err = traverse_visitbp(td, dnp, buf, cbp, &czb);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
} else if (BP_GET_TYPE(bp) == DMU_OT_DNODE) {
|
||||
uint32_t flags = ARC_WAIT;
|
||||
int i, j;
|
||||
int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT;
|
||||
|
||||
err = arc_read(NULL, td->td_spa, bp, pbuf,
|
||||
arc_getbuf_func, &buf,
|
||||
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
/* recursively visitbp() blocks below this */
|
||||
dnp = buf->b_data;
|
||||
for (i = 0; i < epb && err == 0; i++, dnp++) {
|
||||
for (j = 0; j < dnp->dn_nblkptr; j++) {
|
||||
SET_BOOKMARK(&czb, zb->zb_objset,
|
||||
zb->zb_blkid * epb + i,
|
||||
dnp->dn_nlevels - 1, j);
|
||||
err = traverse_visitbp(td, dnp, buf,
|
||||
(blkptr_t *)&dnp->dn_blkptr[j], &czb);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) {
|
||||
uint32_t flags = ARC_WAIT;
|
||||
objset_phys_t *osp;
|
||||
int j;
|
||||
|
||||
err = arc_read_nolock(NULL, td->td_spa, bp,
|
||||
arc_getbuf_func, &buf,
|
||||
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
osp = buf->b_data;
|
||||
/*
|
||||
* traverse_zil is just here for zdb's leak checking.
|
||||
* For other consumers, there will be no ZIL blocks.
|
||||
*/
|
||||
traverse_zil(td, &osp->os_zil_header);
|
||||
|
||||
for (j = 0; j < osp->os_meta_dnode.dn_nblkptr; j++) {
|
||||
SET_BOOKMARK(&czb, zb->zb_objset, 0,
|
||||
osp->os_meta_dnode.dn_nlevels - 1, j);
|
||||
err = traverse_visitbp(td, &osp->os_meta_dnode, buf,
|
||||
(blkptr_t *)&osp->os_meta_dnode.dn_blkptr[j],
|
||||
&czb);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf)
|
||||
(void) arc_buf_remove_ref(buf, &buf);
|
||||
|
||||
if (err == 0 && (td->td_flags & TRAVERSE_POST))
|
||||
err = td->td_func(td->td_spa, bp, zb, dnp, td->td_arg);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
traverse_prefetcher(spa_t *spa, blkptr_t *bp, const zbookmark_t *zb,
|
||||
const dnode_phys_t *dnp, void *arg)
|
||||
{
|
||||
struct prefetch_data *pfd = arg;
|
||||
uint32_t aflags = ARC_NOWAIT | ARC_PREFETCH;
|
||||
|
||||
ASSERT(pfd->pd_blks_fetched >= 0);
|
||||
if (pfd->pd_cancel)
|
||||
return (EINTR);
|
||||
|
||||
if (bp == NULL || !((pfd->pd_flags & TRAVERSE_PREFETCH_DATA) ||
|
||||
BP_GET_TYPE(bp) == DMU_OT_DNODE || BP_GET_LEVEL(bp) > 0))
|
||||
return (0);
|
||||
|
||||
mutex_enter(&pfd->pd_mtx);
|
||||
while (!pfd->pd_cancel && pfd->pd_blks_fetched >= pfd->pd_blks_max)
|
||||
cv_wait(&pfd->pd_cv, &pfd->pd_mtx);
|
||||
pfd->pd_blks_fetched++;
|
||||
cv_broadcast(&pfd->pd_cv);
|
||||
mutex_exit(&pfd->pd_mtx);
|
||||
|
||||
(void) arc_read_nolock(NULL, spa, bp, NULL, NULL,
|
||||
ZIO_PRIORITY_ASYNC_READ,
|
||||
ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE,
|
||||
&aflags, zb);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
traverse_prefetch_thread(void *arg)
|
||||
{
|
||||
struct traverse_data *td_main = arg;
|
||||
struct traverse_data td = *td_main;
|
||||
zbookmark_t czb;
|
||||
|
||||
td.td_func = traverse_prefetcher;
|
||||
td.td_arg = td_main->td_pfd;
|
||||
td.td_pfd = NULL;
|
||||
|
||||
SET_BOOKMARK(&czb, td.td_objset, 0, -1, 0);
|
||||
(void) traverse_visitbp(&td, NULL, NULL, td.td_rootbp, &czb);
|
||||
|
||||
mutex_enter(&td_main->td_pfd->pd_mtx);
|
||||
td_main->td_pfd->pd_exited = B_TRUE;
|
||||
cv_broadcast(&td_main->td_pfd->pd_cv);
|
||||
mutex_exit(&td_main->td_pfd->pd_mtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* NB: dataset must not be changing on-disk (eg, is a snapshot or we are
|
||||
* in syncing context).
|
||||
*/
|
||||
static int
|
||||
traverse_impl(spa_t *spa, uint64_t objset, blkptr_t *rootbp,
|
||||
uint64_t txg_start, int flags, blkptr_cb_t func, void *arg)
|
||||
{
|
||||
struct traverse_data td;
|
||||
struct prefetch_data pd = { 0 };
|
||||
zbookmark_t czb;
|
||||
int err;
|
||||
|
||||
td.td_spa = spa;
|
||||
td.td_objset = objset;
|
||||
td.td_rootbp = rootbp;
|
||||
td.td_min_txg = txg_start;
|
||||
td.td_func = func;
|
||||
td.td_arg = arg;
|
||||
td.td_pfd = &pd;
|
||||
td.td_flags = flags;
|
||||
|
||||
pd.pd_blks_max = 100;
|
||||
pd.pd_flags = flags;
|
||||
mutex_init(&pd.pd_mtx, NULL, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&pd.pd_cv, NULL, CV_DEFAULT, NULL);
|
||||
|
||||
if (!(flags & TRAVERSE_PREFETCH) ||
|
||||
0 == taskq_dispatch(system_taskq, traverse_prefetch_thread,
|
||||
&td, TQ_NOQUEUE))
|
||||
pd.pd_exited = B_TRUE;
|
||||
|
||||
SET_BOOKMARK(&czb, objset, 0, -1, 0);
|
||||
err = traverse_visitbp(&td, NULL, NULL, rootbp, &czb);
|
||||
|
||||
mutex_enter(&pd.pd_mtx);
|
||||
pd.pd_cancel = B_TRUE;
|
||||
cv_broadcast(&pd.pd_cv);
|
||||
while (!pd.pd_exited)
|
||||
cv_wait(&pd.pd_cv, &pd.pd_mtx);
|
||||
mutex_exit(&pd.pd_mtx);
|
||||
|
||||
mutex_destroy(&pd.pd_mtx);
|
||||
cv_destroy(&pd.pd_cv);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* NB: dataset must not be changing on-disk (eg, is a snapshot or we are
|
||||
* in syncing context).
|
||||
*/
|
||||
int
|
||||
traverse_dataset(dsl_dataset_t *ds, uint64_t txg_start, int flags,
|
||||
blkptr_cb_t func, void *arg)
|
||||
{
|
||||
return (traverse_impl(ds->ds_dir->dd_pool->dp_spa, ds->ds_object,
|
||||
&ds->ds_phys->ds_bp, txg_start, flags, func, arg));
|
||||
}
|
||||
|
||||
/*
|
||||
* NB: pool must not be changing on-disk (eg, from zdb or sync context).
|
||||
*/
|
||||
int
|
||||
traverse_pool(spa_t *spa, blkptr_cb_t func, void *arg)
|
||||
{
|
||||
int err;
|
||||
uint64_t obj;
|
||||
dsl_pool_t *dp = spa_get_dsl(spa);
|
||||
objset_t *mos = dp->dp_meta_objset;
|
||||
|
||||
/* visit the MOS */
|
||||
err = traverse_impl(spa, 0, spa_get_rootblkptr(spa),
|
||||
0, TRAVERSE_PRE, func, arg);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
/* visit each dataset */
|
||||
for (obj = 1; err == 0; err = dmu_object_next(mos, &obj, FALSE, 0)) {
|
||||
dmu_object_info_t doi;
|
||||
|
||||
err = dmu_object_info(mos, obj, &doi);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
if (doi.doi_type == DMU_OT_DSL_DATASET) {
|
||||
dsl_dataset_t *ds;
|
||||
rw_enter(&dp->dp_config_rwlock, RW_READER);
|
||||
err = dsl_dataset_hold_obj(dp, obj, FTAG, &ds);
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
if (err)
|
||||
return (err);
|
||||
err = traverse_dataset(ds,
|
||||
ds->ds_phys->ds_prev_snap_txg, TRAVERSE_PRE,
|
||||
func, arg);
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
if (err)
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
if (err == ESRCH)
|
||||
err = 0;
|
||||
return (err);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,651 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_zfetch.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dbuf.h>
|
||||
|
||||
/*
|
||||
* I'm against tune-ables, but these should probably exist as tweakable globals
|
||||
* until we can get this working the way we want it to.
|
||||
*/
|
||||
|
||||
int zfs_prefetch_disable = 0;
|
||||
|
||||
/* max # of streams per zfetch */
|
||||
uint32_t zfetch_max_streams = 8;
|
||||
/* min time before stream reclaim */
|
||||
uint32_t zfetch_min_sec_reap = 2;
|
||||
/* max number of blocks to fetch at a time */
|
||||
uint32_t zfetch_block_cap = 256;
|
||||
/* number of bytes in a array_read at which we stop prefetching (1Mb) */
|
||||
uint64_t zfetch_array_rd_sz = 1024 * 1024;
|
||||
|
||||
/* forward decls for static routines */
|
||||
static int dmu_zfetch_colinear(zfetch_t *, zstream_t *);
|
||||
static void dmu_zfetch_dofetch(zfetch_t *, zstream_t *);
|
||||
static uint64_t dmu_zfetch_fetch(dnode_t *, uint64_t, uint64_t);
|
||||
static uint64_t dmu_zfetch_fetchsz(dnode_t *, uint64_t, uint64_t);
|
||||
static int dmu_zfetch_find(zfetch_t *, zstream_t *, int);
|
||||
static int dmu_zfetch_stream_insert(zfetch_t *, zstream_t *);
|
||||
static zstream_t *dmu_zfetch_stream_reclaim(zfetch_t *);
|
||||
static void dmu_zfetch_stream_remove(zfetch_t *, zstream_t *);
|
||||
static int dmu_zfetch_streams_equal(zstream_t *, zstream_t *);
|
||||
|
||||
/*
|
||||
* Given a zfetch structure and a zstream structure, determine whether the
|
||||
* blocks to be read are part of a co-linear pair of existing prefetch
|
||||
* streams. If a set is found, coalesce the streams, removing one, and
|
||||
* configure the prefetch so it looks for a strided access pattern.
|
||||
*
|
||||
* In other words: if we find two sequential access streams that are
|
||||
* the same length and distance N appart, and this read is N from the
|
||||
* last stream, then we are probably in a strided access pattern. So
|
||||
* combine the two sequential streams into a single strided stream.
|
||||
*
|
||||
* If no co-linear streams are found, return NULL.
|
||||
*/
|
||||
static int
|
||||
dmu_zfetch_colinear(zfetch_t *zf, zstream_t *zh)
|
||||
{
|
||||
zstream_t *z_walk;
|
||||
zstream_t *z_comp;
|
||||
|
||||
if (! rw_tryenter(&zf->zf_rwlock, RW_WRITER))
|
||||
return (0);
|
||||
|
||||
if (zh == NULL) {
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
for (z_walk = list_head(&zf->zf_stream); z_walk;
|
||||
z_walk = list_next(&zf->zf_stream, z_walk)) {
|
||||
for (z_comp = list_next(&zf->zf_stream, z_walk); z_comp;
|
||||
z_comp = list_next(&zf->zf_stream, z_comp)) {
|
||||
int64_t diff;
|
||||
|
||||
if (z_walk->zst_len != z_walk->zst_stride ||
|
||||
z_comp->zst_len != z_comp->zst_stride) {
|
||||
continue;
|
||||
}
|
||||
|
||||
diff = z_comp->zst_offset - z_walk->zst_offset;
|
||||
if (z_comp->zst_offset + diff == zh->zst_offset) {
|
||||
z_walk->zst_offset = zh->zst_offset;
|
||||
z_walk->zst_direction = diff < 0 ? -1 : 1;
|
||||
z_walk->zst_stride =
|
||||
diff * z_walk->zst_direction;
|
||||
z_walk->zst_ph_offset =
|
||||
zh->zst_offset + z_walk->zst_stride;
|
||||
dmu_zfetch_stream_remove(zf, z_comp);
|
||||
mutex_destroy(&z_comp->zst_lock);
|
||||
kmem_free(z_comp, sizeof (zstream_t));
|
||||
|
||||
dmu_zfetch_dofetch(zf, z_walk);
|
||||
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
return (1);
|
||||
}
|
||||
|
||||
diff = z_walk->zst_offset - z_comp->zst_offset;
|
||||
if (z_walk->zst_offset + diff == zh->zst_offset) {
|
||||
z_walk->zst_offset = zh->zst_offset;
|
||||
z_walk->zst_direction = diff < 0 ? -1 : 1;
|
||||
z_walk->zst_stride =
|
||||
diff * z_walk->zst_direction;
|
||||
z_walk->zst_ph_offset =
|
||||
zh->zst_offset + z_walk->zst_stride;
|
||||
dmu_zfetch_stream_remove(zf, z_comp);
|
||||
mutex_destroy(&z_comp->zst_lock);
|
||||
kmem_free(z_comp, sizeof (zstream_t));
|
||||
|
||||
dmu_zfetch_dofetch(zf, z_walk);
|
||||
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a zstream_t, determine the bounds of the prefetch. Then call the
|
||||
* routine that actually prefetches the individual blocks.
|
||||
*/
|
||||
static void
|
||||
dmu_zfetch_dofetch(zfetch_t *zf, zstream_t *zs)
|
||||
{
|
||||
uint64_t prefetch_tail;
|
||||
uint64_t prefetch_limit;
|
||||
uint64_t prefetch_ofst;
|
||||
uint64_t prefetch_len;
|
||||
uint64_t blocks_fetched;
|
||||
|
||||
zs->zst_stride = MAX((int64_t)zs->zst_stride, zs->zst_len);
|
||||
zs->zst_cap = MIN(zfetch_block_cap, 2 * zs->zst_cap);
|
||||
|
||||
prefetch_tail = MAX((int64_t)zs->zst_ph_offset,
|
||||
(int64_t)(zs->zst_offset + zs->zst_stride));
|
||||
/*
|
||||
* XXX: use a faster division method?
|
||||
*/
|
||||
prefetch_limit = zs->zst_offset + zs->zst_len +
|
||||
(zs->zst_cap * zs->zst_stride) / zs->zst_len;
|
||||
|
||||
while (prefetch_tail < prefetch_limit) {
|
||||
prefetch_ofst = zs->zst_offset + zs->zst_direction *
|
||||
(prefetch_tail - zs->zst_offset);
|
||||
|
||||
prefetch_len = zs->zst_len;
|
||||
|
||||
/*
|
||||
* Don't prefetch beyond the end of the file, if working
|
||||
* backwards.
|
||||
*/
|
||||
if ((zs->zst_direction == ZFETCH_BACKWARD) &&
|
||||
(prefetch_ofst > prefetch_tail)) {
|
||||
prefetch_len += prefetch_ofst;
|
||||
prefetch_ofst = 0;
|
||||
}
|
||||
|
||||
/* don't prefetch more than we're supposed to */
|
||||
if (prefetch_len > zs->zst_len)
|
||||
break;
|
||||
|
||||
blocks_fetched = dmu_zfetch_fetch(zf->zf_dnode,
|
||||
prefetch_ofst, zs->zst_len);
|
||||
|
||||
prefetch_tail += zs->zst_stride;
|
||||
/* stop if we've run out of stuff to prefetch */
|
||||
if (blocks_fetched < zs->zst_len)
|
||||
break;
|
||||
}
|
||||
zs->zst_ph_offset = prefetch_tail;
|
||||
zs->zst_last = lbolt;
|
||||
}
|
||||
|
||||
/*
|
||||
* This takes a pointer to a zfetch structure and a dnode. It performs the
|
||||
* necessary setup for the zfetch structure, grokking data from the
|
||||
* associated dnode.
|
||||
*/
|
||||
void
|
||||
dmu_zfetch_init(zfetch_t *zf, dnode_t *dno)
|
||||
{
|
||||
if (zf == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
zf->zf_dnode = dno;
|
||||
zf->zf_stream_cnt = 0;
|
||||
zf->zf_alloc_fail = 0;
|
||||
|
||||
list_create(&zf->zf_stream, sizeof (zstream_t),
|
||||
offsetof(zstream_t, zst_node));
|
||||
|
||||
rw_init(&zf->zf_rwlock, NULL, RW_DEFAULT, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function computes the actual size, in blocks, that can be prefetched,
|
||||
* and fetches it.
|
||||
*/
|
||||
static uint64_t
|
||||
dmu_zfetch_fetch(dnode_t *dn, uint64_t blkid, uint64_t nblks)
|
||||
{
|
||||
uint64_t fetchsz;
|
||||
uint64_t i;
|
||||
|
||||
fetchsz = dmu_zfetch_fetchsz(dn, blkid, nblks);
|
||||
|
||||
for (i = 0; i < fetchsz; i++) {
|
||||
dbuf_prefetch(dn, blkid + i);
|
||||
}
|
||||
|
||||
return (fetchsz);
|
||||
}
|
||||
|
||||
/*
|
||||
* this function returns the number of blocks that would be prefetched, based
|
||||
* upon the supplied dnode, blockid, and nblks. This is used so that we can
|
||||
* update streams in place, and then prefetch with their old value after the
|
||||
* fact. This way, we can delay the prefetch, but subsequent accesses to the
|
||||
* stream won't result in the same data being prefetched multiple times.
|
||||
*/
|
||||
static uint64_t
|
||||
dmu_zfetch_fetchsz(dnode_t *dn, uint64_t blkid, uint64_t nblks)
|
||||
{
|
||||
uint64_t fetchsz;
|
||||
|
||||
if (blkid > dn->dn_maxblkid) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* compute fetch size */
|
||||
if (blkid + nblks + 1 > dn->dn_maxblkid) {
|
||||
fetchsz = (dn->dn_maxblkid - blkid) + 1;
|
||||
ASSERT(blkid + fetchsz - 1 <= dn->dn_maxblkid);
|
||||
} else {
|
||||
fetchsz = nblks;
|
||||
}
|
||||
|
||||
|
||||
return (fetchsz);
|
||||
}
|
||||
|
||||
/*
|
||||
* given a zfetch and a zsearch structure, see if there is an associated zstream
|
||||
* for this block read. If so, it starts a prefetch for the stream it
|
||||
* located and returns true, otherwise it returns false
|
||||
*/
|
||||
static int
|
||||
dmu_zfetch_find(zfetch_t *zf, zstream_t *zh, int prefetched)
|
||||
{
|
||||
zstream_t *zs;
|
||||
int64_t diff;
|
||||
int reset = !prefetched;
|
||||
int rc = 0;
|
||||
|
||||
if (zh == NULL)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* XXX: This locking strategy is a bit coarse; however, it's impact has
|
||||
* yet to be tested. If this turns out to be an issue, it can be
|
||||
* modified in a number of different ways.
|
||||
*/
|
||||
|
||||
rw_enter(&zf->zf_rwlock, RW_READER);
|
||||
top:
|
||||
|
||||
for (zs = list_head(&zf->zf_stream); zs;
|
||||
zs = list_next(&zf->zf_stream, zs)) {
|
||||
|
||||
/*
|
||||
* XXX - should this be an assert?
|
||||
*/
|
||||
if (zs->zst_len == 0) {
|
||||
/* bogus stream */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We hit this case when we are in a strided prefetch stream:
|
||||
* we will read "len" blocks before "striding".
|
||||
*/
|
||||
if (zh->zst_offset >= zs->zst_offset &&
|
||||
zh->zst_offset < zs->zst_offset + zs->zst_len) {
|
||||
/* already fetched */
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the forward sequential read case: we increment
|
||||
* len by one each time we hit here, so we will enter this
|
||||
* case on every read.
|
||||
*/
|
||||
if (zh->zst_offset == zs->zst_offset + zs->zst_len) {
|
||||
|
||||
reset = !prefetched && zs->zst_len > 1;
|
||||
|
||||
mutex_enter(&zs->zst_lock);
|
||||
|
||||
if (zh->zst_offset != zs->zst_offset + zs->zst_len) {
|
||||
mutex_exit(&zs->zst_lock);
|
||||
goto top;
|
||||
}
|
||||
zs->zst_len += zh->zst_len;
|
||||
diff = zs->zst_len - zfetch_block_cap;
|
||||
if (diff > 0) {
|
||||
zs->zst_offset += diff;
|
||||
zs->zst_len = zs->zst_len > diff ?
|
||||
zs->zst_len - diff : 0;
|
||||
}
|
||||
zs->zst_direction = ZFETCH_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
/*
|
||||
* Same as above, but reading backwards through the file.
|
||||
*/
|
||||
} else if (zh->zst_offset == zs->zst_offset - zh->zst_len) {
|
||||
/* backwards sequential access */
|
||||
|
||||
reset = !prefetched && zs->zst_len > 1;
|
||||
|
||||
mutex_enter(&zs->zst_lock);
|
||||
|
||||
if (zh->zst_offset != zs->zst_offset - zh->zst_len) {
|
||||
mutex_exit(&zs->zst_lock);
|
||||
goto top;
|
||||
}
|
||||
|
||||
zs->zst_offset = zs->zst_offset > zh->zst_len ?
|
||||
zs->zst_offset - zh->zst_len : 0;
|
||||
zs->zst_ph_offset = zs->zst_ph_offset > zh->zst_len ?
|
||||
zs->zst_ph_offset - zh->zst_len : 0;
|
||||
zs->zst_len += zh->zst_len;
|
||||
|
||||
diff = zs->zst_len - zfetch_block_cap;
|
||||
if (diff > 0) {
|
||||
zs->zst_ph_offset = zs->zst_ph_offset > diff ?
|
||||
zs->zst_ph_offset - diff : 0;
|
||||
zs->zst_len = zs->zst_len > diff ?
|
||||
zs->zst_len - diff : zs->zst_len;
|
||||
}
|
||||
zs->zst_direction = ZFETCH_BACKWARD;
|
||||
|
||||
break;
|
||||
|
||||
} else if ((zh->zst_offset - zs->zst_offset - zs->zst_stride <
|
||||
zs->zst_len) && (zs->zst_len != zs->zst_stride)) {
|
||||
/* strided forward access */
|
||||
|
||||
mutex_enter(&zs->zst_lock);
|
||||
|
||||
if ((zh->zst_offset - zs->zst_offset - zs->zst_stride >=
|
||||
zs->zst_len) || (zs->zst_len == zs->zst_stride)) {
|
||||
mutex_exit(&zs->zst_lock);
|
||||
goto top;
|
||||
}
|
||||
|
||||
zs->zst_offset += zs->zst_stride;
|
||||
zs->zst_direction = ZFETCH_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
} else if ((zh->zst_offset - zs->zst_offset + zs->zst_stride <
|
||||
zs->zst_len) && (zs->zst_len != zs->zst_stride)) {
|
||||
/* strided reverse access */
|
||||
|
||||
mutex_enter(&zs->zst_lock);
|
||||
|
||||
if ((zh->zst_offset - zs->zst_offset + zs->zst_stride >=
|
||||
zs->zst_len) || (zs->zst_len == zs->zst_stride)) {
|
||||
mutex_exit(&zs->zst_lock);
|
||||
goto top;
|
||||
}
|
||||
|
||||
zs->zst_offset = zs->zst_offset > zs->zst_stride ?
|
||||
zs->zst_offset - zs->zst_stride : 0;
|
||||
zs->zst_ph_offset = (zs->zst_ph_offset >
|
||||
(2 * zs->zst_stride)) ?
|
||||
(zs->zst_ph_offset - (2 * zs->zst_stride)) : 0;
|
||||
zs->zst_direction = ZFETCH_BACKWARD;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (zs) {
|
||||
if (reset) {
|
||||
zstream_t *remove = zs;
|
||||
|
||||
rc = 0;
|
||||
mutex_exit(&zs->zst_lock);
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
rw_enter(&zf->zf_rwlock, RW_WRITER);
|
||||
/*
|
||||
* Relocate the stream, in case someone removes
|
||||
* it while we were acquiring the WRITER lock.
|
||||
*/
|
||||
for (zs = list_head(&zf->zf_stream); zs;
|
||||
zs = list_next(&zf->zf_stream, zs)) {
|
||||
if (zs == remove) {
|
||||
dmu_zfetch_stream_remove(zf, zs);
|
||||
mutex_destroy(&zs->zst_lock);
|
||||
kmem_free(zs, sizeof (zstream_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = 1;
|
||||
dmu_zfetch_dofetch(zf, zs);
|
||||
mutex_exit(&zs->zst_lock);
|
||||
}
|
||||
}
|
||||
out:
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean-up state associated with a zfetch structure. This frees allocated
|
||||
* structure members, empties the zf_stream tree, and generally makes things
|
||||
* nice. This doesn't free the zfetch_t itself, that's left to the caller.
|
||||
*/
|
||||
void
|
||||
dmu_zfetch_rele(zfetch_t *zf)
|
||||
{
|
||||
zstream_t *zs;
|
||||
zstream_t *zs_next;
|
||||
|
||||
ASSERT(!RW_LOCK_HELD(&zf->zf_rwlock));
|
||||
|
||||
for (zs = list_head(&zf->zf_stream); zs; zs = zs_next) {
|
||||
zs_next = list_next(&zf->zf_stream, zs);
|
||||
|
||||
list_remove(&zf->zf_stream, zs);
|
||||
mutex_destroy(&zs->zst_lock);
|
||||
kmem_free(zs, sizeof (zstream_t));
|
||||
}
|
||||
list_destroy(&zf->zf_stream);
|
||||
rw_destroy(&zf->zf_rwlock);
|
||||
|
||||
zf->zf_dnode = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a zfetch and zstream structure, insert the zstream structure into the
|
||||
* AVL tree contained within the zfetch structure. Peform the appropriate
|
||||
* book-keeping. It is possible that another thread has inserted a stream which
|
||||
* matches one that we are about to insert, so we must be sure to check for this
|
||||
* case. If one is found, return failure, and let the caller cleanup the
|
||||
* duplicates.
|
||||
*/
|
||||
static int
|
||||
dmu_zfetch_stream_insert(zfetch_t *zf, zstream_t *zs)
|
||||
{
|
||||
zstream_t *zs_walk;
|
||||
zstream_t *zs_next;
|
||||
|
||||
ASSERT(RW_WRITE_HELD(&zf->zf_rwlock));
|
||||
|
||||
for (zs_walk = list_head(&zf->zf_stream); zs_walk; zs_walk = zs_next) {
|
||||
zs_next = list_next(&zf->zf_stream, zs_walk);
|
||||
|
||||
if (dmu_zfetch_streams_equal(zs_walk, zs)) {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
list_insert_head(&zf->zf_stream, zs);
|
||||
zf->zf_stream_cnt++;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Walk the list of zstreams in the given zfetch, find an old one (by time), and
|
||||
* reclaim it for use by the caller.
|
||||
*/
|
||||
static zstream_t *
|
||||
dmu_zfetch_stream_reclaim(zfetch_t *zf)
|
||||
{
|
||||
zstream_t *zs;
|
||||
|
||||
if (! rw_tryenter(&zf->zf_rwlock, RW_WRITER))
|
||||
return (0);
|
||||
|
||||
for (zs = list_head(&zf->zf_stream); zs;
|
||||
zs = list_next(&zf->zf_stream, zs)) {
|
||||
|
||||
if (((lbolt - zs->zst_last) / hz) > zfetch_min_sec_reap)
|
||||
break;
|
||||
}
|
||||
|
||||
if (zs) {
|
||||
dmu_zfetch_stream_remove(zf, zs);
|
||||
mutex_destroy(&zs->zst_lock);
|
||||
bzero(zs, sizeof (zstream_t));
|
||||
} else {
|
||||
zf->zf_alloc_fail++;
|
||||
}
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
|
||||
return (zs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a zfetch and zstream structure, remove the zstream structure from its
|
||||
* container in the zfetch structure. Perform the appropriate book-keeping.
|
||||
*/
|
||||
static void
|
||||
dmu_zfetch_stream_remove(zfetch_t *zf, zstream_t *zs)
|
||||
{
|
||||
ASSERT(RW_WRITE_HELD(&zf->zf_rwlock));
|
||||
|
||||
list_remove(&zf->zf_stream, zs);
|
||||
zf->zf_stream_cnt--;
|
||||
}
|
||||
|
||||
static int
|
||||
dmu_zfetch_streams_equal(zstream_t *zs1, zstream_t *zs2)
|
||||
{
|
||||
if (zs1->zst_offset != zs2->zst_offset)
|
||||
return (0);
|
||||
|
||||
if (zs1->zst_len != zs2->zst_len)
|
||||
return (0);
|
||||
|
||||
if (zs1->zst_stride != zs2->zst_stride)
|
||||
return (0);
|
||||
|
||||
if (zs1->zst_ph_offset != zs2->zst_ph_offset)
|
||||
return (0);
|
||||
|
||||
if (zs1->zst_cap != zs2->zst_cap)
|
||||
return (0);
|
||||
|
||||
if (zs1->zst_direction != zs2->zst_direction)
|
||||
return (0);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the prefetch entry point. It calls all of the other dmu_zfetch
|
||||
* routines to create, delete, find, or operate upon prefetch streams.
|
||||
*/
|
||||
void
|
||||
dmu_zfetch(zfetch_t *zf, uint64_t offset, uint64_t size, int prefetched)
|
||||
{
|
||||
zstream_t zst;
|
||||
zstream_t *newstream;
|
||||
int fetched;
|
||||
int inserted;
|
||||
unsigned int blkshft;
|
||||
uint64_t blksz;
|
||||
|
||||
if (zfs_prefetch_disable)
|
||||
return;
|
||||
|
||||
/* files that aren't ln2 blocksz are only one block -- nothing to do */
|
||||
if (!zf->zf_dnode->dn_datablkshift)
|
||||
return;
|
||||
|
||||
/* convert offset and size, into blockid and nblocks */
|
||||
blkshft = zf->zf_dnode->dn_datablkshift;
|
||||
blksz = (1 << blkshft);
|
||||
|
||||
bzero(&zst, sizeof (zstream_t));
|
||||
zst.zst_offset = offset >> blkshft;
|
||||
zst.zst_len = (P2ROUNDUP(offset + size, blksz) -
|
||||
P2ALIGN(offset, blksz)) >> blkshft;
|
||||
|
||||
fetched = dmu_zfetch_find(zf, &zst, prefetched);
|
||||
if (!fetched) {
|
||||
fetched = dmu_zfetch_colinear(zf, &zst);
|
||||
}
|
||||
|
||||
if (!fetched) {
|
||||
newstream = dmu_zfetch_stream_reclaim(zf);
|
||||
|
||||
/*
|
||||
* we still couldn't find a stream, drop the lock, and allocate
|
||||
* one if possible. Otherwise, give up and go home.
|
||||
*/
|
||||
if (newstream == NULL) {
|
||||
uint64_t maxblocks;
|
||||
uint32_t max_streams;
|
||||
uint32_t cur_streams;
|
||||
|
||||
cur_streams = zf->zf_stream_cnt;
|
||||
maxblocks = zf->zf_dnode->dn_maxblkid;
|
||||
|
||||
max_streams = MIN(zfetch_max_streams,
|
||||
(maxblocks / zfetch_block_cap));
|
||||
if (max_streams == 0) {
|
||||
max_streams++;
|
||||
}
|
||||
|
||||
if (cur_streams >= max_streams) {
|
||||
return;
|
||||
}
|
||||
|
||||
newstream = kmem_zalloc(sizeof (zstream_t), KM_SLEEP);
|
||||
}
|
||||
|
||||
newstream->zst_offset = zst.zst_offset;
|
||||
newstream->zst_len = zst.zst_len;
|
||||
newstream->zst_stride = zst.zst_len;
|
||||
newstream->zst_ph_offset = zst.zst_len + zst.zst_offset;
|
||||
newstream->zst_cap = zst.zst_len;
|
||||
newstream->zst_direction = ZFETCH_FORWARD;
|
||||
newstream->zst_last = lbolt;
|
||||
|
||||
mutex_init(&newstream->zst_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
|
||||
rw_enter(&zf->zf_rwlock, RW_WRITER);
|
||||
inserted = dmu_zfetch_stream_insert(zf, newstream);
|
||||
rw_exit(&zf->zf_rwlock);
|
||||
|
||||
if (!inserted) {
|
||||
mutex_destroy(&newstream->zst_lock);
|
||||
kmem_free(newstream, sizeof (zstream_t));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,623 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/dbuf.h>
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/spa.h>
|
||||
|
||||
static void
|
||||
dnode_increase_indirection(dnode_t *dn, dmu_tx_t *tx)
|
||||
{
|
||||
dmu_buf_impl_t *db;
|
||||
int txgoff = tx->tx_txg & TXG_MASK;
|
||||
int nblkptr = dn->dn_phys->dn_nblkptr;
|
||||
int old_toplvl = dn->dn_phys->dn_nlevels - 1;
|
||||
int new_level = dn->dn_next_nlevels[txgoff];
|
||||
int i;
|
||||
|
||||
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
|
||||
|
||||
/* this dnode can't be paged out because it's dirty */
|
||||
ASSERT(dn->dn_phys->dn_type != DMU_OT_NONE);
|
||||
ASSERT(RW_WRITE_HELD(&dn->dn_struct_rwlock));
|
||||
ASSERT(new_level > 1 && dn->dn_phys->dn_nlevels > 0);
|
||||
|
||||
db = dbuf_hold_level(dn, dn->dn_phys->dn_nlevels, 0, FTAG);
|
||||
ASSERT(db != NULL);
|
||||
|
||||
dn->dn_phys->dn_nlevels = new_level;
|
||||
dprintf("os=%p obj=%llu, increase to %d\n", dn->dn_objset,
|
||||
dn->dn_object, dn->dn_phys->dn_nlevels);
|
||||
|
||||
/* check for existing blkptrs in the dnode */
|
||||
for (i = 0; i < nblkptr; i++)
|
||||
if (!BP_IS_HOLE(&dn->dn_phys->dn_blkptr[i]))
|
||||
break;
|
||||
if (i != nblkptr) {
|
||||
/* transfer dnode's block pointers to new indirect block */
|
||||
(void) dbuf_read(db, NULL, DB_RF_MUST_SUCCEED|DB_RF_HAVESTRUCT);
|
||||
ASSERT(db->db.db_data);
|
||||
ASSERT(arc_released(db->db_buf));
|
||||
ASSERT3U(sizeof (blkptr_t) * nblkptr, <=, db->db.db_size);
|
||||
bcopy(dn->dn_phys->dn_blkptr, db->db.db_data,
|
||||
sizeof (blkptr_t) * nblkptr);
|
||||
arc_buf_freeze(db->db_buf);
|
||||
}
|
||||
|
||||
/* set dbuf's parent pointers to new indirect buf */
|
||||
for (i = 0; i < nblkptr; i++) {
|
||||
dmu_buf_impl_t *child = dbuf_find(dn, old_toplvl, i);
|
||||
|
||||
if (child == NULL)
|
||||
continue;
|
||||
ASSERT3P(child->db_dnode, ==, dn);
|
||||
if (child->db_parent && child->db_parent != dn->dn_dbuf) {
|
||||
ASSERT(child->db_parent->db_level == db->db_level);
|
||||
ASSERT(child->db_blkptr !=
|
||||
&dn->dn_phys->dn_blkptr[child->db_blkid]);
|
||||
mutex_exit(&child->db_mtx);
|
||||
continue;
|
||||
}
|
||||
ASSERT(child->db_parent == NULL ||
|
||||
child->db_parent == dn->dn_dbuf);
|
||||
|
||||
child->db_parent = db;
|
||||
dbuf_add_ref(db, child);
|
||||
if (db->db.db_data)
|
||||
child->db_blkptr = (blkptr_t *)db->db.db_data + i;
|
||||
else
|
||||
child->db_blkptr = NULL;
|
||||
dprintf_dbuf_bp(child, child->db_blkptr,
|
||||
"changed db_blkptr to new indirect %s", "");
|
||||
|
||||
mutex_exit(&child->db_mtx);
|
||||
}
|
||||
|
||||
bzero(dn->dn_phys->dn_blkptr, sizeof (blkptr_t) * nblkptr);
|
||||
|
||||
dbuf_rele(db, FTAG);
|
||||
|
||||
rw_exit(&dn->dn_struct_rwlock);
|
||||
}
|
||||
|
||||
static int
|
||||
free_blocks(dnode_t *dn, blkptr_t *bp, int num, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_dataset_t *ds = dn->dn_objset->os_dsl_dataset;
|
||||
uint64_t bytesfreed = 0;
|
||||
int i, blocks_freed = 0;
|
||||
|
||||
dprintf("ds=%p obj=%llx num=%d\n", ds, dn->dn_object, num);
|
||||
|
||||
for (i = 0; i < num; i++, bp++) {
|
||||
if (BP_IS_HOLE(bp))
|
||||
continue;
|
||||
|
||||
bytesfreed += dsl_dataset_block_kill(ds, bp, dn->dn_zio, tx);
|
||||
ASSERT3U(bytesfreed, <=, DN_USED_BYTES(dn->dn_phys));
|
||||
bzero(bp, sizeof (blkptr_t));
|
||||
blocks_freed += 1;
|
||||
}
|
||||
dnode_diduse_space(dn, -bytesfreed);
|
||||
return (blocks_freed);
|
||||
}
|
||||
|
||||
#ifdef ZFS_DEBUG
|
||||
static void
|
||||
free_verify(dmu_buf_impl_t *db, uint64_t start, uint64_t end, dmu_tx_t *tx)
|
||||
{
|
||||
int off, num;
|
||||
int i, err, epbs;
|
||||
uint64_t txg = tx->tx_txg;
|
||||
|
||||
epbs = db->db_dnode->dn_phys->dn_indblkshift - SPA_BLKPTRSHIFT;
|
||||
off = start - (db->db_blkid * 1<<epbs);
|
||||
num = end - start + 1;
|
||||
|
||||
ASSERT3U(off, >=, 0);
|
||||
ASSERT3U(num, >=, 0);
|
||||
ASSERT3U(db->db_level, >, 0);
|
||||
ASSERT3U(db->db.db_size, ==, 1<<db->db_dnode->dn_phys->dn_indblkshift);
|
||||
ASSERT3U(off+num, <=, db->db.db_size >> SPA_BLKPTRSHIFT);
|
||||
ASSERT(db->db_blkptr != NULL);
|
||||
|
||||
for (i = off; i < off+num; i++) {
|
||||
uint64_t *buf;
|
||||
dmu_buf_impl_t *child;
|
||||
dbuf_dirty_record_t *dr;
|
||||
int j;
|
||||
|
||||
ASSERT(db->db_level == 1);
|
||||
|
||||
rw_enter(&db->db_dnode->dn_struct_rwlock, RW_READER);
|
||||
err = dbuf_hold_impl(db->db_dnode, db->db_level-1,
|
||||
(db->db_blkid << epbs) + i, TRUE, FTAG, &child);
|
||||
rw_exit(&db->db_dnode->dn_struct_rwlock);
|
||||
if (err == ENOENT)
|
||||
continue;
|
||||
ASSERT(err == 0);
|
||||
ASSERT(child->db_level == 0);
|
||||
dr = child->db_last_dirty;
|
||||
while (dr && dr->dr_txg > txg)
|
||||
dr = dr->dr_next;
|
||||
ASSERT(dr == NULL || dr->dr_txg == txg);
|
||||
|
||||
/* data_old better be zeroed */
|
||||
if (dr) {
|
||||
buf = dr->dt.dl.dr_data->b_data;
|
||||
for (j = 0; j < child->db.db_size >> 3; j++) {
|
||||
if (buf[j] != 0) {
|
||||
panic("freed data not zero: "
|
||||
"child=%p i=%d off=%d num=%d\n",
|
||||
(void *)child, i, off, num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* db_data better be zeroed unless it's dirty in a
|
||||
* future txg.
|
||||
*/
|
||||
mutex_enter(&child->db_mtx);
|
||||
buf = child->db.db_data;
|
||||
if (buf != NULL && child->db_state != DB_FILL &&
|
||||
child->db_last_dirty == NULL) {
|
||||
for (j = 0; j < child->db.db_size >> 3; j++) {
|
||||
if (buf[j] != 0) {
|
||||
panic("freed data not zero: "
|
||||
"child=%p i=%d off=%d num=%d\n",
|
||||
(void *)child, i, off, num);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_exit(&child->db_mtx);
|
||||
|
||||
dbuf_rele(child, FTAG);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ALL -1
|
||||
|
||||
static int
|
||||
free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks, int trunc,
|
||||
dmu_tx_t *tx)
|
||||
{
|
||||
dnode_t *dn = db->db_dnode;
|
||||
blkptr_t *bp;
|
||||
dmu_buf_impl_t *subdb;
|
||||
uint64_t start, end, dbstart, dbend, i;
|
||||
int epbs, shift, err;
|
||||
int all = TRUE;
|
||||
int blocks_freed = 0;
|
||||
|
||||
/*
|
||||
* There is a small possibility that this block will not be cached:
|
||||
* 1 - if level > 1 and there are no children with level <= 1
|
||||
* 2 - if we didn't get a dirty hold (because this block had just
|
||||
* finished being written -- and so had no holds), and then this
|
||||
* block got evicted before we got here.
|
||||
*/
|
||||
if (db->db_state != DB_CACHED)
|
||||
(void) dbuf_read(db, NULL, DB_RF_MUST_SUCCEED);
|
||||
|
||||
arc_release(db->db_buf, db);
|
||||
bp = (blkptr_t *)db->db.db_data;
|
||||
|
||||
epbs = db->db_dnode->dn_phys->dn_indblkshift - SPA_BLKPTRSHIFT;
|
||||
shift = (db->db_level - 1) * epbs;
|
||||
dbstart = db->db_blkid << epbs;
|
||||
start = blkid >> shift;
|
||||
if (dbstart < start) {
|
||||
bp += start - dbstart;
|
||||
all = FALSE;
|
||||
} else {
|
||||
start = dbstart;
|
||||
}
|
||||
dbend = ((db->db_blkid + 1) << epbs) - 1;
|
||||
end = (blkid + nblks - 1) >> shift;
|
||||
if (dbend <= end)
|
||||
end = dbend;
|
||||
else if (all)
|
||||
all = trunc;
|
||||
ASSERT3U(start, <=, end);
|
||||
|
||||
if (db->db_level == 1) {
|
||||
FREE_VERIFY(db, start, end, tx);
|
||||
blocks_freed = free_blocks(dn, bp, end-start+1, tx);
|
||||
arc_buf_freeze(db->db_buf);
|
||||
ASSERT(all || blocks_freed == 0 || db->db_last_dirty);
|
||||
return (all ? ALL : blocks_freed);
|
||||
}
|
||||
|
||||
for (i = start; i <= end; i++, bp++) {
|
||||
if (BP_IS_HOLE(bp))
|
||||
continue;
|
||||
rw_enter(&dn->dn_struct_rwlock, RW_READER);
|
||||
err = dbuf_hold_impl(dn, db->db_level-1, i, TRUE, FTAG, &subdb);
|
||||
ASSERT3U(err, ==, 0);
|
||||
rw_exit(&dn->dn_struct_rwlock);
|
||||
|
||||
if (free_children(subdb, blkid, nblks, trunc, tx) == ALL) {
|
||||
ASSERT3P(subdb->db_blkptr, ==, bp);
|
||||
blocks_freed += free_blocks(dn, bp, 1, tx);
|
||||
} else {
|
||||
all = FALSE;
|
||||
}
|
||||
dbuf_rele(subdb, FTAG);
|
||||
}
|
||||
arc_buf_freeze(db->db_buf);
|
||||
#ifdef ZFS_DEBUG
|
||||
bp -= (end-start)+1;
|
||||
for (i = start; i <= end; i++, bp++) {
|
||||
if (i == start && blkid != 0)
|
||||
continue;
|
||||
else if (i == end && !trunc)
|
||||
continue;
|
||||
ASSERT3U(bp->blk_birth, ==, 0);
|
||||
}
|
||||
#endif
|
||||
ASSERT(all || blocks_freed == 0 || db->db_last_dirty);
|
||||
return (all ? ALL : blocks_freed);
|
||||
}
|
||||
|
||||
/*
|
||||
* free_range: Traverse the indicated range of the provided file
|
||||
* and "free" all the blocks contained there.
|
||||
*/
|
||||
static void
|
||||
dnode_sync_free_range(dnode_t *dn, uint64_t blkid, uint64_t nblks, dmu_tx_t *tx)
|
||||
{
|
||||
blkptr_t *bp = dn->dn_phys->dn_blkptr;
|
||||
dmu_buf_impl_t *db;
|
||||
int trunc, start, end, shift, i, err;
|
||||
int dnlevel = dn->dn_phys->dn_nlevels;
|
||||
|
||||
if (blkid > dn->dn_phys->dn_maxblkid)
|
||||
return;
|
||||
|
||||
ASSERT(dn->dn_phys->dn_maxblkid < UINT64_MAX);
|
||||
trunc = blkid + nblks > dn->dn_phys->dn_maxblkid;
|
||||
if (trunc)
|
||||
nblks = dn->dn_phys->dn_maxblkid - blkid + 1;
|
||||
|
||||
/* There are no indirect blocks in the object */
|
||||
if (dnlevel == 1) {
|
||||
if (blkid >= dn->dn_phys->dn_nblkptr) {
|
||||
/* this range was never made persistent */
|
||||
return;
|
||||
}
|
||||
ASSERT3U(blkid + nblks, <=, dn->dn_phys->dn_nblkptr);
|
||||
(void) free_blocks(dn, bp + blkid, nblks, tx);
|
||||
if (trunc) {
|
||||
uint64_t off = (dn->dn_phys->dn_maxblkid + 1) *
|
||||
(dn->dn_phys->dn_datablkszsec << SPA_MINBLOCKSHIFT);
|
||||
dn->dn_phys->dn_maxblkid = (blkid ? blkid - 1 : 0);
|
||||
ASSERT(off < dn->dn_phys->dn_maxblkid ||
|
||||
dn->dn_phys->dn_maxblkid == 0 ||
|
||||
dnode_next_offset(dn, 0, &off, 1, 1, 0) != 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
shift = (dnlevel - 1) * (dn->dn_phys->dn_indblkshift - SPA_BLKPTRSHIFT);
|
||||
start = blkid >> shift;
|
||||
ASSERT(start < dn->dn_phys->dn_nblkptr);
|
||||
end = (blkid + nblks - 1) >> shift;
|
||||
bp += start;
|
||||
for (i = start; i <= end; i++, bp++) {
|
||||
if (BP_IS_HOLE(bp))
|
||||
continue;
|
||||
rw_enter(&dn->dn_struct_rwlock, RW_READER);
|
||||
err = dbuf_hold_impl(dn, dnlevel-1, i, TRUE, FTAG, &db);
|
||||
ASSERT3U(err, ==, 0);
|
||||
rw_exit(&dn->dn_struct_rwlock);
|
||||
|
||||
if (free_children(db, blkid, nblks, trunc, tx) == ALL) {
|
||||
ASSERT3P(db->db_blkptr, ==, bp);
|
||||
(void) free_blocks(dn, bp, 1, tx);
|
||||
}
|
||||
dbuf_rele(db, FTAG);
|
||||
}
|
||||
if (trunc) {
|
||||
uint64_t off = (dn->dn_phys->dn_maxblkid + 1) *
|
||||
(dn->dn_phys->dn_datablkszsec << SPA_MINBLOCKSHIFT);
|
||||
dn->dn_phys->dn_maxblkid = (blkid ? blkid - 1 : 0);
|
||||
ASSERT(off < dn->dn_phys->dn_maxblkid ||
|
||||
dn->dn_phys->dn_maxblkid == 0 ||
|
||||
dnode_next_offset(dn, 0, &off, 1, 1, 0) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to kick all the dnodes dbufs out of the cache...
|
||||
*/
|
||||
void
|
||||
dnode_evict_dbufs(dnode_t *dn)
|
||||
{
|
||||
int progress;
|
||||
int pass = 0;
|
||||
|
||||
do {
|
||||
dmu_buf_impl_t *db, marker;
|
||||
int evicting = FALSE;
|
||||
|
||||
progress = FALSE;
|
||||
mutex_enter(&dn->dn_dbufs_mtx);
|
||||
list_insert_tail(&dn->dn_dbufs, &marker);
|
||||
db = list_head(&dn->dn_dbufs);
|
||||
for (; db != ▮ db = list_head(&dn->dn_dbufs)) {
|
||||
list_remove(&dn->dn_dbufs, db);
|
||||
list_insert_tail(&dn->dn_dbufs, db);
|
||||
ASSERT3P(db->db_dnode, ==, dn);
|
||||
|
||||
mutex_enter(&db->db_mtx);
|
||||
if (db->db_state == DB_EVICTING) {
|
||||
progress = TRUE;
|
||||
evicting = TRUE;
|
||||
mutex_exit(&db->db_mtx);
|
||||
} else if (refcount_is_zero(&db->db_holds)) {
|
||||
progress = TRUE;
|
||||
dbuf_clear(db); /* exits db_mtx for us */
|
||||
} else {
|
||||
mutex_exit(&db->db_mtx);
|
||||
}
|
||||
|
||||
}
|
||||
list_remove(&dn->dn_dbufs, &marker);
|
||||
/*
|
||||
* NB: we need to drop dn_dbufs_mtx between passes so
|
||||
* that any DB_EVICTING dbufs can make progress.
|
||||
* Ideally, we would have some cv we could wait on, but
|
||||
* since we don't, just wait a bit to give the other
|
||||
* thread a chance to run.
|
||||
*/
|
||||
mutex_exit(&dn->dn_dbufs_mtx);
|
||||
if (evicting)
|
||||
delay(1);
|
||||
pass++;
|
||||
ASSERT(pass < 100); /* sanity check */
|
||||
} while (progress);
|
||||
|
||||
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
|
||||
if (dn->dn_bonus && refcount_is_zero(&dn->dn_bonus->db_holds)) {
|
||||
mutex_enter(&dn->dn_bonus->db_mtx);
|
||||
dbuf_evict(dn->dn_bonus);
|
||||
dn->dn_bonus = NULL;
|
||||
}
|
||||
rw_exit(&dn->dn_struct_rwlock);
|
||||
}
|
||||
|
||||
static void
|
||||
dnode_undirty_dbufs(list_t *list)
|
||||
{
|
||||
dbuf_dirty_record_t *dr;
|
||||
|
||||
while (dr = list_head(list)) {
|
||||
dmu_buf_impl_t *db = dr->dr_dbuf;
|
||||
uint64_t txg = dr->dr_txg;
|
||||
|
||||
mutex_enter(&db->db_mtx);
|
||||
/* XXX - use dbuf_undirty()? */
|
||||
list_remove(list, dr);
|
||||
ASSERT(db->db_last_dirty == dr);
|
||||
db->db_last_dirty = NULL;
|
||||
db->db_dirtycnt -= 1;
|
||||
if (db->db_level == 0) {
|
||||
ASSERT(db->db_blkid == DB_BONUS_BLKID ||
|
||||
dr->dt.dl.dr_data == db->db_buf);
|
||||
dbuf_unoverride(dr);
|
||||
mutex_exit(&db->db_mtx);
|
||||
} else {
|
||||
mutex_exit(&db->db_mtx);
|
||||
dnode_undirty_dbufs(&dr->dt.di.dr_children);
|
||||
}
|
||||
kmem_free(dr, sizeof (dbuf_dirty_record_t));
|
||||
dbuf_rele(db, (void *)(uintptr_t)txg);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dnode_sync_free(dnode_t *dn, dmu_tx_t *tx)
|
||||
{
|
||||
int txgoff = tx->tx_txg & TXG_MASK;
|
||||
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
|
||||
/*
|
||||
* Our contents should have been freed in dnode_sync() by the
|
||||
* free range record inserted by the caller of dnode_free().
|
||||
*/
|
||||
ASSERT3U(DN_USED_BYTES(dn->dn_phys), ==, 0);
|
||||
ASSERT(BP_IS_HOLE(dn->dn_phys->dn_blkptr));
|
||||
|
||||
dnode_undirty_dbufs(&dn->dn_dirty_records[txgoff]);
|
||||
dnode_evict_dbufs(dn);
|
||||
ASSERT3P(list_head(&dn->dn_dbufs), ==, NULL);
|
||||
|
||||
/*
|
||||
* XXX - It would be nice to assert this, but we may still
|
||||
* have residual holds from async evictions from the arc...
|
||||
*
|
||||
* zfs_obj_to_path() also depends on this being
|
||||
* commented out.
|
||||
*
|
||||
* ASSERT3U(refcount_count(&dn->dn_holds), ==, 1);
|
||||
*/
|
||||
|
||||
/* Undirty next bits */
|
||||
dn->dn_next_nlevels[txgoff] = 0;
|
||||
dn->dn_next_indblkshift[txgoff] = 0;
|
||||
dn->dn_next_blksz[txgoff] = 0;
|
||||
|
||||
/* ASSERT(blkptrs are zero); */
|
||||
ASSERT(dn->dn_phys->dn_type != DMU_OT_NONE);
|
||||
ASSERT(dn->dn_type != DMU_OT_NONE);
|
||||
|
||||
ASSERT(dn->dn_free_txg > 0);
|
||||
if (dn->dn_allocated_txg != dn->dn_free_txg)
|
||||
dbuf_will_dirty(dn->dn_dbuf, tx);
|
||||
bzero(dn->dn_phys, sizeof (dnode_phys_t));
|
||||
|
||||
mutex_enter(&dn->dn_mtx);
|
||||
dn->dn_type = DMU_OT_NONE;
|
||||
dn->dn_maxblkid = 0;
|
||||
dn->dn_allocated_txg = 0;
|
||||
dn->dn_free_txg = 0;
|
||||
mutex_exit(&dn->dn_mtx);
|
||||
|
||||
ASSERT(dn->dn_object != DMU_META_DNODE_OBJECT);
|
||||
|
||||
dnode_rele(dn, (void *)(uintptr_t)tx->tx_txg);
|
||||
/*
|
||||
* Now that we've released our hold, the dnode may
|
||||
* be evicted, so we musn't access it.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Write out the dnode's dirty buffers.
|
||||
*
|
||||
* NOTE: The dnode is kept in memory by being dirty. Once the
|
||||
* dirty bit is cleared, it may be evicted. Beware of this!
|
||||
*/
|
||||
void
|
||||
dnode_sync(dnode_t *dn, dmu_tx_t *tx)
|
||||
{
|
||||
free_range_t *rp;
|
||||
dnode_phys_t *dnp = dn->dn_phys;
|
||||
int txgoff = tx->tx_txg & TXG_MASK;
|
||||
list_t *list = &dn->dn_dirty_records[txgoff];
|
||||
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
ASSERT(dnp->dn_type != DMU_OT_NONE || dn->dn_allocated_txg);
|
||||
DNODE_VERIFY(dn);
|
||||
|
||||
ASSERT(dn->dn_dbuf == NULL || arc_released(dn->dn_dbuf->db_buf));
|
||||
|
||||
mutex_enter(&dn->dn_mtx);
|
||||
if (dn->dn_allocated_txg == tx->tx_txg) {
|
||||
/* The dnode is newly allocated or reallocated */
|
||||
if (dnp->dn_type == DMU_OT_NONE) {
|
||||
/* this is a first alloc, not a realloc */
|
||||
/* XXX shouldn't the phys already be zeroed? */
|
||||
bzero(dnp, DNODE_CORE_SIZE);
|
||||
dnp->dn_nlevels = 1;
|
||||
}
|
||||
|
||||
if (dn->dn_nblkptr > dnp->dn_nblkptr) {
|
||||
/* zero the new blkptrs we are gaining */
|
||||
bzero(dnp->dn_blkptr + dnp->dn_nblkptr,
|
||||
sizeof (blkptr_t) *
|
||||
(dn->dn_nblkptr - dnp->dn_nblkptr));
|
||||
}
|
||||
dnp->dn_type = dn->dn_type;
|
||||
dnp->dn_bonustype = dn->dn_bonustype;
|
||||
dnp->dn_bonuslen = dn->dn_bonuslen;
|
||||
dnp->dn_nblkptr = dn->dn_nblkptr;
|
||||
}
|
||||
|
||||
ASSERT(dnp->dn_nlevels > 1 ||
|
||||
BP_IS_HOLE(&dnp->dn_blkptr[0]) ||
|
||||
BP_GET_LSIZE(&dnp->dn_blkptr[0]) ==
|
||||
dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT);
|
||||
|
||||
if (dn->dn_next_blksz[txgoff]) {
|
||||
ASSERT(P2PHASE(dn->dn_next_blksz[txgoff],
|
||||
SPA_MINBLOCKSIZE) == 0);
|
||||
ASSERT(BP_IS_HOLE(&dnp->dn_blkptr[0]) ||
|
||||
dn->dn_maxblkid == 0 || list_head(list) != NULL ||
|
||||
dn->dn_next_blksz[txgoff] >> SPA_MINBLOCKSHIFT ==
|
||||
dnp->dn_datablkszsec);
|
||||
dnp->dn_datablkszsec =
|
||||
dn->dn_next_blksz[txgoff] >> SPA_MINBLOCKSHIFT;
|
||||
dn->dn_next_blksz[txgoff] = 0;
|
||||
}
|
||||
|
||||
if (dn->dn_next_bonuslen[txgoff]) {
|
||||
if (dn->dn_next_bonuslen[txgoff] == DN_ZERO_BONUSLEN)
|
||||
dnp->dn_bonuslen = 0;
|
||||
else
|
||||
dnp->dn_bonuslen = dn->dn_next_bonuslen[txgoff];
|
||||
ASSERT(dnp->dn_bonuslen <= DN_MAX_BONUSLEN);
|
||||
dn->dn_next_bonuslen[txgoff] = 0;
|
||||
}
|
||||
|
||||
if (dn->dn_next_indblkshift[txgoff]) {
|
||||
ASSERT(dnp->dn_nlevels == 1);
|
||||
dnp->dn_indblkshift = dn->dn_next_indblkshift[txgoff];
|
||||
dn->dn_next_indblkshift[txgoff] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Just take the live (open-context) values for checksum and compress.
|
||||
* Strictly speaking it's a future leak, but nothing bad happens if we
|
||||
* start using the new checksum or compress algorithm a little early.
|
||||
*/
|
||||
dnp->dn_checksum = dn->dn_checksum;
|
||||
dnp->dn_compress = dn->dn_compress;
|
||||
|
||||
mutex_exit(&dn->dn_mtx);
|
||||
|
||||
/* process all the "freed" ranges in the file */
|
||||
while (rp = avl_last(&dn->dn_ranges[txgoff])) {
|
||||
dnode_sync_free_range(dn, rp->fr_blkid, rp->fr_nblks, tx);
|
||||
/* grab the mutex so we don't race with dnode_block_freed() */
|
||||
mutex_enter(&dn->dn_mtx);
|
||||
avl_remove(&dn->dn_ranges[txgoff], rp);
|
||||
mutex_exit(&dn->dn_mtx);
|
||||
kmem_free(rp, sizeof (free_range_t));
|
||||
}
|
||||
|
||||
if (dn->dn_free_txg > 0 && dn->dn_free_txg <= tx->tx_txg) {
|
||||
dnode_sync_free(dn, tx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dn->dn_next_nlevels[txgoff]) {
|
||||
dnode_increase_indirection(dn, tx);
|
||||
dn->dn_next_nlevels[txgoff] = 0;
|
||||
}
|
||||
|
||||
dbuf_sync_list(list, tx);
|
||||
|
||||
if (dn->dn_object != DMU_META_DNODE_OBJECT) {
|
||||
ASSERT3P(list_head(list), ==, NULL);
|
||||
dnode_rele(dn, (void *)(uintptr_t)tx->tx_txg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Although we have dropped our reference to the dnode, it
|
||||
* can't be evicted until its written, and we haven't yet
|
||||
* initiated the IO for the dnode's dbuf.
|
||||
*/
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,735 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DSL permissions are stored in a two level zap attribute
|
||||
* mechanism. The first level identifies the "class" of
|
||||
* entry. The class is identified by the first 2 letters of
|
||||
* the attribute. The second letter "l" or "d" identifies whether
|
||||
* it is a local or descendent permission. The first letter
|
||||
* identifies the type of entry.
|
||||
*
|
||||
* ul$<id> identifies permissions granted locally for this userid.
|
||||
* ud$<id> identifies permissions granted on descendent datasets for
|
||||
* this userid.
|
||||
* Ul$<id> identifies permission sets granted locally for this userid.
|
||||
* Ud$<id> identifies permission sets granted on descendent datasets for
|
||||
* this userid.
|
||||
* gl$<id> identifies permissions granted locally for this groupid.
|
||||
* gd$<id> identifies permissions granted on descendent datasets for
|
||||
* this groupid.
|
||||
* Gl$<id> identifies permission sets granted locally for this groupid.
|
||||
* Gd$<id> identifies permission sets granted on descendent datasets for
|
||||
* this groupid.
|
||||
* el$ identifies permissions granted locally for everyone.
|
||||
* ed$ identifies permissions granted on descendent datasets
|
||||
* for everyone.
|
||||
* El$ identifies permission sets granted locally for everyone.
|
||||
* Ed$ identifies permission sets granted to descendent datasets for
|
||||
* everyone.
|
||||
* c-$ identifies permission to create at dataset creation time.
|
||||
* C-$ identifies permission sets to grant locally at dataset creation
|
||||
* time.
|
||||
* s-$@<name> permissions defined in specified set @<name>
|
||||
* S-$@<name> Sets defined in named set @<name>
|
||||
*
|
||||
* Each of the above entities points to another zap attribute that contains one
|
||||
* attribute for each allowed permission, such as create, destroy,...
|
||||
* All of the "upper" case class types will specify permission set names
|
||||
* rather than permissions.
|
||||
*
|
||||
* Basically it looks something like this:
|
||||
* ul$12 -> ZAP OBJ -> permissions...
|
||||
*
|
||||
* The ZAP OBJ is referred to as the jump object.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/dsl_prop.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/dsl_deleg.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/zio_checksum.h> /* for the default checksum value */
|
||||
#include <sys/zap.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/cred.h>
|
||||
#include <sys/sunddi.h>
|
||||
|
||||
#include "zfs_deleg.h"
|
||||
|
||||
/*
|
||||
* Validate that user is allowed to delegate specified permissions.
|
||||
*
|
||||
* In order to delegate "create" you must have "create"
|
||||
* and "allow".
|
||||
*/
|
||||
int
|
||||
dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
|
||||
{
|
||||
nvpair_t *whopair = NULL;
|
||||
int error;
|
||||
|
||||
if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
|
||||
return (error);
|
||||
|
||||
while (whopair = nvlist_next_nvpair(nvp, whopair)) {
|
||||
nvlist_t *perms;
|
||||
nvpair_t *permpair = NULL;
|
||||
|
||||
VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
|
||||
|
||||
while (permpair = nvlist_next_nvpair(perms, permpair)) {
|
||||
const char *perm = nvpair_name(permpair);
|
||||
|
||||
if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
|
||||
return (EPERM);
|
||||
|
||||
if ((error = dsl_deleg_access(ddname, perm, cr)) != 0)
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate that user is allowed to unallow specified permissions. They
|
||||
* must have the 'allow' permission, and even then can only unallow
|
||||
* perms for their uid.
|
||||
*/
|
||||
int
|
||||
dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
|
||||
{
|
||||
nvpair_t *whopair = NULL;
|
||||
int error;
|
||||
char idstr[32];
|
||||
|
||||
if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
|
||||
return (error);
|
||||
|
||||
(void) snprintf(idstr, sizeof (idstr), "%lld",
|
||||
(longlong_t)crgetuid(cr));
|
||||
|
||||
while (whopair = nvlist_next_nvpair(nvp, whopair)) {
|
||||
zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
|
||||
|
||||
if (type != ZFS_DELEG_USER &&
|
||||
type != ZFS_DELEG_USER_SETS)
|
||||
return (EPERM);
|
||||
|
||||
if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
|
||||
return (EPERM);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
dsl_deleg_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_dir_t *dd = arg1;
|
||||
nvlist_t *nvp = arg2;
|
||||
objset_t *mos = dd->dd_pool->dp_meta_objset;
|
||||
nvpair_t *whopair = NULL;
|
||||
uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
|
||||
|
||||
if (zapobj == 0) {
|
||||
dmu_buf_will_dirty(dd->dd_dbuf, tx);
|
||||
zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
|
||||
DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
|
||||
}
|
||||
|
||||
while (whopair = nvlist_next_nvpair(nvp, whopair)) {
|
||||
const char *whokey = nvpair_name(whopair);
|
||||
nvlist_t *perms;
|
||||
nvpair_t *permpair = NULL;
|
||||
uint64_t jumpobj;
|
||||
|
||||
VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
|
||||
|
||||
if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
|
||||
jumpobj = zap_create(mos, DMU_OT_DSL_PERMS,
|
||||
DMU_OT_NONE, 0, tx);
|
||||
VERIFY(zap_update(mos, zapobj,
|
||||
whokey, 8, 1, &jumpobj, tx) == 0);
|
||||
}
|
||||
|
||||
while (permpair = nvlist_next_nvpair(perms, permpair)) {
|
||||
const char *perm = nvpair_name(permpair);
|
||||
uint64_t n = 0;
|
||||
|
||||
VERIFY(zap_update(mos, jumpobj,
|
||||
perm, 8, 1, &n, tx) == 0);
|
||||
spa_history_internal_log(LOG_DS_PERM_UPDATE,
|
||||
dd->dd_pool->dp_spa, tx, cr,
|
||||
"%s %s dataset = %llu", whokey, perm,
|
||||
dd->dd_phys->dd_head_dataset_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dsl_deleg_unset_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_dir_t *dd = arg1;
|
||||
nvlist_t *nvp = arg2;
|
||||
objset_t *mos = dd->dd_pool->dp_meta_objset;
|
||||
nvpair_t *whopair = NULL;
|
||||
uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
|
||||
|
||||
if (zapobj == 0)
|
||||
return;
|
||||
|
||||
while (whopair = nvlist_next_nvpair(nvp, whopair)) {
|
||||
const char *whokey = nvpair_name(whopair);
|
||||
nvlist_t *perms;
|
||||
nvpair_t *permpair = NULL;
|
||||
uint64_t jumpobj;
|
||||
|
||||
if (nvpair_value_nvlist(whopair, &perms) != 0) {
|
||||
if (zap_lookup(mos, zapobj, whokey, 8,
|
||||
1, &jumpobj) == 0) {
|
||||
(void) zap_remove(mos, zapobj, whokey, tx);
|
||||
VERIFY(0 == zap_destroy(mos, jumpobj, tx));
|
||||
}
|
||||
spa_history_internal_log(LOG_DS_PERM_WHO_REMOVE,
|
||||
dd->dd_pool->dp_spa, tx, cr,
|
||||
"%s dataset = %llu", whokey,
|
||||
dd->dd_phys->dd_head_dataset_obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0)
|
||||
continue;
|
||||
|
||||
while (permpair = nvlist_next_nvpair(perms, permpair)) {
|
||||
const char *perm = nvpair_name(permpair);
|
||||
uint64_t n = 0;
|
||||
|
||||
(void) zap_remove(mos, jumpobj, perm, tx);
|
||||
if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
|
||||
(void) zap_remove(mos, zapobj,
|
||||
whokey, tx);
|
||||
VERIFY(0 == zap_destroy(mos,
|
||||
jumpobj, tx));
|
||||
}
|
||||
spa_history_internal_log(LOG_DS_PERM_REMOVE,
|
||||
dd->dd_pool->dp_spa, tx, cr,
|
||||
"%s %s dataset = %llu", whokey, perm,
|
||||
dd->dd_phys->dd_head_dataset_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
|
||||
{
|
||||
dsl_dir_t *dd;
|
||||
int error;
|
||||
nvpair_t *whopair = NULL;
|
||||
int blocks_modified = 0;
|
||||
|
||||
error = dsl_dir_open(ddname, FTAG, &dd, NULL);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) <
|
||||
SPA_VERSION_DELEGATED_PERMS) {
|
||||
dsl_dir_close(dd, FTAG);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
while (whopair = nvlist_next_nvpair(nvp, whopair))
|
||||
blocks_modified++;
|
||||
|
||||
error = dsl_sync_task_do(dd->dd_pool, NULL,
|
||||
unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync,
|
||||
dd, nvp, blocks_modified);
|
||||
dsl_dir_close(dd, FTAG);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all 'allow' permissions from a given point and then continue
|
||||
* traversing up to the root.
|
||||
*
|
||||
* This function constructs an nvlist of nvlists.
|
||||
* each setpoint is an nvlist composed of an nvlist of an nvlist
|
||||
* of the individual * users/groups/everyone/create
|
||||
* permissions.
|
||||
*
|
||||
* The nvlist will look like this.
|
||||
*
|
||||
* { source fsname -> { whokeys { permissions,...}, ...}}
|
||||
*
|
||||
* The fsname nvpairs will be arranged in a bottom up order. For example,
|
||||
* if we have the following structure a/b/c then the nvpairs for the fsnames
|
||||
* will be ordered a/b/c, a/b, a.
|
||||
*/
|
||||
int
|
||||
dsl_deleg_get(const char *ddname, nvlist_t **nvp)
|
||||
{
|
||||
dsl_dir_t *dd, *startdd;
|
||||
dsl_pool_t *dp;
|
||||
int error;
|
||||
objset_t *mos;
|
||||
|
||||
error = dsl_dir_open(ddname, FTAG, &startdd, NULL);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
dp = startdd->dd_pool;
|
||||
mos = dp->dp_meta_objset;
|
||||
|
||||
VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
|
||||
rw_enter(&dp->dp_config_rwlock, RW_READER);
|
||||
for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
|
||||
zap_cursor_t basezc;
|
||||
zap_attribute_t baseza;
|
||||
nvlist_t *sp_nvp;
|
||||
uint64_t n;
|
||||
char source[MAXNAMELEN];
|
||||
|
||||
if (dd->dd_phys->dd_deleg_zapobj &&
|
||||
(zap_count(mos, dd->dd_phys->dd_deleg_zapobj,
|
||||
&n) == 0) && n) {
|
||||
VERIFY(nvlist_alloc(&sp_nvp,
|
||||
NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (zap_cursor_init(&basezc, mos,
|
||||
dd->dd_phys->dd_deleg_zapobj);
|
||||
zap_cursor_retrieve(&basezc, &baseza) == 0;
|
||||
zap_cursor_advance(&basezc)) {
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
nvlist_t *perms_nvp;
|
||||
|
||||
ASSERT(baseza.za_integer_length == 8);
|
||||
ASSERT(baseza.za_num_integers == 1);
|
||||
|
||||
VERIFY(nvlist_alloc(&perms_nvp,
|
||||
NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
VERIFY(nvlist_add_boolean(perms_nvp,
|
||||
za.za_name) == 0);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name,
|
||||
perms_nvp) == 0);
|
||||
nvlist_free(perms_nvp);
|
||||
}
|
||||
|
||||
zap_cursor_fini(&basezc);
|
||||
|
||||
dsl_dir_name(dd, source);
|
||||
VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0);
|
||||
nvlist_free(sp_nvp);
|
||||
}
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
|
||||
dsl_dir_close(startdd, FTAG);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for dsl_deleg_access() -- access checking.
|
||||
*/
|
||||
typedef struct perm_set {
|
||||
avl_node_t p_node;
|
||||
boolean_t p_matched;
|
||||
char p_setname[ZFS_MAX_DELEG_NAME];
|
||||
} perm_set_t;
|
||||
|
||||
static int
|
||||
perm_set_compare(const void *arg1, const void *arg2)
|
||||
{
|
||||
const perm_set_t *node1 = arg1;
|
||||
const perm_set_t *node2 = arg2;
|
||||
int val;
|
||||
|
||||
val = strcmp(node1->p_setname, node2->p_setname);
|
||||
if (val == 0)
|
||||
return (0);
|
||||
return (val > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether a specified permission exists.
|
||||
*
|
||||
* First the base attribute has to be retrieved. i.e. ul$12
|
||||
* Once the base object has been retrieved the actual permission
|
||||
* is lookup up in the zap object the base object points to.
|
||||
*
|
||||
* Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
|
||||
* there is no perm in that jumpobj.
|
||||
*/
|
||||
static int
|
||||
dsl_check_access(objset_t *mos, uint64_t zapobj,
|
||||
char type, char checkflag, void *valp, const char *perm)
|
||||
{
|
||||
int error;
|
||||
uint64_t jumpobj, zero;
|
||||
char whokey[ZFS_MAX_DELEG_NAME];
|
||||
|
||||
zfs_deleg_whokey(whokey, type, checkflag, valp);
|
||||
error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
|
||||
if (error == 0) {
|
||||
error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
|
||||
if (error == ENOENT)
|
||||
error = EPERM;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* check a specified user/group for a requested permission
|
||||
*/
|
||||
static int
|
||||
dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
|
||||
int checkflag, cred_t *cr)
|
||||
{
|
||||
const gid_t *gids;
|
||||
int ngids;
|
||||
int i;
|
||||
uint64_t id;
|
||||
|
||||
/* check for user */
|
||||
id = crgetuid(cr);
|
||||
if (dsl_check_access(mos, zapobj,
|
||||
ZFS_DELEG_USER, checkflag, &id, perm) == 0)
|
||||
return (0);
|
||||
|
||||
/* check for users primary group */
|
||||
id = crgetgid(cr);
|
||||
if (dsl_check_access(mos, zapobj,
|
||||
ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
|
||||
return (0);
|
||||
|
||||
/* check for everyone entry */
|
||||
id = -1;
|
||||
if (dsl_check_access(mos, zapobj,
|
||||
ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
|
||||
return (0);
|
||||
|
||||
/* check each supplemental group user is a member of */
|
||||
ngids = crgetngroups(cr);
|
||||
gids = crgetgroups(cr);
|
||||
for (i = 0; i != ngids; i++) {
|
||||
id = gids[i];
|
||||
if (dsl_check_access(mos, zapobj,
|
||||
ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over the sets specified in the specified zapobj
|
||||
* and load them into the permsets avl tree.
|
||||
*/
|
||||
static int
|
||||
dsl_load_sets(objset_t *mos, uint64_t zapobj,
|
||||
char type, char checkflag, void *valp, avl_tree_t *avl)
|
||||
{
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
perm_set_t *permnode;
|
||||
avl_index_t idx;
|
||||
uint64_t jumpobj;
|
||||
int error;
|
||||
char whokey[ZFS_MAX_DELEG_NAME];
|
||||
|
||||
zfs_deleg_whokey(whokey, type, checkflag, valp);
|
||||
|
||||
error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
for (zap_cursor_init(&zc, mos, jumpobj);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
|
||||
(void) strlcpy(permnode->p_setname, za.za_name,
|
||||
sizeof (permnode->p_setname));
|
||||
permnode->p_matched = B_FALSE;
|
||||
|
||||
if (avl_find(avl, permnode, &idx) == NULL) {
|
||||
avl_insert(avl, permnode, idx);
|
||||
} else {
|
||||
kmem_free(permnode, sizeof (perm_set_t));
|
||||
}
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load all permissions user based on cred belongs to.
|
||||
*/
|
||||
static void
|
||||
dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
|
||||
char checkflag, cred_t *cr)
|
||||
{
|
||||
const gid_t *gids;
|
||||
int ngids, i;
|
||||
uint64_t id;
|
||||
|
||||
id = crgetuid(cr);
|
||||
(void) dsl_load_sets(mos, zapobj,
|
||||
ZFS_DELEG_USER_SETS, checkflag, &id, avl);
|
||||
|
||||
id = crgetgid(cr);
|
||||
(void) dsl_load_sets(mos, zapobj,
|
||||
ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
|
||||
|
||||
(void) dsl_load_sets(mos, zapobj,
|
||||
ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
|
||||
|
||||
ngids = crgetngroups(cr);
|
||||
gids = crgetgroups(cr);
|
||||
for (i = 0; i != ngids; i++) {
|
||||
id = gids[i];
|
||||
(void) dsl_load_sets(mos, zapobj,
|
||||
ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if user has requested permission.
|
||||
*/
|
||||
int
|
||||
dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
|
||||
{
|
||||
dsl_dataset_t *ds;
|
||||
dsl_dir_t *dd;
|
||||
dsl_pool_t *dp;
|
||||
void *cookie;
|
||||
int error;
|
||||
char checkflag = ZFS_DELEG_LOCAL;
|
||||
objset_t *mos;
|
||||
avl_tree_t permsets;
|
||||
perm_set_t *setnode;
|
||||
|
||||
error = dsl_dataset_hold(dsname, FTAG, &ds);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
dp = ds->ds_dir->dd_pool;
|
||||
mos = dp->dp_meta_objset;
|
||||
|
||||
if (dsl_delegation_on(mos) == B_FALSE) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (ECANCELED);
|
||||
}
|
||||
|
||||
if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
|
||||
SPA_VERSION_DELEGATED_PERMS) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
|
||||
offsetof(perm_set_t, p_node));
|
||||
|
||||
rw_enter(&dp->dp_config_rwlock, RW_READER);
|
||||
for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
|
||||
checkflag = ZFS_DELEG_DESCENDENT) {
|
||||
uint64_t zapobj;
|
||||
boolean_t expanded;
|
||||
|
||||
/*
|
||||
* If not in global zone then make sure
|
||||
* the zoned property is set
|
||||
*/
|
||||
if (!INGLOBALZONE(curproc)) {
|
||||
uint64_t zoned;
|
||||
|
||||
if (dsl_prop_get_dd(dd,
|
||||
zfs_prop_to_name(ZFS_PROP_ZONED),
|
||||
8, 1, &zoned, NULL) != 0)
|
||||
break;
|
||||
if (!zoned)
|
||||
break;
|
||||
}
|
||||
zapobj = dd->dd_phys->dd_deleg_zapobj;
|
||||
|
||||
if (zapobj == 0)
|
||||
continue;
|
||||
|
||||
dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
|
||||
again:
|
||||
expanded = B_FALSE;
|
||||
for (setnode = avl_first(&permsets); setnode;
|
||||
setnode = AVL_NEXT(&permsets, setnode)) {
|
||||
if (setnode->p_matched == B_TRUE)
|
||||
continue;
|
||||
|
||||
/* See if this set directly grants this permission */
|
||||
error = dsl_check_access(mos, zapobj,
|
||||
ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
|
||||
if (error == 0)
|
||||
goto success;
|
||||
if (error == EPERM)
|
||||
setnode->p_matched = B_TRUE;
|
||||
|
||||
/* See if this set includes other sets */
|
||||
error = dsl_load_sets(mos, zapobj,
|
||||
ZFS_DELEG_NAMED_SET_SETS, 0,
|
||||
setnode->p_setname, &permsets);
|
||||
if (error == 0)
|
||||
setnode->p_matched = expanded = B_TRUE;
|
||||
}
|
||||
/*
|
||||
* If we expanded any sets, that will define more sets,
|
||||
* which we need to check.
|
||||
*/
|
||||
if (expanded)
|
||||
goto again;
|
||||
|
||||
error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
|
||||
if (error == 0)
|
||||
goto success;
|
||||
}
|
||||
error = EPERM;
|
||||
success:
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
|
||||
cookie = NULL;
|
||||
while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
|
||||
kmem_free(setnode, sizeof (perm_set_t));
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Other routines.
|
||||
*/
|
||||
|
||||
static void
|
||||
copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
|
||||
boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
|
||||
{
|
||||
objset_t *mos = dd->dd_pool->dp_meta_objset;
|
||||
uint64_t jumpobj, pjumpobj;
|
||||
uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
char whokey[ZFS_MAX_DELEG_NAME];
|
||||
|
||||
zfs_deleg_whokey(whokey,
|
||||
dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
|
||||
ZFS_DELEG_LOCAL, NULL);
|
||||
if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
|
||||
return;
|
||||
|
||||
if (zapobj == 0) {
|
||||
dmu_buf_will_dirty(dd->dd_dbuf, tx);
|
||||
zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
|
||||
DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
|
||||
}
|
||||
|
||||
zfs_deleg_whokey(whokey,
|
||||
dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
|
||||
ZFS_DELEG_LOCAL, &uid);
|
||||
if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
|
||||
jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
|
||||
VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
|
||||
}
|
||||
|
||||
for (zap_cursor_init(&zc, mos, pjumpobj);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
uint64_t zero = 0;
|
||||
ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
|
||||
|
||||
VERIFY(zap_update(mos, jumpobj, za.za_name,
|
||||
8, 1, &zero, tx) == 0);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
}
|
||||
|
||||
/*
|
||||
* set all create time permission on new dataset.
|
||||
*/
|
||||
void
|
||||
dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
|
||||
{
|
||||
dsl_dir_t *dd;
|
||||
uint64_t uid = crgetuid(cr);
|
||||
|
||||
if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
|
||||
SPA_VERSION_DELEGATED_PERMS)
|
||||
return;
|
||||
|
||||
for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
|
||||
uint64_t pzapobj = dd->dd_phys->dd_deleg_zapobj;
|
||||
|
||||
if (pzapobj == 0)
|
||||
continue;
|
||||
|
||||
copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
|
||||
copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
|
||||
{
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
|
||||
if (zapobj == 0)
|
||||
return (0);
|
||||
|
||||
for (zap_cursor_init(&zc, mos, zapobj);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
|
||||
VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
VERIFY(0 == zap_destroy(mos, zapobj, tx));
|
||||
return (0);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
dsl_delegation_on(objset_t *os)
|
||||
{
|
||||
return (os->os->os_spa->spa_delegation);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,613 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/dsl_pool.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/arc.h>
|
||||
#include <sys/zap.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/zfs_znode.h>
|
||||
#include <sys/spa_impl.h>
|
||||
|
||||
int zfs_no_write_throttle = 0;
|
||||
int zfs_write_limit_shift = 3; /* 1/8th of physical memory */
|
||||
int zfs_txg_synctime = 5; /* target secs to sync a txg */
|
||||
|
||||
uint64_t zfs_write_limit_min = 32 << 20; /* min write limit is 32MB */
|
||||
uint64_t zfs_write_limit_max = 0; /* max data payload per txg */
|
||||
uint64_t zfs_write_limit_inflated = 0;
|
||||
uint64_t zfs_write_limit_override = 0;
|
||||
|
||||
kmutex_t zfs_write_limit_lock;
|
||||
|
||||
static pgcnt_t old_physmem = 0;
|
||||
|
||||
static int
|
||||
dsl_pool_open_special_dir(dsl_pool_t *dp, const char *name, dsl_dir_t **ddp)
|
||||
{
|
||||
uint64_t obj;
|
||||
int err;
|
||||
|
||||
err = zap_lookup(dp->dp_meta_objset,
|
||||
dp->dp_root_dir->dd_phys->dd_child_dir_zapobj,
|
||||
name, sizeof (obj), 1, &obj);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
return (dsl_dir_open_obj(dp, obj, name, dp, ddp));
|
||||
}
|
||||
|
||||
static dsl_pool_t *
|
||||
dsl_pool_open_impl(spa_t *spa, uint64_t txg)
|
||||
{
|
||||
dsl_pool_t *dp;
|
||||
blkptr_t *bp = spa_get_rootblkptr(spa);
|
||||
|
||||
dp = kmem_zalloc(sizeof (dsl_pool_t), KM_SLEEP);
|
||||
dp->dp_spa = spa;
|
||||
dp->dp_meta_rootbp = *bp;
|
||||
rw_init(&dp->dp_config_rwlock, NULL, RW_DEFAULT, NULL);
|
||||
dp->dp_write_limit = zfs_write_limit_min;
|
||||
txg_init(dp, txg);
|
||||
|
||||
txg_list_create(&dp->dp_dirty_datasets,
|
||||
offsetof(dsl_dataset_t, ds_dirty_link));
|
||||
txg_list_create(&dp->dp_dirty_dirs,
|
||||
offsetof(dsl_dir_t, dd_dirty_link));
|
||||
txg_list_create(&dp->dp_sync_tasks,
|
||||
offsetof(dsl_sync_task_group_t, dstg_node));
|
||||
list_create(&dp->dp_synced_datasets, sizeof (dsl_dataset_t),
|
||||
offsetof(dsl_dataset_t, ds_synced_link));
|
||||
|
||||
mutex_init(&dp->dp_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&dp->dp_scrub_cancel_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
|
||||
return (dp);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_pool_open(spa_t *spa, uint64_t txg, dsl_pool_t **dpp)
|
||||
{
|
||||
int err;
|
||||
dsl_pool_t *dp = dsl_pool_open_impl(spa, txg);
|
||||
dsl_dir_t *dd;
|
||||
dsl_dataset_t *ds;
|
||||
objset_impl_t *osi;
|
||||
|
||||
rw_enter(&dp->dp_config_rwlock, RW_WRITER);
|
||||
err = dmu_objset_open_impl(spa, NULL, &dp->dp_meta_rootbp, &osi);
|
||||
if (err)
|
||||
goto out;
|
||||
dp->dp_meta_objset = &osi->os;
|
||||
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_ROOT_DATASET, sizeof (uint64_t), 1,
|
||||
&dp->dp_root_dir_obj);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = dsl_dir_open_obj(dp, dp->dp_root_dir_obj,
|
||||
NULL, dp, &dp->dp_root_dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = dsl_pool_open_special_dir(dp, MOS_DIR_NAME, &dp->dp_mos_dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (spa_version(spa) >= SPA_VERSION_ORIGIN) {
|
||||
err = dsl_pool_open_special_dir(dp, ORIGIN_DIR_NAME, &dd);
|
||||
if (err)
|
||||
goto out;
|
||||
err = dsl_dataset_hold_obj(dp, dd->dd_phys->dd_head_dataset_obj,
|
||||
FTAG, &ds);
|
||||
if (err)
|
||||
goto out;
|
||||
err = dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
|
||||
dp, &dp->dp_origin_snap);
|
||||
if (err)
|
||||
goto out;
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
dsl_dir_close(dd, dp);
|
||||
}
|
||||
|
||||
/* get scrub status */
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_SCRUB_FUNC, sizeof (uint32_t), 1,
|
||||
&dp->dp_scrub_func);
|
||||
if (err == 0) {
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_SCRUB_QUEUE, sizeof (uint64_t), 1,
|
||||
&dp->dp_scrub_queue_obj);
|
||||
if (err)
|
||||
goto out;
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_SCRUB_MIN_TXG, sizeof (uint64_t), 1,
|
||||
&dp->dp_scrub_min_txg);
|
||||
if (err)
|
||||
goto out;
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_SCRUB_MAX_TXG, sizeof (uint64_t), 1,
|
||||
&dp->dp_scrub_max_txg);
|
||||
if (err)
|
||||
goto out;
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_SCRUB_BOOKMARK, sizeof (uint64_t), 4,
|
||||
&dp->dp_scrub_bookmark);
|
||||
if (err)
|
||||
goto out;
|
||||
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_SCRUB_ERRORS, sizeof (uint64_t), 1,
|
||||
&spa->spa_scrub_errors);
|
||||
if (err)
|
||||
goto out;
|
||||
if (spa_version(spa) < SPA_VERSION_DSL_SCRUB) {
|
||||
/*
|
||||
* A new-type scrub was in progress on an old
|
||||
* pool. Restart from the beginning, since the
|
||||
* old software may have changed the pool in the
|
||||
* meantime.
|
||||
*/
|
||||
dsl_pool_scrub_restart(dp);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* It's OK if there is no scrub in progress (and if
|
||||
* there was an I/O error, ignore it).
|
||||
*/
|
||||
err = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
if (err)
|
||||
dsl_pool_close(dp);
|
||||
else
|
||||
*dpp = dp;
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_close(dsl_pool_t *dp)
|
||||
{
|
||||
/* drop our references from dsl_pool_open() */
|
||||
|
||||
/*
|
||||
* Since we held the origin_snap from "syncing" context (which
|
||||
* includes pool-opening context), it actually only got a "ref"
|
||||
* and not a hold, so just drop that here.
|
||||
*/
|
||||
if (dp->dp_origin_snap)
|
||||
dsl_dataset_drop_ref(dp->dp_origin_snap, dp);
|
||||
if (dp->dp_mos_dir)
|
||||
dsl_dir_close(dp->dp_mos_dir, dp);
|
||||
if (dp->dp_root_dir)
|
||||
dsl_dir_close(dp->dp_root_dir, dp);
|
||||
|
||||
/* undo the dmu_objset_open_impl(mos) from dsl_pool_open() */
|
||||
if (dp->dp_meta_objset)
|
||||
dmu_objset_evict(NULL, dp->dp_meta_objset->os);
|
||||
|
||||
txg_list_destroy(&dp->dp_dirty_datasets);
|
||||
txg_list_destroy(&dp->dp_dirty_dirs);
|
||||
list_destroy(&dp->dp_synced_datasets);
|
||||
|
||||
arc_flush(dp->dp_spa);
|
||||
txg_fini(dp);
|
||||
rw_destroy(&dp->dp_config_rwlock);
|
||||
mutex_destroy(&dp->dp_lock);
|
||||
mutex_destroy(&dp->dp_scrub_cancel_lock);
|
||||
if (dp->dp_blkstats)
|
||||
kmem_free(dp->dp_blkstats, sizeof (zfs_all_blkstats_t));
|
||||
kmem_free(dp, sizeof (dsl_pool_t));
|
||||
}
|
||||
|
||||
dsl_pool_t *
|
||||
dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg)
|
||||
{
|
||||
int err;
|
||||
dsl_pool_t *dp = dsl_pool_open_impl(spa, txg);
|
||||
dmu_tx_t *tx = dmu_tx_create_assigned(dp, txg);
|
||||
objset_impl_t *osip;
|
||||
dsl_dataset_t *ds;
|
||||
uint64_t dsobj;
|
||||
|
||||
/* create and open the MOS (meta-objset) */
|
||||
dp->dp_meta_objset = &dmu_objset_create_impl(spa,
|
||||
NULL, &dp->dp_meta_rootbp, DMU_OST_META, tx)->os;
|
||||
|
||||
/* create the pool directory */
|
||||
err = zap_create_claim(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_OT_OBJECT_DIRECTORY, DMU_OT_NONE, 0, tx);
|
||||
ASSERT3U(err, ==, 0);
|
||||
|
||||
/* create and open the root dir */
|
||||
dp->dp_root_dir_obj = dsl_dir_create_sync(dp, NULL, NULL, tx);
|
||||
VERIFY(0 == dsl_dir_open_obj(dp, dp->dp_root_dir_obj,
|
||||
NULL, dp, &dp->dp_root_dir));
|
||||
|
||||
/* create and open the meta-objset dir */
|
||||
(void) dsl_dir_create_sync(dp, dp->dp_root_dir, MOS_DIR_NAME, tx);
|
||||
VERIFY(0 == dsl_pool_open_special_dir(dp,
|
||||
MOS_DIR_NAME, &dp->dp_mos_dir));
|
||||
|
||||
if (spa_version(spa) >= SPA_VERSION_DSL_SCRUB)
|
||||
dsl_pool_create_origin(dp, tx);
|
||||
|
||||
/* create the root dataset */
|
||||
dsobj = dsl_dataset_create_sync_dd(dp->dp_root_dir, NULL, 0, tx);
|
||||
|
||||
/* create the root objset */
|
||||
VERIFY(0 == dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds));
|
||||
osip = dmu_objset_create_impl(dp->dp_spa, ds,
|
||||
dsl_dataset_get_blkptr(ds), DMU_OST_ZFS, tx);
|
||||
#ifdef _KERNEL
|
||||
zfs_create_fs(&osip->os, kcred, zplprops, tx);
|
||||
#endif
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
return (dp);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_sync(dsl_pool_t *dp, uint64_t txg)
|
||||
{
|
||||
zio_t *zio;
|
||||
dmu_tx_t *tx;
|
||||
dsl_dir_t *dd;
|
||||
dsl_dataset_t *ds;
|
||||
dsl_sync_task_group_t *dstg;
|
||||
objset_impl_t *mosi = dp->dp_meta_objset->os;
|
||||
hrtime_t start, write_time;
|
||||
uint64_t data_written;
|
||||
int err;
|
||||
|
||||
tx = dmu_tx_create_assigned(dp, txg);
|
||||
|
||||
dp->dp_read_overhead = 0;
|
||||
zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
|
||||
while (ds = txg_list_remove(&dp->dp_dirty_datasets, txg)) {
|
||||
if (!list_link_active(&ds->ds_synced_link))
|
||||
list_insert_tail(&dp->dp_synced_datasets, ds);
|
||||
else
|
||||
dmu_buf_rele(ds->ds_dbuf, ds);
|
||||
dsl_dataset_sync(ds, zio, tx);
|
||||
}
|
||||
DTRACE_PROBE(pool_sync__1setup);
|
||||
|
||||
start = gethrtime();
|
||||
err = zio_wait(zio);
|
||||
write_time = gethrtime() - start;
|
||||
ASSERT(err == 0);
|
||||
DTRACE_PROBE(pool_sync__2rootzio);
|
||||
|
||||
while (dstg = txg_list_remove(&dp->dp_sync_tasks, txg))
|
||||
dsl_sync_task_group_sync(dstg, tx);
|
||||
DTRACE_PROBE(pool_sync__3task);
|
||||
|
||||
start = gethrtime();
|
||||
while (dd = txg_list_remove(&dp->dp_dirty_dirs, txg))
|
||||
dsl_dir_sync(dd, tx);
|
||||
write_time += gethrtime() - start;
|
||||
|
||||
if (spa_sync_pass(dp->dp_spa) == 1)
|
||||
dsl_pool_scrub_sync(dp, tx);
|
||||
|
||||
start = gethrtime();
|
||||
if (list_head(&mosi->os_dirty_dnodes[txg & TXG_MASK]) != NULL ||
|
||||
list_head(&mosi->os_free_dnodes[txg & TXG_MASK]) != NULL) {
|
||||
zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
|
||||
dmu_objset_sync(mosi, zio, tx);
|
||||
err = zio_wait(zio);
|
||||
ASSERT(err == 0);
|
||||
dprintf_bp(&dp->dp_meta_rootbp, "meta objset rootbp is %s", "");
|
||||
spa_set_rootblkptr(dp->dp_spa, &dp->dp_meta_rootbp);
|
||||
}
|
||||
write_time += gethrtime() - start;
|
||||
DTRACE_PROBE2(pool_sync__4io, hrtime_t, write_time,
|
||||
hrtime_t, dp->dp_read_overhead);
|
||||
write_time -= dp->dp_read_overhead;
|
||||
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
data_written = dp->dp_space_towrite[txg & TXG_MASK];
|
||||
dp->dp_space_towrite[txg & TXG_MASK] = 0;
|
||||
ASSERT(dp->dp_tempreserved[txg & TXG_MASK] == 0);
|
||||
|
||||
/*
|
||||
* If the write limit max has not been explicitly set, set it
|
||||
* to a fraction of available physical memory (default 1/8th).
|
||||
* Note that we must inflate the limit because the spa
|
||||
* inflates write sizes to account for data replication.
|
||||
* Check this each sync phase to catch changing memory size.
|
||||
*/
|
||||
if (physmem != old_physmem && zfs_write_limit_shift) {
|
||||
mutex_enter(&zfs_write_limit_lock);
|
||||
old_physmem = physmem;
|
||||
zfs_write_limit_max = ptob(physmem) >> zfs_write_limit_shift;
|
||||
zfs_write_limit_inflated = MAX(zfs_write_limit_min,
|
||||
spa_get_asize(dp->dp_spa, zfs_write_limit_max));
|
||||
mutex_exit(&zfs_write_limit_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to keep the sync time consistent by adjusting the
|
||||
* amount of write traffic allowed into each transaction group.
|
||||
* Weight the throughput calculation towards the current value:
|
||||
* thru = 3/4 old_thru + 1/4 new_thru
|
||||
*/
|
||||
ASSERT(zfs_write_limit_min > 0);
|
||||
if (data_written > zfs_write_limit_min / 8 && write_time > 0) {
|
||||
uint64_t throughput = (data_written * NANOSEC) / write_time;
|
||||
if (dp->dp_throughput)
|
||||
dp->dp_throughput = throughput / 4 +
|
||||
3 * dp->dp_throughput / 4;
|
||||
else
|
||||
dp->dp_throughput = throughput;
|
||||
dp->dp_write_limit = MIN(zfs_write_limit_inflated,
|
||||
MAX(zfs_write_limit_min,
|
||||
dp->dp_throughput * zfs_txg_synctime));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_zil_clean(dsl_pool_t *dp)
|
||||
{
|
||||
dsl_dataset_t *ds;
|
||||
|
||||
while (ds = list_head(&dp->dp_synced_datasets)) {
|
||||
list_remove(&dp->dp_synced_datasets, ds);
|
||||
ASSERT(ds->ds_user_ptr != NULL);
|
||||
zil_clean(((objset_impl_t *)ds->ds_user_ptr)->os_zil);
|
||||
dmu_buf_rele(ds->ds_dbuf, ds);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TRUE if the current thread is the tx_sync_thread or if we
|
||||
* are being called from SPA context during pool initialization.
|
||||
*/
|
||||
int
|
||||
dsl_pool_sync_context(dsl_pool_t *dp)
|
||||
{
|
||||
return (curthread == dp->dp_tx.tx_sync_thread ||
|
||||
spa_get_dsl(dp->dp_spa) == NULL);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
dsl_pool_adjustedsize(dsl_pool_t *dp, boolean_t netfree)
|
||||
{
|
||||
uint64_t space, resv;
|
||||
|
||||
/*
|
||||
* Reserve about 1.6% (1/64), or at least 32MB, for allocation
|
||||
* efficiency.
|
||||
* XXX The intent log is not accounted for, so it must fit
|
||||
* within this slop.
|
||||
*
|
||||
* If we're trying to assess whether it's OK to do a free,
|
||||
* cut the reservation in half to allow forward progress
|
||||
* (e.g. make it possible to rm(1) files from a full pool).
|
||||
*/
|
||||
space = spa_get_dspace(dp->dp_spa);
|
||||
resv = MAX(space >> 6, SPA_MINDEVSIZE >> 1);
|
||||
if (netfree)
|
||||
resv >>= 1;
|
||||
|
||||
return (space - resv);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_pool_tempreserve_space(dsl_pool_t *dp, uint64_t space, dmu_tx_t *tx)
|
||||
{
|
||||
uint64_t reserved = 0;
|
||||
uint64_t write_limit = (zfs_write_limit_override ?
|
||||
zfs_write_limit_override : dp->dp_write_limit);
|
||||
|
||||
if (zfs_no_write_throttle) {
|
||||
atomic_add_64(&dp->dp_tempreserved[tx->tx_txg & TXG_MASK],
|
||||
space);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if we have exceeded the maximum allowed IO for
|
||||
* this transaction group. We can do this without locks since
|
||||
* a little slop here is ok. Note that we do the reserved check
|
||||
* with only half the requested reserve: this is because the
|
||||
* reserve requests are worst-case, and we really don't want to
|
||||
* throttle based off of worst-case estimates.
|
||||
*/
|
||||
if (write_limit > 0) {
|
||||
reserved = dp->dp_space_towrite[tx->tx_txg & TXG_MASK]
|
||||
+ dp->dp_tempreserved[tx->tx_txg & TXG_MASK] / 2;
|
||||
|
||||
if (reserved && reserved > write_limit)
|
||||
return (ERESTART);
|
||||
}
|
||||
|
||||
atomic_add_64(&dp->dp_tempreserved[tx->tx_txg & TXG_MASK], space);
|
||||
|
||||
/*
|
||||
* If this transaction group is over 7/8ths capacity, delay
|
||||
* the caller 1 clock tick. This will slow down the "fill"
|
||||
* rate until the sync process can catch up with us.
|
||||
*/
|
||||
if (reserved && reserved > (write_limit - (write_limit >> 3)))
|
||||
txg_delay(dp, tx->tx_txg, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_tempreserve_clear(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx)
|
||||
{
|
||||
ASSERT(dp->dp_tempreserved[tx->tx_txg & TXG_MASK] >= space);
|
||||
atomic_add_64(&dp->dp_tempreserved[tx->tx_txg & TXG_MASK], -space);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_memory_pressure(dsl_pool_t *dp)
|
||||
{
|
||||
uint64_t space_inuse = 0;
|
||||
int i;
|
||||
|
||||
if (dp->dp_write_limit == zfs_write_limit_min)
|
||||
return;
|
||||
|
||||
for (i = 0; i < TXG_SIZE; i++) {
|
||||
space_inuse += dp->dp_space_towrite[i];
|
||||
space_inuse += dp->dp_tempreserved[i];
|
||||
}
|
||||
dp->dp_write_limit = MAX(zfs_write_limit_min,
|
||||
MIN(dp->dp_write_limit, space_inuse / 4));
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_willuse_space(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx)
|
||||
{
|
||||
if (space > 0) {
|
||||
mutex_enter(&dp->dp_lock);
|
||||
dp->dp_space_towrite[tx->tx_txg & TXG_MASK] += space;
|
||||
mutex_exit(&dp->dp_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
upgrade_clones_cb(spa_t *spa, uint64_t dsobj, const char *dsname, void *arg)
|
||||
{
|
||||
dmu_tx_t *tx = arg;
|
||||
dsl_dataset_t *ds, *prev = NULL;
|
||||
int err;
|
||||
dsl_pool_t *dp = spa_get_dsl(spa);
|
||||
|
||||
err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
while (ds->ds_phys->ds_prev_snap_obj != 0) {
|
||||
err = dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
|
||||
FTAG, &prev);
|
||||
if (err) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (prev->ds_phys->ds_next_snap_obj != ds->ds_object)
|
||||
break;
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
ds = prev;
|
||||
prev = NULL;
|
||||
}
|
||||
|
||||
if (prev == NULL) {
|
||||
prev = dp->dp_origin_snap;
|
||||
|
||||
/*
|
||||
* The $ORIGIN can't have any data, or the accounting
|
||||
* will be wrong.
|
||||
*/
|
||||
ASSERT(prev->ds_phys->ds_bp.blk_birth == 0);
|
||||
|
||||
/* The origin doesn't get attached to itself */
|
||||
if (ds->ds_object == prev->ds_object) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (0);
|
||||
}
|
||||
|
||||
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
||||
ds->ds_phys->ds_prev_snap_obj = prev->ds_object;
|
||||
ds->ds_phys->ds_prev_snap_txg = prev->ds_phys->ds_creation_txg;
|
||||
|
||||
dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
|
||||
ds->ds_dir->dd_phys->dd_origin_obj = prev->ds_object;
|
||||
|
||||
dmu_buf_will_dirty(prev->ds_dbuf, tx);
|
||||
prev->ds_phys->ds_num_children++;
|
||||
|
||||
if (ds->ds_phys->ds_next_snap_obj == 0) {
|
||||
ASSERT(ds->ds_prev == NULL);
|
||||
VERIFY(0 == dsl_dataset_hold_obj(dp,
|
||||
ds->ds_phys->ds_prev_snap_obj, ds, &ds->ds_prev));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(ds->ds_dir->dd_phys->dd_origin_obj == prev->ds_object);
|
||||
ASSERT(ds->ds_phys->ds_prev_snap_obj == prev->ds_object);
|
||||
|
||||
if (prev->ds_phys->ds_next_clones_obj == 0) {
|
||||
prev->ds_phys->ds_next_clones_obj =
|
||||
zap_create(dp->dp_meta_objset,
|
||||
DMU_OT_NEXT_CLONES, DMU_OT_NONE, 0, tx);
|
||||
}
|
||||
VERIFY(0 == zap_add_int(dp->dp_meta_objset,
|
||||
prev->ds_phys->ds_next_clones_obj, ds->ds_object, tx));
|
||||
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
if (prev != dp->dp_origin_snap)
|
||||
dsl_dataset_rele(prev, FTAG);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_upgrade_clones(dsl_pool_t *dp, dmu_tx_t *tx)
|
||||
{
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
ASSERT(dp->dp_origin_snap != NULL);
|
||||
|
||||
(void) dmu_objset_find_spa(dp->dp_spa, NULL, upgrade_clones_cb,
|
||||
tx, DS_FIND_CHILDREN);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_pool_create_origin(dsl_pool_t *dp, dmu_tx_t *tx)
|
||||
{
|
||||
uint64_t dsobj;
|
||||
dsl_dataset_t *ds;
|
||||
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
ASSERT(dp->dp_origin_snap == NULL);
|
||||
|
||||
/* create the origin dir, ds, & snap-ds */
|
||||
rw_enter(&dp->dp_config_rwlock, RW_WRITER);
|
||||
dsobj = dsl_dataset_create_sync(dp->dp_root_dir, ORIGIN_DIR_NAME,
|
||||
NULL, 0, kcred, tx);
|
||||
VERIFY(0 == dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds));
|
||||
dsl_dataset_snapshot_sync(ds, ORIGIN_DIR_NAME, kcred, tx);
|
||||
VERIFY(0 == dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
|
||||
dp, &dp->dp_origin_snap));
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
}
|
|
@ -0,0 +1,602 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/dsl_prop.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/zio_checksum.h> /* for the default checksum value */
|
||||
#include <sys/zap.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include "zfs_prop.h"
|
||||
|
||||
static int
|
||||
dodefault(const char *propname, int intsz, int numint, void *buf)
|
||||
{
|
||||
zfs_prop_t prop;
|
||||
|
||||
/*
|
||||
* The setonce properties are read-only, BUT they still
|
||||
* have a default value that can be used as the initial
|
||||
* value.
|
||||
*/
|
||||
if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL ||
|
||||
(zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))
|
||||
return (ENOENT);
|
||||
|
||||
if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
|
||||
if (intsz != 1)
|
||||
return (EOVERFLOW);
|
||||
(void) strncpy(buf, zfs_prop_default_string(prop),
|
||||
numint);
|
||||
} else {
|
||||
if (intsz != 8 || numint < 1)
|
||||
return (EOVERFLOW);
|
||||
|
||||
*(uint64_t *)buf = zfs_prop_default_numeric(prop);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
|
||||
int intsz, int numint, void *buf, char *setpoint)
|
||||
{
|
||||
int err = ENOENT;
|
||||
objset_t *mos = dd->dd_pool->dp_meta_objset;
|
||||
zfs_prop_t prop;
|
||||
|
||||
ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
|
||||
|
||||
if (setpoint)
|
||||
setpoint[0] = '\0';
|
||||
|
||||
prop = zfs_name_to_prop(propname);
|
||||
|
||||
/*
|
||||
* Note: dd may be NULL, therefore we shouldn't dereference it
|
||||
* ouside this loop.
|
||||
*/
|
||||
for (; dd != NULL; dd = dd->dd_parent) {
|
||||
ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
|
||||
err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
|
||||
propname, intsz, numint, buf);
|
||||
if (err != ENOENT) {
|
||||
if (setpoint)
|
||||
dsl_dir_name(dd, setpoint);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Break out of this loop for non-inheritable properties.
|
||||
*/
|
||||
if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
|
||||
break;
|
||||
}
|
||||
if (err == ENOENT)
|
||||
err = dodefault(propname, intsz, numint, buf);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
|
||||
int intsz, int numint, void *buf, char *setpoint)
|
||||
{
|
||||
ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
|
||||
|
||||
if (ds->ds_phys->ds_props_obj) {
|
||||
int err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
|
||||
ds->ds_phys->ds_props_obj, propname, intsz, numint, buf);
|
||||
if (err != ENOENT) {
|
||||
if (setpoint)
|
||||
dsl_dataset_name(ds, setpoint);
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
|
||||
return (dsl_prop_get_dd(ds->ds_dir, propname,
|
||||
intsz, numint, buf, setpoint));
|
||||
}
|
||||
|
||||
/*
|
||||
* Register interest in the named property. We'll call the callback
|
||||
* once to notify it of the current property value, and again each time
|
||||
* the property changes, until this callback is unregistered.
|
||||
*
|
||||
* Return 0 on success, errno if the prop is not an integer value.
|
||||
*/
|
||||
int
|
||||
dsl_prop_register(dsl_dataset_t *ds, const char *propname,
|
||||
dsl_prop_changed_cb_t *callback, void *cbarg)
|
||||
{
|
||||
dsl_dir_t *dd = ds->ds_dir;
|
||||
dsl_pool_t *dp = dd->dd_pool;
|
||||
uint64_t value;
|
||||
dsl_prop_cb_record_t *cbr;
|
||||
int err;
|
||||
int need_rwlock;
|
||||
|
||||
need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
|
||||
if (need_rwlock)
|
||||
rw_enter(&dp->dp_config_rwlock, RW_READER);
|
||||
|
||||
err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
|
||||
if (err != 0) {
|
||||
if (need_rwlock)
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
|
||||
cbr->cbr_ds = ds;
|
||||
cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
|
||||
(void) strcpy((char *)cbr->cbr_propname, propname);
|
||||
cbr->cbr_func = callback;
|
||||
cbr->cbr_arg = cbarg;
|
||||
mutex_enter(&dd->dd_lock);
|
||||
list_insert_head(&dd->dd_prop_cbs, cbr);
|
||||
mutex_exit(&dd->dd_lock);
|
||||
|
||||
cbr->cbr_func(cbr->cbr_arg, value);
|
||||
|
||||
VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
|
||||
NULL, cbr, &dd));
|
||||
if (need_rwlock)
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
/* Leave dir open until this callback is unregistered */
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_prop_get(const char *dsname, const char *propname,
|
||||
int intsz, int numints, void *buf, char *setpoint)
|
||||
{
|
||||
dsl_dataset_t *ds;
|
||||
int err;
|
||||
|
||||
err = dsl_dataset_hold(dsname, FTAG, &ds);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
|
||||
err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
|
||||
rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
|
||||
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current property value. It may have changed by the time this
|
||||
* function returns, so it is NOT safe to follow up with
|
||||
* dsl_prop_register() and assume that the value has not changed in
|
||||
* between.
|
||||
*
|
||||
* Return 0 on success, ENOENT if ddname is invalid.
|
||||
*/
|
||||
int
|
||||
dsl_prop_get_integer(const char *ddname, const char *propname,
|
||||
uint64_t *valuep, char *setpoint)
|
||||
{
|
||||
return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister this callback. Return 0 on success, ENOENT if ddname is
|
||||
* invalid, ENOMSG if no matching callback registered.
|
||||
*/
|
||||
int
|
||||
dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
|
||||
dsl_prop_changed_cb_t *callback, void *cbarg)
|
||||
{
|
||||
dsl_dir_t *dd = ds->ds_dir;
|
||||
dsl_prop_cb_record_t *cbr;
|
||||
|
||||
mutex_enter(&dd->dd_lock);
|
||||
for (cbr = list_head(&dd->dd_prop_cbs);
|
||||
cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
||||
if (cbr->cbr_ds == ds &&
|
||||
cbr->cbr_func == callback &&
|
||||
cbr->cbr_arg == cbarg &&
|
||||
strcmp(cbr->cbr_propname, propname) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cbr == NULL) {
|
||||
mutex_exit(&dd->dd_lock);
|
||||
return (ENOMSG);
|
||||
}
|
||||
|
||||
list_remove(&dd->dd_prop_cbs, cbr);
|
||||
mutex_exit(&dd->dd_lock);
|
||||
kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
|
||||
kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
|
||||
|
||||
/* Clean up from dsl_prop_register */
|
||||
dsl_dir_close(dd, cbr);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of callbacks that are registered for this dataset.
|
||||
*/
|
||||
int
|
||||
dsl_prop_numcb(dsl_dataset_t *ds)
|
||||
{
|
||||
dsl_dir_t *dd = ds->ds_dir;
|
||||
dsl_prop_cb_record_t *cbr;
|
||||
int num = 0;
|
||||
|
||||
mutex_enter(&dd->dd_lock);
|
||||
for (cbr = list_head(&dd->dd_prop_cbs);
|
||||
cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
||||
if (cbr->cbr_ds == ds)
|
||||
num++;
|
||||
}
|
||||
mutex_exit(&dd->dd_lock);
|
||||
|
||||
return (num);
|
||||
}
|
||||
|
||||
static void
|
||||
dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
|
||||
const char *propname, uint64_t value, int first)
|
||||
{
|
||||
dsl_dir_t *dd;
|
||||
dsl_prop_cb_record_t *cbr;
|
||||
objset_t *mos = dp->dp_meta_objset;
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t *za;
|
||||
int err;
|
||||
uint64_t dummyval;
|
||||
|
||||
ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
|
||||
err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
if (!first) {
|
||||
/*
|
||||
* If the prop is set here, then this change is not
|
||||
* being inherited here or below; stop the recursion.
|
||||
*/
|
||||
err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
|
||||
8, 1, &dummyval);
|
||||
if (err == 0) {
|
||||
dsl_dir_close(dd, FTAG);
|
||||
return;
|
||||
}
|
||||
ASSERT3U(err, ==, ENOENT);
|
||||
}
|
||||
|
||||
mutex_enter(&dd->dd_lock);
|
||||
for (cbr = list_head(&dd->dd_prop_cbs); cbr;
|
||||
cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
||||
uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
|
||||
|
||||
if (strcmp(cbr->cbr_propname, propname) != 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the property is set on this ds, then it is not
|
||||
* inherited here; don't call the callback.
|
||||
*/
|
||||
if (propobj && 0 == zap_lookup(mos, propobj, propname,
|
||||
8, 1, &dummyval))
|
||||
continue;
|
||||
|
||||
cbr->cbr_func(cbr->cbr_arg, value);
|
||||
}
|
||||
mutex_exit(&dd->dd_lock);
|
||||
|
||||
za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
|
||||
for (zap_cursor_init(&zc, mos,
|
||||
dd->dd_phys->dd_child_dir_zapobj);
|
||||
zap_cursor_retrieve(&zc, za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
dsl_prop_changed_notify(dp, za->za_first_integer,
|
||||
propname, value, FALSE);
|
||||
}
|
||||
kmem_free(za, sizeof (zap_attribute_t));
|
||||
zap_cursor_fini(&zc);
|
||||
dsl_dir_close(dd, FTAG);
|
||||
}
|
||||
|
||||
struct prop_set_arg {
|
||||
const char *name;
|
||||
int intsz;
|
||||
int numints;
|
||||
const void *buf;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_dataset_t *ds = arg1;
|
||||
struct prop_set_arg *psa = arg2;
|
||||
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
|
||||
uint64_t zapobj, intval;
|
||||
int isint;
|
||||
char valbuf[32];
|
||||
char *valstr;
|
||||
|
||||
isint = (dodefault(psa->name, 8, 1, &intval) == 0);
|
||||
|
||||
if (dsl_dataset_is_snapshot(ds)) {
|
||||
ASSERT(spa_version(ds->ds_dir->dd_pool->dp_spa) >=
|
||||
SPA_VERSION_SNAP_PROPS);
|
||||
if (ds->ds_phys->ds_props_obj == 0) {
|
||||
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
||||
ds->ds_phys->ds_props_obj =
|
||||
zap_create(mos,
|
||||
DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
|
||||
}
|
||||
zapobj = ds->ds_phys->ds_props_obj;
|
||||
} else {
|
||||
zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
|
||||
}
|
||||
|
||||
if (psa->numints == 0) {
|
||||
int err = zap_remove(mos, zapobj, psa->name, tx);
|
||||
ASSERT(err == 0 || err == ENOENT);
|
||||
if (isint) {
|
||||
VERIFY(0 == dsl_prop_get_ds(ds,
|
||||
psa->name, 8, 1, &intval, NULL));
|
||||
}
|
||||
} else {
|
||||
VERIFY(0 == zap_update(mos, zapobj, psa->name,
|
||||
psa->intsz, psa->numints, psa->buf, tx));
|
||||
if (isint)
|
||||
intval = *(uint64_t *)psa->buf;
|
||||
}
|
||||
|
||||
if (isint) {
|
||||
if (dsl_dataset_is_snapshot(ds)) {
|
||||
dsl_prop_cb_record_t *cbr;
|
||||
/*
|
||||
* It's a snapshot; nothing can inherit this
|
||||
* property, so just look for callbacks on this
|
||||
* ds here.
|
||||
*/
|
||||
mutex_enter(&ds->ds_dir->dd_lock);
|
||||
for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
|
||||
cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
|
||||
if (cbr->cbr_ds == ds &&
|
||||
strcmp(cbr->cbr_propname, psa->name) == 0)
|
||||
cbr->cbr_func(cbr->cbr_arg, intval);
|
||||
}
|
||||
mutex_exit(&ds->ds_dir->dd_lock);
|
||||
} else {
|
||||
dsl_prop_changed_notify(ds->ds_dir->dd_pool,
|
||||
ds->ds_dir->dd_object, psa->name, intval, TRUE);
|
||||
}
|
||||
}
|
||||
if (isint) {
|
||||
(void) snprintf(valbuf, sizeof (valbuf),
|
||||
"%lld", (longlong_t)intval);
|
||||
valstr = valbuf;
|
||||
} else {
|
||||
valstr = (char *)psa->buf;
|
||||
}
|
||||
spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT :
|
||||
LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr,
|
||||
"%s=%s dataset = %llu", psa->name, valstr, ds->ds_object);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
|
||||
cred_t *cr, dmu_tx_t *tx)
|
||||
{
|
||||
objset_t *mos = dd->dd_pool->dp_meta_objset;
|
||||
uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
|
||||
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
|
||||
VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx));
|
||||
|
||||
dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE);
|
||||
|
||||
spa_history_internal_log(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
|
||||
"%s=%llu dataset = %llu", name, (u_longlong_t)val,
|
||||
dd->dd_phys->dd_head_dataset_obj);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_prop_set(const char *dsname, const char *propname,
|
||||
int intsz, int numints, const void *buf)
|
||||
{
|
||||
dsl_dataset_t *ds;
|
||||
int err;
|
||||
struct prop_set_arg psa;
|
||||
|
||||
/*
|
||||
* We must do these checks before we get to the syncfunc, since
|
||||
* it can't fail.
|
||||
*/
|
||||
if (strlen(propname) >= ZAP_MAXNAMELEN)
|
||||
return (ENAMETOOLONG);
|
||||
if (intsz * numints >= ZAP_MAXVALUELEN)
|
||||
return (E2BIG);
|
||||
|
||||
err = dsl_dataset_hold(dsname, FTAG, &ds);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
if (dsl_dataset_is_snapshot(ds) &&
|
||||
spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_SNAP_PROPS) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
psa.name = propname;
|
||||
psa.intsz = intsz;
|
||||
psa.numints = numints;
|
||||
psa.buf = buf;
|
||||
err = dsl_sync_task_do(ds->ds_dir->dd_pool,
|
||||
NULL, dsl_prop_set_sync, ds, &psa, 2);
|
||||
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all properties for this dataset and return them in an nvlist.
|
||||
*/
|
||||
int
|
||||
dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local)
|
||||
{
|
||||
dsl_dataset_t *ds = os->os->os_dsl_dataset;
|
||||
dsl_dir_t *dd = ds->ds_dir;
|
||||
boolean_t snapshot = dsl_dataset_is_snapshot(ds);
|
||||
int err = 0;
|
||||
dsl_pool_t *dp = dd->dd_pool;
|
||||
objset_t *mos = dp->dp_meta_objset;
|
||||
uint64_t propobj = ds->ds_phys->ds_props_obj;
|
||||
|
||||
VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
|
||||
if (local && snapshot && !propobj)
|
||||
return (0);
|
||||
|
||||
rw_enter(&dp->dp_config_rwlock, RW_READER);
|
||||
while (dd != NULL) {
|
||||
char setpoint[MAXNAMELEN];
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
dsl_dir_t *dd_next;
|
||||
|
||||
if (propobj) {
|
||||
dsl_dataset_name(ds, setpoint);
|
||||
dd_next = dd;
|
||||
} else {
|
||||
dsl_dir_name(dd, setpoint);
|
||||
propobj = dd->dd_phys->dd_props_zapobj;
|
||||
dd_next = dd->dd_parent;
|
||||
}
|
||||
|
||||
for (zap_cursor_init(&zc, mos, propobj);
|
||||
(err = zap_cursor_retrieve(&zc, &za)) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
nvlist_t *propval;
|
||||
zfs_prop_t prop = zfs_name_to_prop(za.za_name);
|
||||
|
||||
/* Skip non-inheritable properties. */
|
||||
if (prop != ZPROP_INVAL &&
|
||||
!zfs_prop_inheritable(prop) &&
|
||||
(dd != ds->ds_dir || (snapshot && dd != dd_next)))
|
||||
continue;
|
||||
|
||||
/* Skip properties not valid for this type. */
|
||||
if (snapshot && prop != ZPROP_INVAL &&
|
||||
!zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
|
||||
continue;
|
||||
|
||||
/* Skip properties already defined */
|
||||
if (nvlist_lookup_nvlist(*nvp, za.za_name,
|
||||
&propval) == 0)
|
||||
continue;
|
||||
|
||||
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME,
|
||||
KM_SLEEP) == 0);
|
||||
if (za.za_integer_length == 1) {
|
||||
/*
|
||||
* String property
|
||||
*/
|
||||
char *tmp = kmem_alloc(za.za_num_integers,
|
||||
KM_SLEEP);
|
||||
err = zap_lookup(mos, propobj,
|
||||
za.za_name, 1, za.za_num_integers, tmp);
|
||||
if (err != 0) {
|
||||
kmem_free(tmp, za.za_num_integers);
|
||||
break;
|
||||
}
|
||||
VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
|
||||
tmp) == 0);
|
||||
kmem_free(tmp, za.za_num_integers);
|
||||
} else {
|
||||
/*
|
||||
* Integer property
|
||||
*/
|
||||
ASSERT(za.za_integer_length == 8);
|
||||
(void) nvlist_add_uint64(propval, ZPROP_VALUE,
|
||||
za.za_first_integer);
|
||||
}
|
||||
|
||||
VERIFY(nvlist_add_string(propval, ZPROP_SOURCE,
|
||||
setpoint) == 0);
|
||||
VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
|
||||
propval) == 0);
|
||||
nvlist_free(propval);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
|
||||
if (err != ENOENT)
|
||||
break;
|
||||
err = 0;
|
||||
/*
|
||||
* If we are just after the props that have been set
|
||||
* locally, then we are done after the first iteration.
|
||||
*/
|
||||
if (local)
|
||||
break;
|
||||
dd = dd_next;
|
||||
propobj = 0;
|
||||
}
|
||||
rw_exit(&dp->dp_config_rwlock);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
|
||||
{
|
||||
nvlist_t *propval;
|
||||
|
||||
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
|
||||
VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
|
||||
nvlist_free(propval);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
|
||||
{
|
||||
nvlist_t *propval;
|
||||
|
||||
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
|
||||
VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
|
||||
nvlist_free(propval);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dsl_pool.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/cred.h>
|
||||
|
||||
#define DST_AVG_BLKSHIFT 14
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
dsl_null_checkfunc(void *arg1, void *arg2, dmu_tx_t *tx)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
dsl_sync_task_group_t *
|
||||
dsl_sync_task_group_create(dsl_pool_t *dp)
|
||||
{
|
||||
dsl_sync_task_group_t *dstg;
|
||||
|
||||
dstg = kmem_zalloc(sizeof (dsl_sync_task_group_t), KM_SLEEP);
|
||||
list_create(&dstg->dstg_tasks, sizeof (dsl_sync_task_t),
|
||||
offsetof(dsl_sync_task_t, dst_node));
|
||||
dstg->dstg_pool = dp;
|
||||
dstg->dstg_cr = CRED();
|
||||
|
||||
return (dstg);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_sync_task_create(dsl_sync_task_group_t *dstg,
|
||||
dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
|
||||
void *arg1, void *arg2, int blocks_modified)
|
||||
{
|
||||
dsl_sync_task_t *dst;
|
||||
|
||||
if (checkfunc == NULL)
|
||||
checkfunc = dsl_null_checkfunc;
|
||||
dst = kmem_zalloc(sizeof (dsl_sync_task_t), KM_SLEEP);
|
||||
dst->dst_checkfunc = checkfunc;
|
||||
dst->dst_syncfunc = syncfunc;
|
||||
dst->dst_arg1 = arg1;
|
||||
dst->dst_arg2 = arg2;
|
||||
list_insert_tail(&dstg->dstg_tasks, dst);
|
||||
|
||||
dstg->dstg_space += blocks_modified << DST_AVG_BLKSHIFT;
|
||||
}
|
||||
|
||||
int
|
||||
dsl_sync_task_group_wait(dsl_sync_task_group_t *dstg)
|
||||
{
|
||||
dmu_tx_t *tx;
|
||||
uint64_t txg;
|
||||
dsl_sync_task_t *dst;
|
||||
|
||||
top:
|
||||
tx = dmu_tx_create_dd(dstg->dstg_pool->dp_mos_dir);
|
||||
VERIFY(0 == dmu_tx_assign(tx, TXG_WAIT));
|
||||
|
||||
txg = dmu_tx_get_txg(tx);
|
||||
|
||||
/* Do a preliminary error check. */
|
||||
dstg->dstg_err = 0;
|
||||
rw_enter(&dstg->dstg_pool->dp_config_rwlock, RW_READER);
|
||||
for (dst = list_head(&dstg->dstg_tasks); dst;
|
||||
dst = list_next(&dstg->dstg_tasks, dst)) {
|
||||
#ifdef ZFS_DEBUG
|
||||
/*
|
||||
* Only check half the time, otherwise, the sync-context
|
||||
* check will almost never fail.
|
||||
*/
|
||||
if (spa_get_random(2) == 0)
|
||||
continue;
|
||||
#endif
|
||||
dst->dst_err =
|
||||
dst->dst_checkfunc(dst->dst_arg1, dst->dst_arg2, tx);
|
||||
if (dst->dst_err)
|
||||
dstg->dstg_err = dst->dst_err;
|
||||
}
|
||||
rw_exit(&dstg->dstg_pool->dp_config_rwlock);
|
||||
|
||||
if (dstg->dstg_err) {
|
||||
dmu_tx_commit(tx);
|
||||
return (dstg->dstg_err);
|
||||
}
|
||||
|
||||
VERIFY(0 == txg_list_add(&dstg->dstg_pool->dp_sync_tasks, dstg, txg));
|
||||
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
txg_wait_synced(dstg->dstg_pool, txg);
|
||||
|
||||
if (dstg->dstg_err == EAGAIN)
|
||||
goto top;
|
||||
|
||||
return (dstg->dstg_err);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_sync_task_group_nowait(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
|
||||
{
|
||||
uint64_t txg;
|
||||
|
||||
dstg->dstg_nowaiter = B_TRUE;
|
||||
txg = dmu_tx_get_txg(tx);
|
||||
VERIFY(0 == txg_list_add(&dstg->dstg_pool->dp_sync_tasks, dstg, txg));
|
||||
}
|
||||
|
||||
void
|
||||
dsl_sync_task_group_destroy(dsl_sync_task_group_t *dstg)
|
||||
{
|
||||
dsl_sync_task_t *dst;
|
||||
|
||||
while (dst = list_head(&dstg->dstg_tasks)) {
|
||||
list_remove(&dstg->dstg_tasks, dst);
|
||||
kmem_free(dst, sizeof (dsl_sync_task_t));
|
||||
}
|
||||
kmem_free(dstg, sizeof (dsl_sync_task_group_t));
|
||||
}
|
||||
|
||||
void
|
||||
dsl_sync_task_group_sync(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_sync_task_t *dst;
|
||||
void *tr_cookie;
|
||||
|
||||
ASSERT3U(dstg->dstg_err, ==, 0);
|
||||
|
||||
/*
|
||||
* Check for sufficient space.
|
||||
*/
|
||||
dstg->dstg_err = dsl_dir_tempreserve_space(dstg->dstg_pool->dp_mos_dir,
|
||||
dstg->dstg_space, dstg->dstg_space * 3, 0, 0, &tr_cookie, tx);
|
||||
/* don't bother trying again */
|
||||
if (dstg->dstg_err == ERESTART)
|
||||
dstg->dstg_err = EAGAIN;
|
||||
if (dstg->dstg_err)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check for errors by calling checkfuncs.
|
||||
*/
|
||||
rw_enter(&dstg->dstg_pool->dp_config_rwlock, RW_WRITER);
|
||||
for (dst = list_head(&dstg->dstg_tasks); dst;
|
||||
dst = list_next(&dstg->dstg_tasks, dst)) {
|
||||
dst->dst_err =
|
||||
dst->dst_checkfunc(dst->dst_arg1, dst->dst_arg2, tx);
|
||||
if (dst->dst_err)
|
||||
dstg->dstg_err = dst->dst_err;
|
||||
}
|
||||
|
||||
if (dstg->dstg_err == 0) {
|
||||
/*
|
||||
* Execute sync tasks.
|
||||
*/
|
||||
for (dst = list_head(&dstg->dstg_tasks); dst;
|
||||
dst = list_next(&dstg->dstg_tasks, dst)) {
|
||||
dst->dst_syncfunc(dst->dst_arg1, dst->dst_arg2,
|
||||
dstg->dstg_cr, tx);
|
||||
}
|
||||
}
|
||||
rw_exit(&dstg->dstg_pool->dp_config_rwlock);
|
||||
|
||||
dsl_dir_tempreserve_clear(tr_cookie, tx);
|
||||
|
||||
if (dstg->dstg_nowaiter)
|
||||
dsl_sync_task_group_destroy(dstg);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_sync_task_do(dsl_pool_t *dp,
|
||||
dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
|
||||
void *arg1, void *arg2, int blocks_modified)
|
||||
{
|
||||
dsl_sync_task_group_t *dstg;
|
||||
int err;
|
||||
|
||||
dstg = dsl_sync_task_group_create(dp);
|
||||
dsl_sync_task_create(dstg, checkfunc, syncfunc,
|
||||
arg1, arg2, blocks_modified);
|
||||
err = dsl_sync_task_group_wait(dstg);
|
||||
dsl_sync_task_group_destroy(dstg);
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_sync_task_do_nowait(dsl_pool_t *dp,
|
||||
dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
|
||||
void *arg1, void *arg2, int blocks_modified, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_sync_task_group_t *dstg;
|
||||
|
||||
dstg = dsl_sync_task_group_create(dp);
|
||||
dsl_sync_task_create(dstg, checkfunc, syncfunc,
|
||||
arg1, arg2, blocks_modified);
|
||||
dsl_sync_task_group_nowait(dstg, tx);
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <sys/spa.h>
|
||||
|
||||
void
|
||||
fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
|
||||
{
|
||||
const uint64_t *ip = buf;
|
||||
const uint64_t *ipend = ip + (size / sizeof (uint64_t));
|
||||
uint64_t a0, b0, a1, b1;
|
||||
|
||||
for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) {
|
||||
a0 += ip[0];
|
||||
a1 += ip[1];
|
||||
b0 += a0;
|
||||
b1 += a1;
|
||||
}
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1);
|
||||
}
|
||||
|
||||
void
|
||||
fletcher_2_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp)
|
||||
{
|
||||
const uint64_t *ip = buf;
|
||||
const uint64_t *ipend = ip + (size / sizeof (uint64_t));
|
||||
uint64_t a0, b0, a1, b1;
|
||||
|
||||
for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) {
|
||||
a0 += BSWAP_64(ip[0]);
|
||||
a1 += BSWAP_64(ip[1]);
|
||||
b0 += a0;
|
||||
b1 += a1;
|
||||
}
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1);
|
||||
}
|
||||
|
||||
void
|
||||
fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
|
||||
{
|
||||
const uint32_t *ip = buf;
|
||||
const uint32_t *ipend = ip + (size / sizeof (uint32_t));
|
||||
uint64_t a, b, c, d;
|
||||
|
||||
for (a = b = c = d = 0; ip < ipend; ip++) {
|
||||
a += ip[0];
|
||||
b += a;
|
||||
c += b;
|
||||
d += c;
|
||||
}
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp, a, b, c, d);
|
||||
}
|
||||
|
||||
void
|
||||
fletcher_4_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp)
|
||||
{
|
||||
const uint32_t *ip = buf;
|
||||
const uint32_t *ipend = ip + (size / sizeof (uint32_t));
|
||||
uint64_t a, b, c, d;
|
||||
|
||||
for (a = b = c = d = 0; ip < ipend; ip++) {
|
||||
a += BSWAP_32(ip[0]);
|
||||
b += a;
|
||||
c += b;
|
||||
d += c;
|
||||
}
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp, a, b, c, d);
|
||||
}
|
||||
|
||||
void
|
||||
fletcher_4_incremental_native(const void *buf, uint64_t size,
|
||||
zio_cksum_t *zcp)
|
||||
{
|
||||
const uint32_t *ip = buf;
|
||||
const uint32_t *ipend = ip + (size / sizeof (uint32_t));
|
||||
uint64_t a, b, c, d;
|
||||
|
||||
a = zcp->zc_word[0];
|
||||
b = zcp->zc_word[1];
|
||||
c = zcp->zc_word[2];
|
||||
d = zcp->zc_word[3];
|
||||
|
||||
for (; ip < ipend; ip++) {
|
||||
a += ip[0];
|
||||
b += a;
|
||||
c += b;
|
||||
d += c;
|
||||
}
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp, a, b, c, d);
|
||||
}
|
||||
|
||||
void
|
||||
fletcher_4_incremental_byteswap(const void *buf, uint64_t size,
|
||||
zio_cksum_t *zcp)
|
||||
{
|
||||
const uint32_t *ip = buf;
|
||||
const uint32_t *ipend = ip + (size / sizeof (uint32_t));
|
||||
uint64_t a, b, c, d;
|
||||
|
||||
a = zcp->zc_word[0];
|
||||
b = zcp->zc_word[1];
|
||||
c = zcp->zc_word[2];
|
||||
d = zcp->zc_word[3];
|
||||
|
||||
for (; ip < ipend; ip++) {
|
||||
a += BSWAP_32(ip[0]);
|
||||
b += a;
|
||||
c += b;
|
||||
d += c;
|
||||
}
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp, a, b, c, d);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/debug.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/zmod.h>
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/systm.h>
|
||||
#else
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
size_t
|
||||
gzip_compress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n)
|
||||
{
|
||||
size_t dstlen = d_len;
|
||||
|
||||
ASSERT(d_len <= s_len);
|
||||
|
||||
if (z_compress_level(d_start, &dstlen, s_start, s_len, n) != Z_OK) {
|
||||
if (d_len != s_len)
|
||||
return (s_len);
|
||||
|
||||
bcopy(s_start, d_start, s_len);
|
||||
return (s_len);
|
||||
}
|
||||
|
||||
return (dstlen);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
int
|
||||
gzip_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n)
|
||||
{
|
||||
size_t dstlen = d_len;
|
||||
|
||||
ASSERT(d_len >= s_len);
|
||||
|
||||
if (z_uncompress(d_start, &dstlen, s_start, s_len) != Z_OK)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* We keep our own copy of this algorithm for 2 main reasons:
|
||||
* 1. If we didn't, anyone modifying common/os/compress.c would
|
||||
* directly break our on disk format
|
||||
* 2. Our version of lzjb does not have a number of checks that the
|
||||
* common/os version needs and uses
|
||||
* In particular, we are adding the "feature" that compress() can
|
||||
* take a destination buffer size and return -1 if the data will not
|
||||
* compress to d_len or less.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define MATCH_BITS 6
|
||||
#define MATCH_MIN 3
|
||||
#define MATCH_MAX ((1 << MATCH_BITS) + (MATCH_MIN - 1))
|
||||
#define OFFSET_MASK ((1 << (16 - MATCH_BITS)) - 1)
|
||||
#define LEMPEL_SIZE 256
|
||||
|
||||
/*ARGSUSED*/
|
||||
size_t
|
||||
lzjb_compress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n)
|
||||
{
|
||||
uchar_t *src = s_start;
|
||||
uchar_t *dst = d_start;
|
||||
uchar_t *cpy, *copymap;
|
||||
int copymask = 1 << (NBBY - 1);
|
||||
int mlen, offset;
|
||||
uint16_t *hp;
|
||||
uint16_t lempel[LEMPEL_SIZE]; /* uninitialized; see above */
|
||||
|
||||
while (src < (uchar_t *)s_start + s_len) {
|
||||
if ((copymask <<= 1) == (1 << NBBY)) {
|
||||
if (dst >= (uchar_t *)d_start + d_len - 1 - 2 * NBBY) {
|
||||
if (d_len != s_len)
|
||||
return (s_len);
|
||||
mlen = s_len;
|
||||
for (src = s_start, dst = d_start; mlen; mlen--)
|
||||
*dst++ = *src++;
|
||||
return (s_len);
|
||||
}
|
||||
copymask = 1;
|
||||
copymap = dst;
|
||||
*dst++ = 0;
|
||||
}
|
||||
if (src > (uchar_t *)s_start + s_len - MATCH_MAX) {
|
||||
*dst++ = *src++;
|
||||
continue;
|
||||
}
|
||||
hp = &lempel[((src[0] + 13) ^ (src[1] - 13) ^ src[2]) &
|
||||
(LEMPEL_SIZE - 1)];
|
||||
offset = (intptr_t)(src - *hp) & OFFSET_MASK;
|
||||
*hp = (uint16_t)(uintptr_t)src;
|
||||
cpy = src - offset;
|
||||
if (cpy >= (uchar_t *)s_start && cpy != src &&
|
||||
src[0] == cpy[0] && src[1] == cpy[1] && src[2] == cpy[2]) {
|
||||
*copymap |= copymask;
|
||||
for (mlen = MATCH_MIN; mlen < MATCH_MAX; mlen++)
|
||||
if (src[mlen] != cpy[mlen])
|
||||
break;
|
||||
*dst++ = ((mlen - MATCH_MIN) << (NBBY - MATCH_BITS)) |
|
||||
(offset >> NBBY);
|
||||
*dst++ = (uchar_t)offset;
|
||||
src += mlen;
|
||||
} else {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
return (dst - (uchar_t *)d_start);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
int
|
||||
lzjb_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n)
|
||||
{
|
||||
uchar_t *src = s_start;
|
||||
uchar_t *dst = d_start;
|
||||
uchar_t *d_end = (uchar_t *)d_start + d_len;
|
||||
uchar_t *cpy, copymap;
|
||||
int copymask = 1 << (NBBY - 1);
|
||||
|
||||
while (dst < d_end) {
|
||||
if ((copymask <<= 1) == (1 << NBBY)) {
|
||||
copymask = 1;
|
||||
copymap = *src++;
|
||||
}
|
||||
if (copymap & copymask) {
|
||||
int mlen = (src[0] >> (NBBY - MATCH_BITS)) + MATCH_MIN;
|
||||
int offset = ((src[0] << NBBY) | src[1]) & OFFSET_MASK;
|
||||
src += 2;
|
||||
if ((cpy = dst - offset) < (uchar_t *)d_start)
|
||||
return (-1);
|
||||
while (--mlen >= 0 && dst < d_end)
|
||||
*dst++ = *cpy++;
|
||||
} else {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/refcount.h>
|
||||
|
||||
#if defined(DEBUG) || !defined(_KERNEL)
|
||||
|
||||
#ifdef _KERNEL
|
||||
int reference_tracking_enable = FALSE; /* runs out of memory too easily */
|
||||
#else
|
||||
int reference_tracking_enable = TRUE;
|
||||
#endif
|
||||
int reference_history = 4; /* tunable */
|
||||
|
||||
static kmem_cache_t *reference_cache;
|
||||
static kmem_cache_t *reference_history_cache;
|
||||
|
||||
void
|
||||
refcount_init(void)
|
||||
{
|
||||
reference_cache = kmem_cache_create("reference_cache",
|
||||
sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
|
||||
|
||||
reference_history_cache = kmem_cache_create("reference_history_cache",
|
||||
sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void
|
||||
refcount_fini(void)
|
||||
{
|
||||
kmem_cache_destroy(reference_cache);
|
||||
kmem_cache_destroy(reference_history_cache);
|
||||
}
|
||||
|
||||
void
|
||||
refcount_create(refcount_t *rc)
|
||||
{
|
||||
mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
|
||||
list_create(&rc->rc_list, sizeof (reference_t),
|
||||
offsetof(reference_t, ref_link));
|
||||
list_create(&rc->rc_removed, sizeof (reference_t),
|
||||
offsetof(reference_t, ref_link));
|
||||
rc->rc_count = 0;
|
||||
rc->rc_removed_count = 0;
|
||||
}
|
||||
|
||||
void
|
||||
refcount_destroy_many(refcount_t *rc, uint64_t number)
|
||||
{
|
||||
reference_t *ref;
|
||||
|
||||
ASSERT(rc->rc_count == number);
|
||||
while (ref = list_head(&rc->rc_list)) {
|
||||
list_remove(&rc->rc_list, ref);
|
||||
kmem_cache_free(reference_cache, ref);
|
||||
}
|
||||
list_destroy(&rc->rc_list);
|
||||
|
||||
while (ref = list_head(&rc->rc_removed)) {
|
||||
list_remove(&rc->rc_removed, ref);
|
||||
kmem_cache_free(reference_history_cache, ref->ref_removed);
|
||||
kmem_cache_free(reference_cache, ref);
|
||||
}
|
||||
list_destroy(&rc->rc_removed);
|
||||
mutex_destroy(&rc->rc_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
refcount_destroy(refcount_t *rc)
|
||||
{
|
||||
refcount_destroy_many(rc, 0);
|
||||
}
|
||||
|
||||
int
|
||||
refcount_is_zero(refcount_t *rc)
|
||||
{
|
||||
ASSERT(rc->rc_count >= 0);
|
||||
return (rc->rc_count == 0);
|
||||
}
|
||||
|
||||
int64_t
|
||||
refcount_count(refcount_t *rc)
|
||||
{
|
||||
ASSERT(rc->rc_count >= 0);
|
||||
return (rc->rc_count);
|
||||
}
|
||||
|
||||
int64_t
|
||||
refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
|
||||
{
|
||||
reference_t *ref;
|
||||
int64_t count;
|
||||
|
||||
if (reference_tracking_enable) {
|
||||
ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
|
||||
ref->ref_holder = holder;
|
||||
ref->ref_number = number;
|
||||
}
|
||||
mutex_enter(&rc->rc_mtx);
|
||||
ASSERT(rc->rc_count >= 0);
|
||||
if (reference_tracking_enable)
|
||||
list_insert_head(&rc->rc_list, ref);
|
||||
rc->rc_count += number;
|
||||
count = rc->rc_count;
|
||||
mutex_exit(&rc->rc_mtx);
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
int64_t
|
||||
refcount_add(refcount_t *rc, void *holder)
|
||||
{
|
||||
return (refcount_add_many(rc, 1, holder));
|
||||
}
|
||||
|
||||
int64_t
|
||||
refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
|
||||
{
|
||||
reference_t *ref;
|
||||
int64_t count;
|
||||
|
||||
mutex_enter(&rc->rc_mtx);
|
||||
ASSERT(rc->rc_count >= number);
|
||||
|
||||
if (!reference_tracking_enable) {
|
||||
rc->rc_count -= number;
|
||||
count = rc->rc_count;
|
||||
mutex_exit(&rc->rc_mtx);
|
||||
return (count);
|
||||
}
|
||||
|
||||
for (ref = list_head(&rc->rc_list); ref;
|
||||
ref = list_next(&rc->rc_list, ref)) {
|
||||
if (ref->ref_holder == holder && ref->ref_number == number) {
|
||||
list_remove(&rc->rc_list, ref);
|
||||
if (reference_history > 0) {
|
||||
ref->ref_removed =
|
||||
kmem_cache_alloc(reference_history_cache,
|
||||
KM_SLEEP);
|
||||
list_insert_head(&rc->rc_removed, ref);
|
||||
rc->rc_removed_count++;
|
||||
if (rc->rc_removed_count >= reference_history) {
|
||||
ref = list_tail(&rc->rc_removed);
|
||||
list_remove(&rc->rc_removed, ref);
|
||||
kmem_cache_free(reference_history_cache,
|
||||
ref->ref_removed);
|
||||
kmem_cache_free(reference_cache, ref);
|
||||
rc->rc_removed_count--;
|
||||
}
|
||||
} else {
|
||||
kmem_cache_free(reference_cache, ref);
|
||||
}
|
||||
rc->rc_count -= number;
|
||||
count = rc->rc_count;
|
||||
mutex_exit(&rc->rc_mtx);
|
||||
return (count);
|
||||
}
|
||||
}
|
||||
panic("No such hold %p on refcount %llx", holder,
|
||||
(u_longlong_t)(uintptr_t)rc);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int64_t
|
||||
refcount_remove(refcount_t *rc, void *holder)
|
||||
{
|
||||
return (refcount_remove_many(rc, 1, holder));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/refcount.h>
|
||||
#include <sys/rrwlock.h>
|
||||
|
||||
/*
|
||||
* This file contains the implementation of a re-entrant read
|
||||
* reader/writer lock (aka "rrwlock").
|
||||
*
|
||||
* This is a normal reader/writer lock with the additional feature
|
||||
* of allowing threads who have already obtained a read lock to
|
||||
* re-enter another read lock (re-entrant read) - even if there are
|
||||
* waiting writers.
|
||||
*
|
||||
* Callers who have not obtained a read lock give waiting writers priority.
|
||||
*
|
||||
* The rrwlock_t lock does not allow re-entrant writers, nor does it
|
||||
* allow a re-entrant mix of reads and writes (that is, it does not
|
||||
* allow a caller who has already obtained a read lock to be able to
|
||||
* then grab a write lock without first dropping all read locks, and
|
||||
* vice versa).
|
||||
*
|
||||
* The rrwlock_t uses tsd (thread specific data) to keep a list of
|
||||
* nodes (rrw_node_t), where each node keeps track of which specific
|
||||
* lock (rrw_node_t::rn_rrl) the thread has grabbed. Since re-entering
|
||||
* should be rare, a thread that grabs multiple reads on the same rrwlock_t
|
||||
* will store multiple rrw_node_ts of the same 'rrn_rrl'. Nodes on the
|
||||
* tsd list can represent a different rrwlock_t. This allows a thread
|
||||
* to enter multiple and unique rrwlock_ts for read locks at the same time.
|
||||
*
|
||||
* Since using tsd exposes some overhead, the rrwlock_t only needs to
|
||||
* keep tsd data when writers are waiting. If no writers are waiting, then
|
||||
* a reader just bumps the anonymous read count (rr_anon_rcount) - no tsd
|
||||
* is needed. Once a writer attempts to grab the lock, readers then
|
||||
* keep tsd data and bump the linked readers count (rr_linked_rcount).
|
||||
*
|
||||
* If there are waiting writers and there are anonymous readers, then a
|
||||
* reader doesn't know if it is a re-entrant lock. But since it may be one,
|
||||
* we allow the read to proceed (otherwise it could deadlock). Since once
|
||||
* waiting writers are active, readers no longer bump the anonymous count,
|
||||
* the anonymous readers will eventually flush themselves out. At this point,
|
||||
* readers will be able to tell if they are a re-entrant lock (have a
|
||||
* rrw_node_t entry for the lock) or not. If they are a re-entrant lock, then
|
||||
* we must let the proceed. If they are not, then the reader blocks for the
|
||||
* waiting writers. Hence, we do not starve writers.
|
||||
*/
|
||||
|
||||
/* global key for TSD */
|
||||
uint_t rrw_tsd_key;
|
||||
|
||||
typedef struct rrw_node {
|
||||
struct rrw_node *rn_next;
|
||||
rrwlock_t *rn_rrl;
|
||||
} rrw_node_t;
|
||||
|
||||
static rrw_node_t *
|
||||
rrn_find(rrwlock_t *rrl)
|
||||
{
|
||||
rrw_node_t *rn;
|
||||
|
||||
if (refcount_count(&rrl->rr_linked_rcount) == 0)
|
||||
return (NULL);
|
||||
|
||||
for (rn = tsd_get(rrw_tsd_key); rn != NULL; rn = rn->rn_next) {
|
||||
if (rn->rn_rrl == rrl)
|
||||
return (rn);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a node to the head of the singly linked list.
|
||||
*/
|
||||
static void
|
||||
rrn_add(rrwlock_t *rrl)
|
||||
{
|
||||
rrw_node_t *rn;
|
||||
|
||||
rn = kmem_alloc(sizeof (*rn), KM_SLEEP);
|
||||
rn->rn_rrl = rrl;
|
||||
rn->rn_next = tsd_get(rrw_tsd_key);
|
||||
VERIFY(tsd_set(rrw_tsd_key, rn) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a node is found for 'rrl', then remove the node from this
|
||||
* thread's list and return TRUE; otherwise return FALSE.
|
||||
*/
|
||||
static boolean_t
|
||||
rrn_find_and_remove(rrwlock_t *rrl)
|
||||
{
|
||||
rrw_node_t *rn;
|
||||
rrw_node_t *prev = NULL;
|
||||
|
||||
if (refcount_count(&rrl->rr_linked_rcount) == 0)
|
||||
return (NULL);
|
||||
|
||||
for (rn = tsd_get(rrw_tsd_key); rn != NULL; rn = rn->rn_next) {
|
||||
if (rn->rn_rrl == rrl) {
|
||||
if (prev)
|
||||
prev->rn_next = rn->rn_next;
|
||||
else
|
||||
VERIFY(tsd_set(rrw_tsd_key, rn->rn_next) == 0);
|
||||
kmem_free(rn, sizeof (*rn));
|
||||
return (B_TRUE);
|
||||
}
|
||||
prev = rn;
|
||||
}
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
rrw_init(rrwlock_t *rrl)
|
||||
{
|
||||
mutex_init(&rrl->rr_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&rrl->rr_cv, NULL, CV_DEFAULT, NULL);
|
||||
rrl->rr_writer = NULL;
|
||||
refcount_create(&rrl->rr_anon_rcount);
|
||||
refcount_create(&rrl->rr_linked_rcount);
|
||||
rrl->rr_writer_wanted = B_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
rrw_destroy(rrwlock_t *rrl)
|
||||
{
|
||||
mutex_destroy(&rrl->rr_lock);
|
||||
cv_destroy(&rrl->rr_cv);
|
||||
ASSERT(rrl->rr_writer == NULL);
|
||||
refcount_destroy(&rrl->rr_anon_rcount);
|
||||
refcount_destroy(&rrl->rr_linked_rcount);
|
||||
}
|
||||
|
||||
static void
|
||||
rrw_enter_read(rrwlock_t *rrl, void *tag)
|
||||
{
|
||||
mutex_enter(&rrl->rr_lock);
|
||||
ASSERT(rrl->rr_writer != curthread);
|
||||
ASSERT(refcount_count(&rrl->rr_anon_rcount) >= 0);
|
||||
|
||||
while (rrl->rr_writer || (rrl->rr_writer_wanted &&
|
||||
refcount_is_zero(&rrl->rr_anon_rcount) &&
|
||||
rrn_find(rrl) == NULL))
|
||||
cv_wait(&rrl->rr_cv, &rrl->rr_lock);
|
||||
|
||||
if (rrl->rr_writer_wanted) {
|
||||
/* may or may not be a re-entrant enter */
|
||||
rrn_add(rrl);
|
||||
(void) refcount_add(&rrl->rr_linked_rcount, tag);
|
||||
} else {
|
||||
(void) refcount_add(&rrl->rr_anon_rcount, tag);
|
||||
}
|
||||
ASSERT(rrl->rr_writer == NULL);
|
||||
mutex_exit(&rrl->rr_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
rrw_enter_write(rrwlock_t *rrl)
|
||||
{
|
||||
mutex_enter(&rrl->rr_lock);
|
||||
ASSERT(rrl->rr_writer != curthread);
|
||||
|
||||
while (refcount_count(&rrl->rr_anon_rcount) > 0 ||
|
||||
refcount_count(&rrl->rr_linked_rcount) > 0 ||
|
||||
rrl->rr_writer != NULL) {
|
||||
rrl->rr_writer_wanted = B_TRUE;
|
||||
cv_wait(&rrl->rr_cv, &rrl->rr_lock);
|
||||
}
|
||||
rrl->rr_writer_wanted = B_FALSE;
|
||||
rrl->rr_writer = curthread;
|
||||
mutex_exit(&rrl->rr_lock);
|
||||
}
|
||||
|
||||
void
|
||||
rrw_enter(rrwlock_t *rrl, krw_t rw, void *tag)
|
||||
{
|
||||
if (rw == RW_READER)
|
||||
rrw_enter_read(rrl, tag);
|
||||
else
|
||||
rrw_enter_write(rrl);
|
||||
}
|
||||
|
||||
void
|
||||
rrw_exit(rrwlock_t *rrl, void *tag)
|
||||
{
|
||||
mutex_enter(&rrl->rr_lock);
|
||||
ASSERT(!refcount_is_zero(&rrl->rr_anon_rcount) ||
|
||||
!refcount_is_zero(&rrl->rr_linked_rcount) ||
|
||||
rrl->rr_writer != NULL);
|
||||
|
||||
if (rrl->rr_writer == NULL) {
|
||||
if (rrn_find_and_remove(rrl)) {
|
||||
if (refcount_remove(&rrl->rr_linked_rcount, tag) == 0)
|
||||
cv_broadcast(&rrl->rr_cv);
|
||||
|
||||
} else {
|
||||
if (refcount_remove(&rrl->rr_anon_rcount, tag) == 0)
|
||||
cv_broadcast(&rrl->rr_cv);
|
||||
}
|
||||
} else {
|
||||
ASSERT(rrl->rr_writer == curthread);
|
||||
ASSERT(refcount_is_zero(&rrl->rr_anon_rcount) &&
|
||||
refcount_is_zero(&rrl->rr_linked_rcount));
|
||||
rrl->rr_writer = NULL;
|
||||
cv_broadcast(&rrl->rr_cv);
|
||||
}
|
||||
mutex_exit(&rrl->rr_lock);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
rrw_held(rrwlock_t *rrl, krw_t rw)
|
||||
{
|
||||
boolean_t held;
|
||||
|
||||
mutex_enter(&rrl->rr_lock);
|
||||
if (rw == RW_WRITER) {
|
||||
held = (rrl->rr_writer == curthread);
|
||||
} else {
|
||||
held = (!refcount_is_zero(&rrl->rr_anon_rcount) ||
|
||||
!refcount_is_zero(&rrl->rr_linked_rcount));
|
||||
}
|
||||
mutex_exit(&rrl->rr_lock);
|
||||
|
||||
return (held);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/zio_checksum.h>
|
||||
|
||||
/*
|
||||
* SHA-256 checksum, as specified in FIPS 180-3, available at:
|
||||
* http://csrc.nist.gov/publications/PubsFIPS.html
|
||||
*
|
||||
* This is a very compact implementation of SHA-256.
|
||||
* It is designed to be simple and portable, not to be fast.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The literal definitions of Ch() and Maj() according to FIPS 180-3 are:
|
||||
*
|
||||
* Ch(x, y, z) (x & y) ^ (~x & z)
|
||||
* Maj(x, y, z) (x & y) ^ (x & z) ^ (y & z)
|
||||
*
|
||||
* We use equivalent logical reductions here that require one less op.
|
||||
*/
|
||||
#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y))))
|
||||
#define Rot32(x, s) (((x) >> s) | ((x) << (32 - s)))
|
||||
#define SIGMA0(x) (Rot32(x, 2) ^ Rot32(x, 13) ^ Rot32(x, 22))
|
||||
#define SIGMA1(x) (Rot32(x, 6) ^ Rot32(x, 11) ^ Rot32(x, 25))
|
||||
#define sigma0(x) (Rot32(x, 7) ^ Rot32(x, 18) ^ ((x) >> 3))
|
||||
#define sigma1(x) (Rot32(x, 17) ^ Rot32(x, 19) ^ ((x) >> 10))
|
||||
|
||||
static const uint32_t SHA256_K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
static void
|
||||
SHA256Transform(uint32_t *H, const uint8_t *cp)
|
||||
{
|
||||
uint32_t a, b, c, d, e, f, g, h, t, T1, T2, W[64];
|
||||
|
||||
for (t = 0; t < 16; t++, cp += 4)
|
||||
W[t] = (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | cp[3];
|
||||
|
||||
for (t = 16; t < 64; t++)
|
||||
W[t] = sigma1(W[t - 2]) + W[t - 7] +
|
||||
sigma0(W[t - 15]) + W[t - 16];
|
||||
|
||||
a = H[0]; b = H[1]; c = H[2]; d = H[3];
|
||||
e = H[4]; f = H[5]; g = H[6]; h = H[7];
|
||||
|
||||
for (t = 0; t < 64; t++) {
|
||||
T1 = h + SIGMA1(e) + Ch(e, f, g) + SHA256_K[t] + W[t];
|
||||
T2 = SIGMA0(a) + Maj(a, b, c);
|
||||
h = g; g = f; f = e; e = d + T1;
|
||||
d = c; c = b; b = a; a = T1 + T2;
|
||||
}
|
||||
|
||||
H[0] += a; H[1] += b; H[2] += c; H[3] += d;
|
||||
H[4] += e; H[5] += f; H[6] += g; H[7] += h;
|
||||
}
|
||||
|
||||
void
|
||||
zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp)
|
||||
{
|
||||
uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
|
||||
uint8_t pad[128];
|
||||
int i, padsize;
|
||||
|
||||
for (i = 0; i < (size & ~63ULL); i += 64)
|
||||
SHA256Transform(H, (uint8_t *)buf + i);
|
||||
|
||||
for (padsize = 0; i < size; i++)
|
||||
pad[padsize++] = *((uint8_t *)buf + i);
|
||||
|
||||
for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++)
|
||||
pad[padsize] = 0;
|
||||
|
||||
for (i = 56; i >= 0; i -= 8)
|
||||
pad[padsize++] = (size << 3) >> i;
|
||||
|
||||
for (i = 0; i < padsize; i += 64)
|
||||
SHA256Transform(H, pad + i);
|
||||
|
||||
ZIO_SET_CHECKSUM(zcp,
|
||||
(uint64_t)H[0] << 32 | H[1],
|
||||
(uint64_t)H[2] << 32 | H[3],
|
||||
(uint64_t)H[4] << 32 | H[5],
|
||||
(uint64_t)H[6] << 32 | H[7]);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/spa.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/nvpair.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/vdev_impl.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/systeminfo.h>
|
||||
#include <sys/sunddi.h>
|
||||
#ifdef _KERNEL
|
||||
#include <sys/kobj.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pool configuration repository.
|
||||
*
|
||||
* Pool configuration is stored as a packed nvlist on the filesystem. By
|
||||
* default, all pools are stored in /etc/zfs/zpool.cache and loaded on boot
|
||||
* (when the ZFS module is loaded). Pools can also have the 'cachefile'
|
||||
* property set that allows them to be stored in an alternate location until
|
||||
* the control of external software.
|
||||
*
|
||||
* For each cache file, we have a single nvlist which holds all the
|
||||
* configuration information. When the module loads, we read this information
|
||||
* from /etc/zfs/zpool.cache and populate the SPA namespace. This namespace is
|
||||
* maintained independently in spa.c. Whenever the namespace is modified, or
|
||||
* the configuration of a pool is changed, we call spa_config_sync(), which
|
||||
* walks through all the active pools and writes the configuration to disk.
|
||||
*/
|
||||
|
||||
static uint64_t spa_config_generation = 1;
|
||||
|
||||
/*
|
||||
* This can be overridden in userland to preserve an alternate namespace for
|
||||
* userland pools when doing testing.
|
||||
*/
|
||||
const char *spa_config_path = ZPOOL_CACHE;
|
||||
|
||||
/*
|
||||
* Called when the module is first loaded, this routine loads the configuration
|
||||
* file into the SPA namespace. It does not actually open or load the pools; it
|
||||
* only populates the namespace.
|
||||
*/
|
||||
void
|
||||
spa_config_load(void)
|
||||
{
|
||||
void *buf = NULL;
|
||||
nvlist_t *nvlist, *child;
|
||||
nvpair_t *nvpair;
|
||||
spa_t *spa;
|
||||
char *pathname;
|
||||
struct _buf *file;
|
||||
uint64_t fsize;
|
||||
|
||||
/*
|
||||
* Open the configuration file.
|
||||
*/
|
||||
pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
|
||||
|
||||
(void) snprintf(pathname, MAXPATHLEN, "%s%s",
|
||||
(rootdir != NULL) ? "./" : "", spa_config_path);
|
||||
|
||||
file = kobj_open_file(pathname);
|
||||
|
||||
kmem_free(pathname, MAXPATHLEN);
|
||||
|
||||
if (file == (struct _buf *)-1)
|
||||
return;
|
||||
|
||||
if (kobj_get_filesize(file, &fsize) != 0)
|
||||
goto out;
|
||||
|
||||
buf = kmem_alloc(fsize, KM_SLEEP);
|
||||
|
||||
/*
|
||||
* Read the nvlist from the file.
|
||||
*/
|
||||
if (kobj_read_file(file, buf, fsize, 0) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Unpack the nvlist.
|
||||
*/
|
||||
if (nvlist_unpack(buf, fsize, &nvlist, KM_SLEEP) != 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Iterate over all elements in the nvlist, creating a new spa_t for
|
||||
* each one with the specified configuration.
|
||||
*/
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
nvpair = NULL;
|
||||
while ((nvpair = nvlist_next_nvpair(nvlist, nvpair)) != NULL) {
|
||||
|
||||
if (nvpair_type(nvpair) != DATA_TYPE_NVLIST)
|
||||
continue;
|
||||
|
||||
VERIFY(nvpair_value_nvlist(nvpair, &child) == 0);
|
||||
|
||||
if (spa_lookup(nvpair_name(nvpair)) != NULL)
|
||||
continue;
|
||||
spa = spa_add(nvpair_name(nvpair), NULL);
|
||||
|
||||
/*
|
||||
* We blindly duplicate the configuration here. If it's
|
||||
* invalid, we will catch it when the pool is first opened.
|
||||
*/
|
||||
VERIFY(nvlist_dup(child, &spa->spa_config, 0) == 0);
|
||||
}
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
|
||||
nvlist_free(nvlist);
|
||||
|
||||
out:
|
||||
if (buf != NULL)
|
||||
kmem_free(buf, fsize);
|
||||
|
||||
kobj_close_file(file);
|
||||
}
|
||||
|
||||
static void
|
||||
spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl)
|
||||
{
|
||||
size_t buflen;
|
||||
char *buf;
|
||||
vnode_t *vp;
|
||||
int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX;
|
||||
char *temp;
|
||||
|
||||
/*
|
||||
* If the nvlist is empty (NULL), then remove the old cachefile.
|
||||
*/
|
||||
if (nvl == NULL) {
|
||||
(void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pack the configuration into a buffer.
|
||||
*/
|
||||
VERIFY(nvlist_size(nvl, &buflen, NV_ENCODE_XDR) == 0);
|
||||
|
||||
buf = kmem_alloc(buflen, KM_SLEEP);
|
||||
temp = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
|
||||
|
||||
VERIFY(nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_XDR,
|
||||
KM_SLEEP) == 0);
|
||||
|
||||
/*
|
||||
* Write the configuration to disk. We need to do the traditional
|
||||
* 'write to temporary file, sync, move over original' to make sure we
|
||||
* always have a consistent view of the data.
|
||||
*/
|
||||
(void) snprintf(temp, MAXPATHLEN, "%s.tmp", dp->scd_path);
|
||||
|
||||
if (vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) == 0) {
|
||||
if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE,
|
||||
0, RLIM64_INFINITY, kcred, NULL) == 0 &&
|
||||
VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) {
|
||||
(void) vn_rename(temp, dp->scd_path, UIO_SYSSPACE);
|
||||
}
|
||||
(void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL);
|
||||
VN_RELE(vp);
|
||||
}
|
||||
|
||||
(void) vn_remove(temp, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
kmem_free(buf, buflen);
|
||||
kmem_free(temp, MAXPATHLEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronize pool configuration to disk. This must be called with the
|
||||
* namespace lock held.
|
||||
*/
|
||||
void
|
||||
spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent)
|
||||
{
|
||||
spa_config_dirent_t *dp, *tdp;
|
||||
nvlist_t *nvl;
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
|
||||
/*
|
||||
* Iterate over all cachefiles for the pool, past or present. When the
|
||||
* cachefile is changed, the new one is pushed onto this list, allowing
|
||||
* us to update previous cachefiles that no longer contain this pool.
|
||||
*/
|
||||
for (dp = list_head(&target->spa_config_list); dp != NULL;
|
||||
dp = list_next(&target->spa_config_list, dp)) {
|
||||
spa_t *spa = NULL;
|
||||
if (dp->scd_path == NULL)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Iterate over all pools, adding any matching pools to 'nvl'.
|
||||
*/
|
||||
nvl = NULL;
|
||||
while ((spa = spa_next(spa)) != NULL) {
|
||||
if (spa == target && removing)
|
||||
continue;
|
||||
|
||||
mutex_enter(&spa->spa_props_lock);
|
||||
tdp = list_head(&spa->spa_config_list);
|
||||
if (spa->spa_config == NULL ||
|
||||
tdp->scd_path == NULL ||
|
||||
strcmp(tdp->scd_path, dp->scd_path) != 0) {
|
||||
mutex_exit(&spa->spa_props_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nvl == NULL)
|
||||
VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME,
|
||||
KM_SLEEP) == 0);
|
||||
|
||||
VERIFY(nvlist_add_nvlist(nvl, spa->spa_name,
|
||||
spa->spa_config) == 0);
|
||||
mutex_exit(&spa->spa_props_lock);
|
||||
}
|
||||
|
||||
spa_config_write(dp, nvl);
|
||||
nvlist_free(nvl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any config entries older than the current one.
|
||||
*/
|
||||
dp = list_head(&target->spa_config_list);
|
||||
while ((tdp = list_next(&target->spa_config_list, dp)) != NULL) {
|
||||
list_remove(&target->spa_config_list, tdp);
|
||||
if (tdp->scd_path != NULL)
|
||||
spa_strfree(tdp->scd_path);
|
||||
kmem_free(tdp, sizeof (spa_config_dirent_t));
|
||||
}
|
||||
|
||||
spa_config_generation++;
|
||||
|
||||
if (postsysevent)
|
||||
spa_event_notify(target, NULL, ESC_ZFS_CONFIG_SYNC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sigh. Inside a local zone, we don't have access to /etc/zfs/zpool.cache,
|
||||
* and we don't want to allow the local zone to see all the pools anyway.
|
||||
* So we have to invent the ZFS_IOC_CONFIG ioctl to grab the configuration
|
||||
* information for all pool visible within the zone.
|
||||
*/
|
||||
nvlist_t *
|
||||
spa_all_configs(uint64_t *generation)
|
||||
{
|
||||
nvlist_t *pools;
|
||||
spa_t *spa = NULL;
|
||||
|
||||
if (*generation == spa_config_generation)
|
||||
return (NULL);
|
||||
|
||||
VERIFY(nvlist_alloc(&pools, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
while ((spa = spa_next(spa)) != NULL) {
|
||||
if (INGLOBALZONE(curproc) ||
|
||||
zone_dataset_visible(spa_name(spa), NULL)) {
|
||||
mutex_enter(&spa->spa_props_lock);
|
||||
VERIFY(nvlist_add_nvlist(pools, spa_name(spa),
|
||||
spa->spa_config) == 0);
|
||||
mutex_exit(&spa->spa_props_lock);
|
||||
}
|
||||
}
|
||||
*generation = spa_config_generation;
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
|
||||
return (pools);
|
||||
}
|
||||
|
||||
void
|
||||
spa_config_set(spa_t *spa, nvlist_t *config)
|
||||
{
|
||||
mutex_enter(&spa->spa_props_lock);
|
||||
if (spa->spa_config != NULL)
|
||||
nvlist_free(spa->spa_config);
|
||||
spa->spa_config = config;
|
||||
mutex_exit(&spa->spa_props_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the pool's configuration based on the current in-core state.
|
||||
* We infer whether to generate a complete config or just one top-level config
|
||||
* based on whether vd is the root vdev.
|
||||
*/
|
||||
nvlist_t *
|
||||
spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
|
||||
{
|
||||
nvlist_t *config, *nvroot;
|
||||
vdev_t *rvd = spa->spa_root_vdev;
|
||||
unsigned long hostid = 0;
|
||||
boolean_t locked = B_FALSE;
|
||||
|
||||
if (vd == NULL) {
|
||||
vd = rvd;
|
||||
locked = B_TRUE;
|
||||
spa_config_enter(spa, SCL_CONFIG | SCL_STATE, FTAG, RW_READER);
|
||||
}
|
||||
|
||||
ASSERT(spa_config_held(spa, SCL_CONFIG | SCL_STATE, RW_READER) ==
|
||||
(SCL_CONFIG | SCL_STATE));
|
||||
|
||||
/*
|
||||
* If txg is -1, report the current value of spa->spa_config_txg.
|
||||
*/
|
||||
if (txg == -1ULL)
|
||||
txg = spa->spa_config_txg;
|
||||
|
||||
VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_VERSION,
|
||||
spa_version(spa)) == 0);
|
||||
VERIFY(nvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME,
|
||||
spa_name(spa)) == 0);
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE,
|
||||
spa_state(spa)) == 0);
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG,
|
||||
txg) == 0);
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID,
|
||||
spa_guid(spa)) == 0);
|
||||
(void) ddi_strtoul(hw_serial, NULL, 10, &hostid);
|
||||
if (hostid != 0) {
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID,
|
||||
hostid) == 0);
|
||||
}
|
||||
VERIFY(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME,
|
||||
utsname.nodename) == 0);
|
||||
|
||||
if (vd != rvd) {
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_TOP_GUID,
|
||||
vd->vdev_top->vdev_guid) == 0);
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_GUID,
|
||||
vd->vdev_guid) == 0);
|
||||
if (vd->vdev_isspare)
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_IS_SPARE,
|
||||
1ULL) == 0);
|
||||
if (vd->vdev_islog)
|
||||
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_IS_LOG,
|
||||
1ULL) == 0);
|
||||
vd = vd->vdev_top; /* label contains top config */
|
||||
}
|
||||
|
||||
nvroot = vdev_config_generate(spa, vd, getstats, B_FALSE, B_FALSE);
|
||||
VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0);
|
||||
nvlist_free(nvroot);
|
||||
|
||||
if (locked)
|
||||
spa_config_exit(spa, SCL_CONFIG | SCL_STATE, FTAG);
|
||||
|
||||
return (config);
|
||||
}
|
||||
|
||||
/*
|
||||
* For a pool that's not currently a booting rootpool, update all disk labels,
|
||||
* generate a fresh config based on the current in-core state, and sync the
|
||||
* global config cache.
|
||||
*/
|
||||
void
|
||||
spa_config_update(spa_t *spa, int what)
|
||||
{
|
||||
spa_config_update_common(spa, what, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update all disk labels, generate a fresh config based on the current
|
||||
* in-core state, and sync the global config cache (do not sync the config
|
||||
* cache if this is a booting rootpool).
|
||||
*/
|
||||
void
|
||||
spa_config_update_common(spa_t *spa, int what, boolean_t isroot)
|
||||
{
|
||||
vdev_t *rvd = spa->spa_root_vdev;
|
||||
uint64_t txg;
|
||||
int c;
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
|
||||
spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
|
||||
txg = spa_last_synced_txg(spa) + 1;
|
||||
if (what == SPA_CONFIG_UPDATE_POOL) {
|
||||
vdev_config_dirty(rvd);
|
||||
} else {
|
||||
/*
|
||||
* If we have top-level vdevs that were added but have
|
||||
* not yet been prepared for allocation, do that now.
|
||||
* (It's safe now because the config cache is up to date,
|
||||
* so it will be able to translate the new DVAs.)
|
||||
* See comments in spa_vdev_add() for full details.
|
||||
*/
|
||||
for (c = 0; c < rvd->vdev_children; c++) {
|
||||
vdev_t *tvd = rvd->vdev_child[c];
|
||||
if (tvd->vdev_ms_array == 0) {
|
||||
vdev_init(tvd, txg);
|
||||
vdev_config_dirty(tvd);
|
||||
}
|
||||
}
|
||||
}
|
||||
spa_config_exit(spa, SCL_ALL, FTAG);
|
||||
|
||||
/*
|
||||
* Wait for the mosconfig to be regenerated and synced.
|
||||
*/
|
||||
txg_wait_synced(spa->spa_dsl_pool, txg);
|
||||
|
||||
/*
|
||||
* Update the global config cache to reflect the new mosconfig.
|
||||
*/
|
||||
if (!isroot)
|
||||
spa_config_sync(spa, B_FALSE, what != SPA_CONFIG_UPDATE_POOL);
|
||||
|
||||
if (what == SPA_CONFIG_UPDATE_POOL)
|
||||
spa_config_update_common(spa, SPA_CONFIG_UPDATE_VDEVS, isroot);
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* Routines to manage the on-disk persistent error log.
|
||||
*
|
||||
* Each pool stores a log of all logical data errors seen during normal
|
||||
* operation. This is actually the union of two distinct logs: the last log,
|
||||
* and the current log. All errors seen are logged to the current log. When a
|
||||
* scrub completes, the current log becomes the last log, the last log is thrown
|
||||
* out, and the current log is reinitialized. This way, if an error is somehow
|
||||
* corrected, a new scrub will show that that it no longer exists, and will be
|
||||
* deleted from the log when the scrub completes.
|
||||
*
|
||||
* The log is stored using a ZAP object whose key is a string form of the
|
||||
* zbookmark tuple (objset, object, level, blkid), and whose contents is an
|
||||
* optional 'objset:object' human-readable string describing the data. When an
|
||||
* error is first logged, this string will be empty, indicating that no name is
|
||||
* known. This prevents us from having to issue a potentially large amount of
|
||||
* I/O to discover the object name during an error path. Instead, we do the
|
||||
* calculation when the data is requested, storing the result so future queries
|
||||
* will be faster.
|
||||
*
|
||||
* This log is then shipped into an nvlist where the key is the dataset name and
|
||||
* the value is the object name. Userland is then responsible for uniquifying
|
||||
* this list and displaying it to the user.
|
||||
*/
|
||||
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/zap.h>
|
||||
#include <sys/zio.h>
|
||||
|
||||
/*
|
||||
* This is a stripped-down version of strtoull, suitable only for converting
|
||||
* lowercase hexidecimal numbers that don't overflow.
|
||||
*/
|
||||
#ifdef _KERNEL
|
||||
static uint64_t
|
||||
strtonum(char *str, char **nptr)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
char c;
|
||||
int digit;
|
||||
|
||||
while ((c = *str) != '\0') {
|
||||
if (c >= '0' && c <= '9')
|
||||
digit = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
digit = 10 + c - 'a';
|
||||
else
|
||||
break;
|
||||
|
||||
val *= 16;
|
||||
val += digit;
|
||||
|
||||
str++;
|
||||
}
|
||||
|
||||
*nptr = str;
|
||||
|
||||
return (val);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Convert a bookmark to a string.
|
||||
*/
|
||||
static void
|
||||
bookmark_to_name(zbookmark_t *zb, char *buf, size_t len)
|
||||
{
|
||||
(void) snprintf(buf, len, "%llx:%llx:%llx:%llx",
|
||||
(u_longlong_t)zb->zb_objset, (u_longlong_t)zb->zb_object,
|
||||
(u_longlong_t)zb->zb_level, (u_longlong_t)zb->zb_blkid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to a bookmark
|
||||
*/
|
||||
#ifdef _KERNEL
|
||||
static void
|
||||
name_to_bookmark(char *buf, zbookmark_t *zb)
|
||||
{
|
||||
zb->zb_objset = strtonum(buf, &buf);
|
||||
ASSERT(*buf == ':');
|
||||
zb->zb_object = strtonum(buf + 1, &buf);
|
||||
ASSERT(*buf == ':');
|
||||
zb->zb_level = (int)strtonum(buf + 1, &buf);
|
||||
ASSERT(*buf == ':');
|
||||
zb->zb_blkid = strtonum(buf + 1, &buf);
|
||||
ASSERT(*buf == '\0');
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log an uncorrectable error to the persistent error log. We add it to the
|
||||
* spa's list of pending errors. The changes are actually synced out to disk
|
||||
* during spa_errlog_sync().
|
||||
*/
|
||||
void
|
||||
spa_log_error(spa_t *spa, zio_t *zio)
|
||||
{
|
||||
zbookmark_t *zb = &zio->io_logical->io_bookmark;
|
||||
spa_error_entry_t search;
|
||||
spa_error_entry_t *new;
|
||||
avl_tree_t *tree;
|
||||
avl_index_t where;
|
||||
|
||||
/*
|
||||
* If we are trying to import a pool, ignore any errors, as we won't be
|
||||
* writing to the pool any time soon.
|
||||
*/
|
||||
if (spa->spa_load_state == SPA_LOAD_TRYIMPORT)
|
||||
return;
|
||||
|
||||
mutex_enter(&spa->spa_errlist_lock);
|
||||
|
||||
/*
|
||||
* If we have had a request to rotate the log, log it to the next list
|
||||
* instead of the current one.
|
||||
*/
|
||||
if (spa->spa_scrub_active || spa->spa_scrub_finished)
|
||||
tree = &spa->spa_errlist_scrub;
|
||||
else
|
||||
tree = &spa->spa_errlist_last;
|
||||
|
||||
search.se_bookmark = *zb;
|
||||
if (avl_find(tree, &search, &where) != NULL) {
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
new = kmem_zalloc(sizeof (spa_error_entry_t), KM_SLEEP);
|
||||
new->se_bookmark = *zb;
|
||||
avl_insert(tree, new, where);
|
||||
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of errors currently in the error log. This is actually the
|
||||
* sum of both the last log and the current log, since we don't know the union
|
||||
* of these logs until we reach userland.
|
||||
*/
|
||||
uint64_t
|
||||
spa_get_errlog_size(spa_t *spa)
|
||||
{
|
||||
uint64_t total = 0, count;
|
||||
|
||||
mutex_enter(&spa->spa_errlog_lock);
|
||||
if (spa->spa_errlog_scrub != 0 &&
|
||||
zap_count(spa->spa_meta_objset, spa->spa_errlog_scrub,
|
||||
&count) == 0)
|
||||
total += count;
|
||||
|
||||
if (spa->spa_errlog_last != 0 && !spa->spa_scrub_finished &&
|
||||
zap_count(spa->spa_meta_objset, spa->spa_errlog_last,
|
||||
&count) == 0)
|
||||
total += count;
|
||||
mutex_exit(&spa->spa_errlog_lock);
|
||||
|
||||
mutex_enter(&spa->spa_errlist_lock);
|
||||
total += avl_numnodes(&spa->spa_errlist_last);
|
||||
total += avl_numnodes(&spa->spa_errlist_scrub);
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
|
||||
return (total);
|
||||
}
|
||||
|
||||
#ifdef _KERNEL
|
||||
static int
|
||||
process_error_log(spa_t *spa, uint64_t obj, void *addr, size_t *count)
|
||||
{
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
zbookmark_t zb;
|
||||
|
||||
if (obj == 0)
|
||||
return (0);
|
||||
|
||||
for (zap_cursor_init(&zc, spa->spa_meta_objset, obj);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
|
||||
if (*count == 0) {
|
||||
zap_cursor_fini(&zc);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
name_to_bookmark(za.za_name, &zb);
|
||||
|
||||
if (copyout(&zb, (char *)addr +
|
||||
(*count - 1) * sizeof (zbookmark_t),
|
||||
sizeof (zbookmark_t)) != 0)
|
||||
return (EFAULT);
|
||||
|
||||
*count -= 1;
|
||||
}
|
||||
|
||||
zap_cursor_fini(&zc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
process_error_list(avl_tree_t *list, void *addr, size_t *count)
|
||||
{
|
||||
spa_error_entry_t *se;
|
||||
|
||||
for (se = avl_first(list); se != NULL; se = AVL_NEXT(list, se)) {
|
||||
|
||||
if (*count == 0)
|
||||
return (ENOMEM);
|
||||
|
||||
if (copyout(&se->se_bookmark, (char *)addr +
|
||||
(*count - 1) * sizeof (zbookmark_t),
|
||||
sizeof (zbookmark_t)) != 0)
|
||||
return (EFAULT);
|
||||
|
||||
*count -= 1;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copy all known errors to userland as an array of bookmarks. This is
|
||||
* actually a union of the on-disk last log and current log, as well as any
|
||||
* pending error requests.
|
||||
*
|
||||
* Because the act of reading the on-disk log could cause errors to be
|
||||
* generated, we have two separate locks: one for the error log and one for the
|
||||
* in-core error lists. We only need the error list lock to log and error, so
|
||||
* we grab the error log lock while we read the on-disk logs, and only pick up
|
||||
* the error list lock when we are finished.
|
||||
*/
|
||||
int
|
||||
spa_get_errlog(spa_t *spa, void *uaddr, size_t *count)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifdef _KERNEL
|
||||
mutex_enter(&spa->spa_errlog_lock);
|
||||
|
||||
ret = process_error_log(spa, spa->spa_errlog_scrub, uaddr, count);
|
||||
|
||||
if (!ret && !spa->spa_scrub_finished)
|
||||
ret = process_error_log(spa, spa->spa_errlog_last, uaddr,
|
||||
count);
|
||||
|
||||
mutex_enter(&spa->spa_errlist_lock);
|
||||
if (!ret)
|
||||
ret = process_error_list(&spa->spa_errlist_scrub, uaddr,
|
||||
count);
|
||||
if (!ret)
|
||||
ret = process_error_list(&spa->spa_errlist_last, uaddr,
|
||||
count);
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
|
||||
mutex_exit(&spa->spa_errlog_lock);
|
||||
#endif
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a scrub completes. This simply set a bit which tells which AVL
|
||||
* tree to add new errors. spa_errlog_sync() is responsible for actually
|
||||
* syncing the changes to the underlying objects.
|
||||
*/
|
||||
void
|
||||
spa_errlog_rotate(spa_t *spa)
|
||||
{
|
||||
mutex_enter(&spa->spa_errlist_lock);
|
||||
spa->spa_scrub_finished = B_TRUE;
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Discard any pending errors from the spa_t. Called when unloading a faulted
|
||||
* pool, as the errors encountered during the open cannot be synced to disk.
|
||||
*/
|
||||
void
|
||||
spa_errlog_drain(spa_t *spa)
|
||||
{
|
||||
spa_error_entry_t *se;
|
||||
void *cookie;
|
||||
|
||||
mutex_enter(&spa->spa_errlist_lock);
|
||||
|
||||
cookie = NULL;
|
||||
while ((se = avl_destroy_nodes(&spa->spa_errlist_last,
|
||||
&cookie)) != NULL)
|
||||
kmem_free(se, sizeof (spa_error_entry_t));
|
||||
cookie = NULL;
|
||||
while ((se = avl_destroy_nodes(&spa->spa_errlist_scrub,
|
||||
&cookie)) != NULL)
|
||||
kmem_free(se, sizeof (spa_error_entry_t));
|
||||
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a list of errors into the current on-disk log.
|
||||
*/
|
||||
static void
|
||||
sync_error_list(spa_t *spa, avl_tree_t *t, uint64_t *obj, dmu_tx_t *tx)
|
||||
{
|
||||
spa_error_entry_t *se;
|
||||
char buf[64];
|
||||
void *cookie;
|
||||
|
||||
if (avl_numnodes(t) != 0) {
|
||||
/* create log if necessary */
|
||||
if (*obj == 0)
|
||||
*obj = zap_create(spa->spa_meta_objset,
|
||||
DMU_OT_ERROR_LOG, DMU_OT_NONE,
|
||||
0, tx);
|
||||
|
||||
/* add errors to the current log */
|
||||
for (se = avl_first(t); se != NULL; se = AVL_NEXT(t, se)) {
|
||||
char *name = se->se_name ? se->se_name : "";
|
||||
|
||||
bookmark_to_name(&se->se_bookmark, buf, sizeof (buf));
|
||||
|
||||
(void) zap_update(spa->spa_meta_objset,
|
||||
*obj, buf, 1, strlen(name) + 1, name, tx);
|
||||
}
|
||||
|
||||
/* purge the error list */
|
||||
cookie = NULL;
|
||||
while ((se = avl_destroy_nodes(t, &cookie)) != NULL)
|
||||
kmem_free(se, sizeof (spa_error_entry_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync the error log out to disk. This is a little tricky because the act of
|
||||
* writing the error log requires the spa_errlist_lock. So, we need to lock the
|
||||
* error lists, take a copy of the lists, and then reinitialize them. Then, we
|
||||
* drop the error list lock and take the error log lock, at which point we
|
||||
* do the errlog processing. Then, if we encounter an I/O error during this
|
||||
* process, we can successfully add the error to the list. Note that this will
|
||||
* result in the perpetual recycling of errors, but it is an unlikely situation
|
||||
* and not a performance critical operation.
|
||||
*/
|
||||
void
|
||||
spa_errlog_sync(spa_t *spa, uint64_t txg)
|
||||
{
|
||||
dmu_tx_t *tx;
|
||||
avl_tree_t scrub, last;
|
||||
int scrub_finished;
|
||||
|
||||
mutex_enter(&spa->spa_errlist_lock);
|
||||
|
||||
/*
|
||||
* Bail out early under normal circumstances.
|
||||
*/
|
||||
if (avl_numnodes(&spa->spa_errlist_scrub) == 0 &&
|
||||
avl_numnodes(&spa->spa_errlist_last) == 0 &&
|
||||
!spa->spa_scrub_finished) {
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
spa_get_errlists(spa, &last, &scrub);
|
||||
scrub_finished = spa->spa_scrub_finished;
|
||||
spa->spa_scrub_finished = B_FALSE;
|
||||
|
||||
mutex_exit(&spa->spa_errlist_lock);
|
||||
mutex_enter(&spa->spa_errlog_lock);
|
||||
|
||||
tx = dmu_tx_create_assigned(spa->spa_dsl_pool, txg);
|
||||
|
||||
/*
|
||||
* Sync out the current list of errors.
|
||||
*/
|
||||
sync_error_list(spa, &last, &spa->spa_errlog_last, tx);
|
||||
|
||||
/*
|
||||
* Rotate the log if necessary.
|
||||
*/
|
||||
if (scrub_finished) {
|
||||
if (spa->spa_errlog_last != 0)
|
||||
VERIFY(dmu_object_free(spa->spa_meta_objset,
|
||||
spa->spa_errlog_last, tx) == 0);
|
||||
spa->spa_errlog_last = spa->spa_errlog_scrub;
|
||||
spa->spa_errlog_scrub = 0;
|
||||
|
||||
sync_error_list(spa, &scrub, &spa->spa_errlog_last, tx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync out any pending scrub errors.
|
||||
*/
|
||||
sync_error_list(spa, &scrub, &spa->spa_errlog_scrub, tx);
|
||||
|
||||
/*
|
||||
* Update the MOS to reflect the new values.
|
||||
*/
|
||||
(void) zap_update(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_ERRLOG_LAST, sizeof (uint64_t), 1,
|
||||
&spa->spa_errlog_last, tx);
|
||||
(void) zap_update(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_ERRLOG_SCRUB, sizeof (uint64_t), 1,
|
||||
&spa->spa_errlog_scrub, tx);
|
||||
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
mutex_exit(&spa->spa_errlog_lock);
|
||||
}
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/spa.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/zap.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/cmn_err.h>
|
||||
#include <sys/sunddi.h>
|
||||
#ifdef _KERNEL
|
||||
#include <sys/zone.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Routines to manage the on-disk history log.
|
||||
*
|
||||
* The history log is stored as a dmu object containing
|
||||
* <packed record length, record nvlist> tuples.
|
||||
*
|
||||
* Where "record nvlist" is a nvlist containing uint64_ts and strings, and
|
||||
* "packed record length" is the packed length of the "record nvlist" stored
|
||||
* as a little endian uint64_t.
|
||||
*
|
||||
* The log is implemented as a ring buffer, though the original creation
|
||||
* of the pool ('zpool create') is never overwritten.
|
||||
*
|
||||
* The history log is tracked as object 'spa_t::spa_history'. The bonus buffer
|
||||
* of 'spa_history' stores the offsets for logging/retrieving history as
|
||||
* 'spa_history_phys_t'. 'sh_pool_create_len' is the ending offset in bytes of
|
||||
* where the 'zpool create' record is stored. This allows us to never
|
||||
* overwrite the original creation of the pool. 'sh_phys_max_off' is the
|
||||
* physical ending offset in bytes of the log. This tells you the length of
|
||||
* the buffer. 'sh_eof' is the logical EOF (in bytes). Whenever a record
|
||||
* is added, 'sh_eof' is incremented by the the size of the record.
|
||||
* 'sh_eof' is never decremented. 'sh_bof' is the logical BOF (in bytes).
|
||||
* This is where the consumer should start reading from after reading in
|
||||
* the 'zpool create' portion of the log.
|
||||
*
|
||||
* 'sh_records_lost' keeps track of how many records have been overwritten
|
||||
* and permanently lost.
|
||||
*/
|
||||
|
||||
/* convert a logical offset to physical */
|
||||
static uint64_t
|
||||
spa_history_log_to_phys(uint64_t log_off, spa_history_phys_t *shpp)
|
||||
{
|
||||
uint64_t phys_len;
|
||||
|
||||
phys_len = shpp->sh_phys_max_off - shpp->sh_pool_create_len;
|
||||
return ((log_off - shpp->sh_pool_create_len) % phys_len
|
||||
+ shpp->sh_pool_create_len);
|
||||
}
|
||||
|
||||
void
|
||||
spa_history_create_obj(spa_t *spa, dmu_tx_t *tx)
|
||||
{
|
||||
dmu_buf_t *dbp;
|
||||
spa_history_phys_t *shpp;
|
||||
objset_t *mos = spa->spa_meta_objset;
|
||||
|
||||
ASSERT(spa->spa_history == 0);
|
||||
spa->spa_history = dmu_object_alloc(mos, DMU_OT_SPA_HISTORY,
|
||||
SPA_MAXBLOCKSIZE, DMU_OT_SPA_HISTORY_OFFSETS,
|
||||
sizeof (spa_history_phys_t), tx);
|
||||
|
||||
VERIFY(zap_add(mos, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_HISTORY, sizeof (uint64_t), 1,
|
||||
&spa->spa_history, tx) == 0);
|
||||
|
||||
VERIFY(0 == dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp));
|
||||
ASSERT(dbp->db_size >= sizeof (spa_history_phys_t));
|
||||
|
||||
shpp = dbp->db_data;
|
||||
dmu_buf_will_dirty(dbp, tx);
|
||||
|
||||
/*
|
||||
* Figure out maximum size of history log. We set it at
|
||||
* 1% of pool size, with a max of 32MB and min of 128KB.
|
||||
*/
|
||||
shpp->sh_phys_max_off = spa_get_dspace(spa) / 100;
|
||||
shpp->sh_phys_max_off = MIN(shpp->sh_phys_max_off, 32<<20);
|
||||
shpp->sh_phys_max_off = MAX(shpp->sh_phys_max_off, 128<<10);
|
||||
|
||||
dmu_buf_rele(dbp, FTAG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change 'sh_bof' to the beginning of the next record.
|
||||
*/
|
||||
static int
|
||||
spa_history_advance_bof(spa_t *spa, spa_history_phys_t *shpp)
|
||||
{
|
||||
objset_t *mos = spa->spa_meta_objset;
|
||||
uint64_t firstread, reclen, phys_bof;
|
||||
char buf[sizeof (reclen)];
|
||||
int err;
|
||||
|
||||
phys_bof = spa_history_log_to_phys(shpp->sh_bof, shpp);
|
||||
firstread = MIN(sizeof (reclen), shpp->sh_phys_max_off - phys_bof);
|
||||
|
||||
if ((err = dmu_read(mos, spa->spa_history, phys_bof, firstread,
|
||||
buf)) != 0)
|
||||
return (err);
|
||||
if (firstread != sizeof (reclen)) {
|
||||
if ((err = dmu_read(mos, spa->spa_history,
|
||||
shpp->sh_pool_create_len, sizeof (reclen) - firstread,
|
||||
buf + firstread)) != 0)
|
||||
return (err);
|
||||
}
|
||||
|
||||
reclen = LE_64(*((uint64_t *)buf));
|
||||
shpp->sh_bof += reclen + sizeof (reclen);
|
||||
shpp->sh_records_lost++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
spa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
|
||||
dmu_tx_t *tx)
|
||||
{
|
||||
uint64_t firstwrite, phys_eof;
|
||||
objset_t *mos = spa->spa_meta_objset;
|
||||
int err;
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa->spa_history_lock));
|
||||
|
||||
/* see if we need to reset logical BOF */
|
||||
while (shpp->sh_phys_max_off - shpp->sh_pool_create_len -
|
||||
(shpp->sh_eof - shpp->sh_bof) <= len) {
|
||||
if ((err = spa_history_advance_bof(spa, shpp)) != 0) {
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
|
||||
phys_eof = spa_history_log_to_phys(shpp->sh_eof, shpp);
|
||||
firstwrite = MIN(len, shpp->sh_phys_max_off - phys_eof);
|
||||
shpp->sh_eof += len;
|
||||
dmu_write(mos, spa->spa_history, phys_eof, firstwrite, buf, tx);
|
||||
|
||||
len -= firstwrite;
|
||||
if (len > 0) {
|
||||
/* write out the rest at the beginning of physical file */
|
||||
dmu_write(mos, spa->spa_history, shpp->sh_pool_create_len,
|
||||
len, (char *)buf + firstwrite, tx);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static char *
|
||||
spa_history_zone()
|
||||
{
|
||||
#ifdef _KERNEL
|
||||
return (curproc->p_zone->zone_name);
|
||||
#else
|
||||
return ("global");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Write out a history event.
|
||||
*/
|
||||
static void
|
||||
spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
|
||||
{
|
||||
spa_t *spa = arg1;
|
||||
history_arg_t *hap = arg2;
|
||||
const char *history_str = hap->ha_history_str;
|
||||
objset_t *mos = spa->spa_meta_objset;
|
||||
dmu_buf_t *dbp;
|
||||
spa_history_phys_t *shpp;
|
||||
size_t reclen;
|
||||
uint64_t le_len;
|
||||
nvlist_t *nvrecord;
|
||||
char *record_packed = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we have an older pool that doesn't have a command
|
||||
* history object, create it now.
|
||||
*/
|
||||
mutex_enter(&spa->spa_history_lock);
|
||||
if (!spa->spa_history)
|
||||
spa_history_create_obj(spa, tx);
|
||||
mutex_exit(&spa->spa_history_lock);
|
||||
|
||||
/*
|
||||
* Get the offset of where we need to write via the bonus buffer.
|
||||
* Update the offset when the write completes.
|
||||
*/
|
||||
VERIFY(0 == dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp));
|
||||
shpp = dbp->db_data;
|
||||
|
||||
dmu_buf_will_dirty(dbp, tx);
|
||||
|
||||
#ifdef ZFS_DEBUG
|
||||
{
|
||||
dmu_object_info_t doi;
|
||||
dmu_object_info_from_db(dbp, &doi);
|
||||
ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS);
|
||||
}
|
||||
#endif
|
||||
|
||||
VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TIME,
|
||||
gethrestime_sec()) == 0);
|
||||
VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO,
|
||||
(uint64_t)crgetuid(cr)) == 0);
|
||||
if (hap->ha_zone[0] != '\0')
|
||||
VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_ZONE,
|
||||
hap->ha_zone) == 0);
|
||||
#ifdef _KERNEL
|
||||
VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_HOST,
|
||||
utsname.nodename) == 0);
|
||||
#endif
|
||||
if (hap->ha_log_type == LOG_CMD_POOL_CREATE ||
|
||||
hap->ha_log_type == LOG_CMD_NORMAL) {
|
||||
VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD,
|
||||
history_str) == 0);
|
||||
} else {
|
||||
VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_INT_EVENT,
|
||||
hap->ha_event) == 0);
|
||||
VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TXG,
|
||||
tx->tx_txg) == 0);
|
||||
VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_INT_STR,
|
||||
history_str) == 0);
|
||||
}
|
||||
|
||||
VERIFY(nvlist_size(nvrecord, &reclen, NV_ENCODE_XDR) == 0);
|
||||
record_packed = kmem_alloc(reclen, KM_SLEEP);
|
||||
|
||||
VERIFY(nvlist_pack(nvrecord, &record_packed, &reclen,
|
||||
NV_ENCODE_XDR, KM_SLEEP) == 0);
|
||||
|
||||
mutex_enter(&spa->spa_history_lock);
|
||||
if (hap->ha_log_type == LOG_CMD_POOL_CREATE)
|
||||
VERIFY(shpp->sh_eof == shpp->sh_pool_create_len);
|
||||
|
||||
/* write out the packed length as little endian */
|
||||
le_len = LE_64((uint64_t)reclen);
|
||||
ret = spa_history_write(spa, &le_len, sizeof (le_len), shpp, tx);
|
||||
if (!ret)
|
||||
ret = spa_history_write(spa, record_packed, reclen, shpp, tx);
|
||||
|
||||
if (!ret && hap->ha_log_type == LOG_CMD_POOL_CREATE) {
|
||||
shpp->sh_pool_create_len += sizeof (le_len) + reclen;
|
||||
shpp->sh_bof = shpp->sh_pool_create_len;
|
||||
}
|
||||
|
||||
mutex_exit(&spa->spa_history_lock);
|
||||
nvlist_free(nvrecord);
|
||||
kmem_free(record_packed, reclen);
|
||||
dmu_buf_rele(dbp, FTAG);
|
||||
|
||||
if (hap->ha_log_type == LOG_INTERNAL) {
|
||||
kmem_free((void*)hap->ha_history_str, HIS_MAX_RECORD_LEN);
|
||||
kmem_free(hap, sizeof (history_arg_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write out a history event.
|
||||
*/
|
||||
int
|
||||
spa_history_log(spa_t *spa, const char *history_str, history_log_type_t what)
|
||||
{
|
||||
history_arg_t ha;
|
||||
|
||||
ASSERT(what != LOG_INTERNAL);
|
||||
|
||||
ha.ha_history_str = history_str;
|
||||
ha.ha_log_type = what;
|
||||
(void) strlcpy(ha.ha_zone, spa_history_zone(), sizeof (ha.ha_zone));
|
||||
return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_history_log_sync,
|
||||
spa, &ha, 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read out the command history.
|
||||
*/
|
||||
int
|
||||
spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
|
||||
{
|
||||
objset_t *mos = spa->spa_meta_objset;
|
||||
dmu_buf_t *dbp;
|
||||
uint64_t read_len, phys_read_off, phys_eof;
|
||||
uint64_t leftover = 0;
|
||||
spa_history_phys_t *shpp;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If the command history doesn't exist (older pool),
|
||||
* that's ok, just return ENOENT.
|
||||
*/
|
||||
if (!spa->spa_history)
|
||||
return (ENOENT);
|
||||
|
||||
if ((err = dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp)) != 0)
|
||||
return (err);
|
||||
shpp = dbp->db_data;
|
||||
|
||||
#ifdef ZFS_DEBUG
|
||||
{
|
||||
dmu_object_info_t doi;
|
||||
dmu_object_info_from_db(dbp, &doi);
|
||||
ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS);
|
||||
}
|
||||
#endif
|
||||
|
||||
mutex_enter(&spa->spa_history_lock);
|
||||
phys_eof = spa_history_log_to_phys(shpp->sh_eof, shpp);
|
||||
|
||||
if (*offp < shpp->sh_pool_create_len) {
|
||||
/* read in just the zpool create history */
|
||||
phys_read_off = *offp;
|
||||
read_len = MIN(*len, shpp->sh_pool_create_len -
|
||||
phys_read_off);
|
||||
} else {
|
||||
/*
|
||||
* Need to reset passed in offset to BOF if the passed in
|
||||
* offset has since been overwritten.
|
||||
*/
|
||||
*offp = MAX(*offp, shpp->sh_bof);
|
||||
phys_read_off = spa_history_log_to_phys(*offp, shpp);
|
||||
|
||||
/*
|
||||
* Read up to the minimum of what the user passed down or
|
||||
* the EOF (physical or logical). If we hit physical EOF,
|
||||
* use 'leftover' to read from the physical BOF.
|
||||
*/
|
||||
if (phys_read_off <= phys_eof) {
|
||||
read_len = MIN(*len, phys_eof - phys_read_off);
|
||||
} else {
|
||||
read_len = MIN(*len,
|
||||
shpp->sh_phys_max_off - phys_read_off);
|
||||
if (phys_read_off + *len > shpp->sh_phys_max_off) {
|
||||
leftover = MIN(*len - read_len,
|
||||
phys_eof - shpp->sh_pool_create_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* offset for consumer to use next */
|
||||
*offp += read_len + leftover;
|
||||
|
||||
/* tell the consumer how much you actually read */
|
||||
*len = read_len + leftover;
|
||||
|
||||
if (read_len == 0) {
|
||||
mutex_exit(&spa->spa_history_lock);
|
||||
dmu_buf_rele(dbp, FTAG);
|
||||
return (0);
|
||||
}
|
||||
|
||||
err = dmu_read(mos, spa->spa_history, phys_read_off, read_len, buf);
|
||||
if (leftover && err == 0) {
|
||||
err = dmu_read(mos, spa->spa_history, shpp->sh_pool_create_len,
|
||||
leftover, buf + read_len);
|
||||
}
|
||||
mutex_exit(&spa->spa_history_lock);
|
||||
|
||||
dmu_buf_rele(dbp, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
spa_history_internal_log(history_internal_events_t event, spa_t *spa,
|
||||
dmu_tx_t *tx, cred_t *cr, const char *fmt, ...)
|
||||
{
|
||||
history_arg_t *hap;
|
||||
char *str;
|
||||
va_list adx;
|
||||
|
||||
/*
|
||||
* If this is part of creating a pool, not everything is
|
||||
* initialized yet, so don't bother logging the internal events.
|
||||
*/
|
||||
if (tx->tx_txg == TXG_INITIAL)
|
||||
return;
|
||||
|
||||
hap = kmem_alloc(sizeof (history_arg_t), KM_SLEEP);
|
||||
str = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
|
||||
|
||||
va_start(adx, fmt);
|
||||
(void) vsnprintf(str, HIS_MAX_RECORD_LEN, fmt, adx);
|
||||
va_end(adx);
|
||||
|
||||
hap->ha_log_type = LOG_INTERNAL;
|
||||
hap->ha_history_str = str;
|
||||
hap->ha_event = event;
|
||||
hap->ha_zone[0] = '\0';
|
||||
|
||||
if (dmu_tx_is_syncing(tx)) {
|
||||
spa_history_log_sync(spa, hap, cr, tx);
|
||||
} else {
|
||||
dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
|
||||
spa_history_log_sync, spa, hap, 0, tx);
|
||||
}
|
||||
/* spa_history_log_sync() will free hap and str */
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,506 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/space_map.h>
|
||||
|
||||
/*
|
||||
* Space map routines.
|
||||
* NOTE: caller is responsible for all locking.
|
||||
*/
|
||||
static int
|
||||
space_map_seg_compare(const void *x1, const void *x2)
|
||||
{
|
||||
const space_seg_t *s1 = x1;
|
||||
const space_seg_t *s2 = x2;
|
||||
|
||||
if (s1->ss_start < s2->ss_start) {
|
||||
if (s1->ss_end > s2->ss_start)
|
||||
return (0);
|
||||
return (-1);
|
||||
}
|
||||
if (s1->ss_start > s2->ss_start) {
|
||||
if (s1->ss_start < s2->ss_end)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_create(space_map_t *sm, uint64_t start, uint64_t size, uint8_t shift,
|
||||
kmutex_t *lp)
|
||||
{
|
||||
bzero(sm, sizeof (*sm));
|
||||
|
||||
avl_create(&sm->sm_root, space_map_seg_compare,
|
||||
sizeof (space_seg_t), offsetof(struct space_seg, ss_node));
|
||||
|
||||
sm->sm_start = start;
|
||||
sm->sm_size = size;
|
||||
sm->sm_shift = shift;
|
||||
sm->sm_lock = lp;
|
||||
}
|
||||
|
||||
void
|
||||
space_map_destroy(space_map_t *sm)
|
||||
{
|
||||
ASSERT(!sm->sm_loaded && !sm->sm_loading);
|
||||
VERIFY3U(sm->sm_space, ==, 0);
|
||||
avl_destroy(&sm->sm_root);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_add(space_map_t *sm, uint64_t start, uint64_t size)
|
||||
{
|
||||
avl_index_t where;
|
||||
space_seg_t ssearch, *ss_before, *ss_after, *ss;
|
||||
uint64_t end = start + size;
|
||||
int merge_before, merge_after;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
VERIFY(size != 0);
|
||||
VERIFY3U(start, >=, sm->sm_start);
|
||||
VERIFY3U(end, <=, sm->sm_start + sm->sm_size);
|
||||
VERIFY(sm->sm_space + size <= sm->sm_size);
|
||||
VERIFY(P2PHASE(start, 1ULL << sm->sm_shift) == 0);
|
||||
VERIFY(P2PHASE(size, 1ULL << sm->sm_shift) == 0);
|
||||
|
||||
ssearch.ss_start = start;
|
||||
ssearch.ss_end = end;
|
||||
ss = avl_find(&sm->sm_root, &ssearch, &where);
|
||||
|
||||
if (ss != NULL && ss->ss_start <= start && ss->ss_end >= end) {
|
||||
zfs_panic_recover("zfs: allocating allocated segment"
|
||||
"(offset=%llu size=%llu)\n",
|
||||
(longlong_t)start, (longlong_t)size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure we don't overlap with either of our neighbors */
|
||||
VERIFY(ss == NULL);
|
||||
|
||||
ss_before = avl_nearest(&sm->sm_root, where, AVL_BEFORE);
|
||||
ss_after = avl_nearest(&sm->sm_root, where, AVL_AFTER);
|
||||
|
||||
merge_before = (ss_before != NULL && ss_before->ss_end == start);
|
||||
merge_after = (ss_after != NULL && ss_after->ss_start == end);
|
||||
|
||||
if (merge_before && merge_after) {
|
||||
avl_remove(&sm->sm_root, ss_before);
|
||||
ss_after->ss_start = ss_before->ss_start;
|
||||
kmem_free(ss_before, sizeof (*ss_before));
|
||||
} else if (merge_before) {
|
||||
ss_before->ss_end = end;
|
||||
} else if (merge_after) {
|
||||
ss_after->ss_start = start;
|
||||
} else {
|
||||
ss = kmem_alloc(sizeof (*ss), KM_SLEEP);
|
||||
ss->ss_start = start;
|
||||
ss->ss_end = end;
|
||||
avl_insert(&sm->sm_root, ss, where);
|
||||
}
|
||||
|
||||
sm->sm_space += size;
|
||||
}
|
||||
|
||||
void
|
||||
space_map_remove(space_map_t *sm, uint64_t start, uint64_t size)
|
||||
{
|
||||
avl_index_t where;
|
||||
space_seg_t ssearch, *ss, *newseg;
|
||||
uint64_t end = start + size;
|
||||
int left_over, right_over;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
VERIFY(size != 0);
|
||||
VERIFY(P2PHASE(start, 1ULL << sm->sm_shift) == 0);
|
||||
VERIFY(P2PHASE(size, 1ULL << sm->sm_shift) == 0);
|
||||
|
||||
ssearch.ss_start = start;
|
||||
ssearch.ss_end = end;
|
||||
ss = avl_find(&sm->sm_root, &ssearch, &where);
|
||||
|
||||
/* Make sure we completely overlap with someone */
|
||||
if (ss == NULL) {
|
||||
zfs_panic_recover("zfs: freeing free segment "
|
||||
"(offset=%llu size=%llu)",
|
||||
(longlong_t)start, (longlong_t)size);
|
||||
return;
|
||||
}
|
||||
VERIFY3U(ss->ss_start, <=, start);
|
||||
VERIFY3U(ss->ss_end, >=, end);
|
||||
VERIFY(sm->sm_space - size <= sm->sm_size);
|
||||
|
||||
left_over = (ss->ss_start != start);
|
||||
right_over = (ss->ss_end != end);
|
||||
|
||||
if (left_over && right_over) {
|
||||
newseg = kmem_alloc(sizeof (*newseg), KM_SLEEP);
|
||||
newseg->ss_start = end;
|
||||
newseg->ss_end = ss->ss_end;
|
||||
ss->ss_end = start;
|
||||
avl_insert_here(&sm->sm_root, newseg, ss, AVL_AFTER);
|
||||
} else if (left_over) {
|
||||
ss->ss_end = start;
|
||||
} else if (right_over) {
|
||||
ss->ss_start = end;
|
||||
} else {
|
||||
avl_remove(&sm->sm_root, ss);
|
||||
kmem_free(ss, sizeof (*ss));
|
||||
}
|
||||
|
||||
sm->sm_space -= size;
|
||||
}
|
||||
|
||||
int
|
||||
space_map_contains(space_map_t *sm, uint64_t start, uint64_t size)
|
||||
{
|
||||
avl_index_t where;
|
||||
space_seg_t ssearch, *ss;
|
||||
uint64_t end = start + size;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
VERIFY(size != 0);
|
||||
VERIFY(P2PHASE(start, 1ULL << sm->sm_shift) == 0);
|
||||
VERIFY(P2PHASE(size, 1ULL << sm->sm_shift) == 0);
|
||||
|
||||
ssearch.ss_start = start;
|
||||
ssearch.ss_end = end;
|
||||
ss = avl_find(&sm->sm_root, &ssearch, &where);
|
||||
|
||||
return (ss != NULL && ss->ss_start <= start && ss->ss_end >= end);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_vacate(space_map_t *sm, space_map_func_t *func, space_map_t *mdest)
|
||||
{
|
||||
space_seg_t *ss;
|
||||
void *cookie = NULL;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
|
||||
while ((ss = avl_destroy_nodes(&sm->sm_root, &cookie)) != NULL) {
|
||||
if (func != NULL)
|
||||
func(mdest, ss->ss_start, ss->ss_end - ss->ss_start);
|
||||
kmem_free(ss, sizeof (*ss));
|
||||
}
|
||||
sm->sm_space = 0;
|
||||
}
|
||||
|
||||
void
|
||||
space_map_walk(space_map_t *sm, space_map_func_t *func, space_map_t *mdest)
|
||||
{
|
||||
space_seg_t *ss;
|
||||
|
||||
for (ss = avl_first(&sm->sm_root); ss; ss = AVL_NEXT(&sm->sm_root, ss))
|
||||
func(mdest, ss->ss_start, ss->ss_end - ss->ss_start);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_excise(space_map_t *sm, uint64_t start, uint64_t size)
|
||||
{
|
||||
avl_tree_t *t = &sm->sm_root;
|
||||
avl_index_t where;
|
||||
space_seg_t *ss, search;
|
||||
uint64_t end = start + size;
|
||||
uint64_t rm_start, rm_end;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
|
||||
search.ss_start = start;
|
||||
search.ss_end = start;
|
||||
|
||||
for (;;) {
|
||||
ss = avl_find(t, &search, &where);
|
||||
|
||||
if (ss == NULL)
|
||||
ss = avl_nearest(t, where, AVL_AFTER);
|
||||
|
||||
if (ss == NULL || ss->ss_start >= end)
|
||||
break;
|
||||
|
||||
rm_start = MAX(ss->ss_start, start);
|
||||
rm_end = MIN(ss->ss_end, end);
|
||||
|
||||
space_map_remove(sm, rm_start, rm_end - rm_start);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace smd with the union of smd and sms.
|
||||
*/
|
||||
void
|
||||
space_map_union(space_map_t *smd, space_map_t *sms)
|
||||
{
|
||||
avl_tree_t *t = &sms->sm_root;
|
||||
space_seg_t *ss;
|
||||
|
||||
ASSERT(MUTEX_HELD(smd->sm_lock));
|
||||
|
||||
/*
|
||||
* For each source segment, remove any intersections with the
|
||||
* destination, then add the source segment to the destination.
|
||||
*/
|
||||
for (ss = avl_first(t); ss != NULL; ss = AVL_NEXT(t, ss)) {
|
||||
space_map_excise(smd, ss->ss_start, ss->ss_end - ss->ss_start);
|
||||
space_map_add(smd, ss->ss_start, ss->ss_end - ss->ss_start);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for any in-progress space_map_load() to complete.
|
||||
*/
|
||||
void
|
||||
space_map_load_wait(space_map_t *sm)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
|
||||
while (sm->sm_loading)
|
||||
cv_wait(&sm->sm_load_cv, sm->sm_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: space_map_load() will drop sm_lock across dmu_read() calls.
|
||||
* The caller must be OK with this.
|
||||
*/
|
||||
int
|
||||
space_map_load(space_map_t *sm, space_map_ops_t *ops, uint8_t maptype,
|
||||
space_map_obj_t *smo, objset_t *os)
|
||||
{
|
||||
uint64_t *entry, *entry_map, *entry_map_end;
|
||||
uint64_t bufsize, size, offset, end, space;
|
||||
uint64_t mapstart = sm->sm_start;
|
||||
int error = 0;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
|
||||
space_map_load_wait(sm);
|
||||
|
||||
if (sm->sm_loaded)
|
||||
return (0);
|
||||
|
||||
sm->sm_loading = B_TRUE;
|
||||
end = smo->smo_objsize;
|
||||
space = smo->smo_alloc;
|
||||
|
||||
ASSERT(sm->sm_ops == NULL);
|
||||
VERIFY3U(sm->sm_space, ==, 0);
|
||||
|
||||
if (maptype == SM_FREE) {
|
||||
space_map_add(sm, sm->sm_start, sm->sm_size);
|
||||
space = sm->sm_size - space;
|
||||
}
|
||||
|
||||
bufsize = 1ULL << SPACE_MAP_BLOCKSHIFT;
|
||||
entry_map = zio_buf_alloc(bufsize);
|
||||
|
||||
mutex_exit(sm->sm_lock);
|
||||
if (end > bufsize)
|
||||
dmu_prefetch(os, smo->smo_object, bufsize, end - bufsize);
|
||||
mutex_enter(sm->sm_lock);
|
||||
|
||||
for (offset = 0; offset < end; offset += bufsize) {
|
||||
size = MIN(end - offset, bufsize);
|
||||
VERIFY(P2PHASE(size, sizeof (uint64_t)) == 0);
|
||||
VERIFY(size != 0);
|
||||
|
||||
dprintf("object=%llu offset=%llx size=%llx\n",
|
||||
smo->smo_object, offset, size);
|
||||
|
||||
mutex_exit(sm->sm_lock);
|
||||
error = dmu_read(os, smo->smo_object, offset, size, entry_map);
|
||||
mutex_enter(sm->sm_lock);
|
||||
if (error != 0)
|
||||
break;
|
||||
|
||||
entry_map_end = entry_map + (size / sizeof (uint64_t));
|
||||
for (entry = entry_map; entry < entry_map_end; entry++) {
|
||||
uint64_t e = *entry;
|
||||
|
||||
if (SM_DEBUG_DECODE(e)) /* Skip debug entries */
|
||||
continue;
|
||||
|
||||
(SM_TYPE_DECODE(e) == maptype ?
|
||||
space_map_add : space_map_remove)(sm,
|
||||
(SM_OFFSET_DECODE(e) << sm->sm_shift) + mapstart,
|
||||
SM_RUN_DECODE(e) << sm->sm_shift);
|
||||
}
|
||||
}
|
||||
|
||||
if (error == 0) {
|
||||
VERIFY3U(sm->sm_space, ==, space);
|
||||
|
||||
sm->sm_loaded = B_TRUE;
|
||||
sm->sm_ops = ops;
|
||||
if (ops != NULL)
|
||||
ops->smop_load(sm);
|
||||
} else {
|
||||
space_map_vacate(sm, NULL, NULL);
|
||||
}
|
||||
|
||||
zio_buf_free(entry_map, bufsize);
|
||||
|
||||
sm->sm_loading = B_FALSE;
|
||||
|
||||
cv_broadcast(&sm->sm_load_cv);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_unload(space_map_t *sm)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
|
||||
if (sm->sm_loaded && sm->sm_ops != NULL)
|
||||
sm->sm_ops->smop_unload(sm);
|
||||
|
||||
sm->sm_loaded = B_FALSE;
|
||||
sm->sm_ops = NULL;
|
||||
|
||||
space_map_vacate(sm, NULL, NULL);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
space_map_alloc(space_map_t *sm, uint64_t size)
|
||||
{
|
||||
uint64_t start;
|
||||
|
||||
start = sm->sm_ops->smop_alloc(sm, size);
|
||||
if (start != -1ULL)
|
||||
space_map_remove(sm, start, size);
|
||||
return (start);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_claim(space_map_t *sm, uint64_t start, uint64_t size)
|
||||
{
|
||||
sm->sm_ops->smop_claim(sm, start, size);
|
||||
space_map_remove(sm, start, size);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_free(space_map_t *sm, uint64_t start, uint64_t size)
|
||||
{
|
||||
space_map_add(sm, start, size);
|
||||
sm->sm_ops->smop_free(sm, start, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: space_map_sync() will drop sm_lock across dmu_write() calls.
|
||||
*/
|
||||
void
|
||||
space_map_sync(space_map_t *sm, uint8_t maptype,
|
||||
space_map_obj_t *smo, objset_t *os, dmu_tx_t *tx)
|
||||
{
|
||||
spa_t *spa = dmu_objset_spa(os);
|
||||
void *cookie = NULL;
|
||||
space_seg_t *ss;
|
||||
uint64_t bufsize, start, size, run_len;
|
||||
uint64_t *entry, *entry_map, *entry_map_end;
|
||||
|
||||
ASSERT(MUTEX_HELD(sm->sm_lock));
|
||||
|
||||
if (sm->sm_space == 0)
|
||||
return;
|
||||
|
||||
dprintf("object %4llu, txg %llu, pass %d, %c, count %lu, space %llx\n",
|
||||
smo->smo_object, dmu_tx_get_txg(tx), spa_sync_pass(spa),
|
||||
maptype == SM_ALLOC ? 'A' : 'F', avl_numnodes(&sm->sm_root),
|
||||
sm->sm_space);
|
||||
|
||||
if (maptype == SM_ALLOC)
|
||||
smo->smo_alloc += sm->sm_space;
|
||||
else
|
||||
smo->smo_alloc -= sm->sm_space;
|
||||
|
||||
bufsize = (8 + avl_numnodes(&sm->sm_root)) * sizeof (uint64_t);
|
||||
bufsize = MIN(bufsize, 1ULL << SPACE_MAP_BLOCKSHIFT);
|
||||
entry_map = zio_buf_alloc(bufsize);
|
||||
entry_map_end = entry_map + (bufsize / sizeof (uint64_t));
|
||||
entry = entry_map;
|
||||
|
||||
*entry++ = SM_DEBUG_ENCODE(1) |
|
||||
SM_DEBUG_ACTION_ENCODE(maptype) |
|
||||
SM_DEBUG_SYNCPASS_ENCODE(spa_sync_pass(spa)) |
|
||||
SM_DEBUG_TXG_ENCODE(dmu_tx_get_txg(tx));
|
||||
|
||||
while ((ss = avl_destroy_nodes(&sm->sm_root, &cookie)) != NULL) {
|
||||
size = ss->ss_end - ss->ss_start;
|
||||
start = (ss->ss_start - sm->sm_start) >> sm->sm_shift;
|
||||
|
||||
sm->sm_space -= size;
|
||||
size >>= sm->sm_shift;
|
||||
|
||||
while (size) {
|
||||
run_len = MIN(size, SM_RUN_MAX);
|
||||
|
||||
if (entry == entry_map_end) {
|
||||
mutex_exit(sm->sm_lock);
|
||||
dmu_write(os, smo->smo_object, smo->smo_objsize,
|
||||
bufsize, entry_map, tx);
|
||||
mutex_enter(sm->sm_lock);
|
||||
smo->smo_objsize += bufsize;
|
||||
entry = entry_map;
|
||||
}
|
||||
|
||||
*entry++ = SM_OFFSET_ENCODE(start) |
|
||||
SM_TYPE_ENCODE(maptype) |
|
||||
SM_RUN_ENCODE(run_len);
|
||||
|
||||
start += run_len;
|
||||
size -= run_len;
|
||||
}
|
||||
kmem_free(ss, sizeof (*ss));
|
||||
}
|
||||
|
||||
if (entry != entry_map) {
|
||||
size = (entry - entry_map) * sizeof (uint64_t);
|
||||
mutex_exit(sm->sm_lock);
|
||||
dmu_write(os, smo->smo_object, smo->smo_objsize,
|
||||
size, entry_map, tx);
|
||||
mutex_enter(sm->sm_lock);
|
||||
smo->smo_objsize += size;
|
||||
}
|
||||
|
||||
zio_buf_free(entry_map, bufsize);
|
||||
|
||||
VERIFY3U(sm->sm_space, ==, 0);
|
||||
}
|
||||
|
||||
void
|
||||
space_map_truncate(space_map_obj_t *smo, objset_t *os, dmu_tx_t *tx)
|
||||
{
|
||||
VERIFY(dmu_free_range(os, smo->smo_object, 0, -1ULL, tx) == 0);
|
||||
|
||||
smo->smo_objsize = 0;
|
||||
smo->smo_alloc = 0;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_ARC_H
|
||||
#define _SYS_ARC_H
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/zio.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/spa.h>
|
||||
|
||||
typedef struct arc_buf_hdr arc_buf_hdr_t;
|
||||
typedef struct arc_buf arc_buf_t;
|
||||
typedef void arc_done_func_t(zio_t *zio, arc_buf_t *buf, void *private);
|
||||
typedef int arc_evict_func_t(void *private);
|
||||
|
||||
/* generic arc_done_func_t's which you can use */
|
||||
arc_done_func_t arc_bcopy_func;
|
||||
arc_done_func_t arc_getbuf_func;
|
||||
|
||||
struct arc_buf {
|
||||
arc_buf_hdr_t *b_hdr;
|
||||
arc_buf_t *b_next;
|
||||
krwlock_t b_lock;
|
||||
void *b_data;
|
||||
arc_evict_func_t *b_efunc;
|
||||
void *b_private;
|
||||
};
|
||||
|
||||
typedef enum arc_buf_contents {
|
||||
ARC_BUFC_DATA, /* buffer contains data */
|
||||
ARC_BUFC_METADATA, /* buffer contains metadata */
|
||||
ARC_BUFC_NUMTYPES
|
||||
} arc_buf_contents_t;
|
||||
/*
|
||||
* These are the flags we pass into calls to the arc
|
||||
*/
|
||||
#define ARC_WAIT (1 << 1) /* perform I/O synchronously */
|
||||
#define ARC_NOWAIT (1 << 2) /* perform I/O asynchronously */
|
||||
#define ARC_PREFETCH (1 << 3) /* I/O is a prefetch */
|
||||
#define ARC_CACHED (1 << 4) /* I/O was already in cache */
|
||||
#define ARC_L2CACHE (1 << 5) /* cache in L2ARC */
|
||||
|
||||
void arc_space_consume(uint64_t space);
|
||||
void arc_space_return(uint64_t space);
|
||||
void *arc_data_buf_alloc(uint64_t space);
|
||||
void arc_data_buf_free(void *buf, uint64_t space);
|
||||
arc_buf_t *arc_buf_alloc(spa_t *spa, int size, void *tag,
|
||||
arc_buf_contents_t type);
|
||||
void arc_buf_add_ref(arc_buf_t *buf, void *tag);
|
||||
int arc_buf_remove_ref(arc_buf_t *buf, void *tag);
|
||||
int arc_buf_size(arc_buf_t *buf);
|
||||
void arc_release(arc_buf_t *buf, void *tag);
|
||||
int arc_released(arc_buf_t *buf);
|
||||
int arc_has_callback(arc_buf_t *buf);
|
||||
void arc_buf_freeze(arc_buf_t *buf);
|
||||
void arc_buf_thaw(arc_buf_t *buf);
|
||||
#ifdef ZFS_DEBUG
|
||||
int arc_referenced(arc_buf_t *buf);
|
||||
#endif
|
||||
|
||||
typedef struct writeprops {
|
||||
dmu_object_type_t wp_type;
|
||||
uint8_t wp_level;
|
||||
uint8_t wp_copies;
|
||||
uint8_t wp_dncompress, wp_oscompress;
|
||||
uint8_t wp_dnchecksum, wp_oschecksum;
|
||||
} writeprops_t;
|
||||
|
||||
void write_policy(spa_t *spa, const writeprops_t *wp, zio_prop_t *zp);
|
||||
int arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_buf_t *pbuf,
|
||||
arc_done_func_t *done, void *private, int priority, int zio_flags,
|
||||
uint32_t *arc_flags, const zbookmark_t *zb);
|
||||
int arc_read_nolock(zio_t *pio, spa_t *spa, blkptr_t *bp,
|
||||
arc_done_func_t *done, void *private, int priority, int flags,
|
||||
uint32_t *arc_flags, const zbookmark_t *zb);
|
||||
zio_t *arc_write(zio_t *pio, spa_t *spa, const writeprops_t *wp,
|
||||
boolean_t l2arc, uint64_t txg, blkptr_t *bp, arc_buf_t *buf,
|
||||
arc_done_func_t *ready, arc_done_func_t *done, void *private, int priority,
|
||||
int zio_flags, const zbookmark_t *zb);
|
||||
int arc_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
|
||||
zio_done_func_t *done, void *private, uint32_t arc_flags);
|
||||
int arc_tryread(spa_t *spa, blkptr_t *bp, void *data);
|
||||
|
||||
void arc_set_callback(arc_buf_t *buf, arc_evict_func_t *func, void *private);
|
||||
int arc_buf_evict(arc_buf_t *buf);
|
||||
|
||||
void arc_flush(spa_t *spa);
|
||||
void arc_tempreserve_clear(uint64_t reserve);
|
||||
int arc_tempreserve_space(uint64_t reserve, uint64_t txg);
|
||||
|
||||
void arc_init(void);
|
||||
void arc_fini(void);
|
||||
|
||||
/*
|
||||
* Level 2 ARC
|
||||
*/
|
||||
|
||||
void l2arc_add_vdev(spa_t *spa, vdev_t *vd, uint64_t start, uint64_t end);
|
||||
void l2arc_remove_vdev(vdev_t *vd);
|
||||
boolean_t l2arc_vdev_present(vdev_t *vd);
|
||||
void l2arc_init(void);
|
||||
void l2arc_fini(void);
|
||||
void l2arc_start(void);
|
||||
void l2arc_stop(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_ARC_H */
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_BPLIST_H
|
||||
#define _SYS_BPLIST_H
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/txg.h>
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct bplist_phys {
|
||||
/*
|
||||
* This is the bonus buffer for the dead lists. The object's
|
||||
* contents is an array of bpl_entries blkptr_t's, representing
|
||||
* a total of bpl_bytes physical space.
|
||||
*/
|
||||
uint64_t bpl_entries;
|
||||
uint64_t bpl_bytes;
|
||||
uint64_t bpl_comp;
|
||||
uint64_t bpl_uncomp;
|
||||
} bplist_phys_t;
|
||||
|
||||
#define BPLIST_SIZE_V0 (2 * sizeof (uint64_t))
|
||||
|
||||
typedef struct bplist_q {
|
||||
blkptr_t bpq_blk;
|
||||
void *bpq_next;
|
||||
} bplist_q_t;
|
||||
|
||||
typedef struct bplist {
|
||||
kmutex_t bpl_lock;
|
||||
objset_t *bpl_mos;
|
||||
uint64_t bpl_object;
|
||||
uint8_t bpl_blockshift;
|
||||
uint8_t bpl_bpshift;
|
||||
uint8_t bpl_havecomp;
|
||||
bplist_q_t *bpl_queue;
|
||||
bplist_phys_t *bpl_phys;
|
||||
dmu_buf_t *bpl_dbuf;
|
||||
dmu_buf_t *bpl_cached_dbuf;
|
||||
} bplist_t;
|
||||
|
||||
extern uint64_t bplist_create(objset_t *mos, int blocksize, dmu_tx_t *tx);
|
||||
extern void bplist_destroy(objset_t *mos, uint64_t object, dmu_tx_t *tx);
|
||||
extern int bplist_open(bplist_t *bpl, objset_t *mos, uint64_t object);
|
||||
extern void bplist_close(bplist_t *bpl);
|
||||
extern boolean_t bplist_empty(bplist_t *bpl);
|
||||
extern int bplist_iterate(bplist_t *bpl, uint64_t *itorp, blkptr_t *bp);
|
||||
extern int bplist_enqueue(bplist_t *bpl, const blkptr_t *bp, dmu_tx_t *tx);
|
||||
extern void bplist_enqueue_deferred(bplist_t *bpl, const blkptr_t *bp);
|
||||
extern void bplist_sync(bplist_t *bpl, dmu_tx_t *tx);
|
||||
extern void bplist_vacate(bplist_t *bpl, dmu_tx_t *tx);
|
||||
extern int bplist_space(bplist_t *bpl,
|
||||
uint64_t *usedp, uint64_t *compp, uint64_t *uncompp);
|
||||
extern int bplist_space_birthrange(bplist_t *bpl,
|
||||
uint64_t mintxg, uint64_t maxtxg, uint64_t *dasizep);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_BPLIST_H */
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_DBUF_H
|
||||
#define _SYS_DBUF_H
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/txg.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/arc.h>
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/refcount.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DB_BONUS_BLKID (-1ULL)
|
||||
#define IN_DMU_SYNC 2
|
||||
|
||||
/*
|
||||
* define flags for dbuf_read
|
||||
*/
|
||||
|
||||
#define DB_RF_MUST_SUCCEED (1 << 0)
|
||||
#define DB_RF_CANFAIL (1 << 1)
|
||||
#define DB_RF_HAVESTRUCT (1 << 2)
|
||||
#define DB_RF_NOPREFETCH (1 << 3)
|
||||
#define DB_RF_NEVERWAIT (1 << 4)
|
||||
#define DB_RF_CACHED (1 << 5)
|
||||
|
||||
/*
|
||||
* The simplified state transition diagram for dbufs looks like:
|
||||
*
|
||||
* +----> READ ----+
|
||||
* | |
|
||||
* | V
|
||||
* (alloc)-->UNCACHED CACHED-->EVICTING-->(free)
|
||||
* | ^ ^
|
||||
* | | |
|
||||
* +----> FILL ----+ |
|
||||
* | |
|
||||
* | |
|
||||
* +--------> NOFILL -------+
|
||||
*/
|
||||
typedef enum dbuf_states {
|
||||
DB_UNCACHED,
|
||||
DB_FILL,
|
||||
DB_NOFILL,
|
||||
DB_READ,
|
||||
DB_CACHED,
|
||||
DB_EVICTING
|
||||
} dbuf_states_t;
|
||||
|
||||
struct objset_impl;
|
||||
struct dnode;
|
||||
struct dmu_tx;
|
||||
|
||||
/*
|
||||
* level = 0 means the user data
|
||||
* level = 1 means the single indirect block
|
||||
* etc.
|
||||
*/
|
||||
|
||||
#define LIST_LINK_INACTIVE(link) \
|
||||
((link)->list_next == NULL && (link)->list_prev == NULL)
|
||||
|
||||
struct dmu_buf_impl;
|
||||
|
||||
typedef enum override_states {
|
||||
DR_NOT_OVERRIDDEN,
|
||||
DR_IN_DMU_SYNC,
|
||||
DR_OVERRIDDEN
|
||||
} override_states_t;
|
||||
|
||||
typedef struct dbuf_dirty_record {
|
||||
/* link on our parents dirty list */
|
||||
list_node_t dr_dirty_node;
|
||||
|
||||
/* transaction group this data will sync in */
|
||||
uint64_t dr_txg;
|
||||
|
||||
/* zio of outstanding write IO */
|
||||
zio_t *dr_zio;
|
||||
|
||||
/* pointer back to our dbuf */
|
||||
struct dmu_buf_impl *dr_dbuf;
|
||||
|
||||
/* pointer to next dirty record */
|
||||
struct dbuf_dirty_record *dr_next;
|
||||
|
||||
/* pointer to parent dirty record */
|
||||
struct dbuf_dirty_record *dr_parent;
|
||||
|
||||
union dirty_types {
|
||||
struct dirty_indirect {
|
||||
|
||||
/* protect access to list */
|
||||
kmutex_t dr_mtx;
|
||||
|
||||
/* Our list of dirty children */
|
||||
list_t dr_children;
|
||||
} di;
|
||||
struct dirty_leaf {
|
||||
|
||||
/*
|
||||
* dr_data is set when we dirty the buffer
|
||||
* so that we can retain the pointer even if it
|
||||
* gets COW'd in a subsequent transaction group.
|
||||
*/
|
||||
arc_buf_t *dr_data;
|
||||
blkptr_t dr_overridden_by;
|
||||
override_states_t dr_override_state;
|
||||
} dl;
|
||||
} dt;
|
||||
} dbuf_dirty_record_t;
|
||||
|
||||
typedef struct dmu_buf_impl {
|
||||
/*
|
||||
* The following members are immutable, with the exception of
|
||||
* db.db_data, which is protected by db_mtx.
|
||||
*/
|
||||
|
||||
/* the publicly visible structure */
|
||||
dmu_buf_t db;
|
||||
|
||||
/* the objset we belong to */
|
||||
struct objset_impl *db_objset;
|
||||
|
||||
/*
|
||||
* the dnode we belong to (NULL when evicted)
|
||||
*/
|
||||
struct dnode *db_dnode;
|
||||
|
||||
/*
|
||||
* our parent buffer; if the dnode points to us directly,
|
||||
* db_parent == db_dnode->dn_dbuf
|
||||
* only accessed by sync thread ???
|
||||
* (NULL when evicted)
|
||||
*/
|
||||
struct dmu_buf_impl *db_parent;
|
||||
|
||||
/*
|
||||
* link for hash table of all dmu_buf_impl_t's
|
||||
*/
|
||||
struct dmu_buf_impl *db_hash_next;
|
||||
|
||||
/* our block number */
|
||||
uint64_t db_blkid;
|
||||
|
||||
/*
|
||||
* Pointer to the blkptr_t which points to us. May be NULL if we
|
||||
* don't have one yet. (NULL when evicted)
|
||||
*/
|
||||
blkptr_t *db_blkptr;
|
||||
|
||||
/*
|
||||
* Our indirection level. Data buffers have db_level==0.
|
||||
* Indirect buffers which point to data buffers have
|
||||
* db_level==1. etc. Buffers which contain dnodes have
|
||||
* db_level==0, since the dnodes are stored in a file.
|
||||
*/
|
||||
uint8_t db_level;
|
||||
|
||||
/* db_mtx protects the members below */
|
||||
kmutex_t db_mtx;
|
||||
|
||||
/*
|
||||
* Current state of the buffer
|
||||
*/
|
||||
dbuf_states_t db_state;
|
||||
|
||||
/*
|
||||
* Refcount accessed by dmu_buf_{hold,rele}.
|
||||
* If nonzero, the buffer can't be destroyed.
|
||||
* Protected by db_mtx.
|
||||
*/
|
||||
refcount_t db_holds;
|
||||
|
||||
/* buffer holding our data */
|
||||
arc_buf_t *db_buf;
|
||||
|
||||
kcondvar_t db_changed;
|
||||
dbuf_dirty_record_t *db_data_pending;
|
||||
|
||||
/* pointer to most recent dirty record for this buffer */
|
||||
dbuf_dirty_record_t *db_last_dirty;
|
||||
|
||||
/*
|
||||
* Our link on the owner dnodes's dn_dbufs list.
|
||||
* Protected by its dn_dbufs_mtx.
|
||||
*/
|
||||
list_node_t db_link;
|
||||
|
||||
/* Data which is unique to data (leaf) blocks: */
|
||||
|
||||
/* stuff we store for the user (see dmu_buf_set_user) */
|
||||
void *db_user_ptr;
|
||||
void **db_user_data_ptr_ptr;
|
||||
dmu_buf_evict_func_t *db_evict_func;
|
||||
|
||||
uint8_t db_immediate_evict;
|
||||
uint8_t db_freed_in_flight;
|
||||
|
||||
uint8_t db_dirtycnt;
|
||||
} dmu_buf_impl_t;
|
||||
|
||||
/* Note: the dbuf hash table is exposed only for the mdb module */
|
||||
#define DBUF_MUTEXES 256
|
||||
#define DBUF_HASH_MUTEX(h, idx) (&(h)->hash_mutexes[(idx) & (DBUF_MUTEXES-1)])
|
||||
typedef struct dbuf_hash_table {
|
||||
uint64_t hash_table_mask;
|
||||
dmu_buf_impl_t **hash_table;
|
||||
kmutex_t hash_mutexes[DBUF_MUTEXES];
|
||||
} dbuf_hash_table_t;
|
||||
|
||||
|
||||
uint64_t dbuf_whichblock(struct dnode *di, uint64_t offset);
|
||||
|
||||
dmu_buf_impl_t *dbuf_create_tlib(struct dnode *dn, char *data);
|
||||
void dbuf_create_bonus(struct dnode *dn);
|
||||
|
||||
dmu_buf_impl_t *dbuf_hold(struct dnode *dn, uint64_t blkid, void *tag);
|
||||
dmu_buf_impl_t *dbuf_hold_level(struct dnode *dn, int level, uint64_t blkid,
|
||||
void *tag);
|
||||
int dbuf_hold_impl(struct dnode *dn, uint8_t level, uint64_t blkid, int create,
|
||||
void *tag, dmu_buf_impl_t **dbp);
|
||||
|
||||
void dbuf_prefetch(struct dnode *dn, uint64_t blkid);
|
||||
|
||||
void dbuf_add_ref(dmu_buf_impl_t *db, void *tag);
|
||||
uint64_t dbuf_refcount(dmu_buf_impl_t *db);
|
||||
|
||||
void dbuf_rele(dmu_buf_impl_t *db, void *tag);
|
||||
|
||||
dmu_buf_impl_t *dbuf_find(struct dnode *dn, uint8_t level, uint64_t blkid);
|
||||
|
||||
int dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags);
|
||||
void dbuf_will_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
|
||||
void dbuf_fill_done(dmu_buf_impl_t *db, dmu_tx_t *tx);
|
||||
void dmu_buf_will_not_fill(dmu_buf_t *db, dmu_tx_t *tx);
|
||||
void dmu_buf_will_fill(dmu_buf_t *db, dmu_tx_t *tx);
|
||||
void dmu_buf_fill_done(dmu_buf_t *db, dmu_tx_t *tx);
|
||||
dbuf_dirty_record_t *dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
|
||||
|
||||
void dbuf_clear(dmu_buf_impl_t *db);
|
||||
void dbuf_evict(dmu_buf_impl_t *db);
|
||||
|
||||
void dbuf_setdirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
|
||||
void dbuf_unoverride(dbuf_dirty_record_t *dr);
|
||||
void dbuf_sync_list(list_t *list, dmu_tx_t *tx);
|
||||
|
||||
void dbuf_free_range(struct dnode *dn, uint64_t start, uint64_t end,
|
||||
struct dmu_tx *);
|
||||
|
||||
void dbuf_new_size(dmu_buf_impl_t *db, int size, dmu_tx_t *tx);
|
||||
|
||||
void dbuf_init(void);
|
||||
void dbuf_fini(void);
|
||||
|
||||
#define DBUF_IS_METADATA(db) \
|
||||
((db)->db_level > 0 || dmu_ot[(db)->db_dnode->dn_type].ot_metadata)
|
||||
|
||||
#define DBUF_GET_BUFC_TYPE(db) \
|
||||
(DBUF_IS_METADATA(db) ? ARC_BUFC_METADATA : ARC_BUFC_DATA)
|
||||
|
||||
#define DBUF_IS_CACHEABLE(db) \
|
||||
((db)->db_objset->os_primary_cache == ZFS_CACHE_ALL || \
|
||||
(DBUF_IS_METADATA(db) && \
|
||||
((db)->db_objset->os_primary_cache == ZFS_CACHE_METADATA)))
|
||||
|
||||
#define DBUF_IS_L2CACHEABLE(db) \
|
||||
((db)->db_objset->os_secondary_cache == ZFS_CACHE_ALL || \
|
||||
(DBUF_IS_METADATA(db) && \
|
||||
((db)->db_objset->os_secondary_cache == ZFS_CACHE_METADATA)))
|
||||
|
||||
#ifdef ZFS_DEBUG
|
||||
|
||||
/*
|
||||
* There should be a ## between the string literal and fmt, to make it
|
||||
* clear that we're joining two strings together, but gcc does not
|
||||
* support that preprocessor token.
|
||||
*/
|
||||
#define dprintf_dbuf(dbuf, fmt, ...) do { \
|
||||
if (zfs_flags & ZFS_DEBUG_DPRINTF) { \
|
||||
char __db_buf[32]; \
|
||||
uint64_t __db_obj = (dbuf)->db.db_object; \
|
||||
if (__db_obj == DMU_META_DNODE_OBJECT) \
|
||||
(void) strcpy(__db_buf, "mdn"); \
|
||||
else \
|
||||
(void) snprintf(__db_buf, sizeof (__db_buf), "%lld", \
|
||||
(u_longlong_t)__db_obj); \
|
||||
dprintf_ds((dbuf)->db_objset->os_dsl_dataset, \
|
||||
"obj=%s lvl=%u blkid=%lld " fmt, \
|
||||
__db_buf, (dbuf)->db_level, \
|
||||
(u_longlong_t)(dbuf)->db_blkid, __VA_ARGS__); \
|
||||
} \
|
||||
_NOTE(CONSTCOND) } while (0)
|
||||
|
||||
#define dprintf_dbuf_bp(db, bp, fmt, ...) do { \
|
||||
if (zfs_flags & ZFS_DEBUG_DPRINTF) { \
|
||||
char *__blkbuf = kmem_alloc(BP_SPRINTF_LEN, KM_SLEEP); \
|
||||
sprintf_blkptr(__blkbuf, BP_SPRINTF_LEN, bp); \
|
||||
dprintf_dbuf(db, fmt " %s\n", __VA_ARGS__, __blkbuf); \
|
||||
kmem_free(__blkbuf, BP_SPRINTF_LEN); \
|
||||
} \
|
||||
_NOTE(CONSTCOND) } while (0)
|
||||
|
||||
#define DBUF_VERIFY(db) dbuf_verify(db)
|
||||
|
||||
#else
|
||||
|
||||
#define dprintf_dbuf(db, fmt, ...)
|
||||
#define dprintf_dbuf_bp(db, bp, fmt, ...)
|
||||
#define DBUF_VERIFY(db)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_DBUF_H */
|
|
@ -0,0 +1,638 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_DMU_H
|
||||
#define _SYS_DMU_H
|
||||
|
||||
/*
|
||||
* This file describes the interface that the DMU provides for its
|
||||
* consumers.
|
||||
*
|
||||
* The DMU also interacts with the SPA. That interface is described in
|
||||
* dmu_spa.h.
|
||||
*/
|
||||
|
||||
#include <sys/inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/cred.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct uio;
|
||||
struct page;
|
||||
struct vnode;
|
||||
struct spa;
|
||||
struct zilog;
|
||||
struct zio;
|
||||
struct blkptr;
|
||||
struct zap_cursor;
|
||||
struct dsl_dataset;
|
||||
struct dsl_pool;
|
||||
struct dnode;
|
||||
struct drr_begin;
|
||||
struct drr_end;
|
||||
struct zbookmark;
|
||||
struct spa;
|
||||
struct nvlist;
|
||||
struct objset_impl;
|
||||
|
||||
typedef struct objset objset_t;
|
||||
typedef struct dmu_tx dmu_tx_t;
|
||||
typedef struct dsl_dir dsl_dir_t;
|
||||
|
||||
typedef enum dmu_object_type {
|
||||
DMU_OT_NONE,
|
||||
/* general: */
|
||||
DMU_OT_OBJECT_DIRECTORY, /* ZAP */
|
||||
DMU_OT_OBJECT_ARRAY, /* UINT64 */
|
||||
DMU_OT_PACKED_NVLIST, /* UINT8 (XDR by nvlist_pack/unpack) */
|
||||
DMU_OT_PACKED_NVLIST_SIZE, /* UINT64 */
|
||||
DMU_OT_BPLIST, /* UINT64 */
|
||||
DMU_OT_BPLIST_HDR, /* UINT64 */
|
||||
/* spa: */
|
||||
DMU_OT_SPACE_MAP_HEADER, /* UINT64 */
|
||||
DMU_OT_SPACE_MAP, /* UINT64 */
|
||||
/* zil: */
|
||||
DMU_OT_INTENT_LOG, /* UINT64 */
|
||||
/* dmu: */
|
||||
DMU_OT_DNODE, /* DNODE */
|
||||
DMU_OT_OBJSET, /* OBJSET */
|
||||
/* dsl: */
|
||||
DMU_OT_DSL_DIR, /* UINT64 */
|
||||
DMU_OT_DSL_DIR_CHILD_MAP, /* ZAP */
|
||||
DMU_OT_DSL_DS_SNAP_MAP, /* ZAP */
|
||||
DMU_OT_DSL_PROPS, /* ZAP */
|
||||
DMU_OT_DSL_DATASET, /* UINT64 */
|
||||
/* zpl: */
|
||||
DMU_OT_ZNODE, /* ZNODE */
|
||||
DMU_OT_OLDACL, /* Old ACL */
|
||||
DMU_OT_PLAIN_FILE_CONTENTS, /* UINT8 */
|
||||
DMU_OT_DIRECTORY_CONTENTS, /* ZAP */
|
||||
DMU_OT_MASTER_NODE, /* ZAP */
|
||||
DMU_OT_UNLINKED_SET, /* ZAP */
|
||||
/* zvol: */
|
||||
DMU_OT_ZVOL, /* UINT8 */
|
||||
DMU_OT_ZVOL_PROP, /* ZAP */
|
||||
/* other; for testing only! */
|
||||
DMU_OT_PLAIN_OTHER, /* UINT8 */
|
||||
DMU_OT_UINT64_OTHER, /* UINT64 */
|
||||
DMU_OT_ZAP_OTHER, /* ZAP */
|
||||
/* new object types: */
|
||||
DMU_OT_ERROR_LOG, /* ZAP */
|
||||
DMU_OT_SPA_HISTORY, /* UINT8 */
|
||||
DMU_OT_SPA_HISTORY_OFFSETS, /* spa_his_phys_t */
|
||||
DMU_OT_POOL_PROPS, /* ZAP */
|
||||
DMU_OT_DSL_PERMS, /* ZAP */
|
||||
DMU_OT_ACL, /* ACL */
|
||||
DMU_OT_SYSACL, /* SYSACL */
|
||||
DMU_OT_FUID, /* FUID table (Packed NVLIST UINT8) */
|
||||
DMU_OT_FUID_SIZE, /* FUID table size UINT64 */
|
||||
DMU_OT_NEXT_CLONES, /* ZAP */
|
||||
DMU_OT_SCRUB_QUEUE, /* ZAP */
|
||||
DMU_OT_NUMTYPES
|
||||
} dmu_object_type_t;
|
||||
|
||||
typedef enum dmu_objset_type {
|
||||
DMU_OST_NONE,
|
||||
DMU_OST_META,
|
||||
DMU_OST_ZFS,
|
||||
DMU_OST_ZVOL,
|
||||
DMU_OST_OTHER, /* For testing only! */
|
||||
DMU_OST_ANY, /* Be careful! */
|
||||
DMU_OST_NUMTYPES
|
||||
} dmu_objset_type_t;
|
||||
|
||||
void byteswap_uint64_array(void *buf, size_t size);
|
||||
void byteswap_uint32_array(void *buf, size_t size);
|
||||
void byteswap_uint16_array(void *buf, size_t size);
|
||||
void byteswap_uint8_array(void *buf, size_t size);
|
||||
void zap_byteswap(void *buf, size_t size);
|
||||
void zfs_oldacl_byteswap(void *buf, size_t size);
|
||||
void zfs_acl_byteswap(void *buf, size_t size);
|
||||
void zfs_znode_byteswap(void *buf, size_t size);
|
||||
|
||||
#define DS_MODE_NOHOLD 0 /* internal use only */
|
||||
#define DS_MODE_USER 1 /* simple access, no special needs */
|
||||
#define DS_MODE_OWNER 2 /* the "main" access, e.g. a mount */
|
||||
#define DS_MODE_TYPE_MASK 0x3
|
||||
#define DS_MODE_TYPE(x) ((x) & DS_MODE_TYPE_MASK)
|
||||
#define DS_MODE_READONLY 0x8
|
||||
#define DS_MODE_IS_READONLY(x) ((x) & DS_MODE_READONLY)
|
||||
#define DS_MODE_INCONSISTENT 0x10
|
||||
#define DS_MODE_IS_INCONSISTENT(x) ((x) & DS_MODE_INCONSISTENT)
|
||||
|
||||
#define DS_FIND_SNAPSHOTS (1<<0)
|
||||
#define DS_FIND_CHILDREN (1<<1)
|
||||
|
||||
/*
|
||||
* The maximum number of bytes that can be accessed as part of one
|
||||
* operation, including metadata.
|
||||
*/
|
||||
#define DMU_MAX_ACCESS (10<<20) /* 10MB */
|
||||
#define DMU_MAX_DELETEBLKCNT (20480) /* ~5MB of indirect blocks */
|
||||
|
||||
/*
|
||||
* Public routines to create, destroy, open, and close objsets.
|
||||
*/
|
||||
int dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
|
||||
objset_t **osp);
|
||||
int dmu_objset_open_ds(struct dsl_dataset *ds, dmu_objset_type_t type,
|
||||
objset_t **osp);
|
||||
void dmu_objset_close(objset_t *os);
|
||||
int dmu_objset_evict_dbufs(objset_t *os);
|
||||
int dmu_objset_create(const char *name, dmu_objset_type_t type,
|
||||
objset_t *clone_parent, uint64_t flags,
|
||||
void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
|
||||
int dmu_objset_destroy(const char *name);
|
||||
int dmu_snapshots_destroy(char *fsname, char *snapname);
|
||||
int dmu_objset_rollback(objset_t *os);
|
||||
int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
|
||||
int dmu_objset_rename(const char *name, const char *newname,
|
||||
boolean_t recursive);
|
||||
int dmu_objset_find(char *name, int func(char *, void *), void *arg,
|
||||
int flags);
|
||||
void dmu_objset_byteswap(void *buf, size_t size);
|
||||
|
||||
typedef struct dmu_buf {
|
||||
uint64_t db_object; /* object that this buffer is part of */
|
||||
uint64_t db_offset; /* byte offset in this object */
|
||||
uint64_t db_size; /* size of buffer in bytes */
|
||||
void *db_data; /* data in buffer */
|
||||
} dmu_buf_t;
|
||||
|
||||
typedef void dmu_buf_evict_func_t(struct dmu_buf *db, void *user_ptr);
|
||||
|
||||
/*
|
||||
* The names of zap entries in the DIRECTORY_OBJECT of the MOS.
|
||||
*/
|
||||
#define DMU_POOL_DIRECTORY_OBJECT 1
|
||||
#define DMU_POOL_CONFIG "config"
|
||||
#define DMU_POOL_ROOT_DATASET "root_dataset"
|
||||
#define DMU_POOL_SYNC_BPLIST "sync_bplist"
|
||||
#define DMU_POOL_ERRLOG_SCRUB "errlog_scrub"
|
||||
#define DMU_POOL_ERRLOG_LAST "errlog_last"
|
||||
#define DMU_POOL_SPARES "spares"
|
||||
#define DMU_POOL_DEFLATE "deflate"
|
||||
#define DMU_POOL_HISTORY "history"
|
||||
#define DMU_POOL_PROPS "pool_props"
|
||||
#define DMU_POOL_L2CACHE "l2cache"
|
||||
|
||||
/* 4x8 zbookmark_t */
|
||||
#define DMU_POOL_SCRUB_BOOKMARK "scrub_bookmark"
|
||||
/* 1x8 zap obj DMU_OT_SCRUB_QUEUE */
|
||||
#define DMU_POOL_SCRUB_QUEUE "scrub_queue"
|
||||
/* 1x8 txg */
|
||||
#define DMU_POOL_SCRUB_MIN_TXG "scrub_min_txg"
|
||||
/* 1x8 txg */
|
||||
#define DMU_POOL_SCRUB_MAX_TXG "scrub_max_txg"
|
||||
/* 1x4 enum scrub_func */
|
||||
#define DMU_POOL_SCRUB_FUNC "scrub_func"
|
||||
/* 1x8 count */
|
||||
#define DMU_POOL_SCRUB_ERRORS "scrub_errors"
|
||||
|
||||
/*
|
||||
* Allocate an object from this objset. The range of object numbers
|
||||
* available is (0, DN_MAX_OBJECT). Object 0 is the meta-dnode.
|
||||
*
|
||||
* The transaction must be assigned to a txg. The newly allocated
|
||||
* object will be "held" in the transaction (ie. you can modify the
|
||||
* newly allocated object in this transaction).
|
||||
*
|
||||
* dmu_object_alloc() chooses an object and returns it in *objectp.
|
||||
*
|
||||
* dmu_object_claim() allocates a specific object number. If that
|
||||
* number is already allocated, it fails and returns EEXIST.
|
||||
*
|
||||
* Return 0 on success, or ENOSPC or EEXIST as specified above.
|
||||
*/
|
||||
uint64_t dmu_object_alloc(objset_t *os, dmu_object_type_t ot,
|
||||
int blocksize, dmu_object_type_t bonus_type, int bonus_len, dmu_tx_t *tx);
|
||||
int dmu_object_claim(objset_t *os, uint64_t object, dmu_object_type_t ot,
|
||||
int blocksize, dmu_object_type_t bonus_type, int bonus_len, dmu_tx_t *tx);
|
||||
int dmu_object_reclaim(objset_t *os, uint64_t object, dmu_object_type_t ot,
|
||||
int blocksize, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Free an object from this objset.
|
||||
*
|
||||
* The object's data will be freed as well (ie. you don't need to call
|
||||
* dmu_free(object, 0, -1, tx)).
|
||||
*
|
||||
* The object need not be held in the transaction.
|
||||
*
|
||||
* If there are any holds on this object's buffers (via dmu_buf_hold()),
|
||||
* or tx holds on the object (via dmu_tx_hold_object()), you can not
|
||||
* free it; it fails and returns EBUSY.
|
||||
*
|
||||
* If the object is not allocated, it fails and returns ENOENT.
|
||||
*
|
||||
* Return 0 on success, or EBUSY or ENOENT as specified above.
|
||||
*/
|
||||
int dmu_object_free(objset_t *os, uint64_t object, dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Find the next allocated or free object.
|
||||
*
|
||||
* The objectp parameter is in-out. It will be updated to be the next
|
||||
* object which is allocated. Ignore objects which have not been
|
||||
* modified since txg.
|
||||
*
|
||||
* XXX Can only be called on a objset with no dirty data.
|
||||
*
|
||||
* Returns 0 on success, or ENOENT if there are no more objects.
|
||||
*/
|
||||
int dmu_object_next(objset_t *os, uint64_t *objectp,
|
||||
boolean_t hole, uint64_t txg);
|
||||
|
||||
/*
|
||||
* Set the data blocksize for an object.
|
||||
*
|
||||
* The object cannot have any blocks allcated beyond the first. If
|
||||
* the first block is allocated already, the new size must be greater
|
||||
* than the current block size. If these conditions are not met,
|
||||
* ENOTSUP will be returned.
|
||||
*
|
||||
* Returns 0 on success, or EBUSY if there are any holds on the object
|
||||
* contents, or ENOTSUP as described above.
|
||||
*/
|
||||
int dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size,
|
||||
int ibs, dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Set the checksum property on a dnode. The new checksum algorithm will
|
||||
* apply to all newly written blocks; existing blocks will not be affected.
|
||||
*/
|
||||
void dmu_object_set_checksum(objset_t *os, uint64_t object, uint8_t checksum,
|
||||
dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Set the compress property on a dnode. The new compression algorithm will
|
||||
* apply to all newly written blocks; existing blocks will not be affected.
|
||||
*/
|
||||
void dmu_object_set_compress(objset_t *os, uint64_t object, uint8_t compress,
|
||||
dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Decide how many copies of a given block we should make. Can be from
|
||||
* 1 to SPA_DVAS_PER_BP.
|
||||
*/
|
||||
int dmu_get_replication_level(struct objset_impl *, struct zbookmark *zb,
|
||||
dmu_object_type_t ot);
|
||||
/*
|
||||
* The bonus data is accessed more or less like a regular buffer.
|
||||
* You must dmu_bonus_hold() to get the buffer, which will give you a
|
||||
* dmu_buf_t with db_offset==-1ULL, and db_size = the size of the bonus
|
||||
* data. As with any normal buffer, you must call dmu_buf_read() to
|
||||
* read db_data, dmu_buf_will_dirty() before modifying it, and the
|
||||
* object must be held in an assigned transaction before calling
|
||||
* dmu_buf_will_dirty. You may use dmu_buf_set_user() on the bonus
|
||||
* buffer as well. You must release your hold with dmu_buf_rele().
|
||||
*/
|
||||
int dmu_bonus_hold(objset_t *os, uint64_t object, void *tag, dmu_buf_t **);
|
||||
int dmu_bonus_max(void);
|
||||
int dmu_set_bonus(dmu_buf_t *, int, dmu_tx_t *);
|
||||
|
||||
/*
|
||||
* Obtain the DMU buffer from the specified object which contains the
|
||||
* specified offset. dmu_buf_hold() puts a "hold" on the buffer, so
|
||||
* that it will remain in memory. You must release the hold with
|
||||
* dmu_buf_rele(). You musn't access the dmu_buf_t after releasing your
|
||||
* hold. You must have a hold on any dmu_buf_t* you pass to the DMU.
|
||||
*
|
||||
* You must call dmu_buf_read, dmu_buf_will_dirty, or dmu_buf_will_fill
|
||||
* on the returned buffer before reading or writing the buffer's
|
||||
* db_data. The comments for those routines describe what particular
|
||||
* operations are valid after calling them.
|
||||
*
|
||||
* The object number must be a valid, allocated object number.
|
||||
*/
|
||||
int dmu_buf_hold(objset_t *os, uint64_t object, uint64_t offset,
|
||||
void *tag, dmu_buf_t **);
|
||||
void dmu_buf_add_ref(dmu_buf_t *db, void* tag);
|
||||
void dmu_buf_rele(dmu_buf_t *db, void *tag);
|
||||
uint64_t dmu_buf_refcount(dmu_buf_t *db);
|
||||
|
||||
/*
|
||||
* dmu_buf_hold_array holds the DMU buffers which contain all bytes in a
|
||||
* range of an object. A pointer to an array of dmu_buf_t*'s is
|
||||
* returned (in *dbpp).
|
||||
*
|
||||
* dmu_buf_rele_array releases the hold on an array of dmu_buf_t*'s, and
|
||||
* frees the array. The hold on the array of buffers MUST be released
|
||||
* with dmu_buf_rele_array. You can NOT release the hold on each buffer
|
||||
* individually with dmu_buf_rele.
|
||||
*/
|
||||
int dmu_buf_hold_array_by_bonus(dmu_buf_t *db, uint64_t offset,
|
||||
uint64_t length, int read, void *tag, int *numbufsp, dmu_buf_t ***dbpp);
|
||||
void dmu_buf_rele_array(dmu_buf_t **, int numbufs, void *tag);
|
||||
|
||||
/*
|
||||
* Returns NULL on success, or the existing user ptr if it's already
|
||||
* been set.
|
||||
*
|
||||
* user_ptr is for use by the user and can be obtained via dmu_buf_get_user().
|
||||
*
|
||||
* user_data_ptr_ptr should be NULL, or a pointer to a pointer which
|
||||
* will be set to db->db_data when you are allowed to access it. Note
|
||||
* that db->db_data (the pointer) can change when you do dmu_buf_read(),
|
||||
* dmu_buf_tryupgrade(), dmu_buf_will_dirty(), or dmu_buf_will_fill().
|
||||
* *user_data_ptr_ptr will be set to the new value when it changes.
|
||||
*
|
||||
* If non-NULL, pageout func will be called when this buffer is being
|
||||
* excised from the cache, so that you can clean up the data structure
|
||||
* pointed to by user_ptr.
|
||||
*
|
||||
* dmu_evict_user() will call the pageout func for all buffers in a
|
||||
* objset with a given pageout func.
|
||||
*/
|
||||
void *dmu_buf_set_user(dmu_buf_t *db, void *user_ptr, void *user_data_ptr_ptr,
|
||||
dmu_buf_evict_func_t *pageout_func);
|
||||
/*
|
||||
* set_user_ie is the same as set_user, but request immediate eviction
|
||||
* when hold count goes to zero.
|
||||
*/
|
||||
void *dmu_buf_set_user_ie(dmu_buf_t *db, void *user_ptr,
|
||||
void *user_data_ptr_ptr, dmu_buf_evict_func_t *pageout_func);
|
||||
void *dmu_buf_update_user(dmu_buf_t *db_fake, void *old_user_ptr,
|
||||
void *user_ptr, void *user_data_ptr_ptr,
|
||||
dmu_buf_evict_func_t *pageout_func);
|
||||
void dmu_evict_user(objset_t *os, dmu_buf_evict_func_t *func);
|
||||
|
||||
/*
|
||||
* Returns the user_ptr set with dmu_buf_set_user(), or NULL if not set.
|
||||
*/
|
||||
void *dmu_buf_get_user(dmu_buf_t *db);
|
||||
|
||||
/*
|
||||
* Indicate that you are going to modify the buffer's data (db_data).
|
||||
*
|
||||
* The transaction (tx) must be assigned to a txg (ie. you've called
|
||||
* dmu_tx_assign()). The buffer's object must be held in the tx
|
||||
* (ie. you've called dmu_tx_hold_object(tx, db->db_object)).
|
||||
*/
|
||||
void dmu_buf_will_dirty(dmu_buf_t *db, dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* You must create a transaction, then hold the objects which you will
|
||||
* (or might) modify as part of this transaction. Then you must assign
|
||||
* the transaction to a transaction group. Once the transaction has
|
||||
* been assigned, you can modify buffers which belong to held objects as
|
||||
* part of this transaction. You can't modify buffers before the
|
||||
* transaction has been assigned; you can't modify buffers which don't
|
||||
* belong to objects which this transaction holds; you can't hold
|
||||
* objects once the transaction has been assigned. You may hold an
|
||||
* object which you are going to free (with dmu_object_free()), but you
|
||||
* don't have to.
|
||||
*
|
||||
* You can abort the transaction before it has been assigned.
|
||||
*
|
||||
* Note that you may hold buffers (with dmu_buf_hold) at any time,
|
||||
* regardless of transaction state.
|
||||
*/
|
||||
|
||||
#define DMU_NEW_OBJECT (-1ULL)
|
||||
#define DMU_OBJECT_END (-1ULL)
|
||||
|
||||
dmu_tx_t *dmu_tx_create(objset_t *os);
|
||||
void dmu_tx_hold_write(dmu_tx_t *tx, uint64_t object, uint64_t off, int len);
|
||||
void dmu_tx_hold_free(dmu_tx_t *tx, uint64_t object, uint64_t off,
|
||||
uint64_t len);
|
||||
void dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, char *name);
|
||||
void dmu_tx_hold_bonus(dmu_tx_t *tx, uint64_t object);
|
||||
void dmu_tx_abort(dmu_tx_t *tx);
|
||||
int dmu_tx_assign(dmu_tx_t *tx, uint64_t txg_how);
|
||||
void dmu_tx_wait(dmu_tx_t *tx);
|
||||
void dmu_tx_commit(dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Free up the data blocks for a defined range of a file. If size is
|
||||
* zero, the range from offset to end-of-file is freed.
|
||||
*/
|
||||
int dmu_free_range(objset_t *os, uint64_t object, uint64_t offset,
|
||||
uint64_t size, dmu_tx_t *tx);
|
||||
int dmu_free_long_range(objset_t *os, uint64_t object, uint64_t offset,
|
||||
uint64_t size);
|
||||
int dmu_free_object(objset_t *os, uint64_t object);
|
||||
|
||||
/*
|
||||
* Convenience functions.
|
||||
*
|
||||
* Canfail routines will return 0 on success, or an errno if there is a
|
||||
* nonrecoverable I/O error.
|
||||
*/
|
||||
int dmu_read(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
|
||||
void *buf);
|
||||
void dmu_write(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
|
||||
const void *buf, dmu_tx_t *tx);
|
||||
void dmu_prealloc(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
|
||||
dmu_tx_t *tx);
|
||||
int dmu_read_uio(objset_t *os, uint64_t object, struct uio *uio, uint64_t size);
|
||||
int dmu_write_uio(objset_t *os, uint64_t object, struct uio *uio, uint64_t size,
|
||||
dmu_tx_t *tx);
|
||||
int dmu_write_pages(objset_t *os, uint64_t object, uint64_t offset,
|
||||
uint64_t size, struct page *pp, dmu_tx_t *tx);
|
||||
|
||||
extern int zfs_prefetch_disable;
|
||||
|
||||
/*
|
||||
* Asynchronously try to read in the data.
|
||||
*/
|
||||
void dmu_prefetch(objset_t *os, uint64_t object, uint64_t offset,
|
||||
uint64_t len);
|
||||
|
||||
typedef struct dmu_object_info {
|
||||
/* All sizes are in bytes. */
|
||||
uint32_t doi_data_block_size;
|
||||
uint32_t doi_metadata_block_size;
|
||||
uint64_t doi_bonus_size;
|
||||
dmu_object_type_t doi_type;
|
||||
dmu_object_type_t doi_bonus_type;
|
||||
uint8_t doi_indirection; /* 2 = dnode->indirect->data */
|
||||
uint8_t doi_checksum;
|
||||
uint8_t doi_compress;
|
||||
uint8_t doi_pad[5];
|
||||
/* Values below are number of 512-byte blocks. */
|
||||
uint64_t doi_physical_blks; /* data + metadata */
|
||||
uint64_t doi_max_block_offset;
|
||||
} dmu_object_info_t;
|
||||
|
||||
typedef void arc_byteswap_func_t(void *buf, size_t size);
|
||||
|
||||
typedef struct dmu_object_type_info {
|
||||
arc_byteswap_func_t *ot_byteswap;
|
||||
boolean_t ot_metadata;
|
||||
char *ot_name;
|
||||
} dmu_object_type_info_t;
|
||||
|
||||
extern const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES];
|
||||
|
||||
/*
|
||||
* Get information on a DMU object.
|
||||
*
|
||||
* Return 0 on success or ENOENT if object is not allocated.
|
||||
*
|
||||
* If doi is NULL, just indicates whether the object exists.
|
||||
*/
|
||||
int dmu_object_info(objset_t *os, uint64_t object, dmu_object_info_t *doi);
|
||||
void dmu_object_info_from_dnode(struct dnode *dn, dmu_object_info_t *doi);
|
||||
void dmu_object_info_from_db(dmu_buf_t *db, dmu_object_info_t *doi);
|
||||
void dmu_object_size_from_db(dmu_buf_t *db, uint32_t *blksize,
|
||||
u_longlong_t *nblk512);
|
||||
|
||||
typedef struct dmu_objset_stats {
|
||||
uint64_t dds_num_clones; /* number of clones of this */
|
||||
uint64_t dds_creation_txg;
|
||||
uint64_t dds_guid;
|
||||
dmu_objset_type_t dds_type;
|
||||
uint8_t dds_is_snapshot;
|
||||
uint8_t dds_inconsistent;
|
||||
char dds_origin[MAXNAMELEN];
|
||||
} dmu_objset_stats_t;
|
||||
|
||||
/*
|
||||
* Get stats on a dataset.
|
||||
*/
|
||||
void dmu_objset_fast_stat(objset_t *os, dmu_objset_stats_t *stat);
|
||||
|
||||
/*
|
||||
* Add entries to the nvlist for all the objset's properties. See
|
||||
* zfs_prop_table[] and zfs(1m) for details on the properties.
|
||||
*/
|
||||
void dmu_objset_stats(objset_t *os, struct nvlist *nv);
|
||||
|
||||
/*
|
||||
* Get the space usage statistics for statvfs().
|
||||
*
|
||||
* refdbytes is the amount of space "referenced" by this objset.
|
||||
* availbytes is the amount of space available to this objset, taking
|
||||
* into account quotas & reservations, assuming that no other objsets
|
||||
* use the space first. These values correspond to the 'referenced' and
|
||||
* 'available' properties, described in the zfs(1m) manpage.
|
||||
*
|
||||
* usedobjs and availobjs are the number of objects currently allocated,
|
||||
* and available.
|
||||
*/
|
||||
void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
|
||||
uint64_t *usedobjsp, uint64_t *availobjsp);
|
||||
|
||||
/*
|
||||
* The fsid_guid is a 56-bit ID that can change to avoid collisions.
|
||||
* (Contrast with the ds_guid which is a 64-bit ID that will never
|
||||
* change, so there is a small probability that it will collide.)
|
||||
*/
|
||||
uint64_t dmu_objset_fsid_guid(objset_t *os);
|
||||
|
||||
int dmu_objset_is_snapshot(objset_t *os);
|
||||
|
||||
extern struct spa *dmu_objset_spa(objset_t *os);
|
||||
extern struct zilog *dmu_objset_zil(objset_t *os);
|
||||
extern struct dsl_pool *dmu_objset_pool(objset_t *os);
|
||||
extern struct dsl_dataset *dmu_objset_ds(objset_t *os);
|
||||
extern void dmu_objset_name(objset_t *os, char *buf);
|
||||
extern dmu_objset_type_t dmu_objset_type(objset_t *os);
|
||||
extern uint64_t dmu_objset_id(objset_t *os);
|
||||
extern int dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
|
||||
uint64_t *id, uint64_t *offp, boolean_t *case_conflict);
|
||||
extern int dmu_snapshot_realname(objset_t *os, char *name, char *real,
|
||||
int maxlen, boolean_t *conflict);
|
||||
extern int dmu_dir_list_next(objset_t *os, int namelen, char *name,
|
||||
uint64_t *idp, uint64_t *offp);
|
||||
extern void dmu_objset_set_user(objset_t *os, void *user_ptr);
|
||||
extern void *dmu_objset_get_user(objset_t *os);
|
||||
|
||||
/*
|
||||
* Return the txg number for the given assigned transaction.
|
||||
*/
|
||||
uint64_t dmu_tx_get_txg(dmu_tx_t *tx);
|
||||
|
||||
/*
|
||||
* Synchronous write.
|
||||
* If a parent zio is provided this function initiates a write on the
|
||||
* provided buffer as a child of the parent zio.
|
||||
* In the absence of a parent zio, the write is completed synchronously.
|
||||
* At write completion, blk is filled with the bp of the written block.
|
||||
* Note that while the data covered by this function will be on stable
|
||||
* storage when the write completes this new data does not become a
|
||||
* permanent part of the file until the associated transaction commits.
|
||||
*/
|
||||
typedef void dmu_sync_cb_t(dmu_buf_t *db, void *arg);
|
||||
int dmu_sync(struct zio *zio, dmu_buf_t *db,
|
||||
struct blkptr *bp, uint64_t txg, dmu_sync_cb_t *done, void *arg);
|
||||
|
||||
/*
|
||||
* Find the next hole or data block in file starting at *off
|
||||
* Return found offset in *off. Return ESRCH for end of file.
|
||||
*/
|
||||
int dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole,
|
||||
uint64_t *off);
|
||||
|
||||
/*
|
||||
* Initial setup and final teardown.
|
||||
*/
|
||||
extern void dmu_init(void);
|
||||
extern void dmu_fini(void);
|
||||
|
||||
typedef void (*dmu_traverse_cb_t)(objset_t *os, void *arg, struct blkptr *bp,
|
||||
uint64_t object, uint64_t offset, int len);
|
||||
void dmu_traverse_objset(objset_t *os, uint64_t txg_start,
|
||||
dmu_traverse_cb_t cb, void *arg);
|
||||
|
||||
int dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
|
||||
struct vnode *vp, offset_t *off);
|
||||
|
||||
typedef struct dmu_recv_cookie {
|
||||
/*
|
||||
* This structure is opaque!
|
||||
*
|
||||
* If logical and real are different, we are recving the stream
|
||||
* into the "real" temporary clone, and then switching it with
|
||||
* the "logical" target.
|
||||
*/
|
||||
struct dsl_dataset *drc_logical_ds;
|
||||
struct dsl_dataset *drc_real_ds;
|
||||
struct drr_begin *drc_drrb;
|
||||
char *drc_tosnap;
|
||||
boolean_t drc_newfs;
|
||||
boolean_t drc_force;
|
||||
} dmu_recv_cookie_t;
|
||||
|
||||
int dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *,
|
||||
boolean_t force, objset_t *origin, boolean_t online, dmu_recv_cookie_t *);
|
||||
int dmu_recv_stream(dmu_recv_cookie_t *drc, struct vnode *vp, offset_t *voffp);
|
||||
int dmu_recv_end(dmu_recv_cookie_t *drc);
|
||||
void dmu_recv_abort_cleanup(dmu_recv_cookie_t *drc);
|
||||
|
||||
/* CRC64 table */
|
||||
#define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */
|
||||
extern uint64_t zfs_crc64_table[256];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_DMU_H */
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_DMU_IMPL_H
|
||||
#define _SYS_DMU_IMPL_H
|
||||
|
||||
#include <sys/txg_impl.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the locking strategy for the DMU. Numbers in parenthesis are
|
||||
* cases that use that lock order, referenced below:
|
||||
*
|
||||
* ARC is self-contained
|
||||
* bplist is self-contained
|
||||
* refcount is self-contained
|
||||
* txg is self-contained (hopefully!)
|
||||
* zst_lock
|
||||
* zf_rwlock
|
||||
*
|
||||
* XXX try to improve evicting path?
|
||||
*
|
||||
* dp_config_rwlock > os_obj_lock > dn_struct_rwlock >
|
||||
* dn_dbufs_mtx > hash_mutexes > db_mtx > dd_lock > leafs
|
||||
*
|
||||
* dp_config_rwlock
|
||||
* must be held before: everything
|
||||
* protects dd namespace changes
|
||||
* protects property changes globally
|
||||
* held from:
|
||||
* dsl_dir_open/r:
|
||||
* dsl_dir_create_sync/w:
|
||||
* dsl_dir_sync_destroy/w:
|
||||
* dsl_dir_rename_sync/w:
|
||||
* dsl_prop_changed_notify/r:
|
||||
*
|
||||
* os_obj_lock
|
||||
* must be held before:
|
||||
* everything except dp_config_rwlock
|
||||
* protects os_obj_next
|
||||
* held from:
|
||||
* dmu_object_alloc: dn_dbufs_mtx, db_mtx, hash_mutexes, dn_struct_rwlock
|
||||
*
|
||||
* dn_struct_rwlock
|
||||
* must be held before:
|
||||
* everything except dp_config_rwlock and os_obj_lock
|
||||
* protects structure of dnode (eg. nlevels)
|
||||
* db_blkptr can change when syncing out change to nlevels
|
||||
* dn_maxblkid
|
||||
* dn_nlevels
|
||||
* dn_*blksz*
|
||||
* phys nlevels, maxblkid, physical blkptr_t's (?)
|
||||
* held from:
|
||||
* callers of dbuf_read_impl, dbuf_hold[_impl], dbuf_prefetch
|
||||
* dmu_object_info_from_dnode: dn_dirty_mtx (dn_datablksz)
|
||||
* dmu_tx_count_free:
|
||||
* dbuf_read_impl: db_mtx, dmu_zfetch()
|
||||
* dmu_zfetch: zf_rwlock/r, zst_lock, dbuf_prefetch()
|
||||
* dbuf_new_size: db_mtx
|
||||
* dbuf_dirty: db_mtx
|
||||
* dbuf_findbp: (callers, phys? - the real need)
|
||||
* dbuf_create: dn_dbufs_mtx, hash_mutexes, db_mtx (phys?)
|
||||
* dbuf_prefetch: dn_dirty_mtx, hash_mutexes, db_mtx, dn_dbufs_mtx
|
||||
* dbuf_hold_impl: hash_mutexes, db_mtx, dn_dbufs_mtx, dbuf_findbp()
|
||||
* dnode_sync/w (increase_indirection): db_mtx (phys)
|
||||
* dnode_set_blksz/w: dn_dbufs_mtx (dn_*blksz*)
|
||||
* dnode_new_blkid/w: (dn_maxblkid)
|
||||
* dnode_free_range/w: dn_dirty_mtx (dn_maxblkid)
|
||||
* dnode_next_offset: (phys)
|
||||
*
|
||||
* dn_dbufs_mtx
|
||||
* must be held before:
|
||||
* db_mtx, hash_mutexes
|
||||
* protects:
|
||||
* dn_dbufs
|
||||
* dn_evicted
|
||||
* held from:
|
||||
* dmu_evict_user: db_mtx (dn_dbufs)
|
||||
* dbuf_free_range: db_mtx (dn_dbufs)
|
||||
* dbuf_remove_ref: db_mtx, callees:
|
||||
* dbuf_hash_remove: hash_mutexes, db_mtx
|
||||
* dbuf_create: hash_mutexes, db_mtx (dn_dbufs)
|
||||
* dnode_set_blksz: (dn_dbufs)
|
||||
*
|
||||
* hash_mutexes (global)
|
||||
* must be held before:
|
||||
* db_mtx
|
||||
* protects dbuf_hash_table (global) and db_hash_next
|
||||
* held from:
|
||||
* dbuf_find: db_mtx
|
||||
* dbuf_hash_insert: db_mtx
|
||||
* dbuf_hash_remove: db_mtx
|
||||
*
|
||||
* db_mtx (meta-leaf)
|
||||
* must be held before:
|
||||
* dn_mtx, dn_dirty_mtx, dd_lock (leaf mutexes)
|
||||
* protects:
|
||||
* db_state
|
||||
* db_holds
|
||||
* db_buf
|
||||
* db_changed
|
||||
* db_data_pending
|
||||
* db_dirtied
|
||||
* db_link
|
||||
* db_dirty_node (??)
|
||||
* db_dirtycnt
|
||||
* db_d.*
|
||||
* db.*
|
||||
* held from:
|
||||
* dbuf_dirty: dn_mtx, dn_dirty_mtx
|
||||
* dbuf_dirty->dsl_dir_willuse_space: dd_lock
|
||||
* dbuf_dirty->dbuf_new_block->dsl_dataset_block_freeable: dd_lock
|
||||
* dbuf_undirty: dn_dirty_mtx (db_d)
|
||||
* dbuf_write_done: dn_dirty_mtx (db_state)
|
||||
* dbuf_*
|
||||
* dmu_buf_update_user: none (db_d)
|
||||
* dmu_evict_user: none (db_d) (maybe can eliminate)
|
||||
* dbuf_find: none (db_holds)
|
||||
* dbuf_hash_insert: none (db_holds)
|
||||
* dmu_buf_read_array_impl: none (db_state, db_changed)
|
||||
* dmu_sync: none (db_dirty_node, db_d)
|
||||
* dnode_reallocate: none (db)
|
||||
*
|
||||
* dn_mtx (leaf)
|
||||
* protects:
|
||||
* dn_dirty_dbufs
|
||||
* dn_ranges
|
||||
* phys accounting
|
||||
* dn_allocated_txg
|
||||
* dn_free_txg
|
||||
* dn_assigned_txg
|
||||
* dd_assigned_tx
|
||||
* dn_notxholds
|
||||
* dn_dirtyctx
|
||||
* dn_dirtyctx_firstset
|
||||
* (dn_phys copy fields?)
|
||||
* (dn_phys contents?)
|
||||
* held from:
|
||||
* dnode_*
|
||||
* dbuf_dirty: none
|
||||
* dbuf_sync: none (phys accounting)
|
||||
* dbuf_undirty: none (dn_ranges, dn_dirty_dbufs)
|
||||
* dbuf_write_done: none (phys accounting)
|
||||
* dmu_object_info_from_dnode: none (accounting)
|
||||
* dmu_tx_commit: none
|
||||
* dmu_tx_hold_object_impl: none
|
||||
* dmu_tx_try_assign: dn_notxholds(cv)
|
||||
* dmu_tx_unassign: none
|
||||
*
|
||||
* dd_lock
|
||||
* must be held before:
|
||||
* ds_lock
|
||||
* ancestors' dd_lock
|
||||
* protects:
|
||||
* dd_prop_cbs
|
||||
* dd_sync_*
|
||||
* dd_used_bytes
|
||||
* dd_tempreserved
|
||||
* dd_space_towrite
|
||||
* dd_myname
|
||||
* dd_phys accounting?
|
||||
* held from:
|
||||
* dsl_dir_*
|
||||
* dsl_prop_changed_notify: none (dd_prop_cbs)
|
||||
* dsl_prop_register: none (dd_prop_cbs)
|
||||
* dsl_prop_unregister: none (dd_prop_cbs)
|
||||
* dsl_dataset_block_freeable: none (dd_sync_*)
|
||||
*
|
||||
* os_lock (leaf)
|
||||
* protects:
|
||||
* os_dirty_dnodes
|
||||
* os_free_dnodes
|
||||
* os_dnodes
|
||||
* os_downgraded_dbufs
|
||||
* dn_dirtyblksz
|
||||
* dn_dirty_link
|
||||
* held from:
|
||||
* dnode_create: none (os_dnodes)
|
||||
* dnode_destroy: none (os_dnodes)
|
||||
* dnode_setdirty: none (dn_dirtyblksz, os_*_dnodes)
|
||||
* dnode_free: none (dn_dirtyblksz, os_*_dnodes)
|
||||
*
|
||||
* ds_lock
|
||||
* protects:
|
||||
* ds_user_ptr
|
||||
* ds_user_evice_func
|
||||
* ds_open_refcount
|
||||
* ds_snapname
|
||||
* ds_phys accounting
|
||||
* ds_reserved
|
||||
* held from:
|
||||
* dsl_dataset_*
|
||||
*
|
||||
* dr_mtx (leaf)
|
||||
* protects:
|
||||
* dr_children
|
||||
* held from:
|
||||
* dbuf_dirty
|
||||
* dbuf_undirty
|
||||
* dbuf_sync_indirect
|
||||
* dnode_new_blkid
|
||||
*/
|
||||
|
||||
struct objset;
|
||||
struct dmu_pool;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_DMU_IMPL_H */
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_DMU_OBJSET_H
|
||||
#define _SYS_DMU_OBJSET_H
|
||||
|
||||
#include <sys/spa.h>
|
||||
#include <sys/arc.h>
|
||||
#include <sys/txg.h>
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/zio.h>
|
||||
#include <sys/zil.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dsl_dataset;
|
||||
struct dmu_tx;
|
||||
struct objset_impl;
|
||||
|
||||
typedef struct objset_phys {
|
||||
dnode_phys_t os_meta_dnode;
|
||||
zil_header_t os_zil_header;
|
||||
uint64_t os_type;
|
||||
char os_pad[1024 - sizeof (dnode_phys_t) - sizeof (zil_header_t) -
|
||||
sizeof (uint64_t)];
|
||||
} objset_phys_t;
|
||||
|
||||
struct objset {
|
||||
struct objset_impl *os;
|
||||
int os_mode;
|
||||
};
|
||||
|
||||
typedef struct objset_impl {
|
||||
/* Immutable: */
|
||||
struct dsl_dataset *os_dsl_dataset;
|
||||
spa_t *os_spa;
|
||||
arc_buf_t *os_phys_buf;
|
||||
objset_phys_t *os_phys;
|
||||
dnode_t *os_meta_dnode;
|
||||
zilog_t *os_zil;
|
||||
objset_t os;
|
||||
uint8_t os_checksum; /* can change, under dsl_dir's locks */
|
||||
uint8_t os_compress; /* can change, under dsl_dir's locks */
|
||||
uint8_t os_copies; /* can change, under dsl_dir's locks */
|
||||
uint8_t os_primary_cache; /* can change, under dsl_dir's locks */
|
||||
uint8_t os_secondary_cache; /* can change, under dsl_dir's locks */
|
||||
|
||||
/* no lock needed: */
|
||||
struct dmu_tx *os_synctx; /* XXX sketchy */
|
||||
blkptr_t *os_rootbp;
|
||||
zil_header_t os_zil_header;
|
||||
|
||||
/* Protected by os_obj_lock */
|
||||
kmutex_t os_obj_lock;
|
||||
uint64_t os_obj_next;
|
||||
|
||||
/* Protected by os_lock */
|
||||
kmutex_t os_lock;
|
||||
list_t os_dirty_dnodes[TXG_SIZE];
|
||||
list_t os_free_dnodes[TXG_SIZE];
|
||||
list_t os_dnodes;
|
||||
list_t os_downgraded_dbufs;
|
||||
|
||||
/* stuff we store for the user */
|
||||
kmutex_t os_user_ptr_lock;
|
||||
void *os_user_ptr;
|
||||
} objset_impl_t;
|
||||
|
||||
#define DMU_META_DNODE_OBJECT 0
|
||||
|
||||
#define DMU_OS_IS_L2CACHEABLE(os) \
|
||||
((os)->os_secondary_cache == ZFS_CACHE_ALL || \
|
||||
(os)->os_secondary_cache == ZFS_CACHE_METADATA)
|
||||
|
||||
/* called from zpl */
|
||||
int dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
|
||||
objset_t **osp);
|
||||
void dmu_objset_close(objset_t *os);
|
||||
int dmu_objset_create(const char *name, dmu_objset_type_t type,
|
||||
objset_t *clone_parent, uint64_t flags,
|
||||
void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
|
||||
int dmu_objset_destroy(const char *name);
|
||||
int dmu_objset_rollback(objset_t *os);
|
||||
int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
|
||||
void dmu_objset_stats(objset_t *os, nvlist_t *nv);
|
||||
void dmu_objset_fast_stat(objset_t *os, dmu_objset_stats_t *stat);
|
||||
void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
|
||||
uint64_t *usedobjsp, uint64_t *availobjsp);
|
||||
uint64_t dmu_objset_fsid_guid(objset_t *os);
|
||||
int dmu_objset_find(char *name, int func(char *, void *), void *arg,
|
||||
int flags);
|
||||
int dmu_objset_find_spa(spa_t *spa, const char *name,
|
||||
int func(spa_t *, uint64_t, const char *, void *), void *arg, int flags);
|
||||
int dmu_objset_prefetch(char *name, void *arg);
|
||||
void dmu_objset_byteswap(void *buf, size_t size);
|
||||
int dmu_objset_evict_dbufs(objset_t *os);
|
||||
|
||||
/* called from dsl */
|
||||
void dmu_objset_sync(objset_impl_t *os, zio_t *zio, dmu_tx_t *tx);
|
||||
objset_impl_t *dmu_objset_create_impl(spa_t *spa, struct dsl_dataset *ds,
|
||||
blkptr_t *bp, dmu_objset_type_t type, dmu_tx_t *tx);
|
||||
int dmu_objset_open_impl(spa_t *spa, struct dsl_dataset *ds, blkptr_t *bp,
|
||||
objset_impl_t **osip);
|
||||
void dmu_objset_evict(struct dsl_dataset *ds, void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_DMU_OBJSET_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue