mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-21 18:26:47 +03:00
Move the world out of /zfs/ and seperate out module build tree
This commit is contained in:
+2418
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,664 @@
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* zdump 7.24
|
||||
* Taken from elsie.nci.nih.gov to replace the existing Solaris zdump,
|
||||
* which was based on an earlier version of the elsie code.
|
||||
*
|
||||
* For zdump 7.24, the following changes were made to the elsie code:
|
||||
* locale/textdomain/messages to match existing Solaris style.
|
||||
* Solaris verbose mode is documented to display the current time first.
|
||||
* cstyle cleaned code.
|
||||
* removed old locale/textdomain code.
|
||||
*/
|
||||
|
||||
static char elsieid[] = "@(#)zdump.c 7.74";
|
||||
|
||||
/*
|
||||
* This code has been made independent of the rest of the time
|
||||
* conversion package to increase confidence in the verification it provides.
|
||||
* You can use this code to help in verifying other implementations.
|
||||
*/
|
||||
|
||||
#include "stdio.h" /* for stdout, stderr, perror */
|
||||
#include "string.h" /* for strcpy */
|
||||
#include "sys/types.h" /* for time_t */
|
||||
#include "time.h" /* for struct tm */
|
||||
#include "stdlib.h" /* for exit, malloc, atoi */
|
||||
#include "locale.h" /* for setlocale, textdomain */
|
||||
#include "libintl.h"
|
||||
#include <ctype.h>
|
||||
#include "tzfile.h" /* for defines */
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef ZDUMP_LO_YEAR
|
||||
#define ZDUMP_LO_YEAR (-500)
|
||||
#endif /* !defined ZDUMP_LO_YEAR */
|
||||
|
||||
#ifndef ZDUMP_HI_YEAR
|
||||
#define ZDUMP_HI_YEAR 2500
|
||||
#endif /* !defined ZDUMP_HI_YEAR */
|
||||
|
||||
#ifndef MAX_STRING_LENGTH
|
||||
#define MAX_STRING_LENGTH 1024
|
||||
#endif /* !defined MAX_STRING_LENGTH */
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif /* !defined TRUE */
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif /* !defined FALSE */
|
||||
|
||||
#ifndef isleap_sum
|
||||
/*
|
||||
* See tzfile.h for details on isleap_sum.
|
||||
*/
|
||||
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
|
||||
#endif /* !defined isleap_sum */
|
||||
|
||||
#ifndef SECSPERDAY
|
||||
#define SECSPERDAY ((long)SECSPERHOUR * HOURSPERDAY)
|
||||
#endif
|
||||
#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
|
||||
#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
|
||||
|
||||
#ifndef GNUC_or_lint
|
||||
#ifdef lint
|
||||
#define GNUC_or_lint
|
||||
#else /* !defined lint */
|
||||
#ifdef __GNUC__
|
||||
#define GNUC_or_lint
|
||||
#endif /* defined __GNUC__ */
|
||||
#endif /* !defined lint */
|
||||
#endif /* !defined GNUC_or_lint */
|
||||
|
||||
#ifndef INITIALIZE
|
||||
#ifdef GNUC_or_lint
|
||||
#define INITIALIZE(x) ((x) = 0)
|
||||
#else /* !defined GNUC_or_lint */
|
||||
#define INITIALIZE(x)
|
||||
#endif /* !defined GNUC_or_lint */
|
||||
#endif /* !defined INITIALIZE */
|
||||
|
||||
static time_t absolute_min_time;
|
||||
static time_t absolute_max_time;
|
||||
static size_t longest;
|
||||
static char *progname;
|
||||
static int warned;
|
||||
|
||||
static char *abbr(struct tm *);
|
||||
static void abbrok(const char *, const char *);
|
||||
static long delta(struct tm *, struct tm *);
|
||||
static void dumptime(const struct tm *);
|
||||
static time_t hunt(char *, time_t, time_t);
|
||||
static void setabsolutes(void);
|
||||
static void show(char *, time_t, int);
|
||||
static void usage(void);
|
||||
static const char *tformat(void);
|
||||
static time_t yeartot(long y);
|
||||
|
||||
#ifndef TYPECHECK
|
||||
#define my_localtime localtime
|
||||
#else /* !defined TYPECHECK */
|
||||
static struct tm *
|
||||
my_localtime(tp)
|
||||
time_t *tp;
|
||||
{
|
||||
register struct tm *tmp;
|
||||
|
||||
tmp = localtime(tp);
|
||||
if (tp != NULL && tmp != NULL) {
|
||||
struct tm tm;
|
||||
register time_t t;
|
||||
|
||||
tm = *tmp;
|
||||
t = mktime(&tm);
|
||||
if (t - *tp >= 1 || *tp - t >= 1) {
|
||||
(void) fflush(stdout);
|
||||
(void) fprintf(stderr, "\n%s: ", progname);
|
||||
(void) fprintf(stderr, tformat(), *tp);
|
||||
(void) fprintf(stderr, " ->");
|
||||
(void) fprintf(stderr, " year=%d", tmp->tm_year);
|
||||
(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
|
||||
(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
|
||||
(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
|
||||
(void) fprintf(stderr, " min=%d", tmp->tm_min);
|
||||
(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
|
||||
(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
|
||||
(void) fprintf(stderr, " -> ");
|
||||
(void) fprintf(stderr, tformat(), t);
|
||||
(void) fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
return (tmp);
|
||||
}
|
||||
#endif /* !defined TYPECHECK */
|
||||
|
||||
static void
|
||||
abbrok(abbrp, zone)
|
||||
const char * const abbrp;
|
||||
const char * const zone;
|
||||
{
|
||||
register const char *cp;
|
||||
int error = 0;
|
||||
|
||||
if (warned)
|
||||
return;
|
||||
cp = abbrp;
|
||||
while (isascii(*cp) && isalpha((unsigned char)*cp))
|
||||
++cp;
|
||||
(void) fflush(stdout);
|
||||
if (cp - abbrp == 0) {
|
||||
/*
|
||||
* TRANSLATION_NOTE
|
||||
* The first %s prints for the program name (zdump),
|
||||
* the second %s prints the timezone name, the third
|
||||
* %s prints the timezone abbreviation (tzname[0] or
|
||||
* tzname[1]).
|
||||
*/
|
||||
(void) fprintf(stderr, gettext("%s: warning: zone \"%s\" "
|
||||
"abbreviation \"%s\" lacks alphabetic at start\n"),
|
||||
progname, zone, abbrp);
|
||||
error = 1;
|
||||
} else if (cp - abbrp < 3) {
|
||||
(void) fprintf(stderr, gettext("%s: warning: zone \"%s\" "
|
||||
"abbreviation \"%s\" has fewer than 3 alphabetics\n"),
|
||||
progname, zone, abbrp);
|
||||
error = 1;
|
||||
} else if (cp - abbrp > 6) {
|
||||
(void) fprintf(stderr, gettext("%s: warning: zone \"%s\" "
|
||||
"abbreviation \"%s\" has more than 6 alphabetics\n"),
|
||||
progname, zone, abbrp);
|
||||
error = 1;
|
||||
}
|
||||
if (error == 0 && (*cp == '+' || *cp == '-')) {
|
||||
++cp;
|
||||
if (isascii(*cp) && isdigit((unsigned char)*cp))
|
||||
if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
|
||||
++cp;
|
||||
if (*cp != '\0') {
|
||||
(void) fprintf(stderr, gettext("%s: warning: "
|
||||
"zone \"%s\" abbreviation \"%s\" differs from "
|
||||
"POSIX standard\n"), progname, zone, abbrp);
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
if (error)
|
||||
warned = TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
register int i;
|
||||
register int c;
|
||||
register int vflag;
|
||||
register char *cutarg;
|
||||
register long cutloyear = ZDUMP_LO_YEAR;
|
||||
register long cuthiyear = ZDUMP_HI_YEAR;
|
||||
register time_t cutlotime;
|
||||
register time_t cuthitime;
|
||||
time_t now;
|
||||
time_t t;
|
||||
time_t newt;
|
||||
struct tm tm;
|
||||
struct tm newtm;
|
||||
register struct tm *tmp;
|
||||
register struct tm *newtmp;
|
||||
|
||||
INITIALIZE(cutlotime);
|
||||
INITIALIZE(cuthitime);
|
||||
|
||||
(void) setlocale(LC_ALL, "");
|
||||
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
|
||||
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
|
||||
#endif
|
||||
(void) textdomain(TEXT_DOMAIN);
|
||||
|
||||
progname = argv[0];
|
||||
for (i = 1; i < argc; ++i)
|
||||
if (strcmp(argv[i], "--version") == 0) {
|
||||
(void) printf("%s\n", elsieid);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
vflag = 0;
|
||||
cutarg = NULL;
|
||||
while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
|
||||
if (c == 'v')
|
||||
vflag = 1;
|
||||
else cutarg = optarg;
|
||||
if (c != EOF ||
|
||||
(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
if (vflag) {
|
||||
if (cutarg != NULL) {
|
||||
long lo;
|
||||
long hi;
|
||||
char dummy;
|
||||
|
||||
if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
|
||||
cuthiyear = hi;
|
||||
} else if (sscanf(cutarg, "%ld,%ld%c",
|
||||
&lo, &hi, &dummy) == 2) {
|
||||
cutloyear = lo;
|
||||
cuthiyear = hi;
|
||||
} else {
|
||||
(void) fprintf(stderr, gettext("%s: wild -c argument %s\n"),
|
||||
progname, cutarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
setabsolutes();
|
||||
cutlotime = yeartot(cutloyear);
|
||||
cuthitime = yeartot(cuthiyear);
|
||||
}
|
||||
(void) time(&now);
|
||||
longest = 0;
|
||||
for (i = optind; i < argc; ++i)
|
||||
if (strlen(argv[i]) > longest)
|
||||
longest = strlen(argv[i]);
|
||||
|
||||
for (i = optind; i < argc; ++i) {
|
||||
static char buf[MAX_STRING_LENGTH];
|
||||
static char *tzp = NULL;
|
||||
|
||||
(void) unsetenv("TZ");
|
||||
if (tzp != NULL)
|
||||
free(tzp);
|
||||
if ((tzp = malloc(3 + strlen(argv[i]) + 1)) == NULL) {
|
||||
perror(progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
(void) strcpy(tzp, "TZ=");
|
||||
(void) strcat(tzp, argv[i]);
|
||||
if (putenv(tzp) != 0) {
|
||||
perror(progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!vflag) {
|
||||
show(argv[i], now, FALSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(sun)
|
||||
/*
|
||||
* We show the current time first, probably because we froze
|
||||
* the behavior of zdump some time ago and then it got
|
||||
* changed.
|
||||
*/
|
||||
show(argv[i], now, TRUE);
|
||||
#endif
|
||||
warned = FALSE;
|
||||
t = absolute_min_time;
|
||||
show(argv[i], t, TRUE);
|
||||
t += SECSPERHOUR * HOURSPERDAY;
|
||||
show(argv[i], t, TRUE);
|
||||
if (t < cutlotime)
|
||||
t = cutlotime;
|
||||
tmp = my_localtime(&t);
|
||||
if (tmp != NULL) {
|
||||
tm = *tmp;
|
||||
(void) strncpy(buf, abbr(&tm), sizeof (buf) - 1);
|
||||
}
|
||||
for (;;) {
|
||||
if (t >= cuthitime)
|
||||
break;
|
||||
/* check if newt will overrun maximum time_t value */
|
||||
if (t > LONG_MAX - (SECSPERHOUR * 12))
|
||||
break;
|
||||
newt = t + SECSPERHOUR * 12;
|
||||
if (newt >= cuthitime)
|
||||
break;
|
||||
newtmp = localtime(&newt);
|
||||
if (newtmp != NULL)
|
||||
newtm = *newtmp;
|
||||
if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
|
||||
(delta(&newtm, &tm) != (newt - t) ||
|
||||
newtm.tm_isdst != tm.tm_isdst ||
|
||||
strcmp(abbr(&newtm), buf) != 0)) {
|
||||
newt = hunt(argv[i], t, newt);
|
||||
newtmp = localtime(&newt);
|
||||
if (newtmp != NULL) {
|
||||
newtm = *newtmp;
|
||||
(void) strncpy(buf,
|
||||
abbr(&newtm),
|
||||
sizeof (buf) - 1);
|
||||
}
|
||||
}
|
||||
t = newt;
|
||||
tm = newtm;
|
||||
tmp = newtmp;
|
||||
}
|
||||
t = absolute_max_time;
|
||||
#if defined(sun)
|
||||
show(argv[i], t, TRUE);
|
||||
t -= SECSPERHOUR * HOURSPERDAY;
|
||||
show(argv[i], t, TRUE);
|
||||
#else /* !defined(sun) */
|
||||
t -= SECSPERHOUR * HOURSPERDAY;
|
||||
show(argv[i], t, TRUE);
|
||||
t += SECSPERHOUR * HOURSPERDAY;
|
||||
show(argv[i], t, TRUE);
|
||||
#endif /* !defined(sun) */
|
||||
}
|
||||
if (fflush(stdout) || ferror(stdout)) {
|
||||
(void) fprintf(stderr, "%s: ", progname);
|
||||
(void) perror(gettext("Error writing standard output"));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
setabsolutes()
|
||||
{
|
||||
#if defined(sun)
|
||||
absolute_min_time = LONG_MIN;
|
||||
absolute_max_time = LONG_MAX;
|
||||
#else
|
||||
if (0.5 == (time_t)0.5) {
|
||||
/*
|
||||
* time_t is floating.
|
||||
*/
|
||||
if (sizeof (time_t) == sizeof (float)) {
|
||||
absolute_min_time = (time_t)-FLT_MAX;
|
||||
absolute_max_time = (time_t)FLT_MAX;
|
||||
} else if (sizeof (time_t) == sizeof (double)) {
|
||||
absolute_min_time = (time_t)-DBL_MAX;
|
||||
absolute_max_time = (time_t)DBL_MAX;
|
||||
} else {
|
||||
(void) fprintf(stderr, gettext("%s: use of -v on "
|
||||
"system with floating time_t other than float "
|
||||
"or double\n"), progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else
|
||||
/*CONSTANTCONDITION*/
|
||||
if (0 > (time_t)-1) {
|
||||
/*
|
||||
* time_t is signed.
|
||||
*/
|
||||
register time_t hibit;
|
||||
|
||||
for (hibit = 1; (hibit * 2) != 0; hibit *= 2)
|
||||
continue;
|
||||
absolute_min_time = hibit;
|
||||
absolute_max_time = -(hibit + 1);
|
||||
} else {
|
||||
/*
|
||||
* time_t is unsigned.
|
||||
*/
|
||||
absolute_min_time = 0;
|
||||
absolute_max_time = absolute_min_time - 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static time_t
|
||||
yeartot(y)
|
||||
const long y;
|
||||
{
|
||||
register long myy;
|
||||
register long seconds;
|
||||
register time_t t;
|
||||
|
||||
myy = EPOCH_YEAR;
|
||||
t = 0;
|
||||
while (myy != y) {
|
||||
if (myy < y) {
|
||||
seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
|
||||
++myy;
|
||||
if (t > absolute_max_time - seconds) {
|
||||
t = absolute_max_time;
|
||||
break;
|
||||
}
|
||||
t += seconds;
|
||||
} else {
|
||||
--myy;
|
||||
seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
|
||||
if (t < absolute_min_time + seconds) {
|
||||
t = absolute_min_time;
|
||||
break;
|
||||
}
|
||||
t -= seconds;
|
||||
}
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
static time_t
|
||||
hunt(name, lot, hit)
|
||||
char *name;
|
||||
time_t lot;
|
||||
time_t hit;
|
||||
{
|
||||
time_t t;
|
||||
long diff;
|
||||
struct tm lotm;
|
||||
register struct tm *lotmp;
|
||||
struct tm tm;
|
||||
register struct tm *tmp;
|
||||
char loab[MAX_STRING_LENGTH];
|
||||
|
||||
lotmp = my_localtime(&lot);
|
||||
if (lotmp != NULL) {
|
||||
lotm = *lotmp;
|
||||
(void) strncpy(loab, abbr(&lotm), sizeof (loab) - 1);
|
||||
}
|
||||
for (;;) {
|
||||
diff = (long)(hit - lot);
|
||||
if (diff < 2)
|
||||
break;
|
||||
t = lot;
|
||||
t += diff / 2;
|
||||
if (t <= lot)
|
||||
++t;
|
||||
else if (t >= hit)
|
||||
--t;
|
||||
tmp = my_localtime(&t);
|
||||
if (tmp != NULL)
|
||||
tm = *tmp;
|
||||
if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
|
||||
(delta(&tm, &lotm) == (t - lot) &&
|
||||
tm.tm_isdst == lotm.tm_isdst &&
|
||||
strcmp(abbr(&tm), loab) == 0)) {
|
||||
lot = t;
|
||||
lotm = tm;
|
||||
lotmp = tmp;
|
||||
} else hit = t;
|
||||
}
|
||||
show(name, lot, TRUE);
|
||||
show(name, hit, TRUE);
|
||||
return (hit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Thanks to Paul Eggert for logic used in delta.
|
||||
*/
|
||||
|
||||
static long
|
||||
delta(newp, oldp)
|
||||
struct tm *newp;
|
||||
struct tm *oldp;
|
||||
{
|
||||
register long result;
|
||||
register int tmy;
|
||||
|
||||
if (newp->tm_year < oldp->tm_year)
|
||||
return (-delta(oldp, newp));
|
||||
result = 0;
|
||||
for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
|
||||
result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
|
||||
result += newp->tm_yday - oldp->tm_yday;
|
||||
result *= HOURSPERDAY;
|
||||
result += newp->tm_hour - oldp->tm_hour;
|
||||
result *= MINSPERHOUR;
|
||||
result += newp->tm_min - oldp->tm_min;
|
||||
result *= SECSPERMIN;
|
||||
result += newp->tm_sec - oldp->tm_sec;
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void
|
||||
show(zone, t, v)
|
||||
char *zone;
|
||||
time_t t;
|
||||
int v;
|
||||
{
|
||||
register struct tm *tmp;
|
||||
|
||||
(void) printf("%-*s ", (int)longest, zone);
|
||||
if (v) {
|
||||
tmp = gmtime(&t);
|
||||
if (tmp == NULL) {
|
||||
(void) printf(tformat(), t);
|
||||
} else {
|
||||
dumptime(tmp);
|
||||
(void) printf(" UTC");
|
||||
}
|
||||
(void) printf(" = ");
|
||||
}
|
||||
tmp = my_localtime(&t);
|
||||
dumptime(tmp);
|
||||
if (tmp != NULL) {
|
||||
if (*abbr(tmp) != '\0')
|
||||
(void) printf(" %s", abbr(tmp));
|
||||
if (v) {
|
||||
(void) printf(" isdst=%d", tmp->tm_isdst);
|
||||
#ifdef TM_GMTOFF
|
||||
(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
|
||||
#endif /* defined TM_GMTOFF */
|
||||
}
|
||||
}
|
||||
(void) printf("\n");
|
||||
if (tmp != NULL && *abbr(tmp) != '\0')
|
||||
abbrok(abbr(tmp), zone);
|
||||
}
|
||||
|
||||
static char *
|
||||
abbr(tmp)
|
||||
struct tm *tmp;
|
||||
{
|
||||
register char *result;
|
||||
static char nada;
|
||||
|
||||
if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
|
||||
return (&nada);
|
||||
result = tzname[tmp->tm_isdst];
|
||||
return ((result == NULL) ? &nada : result);
|
||||
}
|
||||
|
||||
/*
|
||||
* The code below can fail on certain theoretical systems;
|
||||
* it works on all known real-world systems as of 2004-12-30.
|
||||
*/
|
||||
|
||||
static const char *
|
||||
tformat()
|
||||
{
|
||||
#if defined(sun)
|
||||
/* time_t is signed long */
|
||||
return ("%ld");
|
||||
#else
|
||||
/*CONSTANTCONDITION*/
|
||||
if (0.5 == (time_t)0.5) { /* floating */
|
||||
/*CONSTANTCONDITION*/
|
||||
if (sizeof (time_t) > sizeof (double))
|
||||
return ("%Lg");
|
||||
return ("%g");
|
||||
}
|
||||
/*CONSTANTCONDITION*/
|
||||
if (0 > (time_t)-1) { /* signed */
|
||||
/*CONSTANTCONDITION*/
|
||||
if (sizeof (time_t) > sizeof (long))
|
||||
return ("%lld");
|
||||
/*CONSTANTCONDITION*/
|
||||
if (sizeof (time_t) > sizeof (int))
|
||||
return ("%ld");
|
||||
return ("%d");
|
||||
}
|
||||
/*CONSTANTCONDITION*/
|
||||
if (sizeof (time_t) > sizeof (unsigned long))
|
||||
return ("%llu");
|
||||
/*CONSTANTCONDITION*/
|
||||
if (sizeof (time_t) > sizeof (unsigned int))
|
||||
return ("%lu");
|
||||
return ("%u");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
dumptime(timeptr)
|
||||
register const struct tm *timeptr;
|
||||
{
|
||||
static const char wday_name[][3] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
static const char mon_name[][3] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
register const char *wn;
|
||||
register const char *mn;
|
||||
register int lead;
|
||||
register int trail;
|
||||
|
||||
if (timeptr == NULL) {
|
||||
(void) printf("NULL");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* The packaged versions of localtime and gmtime never put out-of-range
|
||||
* values in tm_wday or tm_mon, but since this code might be compiled
|
||||
* with other (perhaps experimental) versions, paranoia is in order.
|
||||
*/
|
||||
if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
|
||||
(int)(sizeof (wday_name) / sizeof (wday_name[0])))
|
||||
wn = "???";
|
||||
else wn = wday_name[timeptr->tm_wday];
|
||||
if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
|
||||
(int)(sizeof (mon_name) / sizeof (mon_name[0])))
|
||||
mn = "???";
|
||||
else mn = mon_name[timeptr->tm_mon];
|
||||
(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
|
||||
wn, mn,
|
||||
timeptr->tm_mday, timeptr->tm_hour,
|
||||
timeptr->tm_min, timeptr->tm_sec);
|
||||
#define DIVISOR 10
|
||||
trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
|
||||
lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
|
||||
trail / DIVISOR;
|
||||
trail %= DIVISOR;
|
||||
if (trail < 0 && lead > 0) {
|
||||
trail += DIVISOR;
|
||||
--lead;
|
||||
} else if (lead < 0 && trail > 0) {
|
||||
trail -= DIVISOR;
|
||||
++lead;
|
||||
}
|
||||
if (lead == 0)
|
||||
(void) printf("%d", trail);
|
||||
else
|
||||
(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
|
||||
}
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
(void) fprintf(stderr, gettext(
|
||||
"%s: [ --version ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n"),
|
||||
progname);
|
||||
exit(EXIT_FAILURE);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
@@ -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 */
|
||||
+4229
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,474 @@
|
||||
/*
|
||||
* 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 <libzfs.h>
|
||||
|
||||
#undef verify /* both libzfs.h and zfs_context.h want to define this */
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mntent.h>
|
||||
#include <sys/mnttab.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/vdev_impl.h>
|
||||
|
||||
#include <sys/mkdev.h>
|
||||
|
||||
#include "zinject.h"
|
||||
|
||||
extern void kernel_init(int);
|
||||
extern void kernel_fini(void);
|
||||
|
||||
static int debug;
|
||||
|
||||
static void
|
||||
ziprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!debug)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
(void) vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a full path to a file, translate into a dataset name and a relative
|
||||
* path within the dataset. 'dataset' must be at least MAXNAMELEN characters,
|
||||
* and 'relpath' must be at least MAXPATHLEN characters. We also pass a stat64
|
||||
* buffer, which we need later to get the object ID.
|
||||
*/
|
||||
static int
|
||||
parse_pathname(const char *fullpath, char *dataset, char *relpath,
|
||||
struct stat64 *statbuf)
|
||||
{
|
||||
struct extmnttab mp;
|
||||
FILE *fp;
|
||||
int match;
|
||||
const char *rel;
|
||||
|
||||
if (fullpath[0] != '/') {
|
||||
(void) fprintf(stderr, "invalid object '%s': must be full "
|
||||
"path\n", fullpath);
|
||||
usage();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (strlen(fullpath) >= MAXPATHLEN) {
|
||||
(void) fprintf(stderr, "invalid object; pathname too long\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (stat64(fullpath, statbuf) != 0) {
|
||||
(void) fprintf(stderr, "cannot open '%s': %s\n",
|
||||
fullpath, strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((fp = fopen(MNTTAB, "r")) == NULL) {
|
||||
(void) fprintf(stderr, "cannot open /etc/mnttab\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
match = 0;
|
||||
while (getextmntent(fp, &mp, sizeof (mp)) == 0) {
|
||||
if (makedev(mp.mnt_major, mp.mnt_minor) == statbuf->st_dev) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
(void) fprintf(stderr, "cannot find mountpoint for '%s'\n",
|
||||
fullpath);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (strcmp(mp.mnt_fstype, MNTTYPE_ZFS) != 0) {
|
||||
(void) fprintf(stderr, "invalid path '%s': not a ZFS "
|
||||
"filesystem\n", fullpath);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (strncmp(fullpath, mp.mnt_mountp, strlen(mp.mnt_mountp)) != 0) {
|
||||
(void) fprintf(stderr, "invalid path '%s': mountpoint "
|
||||
"doesn't match path\n", fullpath);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
(void) strcpy(dataset, mp.mnt_special);
|
||||
|
||||
rel = fullpath + strlen(mp.mnt_mountp);
|
||||
if (rel[0] == '/')
|
||||
rel++;
|
||||
(void) strcpy(relpath, rel);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert from a (dataset, path) pair into a (objset, object) pair. Note that
|
||||
* we grab the object number from the inode number, since looking this up via
|
||||
* libzpool is a real pain.
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
object_from_path(const char *dataset, const char *path, struct stat64 *statbuf,
|
||||
zinject_record_t *record)
|
||||
{
|
||||
objset_t *os;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Before doing any libzpool operations, call sync() to ensure that the
|
||||
* on-disk state is consistent with the in-core state.
|
||||
*/
|
||||
sync();
|
||||
|
||||
if ((err = dmu_objset_open(dataset, DMU_OST_ZFS,
|
||||
DS_MODE_USER | DS_MODE_READONLY, &os)) != 0) {
|
||||
(void) fprintf(stderr, "cannot open dataset '%s': %s\n",
|
||||
dataset, strerror(err));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
record->zi_objset = dmu_objset_id(os);
|
||||
record->zi_object = statbuf->st_ino;
|
||||
|
||||
dmu_objset_close(os);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the real range based on the type, level, and range given.
|
||||
*/
|
||||
static int
|
||||
calculate_range(const char *dataset, err_type_t type, int level, char *range,
|
||||
zinject_record_t *record)
|
||||
{
|
||||
objset_t *os = NULL;
|
||||
dnode_t *dn = NULL;
|
||||
int err;
|
||||
int ret = -1;
|
||||
|
||||
/*
|
||||
* Determine the numeric range from the string.
|
||||
*/
|
||||
if (range == NULL) {
|
||||
/*
|
||||
* If range is unspecified, set the range to [0,-1], which
|
||||
* indicates that the whole object should be treated as an
|
||||
* error.
|
||||
*/
|
||||
record->zi_start = 0;
|
||||
record->zi_end = -1ULL;
|
||||
} else {
|
||||
char *end;
|
||||
|
||||
/* XXX add support for suffixes */
|
||||
record->zi_start = strtoull(range, &end, 10);
|
||||
|
||||
|
||||
if (*end == '\0')
|
||||
record->zi_end = record->zi_start + 1;
|
||||
else if (*end == ',')
|
||||
record->zi_end = strtoull(end + 1, &end, 10);
|
||||
|
||||
if (*end != '\0') {
|
||||
(void) fprintf(stderr, "invalid range '%s': must be "
|
||||
"a numeric range of the form 'start[,end]'\n",
|
||||
range);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TYPE_DATA:
|
||||
break;
|
||||
|
||||
case TYPE_DNODE:
|
||||
/*
|
||||
* If this is a request to inject faults into the dnode, then we
|
||||
* must translate the current (objset,object) pair into an
|
||||
* offset within the metadnode for the objset. Specifying any
|
||||
* kind of range with type 'dnode' is illegal.
|
||||
*/
|
||||
if (range != NULL) {
|
||||
(void) fprintf(stderr, "range cannot be specified when "
|
||||
"type is 'dnode'\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
record->zi_start = record->zi_object * sizeof (dnode_phys_t);
|
||||
record->zi_end = record->zi_start + sizeof (dnode_phys_t);
|
||||
record->zi_object = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the dnode associated with object, so we can calculate the block
|
||||
* size.
|
||||
*/
|
||||
if ((err = dmu_objset_open(dataset, DMU_OST_ANY,
|
||||
DS_MODE_USER | DS_MODE_READONLY, &os)) != 0) {
|
||||
(void) fprintf(stderr, "cannot open dataset '%s': %s\n",
|
||||
dataset, strerror(err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (record->zi_object == 0) {
|
||||
dn = os->os->os_meta_dnode;
|
||||
} else {
|
||||
err = dnode_hold(os->os, record->zi_object, FTAG, &dn);
|
||||
if (err != 0) {
|
||||
(void) fprintf(stderr, "failed to hold dnode "
|
||||
"for object %llu\n",
|
||||
(u_longlong_t)record->zi_object);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ziprintf("data shift: %d\n", (int)dn->dn_datablkshift);
|
||||
ziprintf(" ind shift: %d\n", (int)dn->dn_indblkshift);
|
||||
|
||||
/*
|
||||
* Translate range into block IDs.
|
||||
*/
|
||||
if (record->zi_start != 0 || record->zi_end != -1ULL) {
|
||||
record->zi_start >>= dn->dn_datablkshift;
|
||||
record->zi_end >>= dn->dn_datablkshift;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check level, and then translate level 0 blkids into ranges
|
||||
* appropriate for level of indirection.
|
||||
*/
|
||||
record->zi_level = level;
|
||||
if (level > 0) {
|
||||
ziprintf("level 0 blkid range: [%llu, %llu]\n",
|
||||
record->zi_start, record->zi_end);
|
||||
|
||||
if (level >= dn->dn_nlevels) {
|
||||
(void) fprintf(stderr, "level %d exceeds max level "
|
||||
"of object (%d)\n", level, dn->dn_nlevels - 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (record->zi_start != 0 || record->zi_end != 0) {
|
||||
int shift = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
|
||||
|
||||
for (; level > 0; level--) {
|
||||
record->zi_start >>= shift;
|
||||
record->zi_end >>= shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (dn) {
|
||||
if (dn != os->os->os_meta_dnode)
|
||||
dnode_rele(dn, FTAG);
|
||||
}
|
||||
if (os)
|
||||
dmu_objset_close(os);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
translate_record(err_type_t type, const char *object, const char *range,
|
||||
int level, zinject_record_t *record, char *poolname, char *dataset)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
char *slash;
|
||||
struct stat64 statbuf;
|
||||
int ret = -1;
|
||||
|
||||
kernel_init(FREAD);
|
||||
|
||||
debug = (getenv("ZINJECT_DEBUG") != NULL);
|
||||
|
||||
ziprintf("translating: %s\n", object);
|
||||
|
||||
if (MOS_TYPE(type)) {
|
||||
/*
|
||||
* MOS objects are treated specially.
|
||||
*/
|
||||
switch (type) {
|
||||
case TYPE_MOS:
|
||||
record->zi_type = 0;
|
||||
break;
|
||||
case TYPE_MOSDIR:
|
||||
record->zi_type = DMU_OT_OBJECT_DIRECTORY;
|
||||
break;
|
||||
case TYPE_METASLAB:
|
||||
record->zi_type = DMU_OT_OBJECT_ARRAY;
|
||||
break;
|
||||
case TYPE_CONFIG:
|
||||
record->zi_type = DMU_OT_PACKED_NVLIST;
|
||||
break;
|
||||
case TYPE_BPLIST:
|
||||
record->zi_type = DMU_OT_BPLIST;
|
||||
break;
|
||||
case TYPE_SPACEMAP:
|
||||
record->zi_type = DMU_OT_SPACE_MAP;
|
||||
break;
|
||||
case TYPE_ERRLOG:
|
||||
record->zi_type = DMU_OT_ERROR_LOG;
|
||||
break;
|
||||
}
|
||||
|
||||
dataset[0] = '\0';
|
||||
(void) strcpy(poolname, object);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a full path into a (dataset, file) pair.
|
||||
*/
|
||||
if (parse_pathname(object, dataset, path, &statbuf) != 0)
|
||||
goto err;
|
||||
|
||||
ziprintf(" dataset: %s\n", dataset);
|
||||
ziprintf(" path: %s\n", path);
|
||||
|
||||
/*
|
||||
* Convert (dataset, file) into (objset, object)
|
||||
*/
|
||||
if (object_from_path(dataset, path, &statbuf, record) != 0)
|
||||
goto err;
|
||||
|
||||
ziprintf("raw objset: %llu\n", record->zi_objset);
|
||||
ziprintf("raw object: %llu\n", record->zi_object);
|
||||
|
||||
/*
|
||||
* For the given object, calculate the real (type, level, range)
|
||||
*/
|
||||
if (calculate_range(dataset, type, level, (char *)range, record) != 0)
|
||||
goto err;
|
||||
|
||||
ziprintf(" objset: %llu\n", record->zi_objset);
|
||||
ziprintf(" object: %llu\n", record->zi_object);
|
||||
if (record->zi_start == 0 &&
|
||||
record->zi_end == -1ULL)
|
||||
ziprintf(" range: all\n");
|
||||
else
|
||||
ziprintf(" range: [%llu, %llu]\n", record->zi_start,
|
||||
record->zi_end);
|
||||
|
||||
/*
|
||||
* Copy the pool name
|
||||
*/
|
||||
(void) strcpy(poolname, dataset);
|
||||
if ((slash = strchr(poolname, '/')) != NULL)
|
||||
*slash = '\0';
|
||||
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
kernel_fini();
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
translate_raw(const char *str, zinject_record_t *record)
|
||||
{
|
||||
/*
|
||||
* A raw bookmark of the form objset:object:level:blkid, where each
|
||||
* number is a hexidecimal value.
|
||||
*/
|
||||
if (sscanf(str, "%llx:%llx:%x:%llx", (u_longlong_t *)&record->zi_objset,
|
||||
(u_longlong_t *)&record->zi_object, &record->zi_level,
|
||||
(u_longlong_t *)&record->zi_start) != 4) {
|
||||
(void) fprintf(stderr, "bad raw spec '%s': must be of the form "
|
||||
"'objset:object:level:blkid'\n", str);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
record->zi_end = record->zi_start;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
translate_device(const char *pool, const char *device, err_type_t label_type,
|
||||
zinject_record_t *record)
|
||||
{
|
||||
char *end;
|
||||
zpool_handle_t *zhp;
|
||||
nvlist_t *tgt;
|
||||
boolean_t isspare, iscache;
|
||||
|
||||
/*
|
||||
* Given a device name or GUID, create an appropriate injection record
|
||||
* with zi_guid set.
|
||||
*/
|
||||
if ((zhp = zpool_open(g_zfs, pool)) == NULL)
|
||||
return (-1);
|
||||
|
||||
record->zi_guid = strtoull(device, &end, 16);
|
||||
if (record->zi_guid == 0 || *end != '\0') {
|
||||
tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);
|
||||
|
||||
if (tgt == NULL) {
|
||||
(void) fprintf(stderr, "cannot find device '%s' in "
|
||||
"pool '%s'\n", device, pool);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
|
||||
&record->zi_guid) == 0);
|
||||
}
|
||||
|
||||
switch (label_type) {
|
||||
case TYPE_LABEL_UBERBLOCK:
|
||||
record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);
|
||||
record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;
|
||||
break;
|
||||
case TYPE_LABEL_NVLIST:
|
||||
record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);
|
||||
record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
/*
|
||||
* ZFS Fault Injector
|
||||
*
|
||||
* This userland component takes a set of options and uses libzpool to translate
|
||||
* from a user-visible object type and name to an internal representation.
|
||||
* There are two basic types of faults: device faults and data faults.
|
||||
*
|
||||
*
|
||||
* DEVICE FAULTS
|
||||
*
|
||||
* Errors can be injected into a particular vdev using the '-d' option. This
|
||||
* option takes a path or vdev GUID to uniquely identify the device within a
|
||||
* pool. There are two types of errors that can be injected, EIO and ENXIO,
|
||||
* that can be controlled through the '-e' option. The default is ENXIO. For
|
||||
* EIO failures, any attempt to read data from the device will return EIO, but
|
||||
* subsequent attempt to reopen the device will succeed. For ENXIO failures,
|
||||
* any attempt to read from the device will return EIO, but any attempt to
|
||||
* reopen the device will also return ENXIO.
|
||||
* For label faults, the -L option must be specified. This allows faults
|
||||
* to be injected into either the nvlist or uberblock region of all the labels
|
||||
* for the specified device.
|
||||
*
|
||||
* This form of the command looks like:
|
||||
*
|
||||
* zinject -d device [-e errno] [-L <uber | nvlist>] pool
|
||||
*
|
||||
*
|
||||
* DATA FAULTS
|
||||
*
|
||||
* We begin with a tuple of the form:
|
||||
*
|
||||
* <type,level,range,object>
|
||||
*
|
||||
* type A string describing the type of data to target. Each type
|
||||
* implicitly describes how to interpret 'object'. Currently,
|
||||
* the following values are supported:
|
||||
*
|
||||
* data User data for a file
|
||||
* dnode Dnode for a file or directory
|
||||
*
|
||||
* The following MOS objects are special. Instead of injecting
|
||||
* errors on a particular object or blkid, we inject errors across
|
||||
* all objects of the given type.
|
||||
*
|
||||
* mos Any data in the MOS
|
||||
* mosdir object directory
|
||||
* config pool configuration
|
||||
* bplist blkptr list
|
||||
* spacemap spacemap
|
||||
* metaslab metaslab
|
||||
* errlog persistent error log
|
||||
*
|
||||
* level Object level. Defaults to '0', not applicable to all types. If
|
||||
* a range is given, this corresponds to the indirect block
|
||||
* corresponding to the specific range.
|
||||
*
|
||||
* range A numerical range [start,end) within the object. Defaults to
|
||||
* the full size of the file.
|
||||
*
|
||||
* object A string describing the logical location of the object. For
|
||||
* files and directories (currently the only supported types),
|
||||
* this is the path of the object on disk.
|
||||
*
|
||||
* This is translated, via libzpool, into the following internal representation:
|
||||
*
|
||||
* <type,objset,object,level,range>
|
||||
*
|
||||
* These types should be self-explanatory. This tuple is then passed to the
|
||||
* kernel via a special ioctl() to initiate fault injection for the given
|
||||
* object. Note that 'type' is not strictly necessary for fault injection, but
|
||||
* is used when translating existing faults into a human-readable string.
|
||||
*
|
||||
*
|
||||
* The command itself takes one of the forms:
|
||||
*
|
||||
* zinject
|
||||
* zinject <-a | -u pool>
|
||||
* zinject -c <id|all>
|
||||
* zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
|
||||
* [-r range] <object>
|
||||
* zinject [-f freq] [-a] [-m] [-u] -b objset:object:level:start:end pool
|
||||
*
|
||||
* With no arguments, the command prints all currently registered injection
|
||||
* handlers, with their numeric identifiers.
|
||||
*
|
||||
* The '-c' option will clear the given handler, or all handlers if 'all' is
|
||||
* specified.
|
||||
*
|
||||
* The '-e' option takes a string describing the errno to simulate. This must
|
||||
* be either 'io' or 'checksum'. In most cases this will result in the same
|
||||
* behavior, but RAID-Z will produce a different set of ereports for this
|
||||
* situation.
|
||||
*
|
||||
* The '-a', '-u', and '-m' flags toggle internal flush behavior. If '-a' is
|
||||
* specified, then the ARC cache is flushed appropriately. If '-u' is
|
||||
* specified, then the underlying SPA is unloaded. Either of these flags can be
|
||||
* specified independently of any other handlers. The '-m' flag automatically
|
||||
* does an unmount and remount of the underlying dataset to aid in flushing the
|
||||
* cache.
|
||||
*
|
||||
* The '-f' flag controls the frequency of errors injected, expressed as a
|
||||
* integer percentage between 1 and 100. The default is 100.
|
||||
*
|
||||
* The this form is responsible for actually injecting the handler into the
|
||||
* framework. It takes the arguments described above, translates them to the
|
||||
* internal tuple using libzpool, and then issues an ioctl() to register the
|
||||
* handler.
|
||||
*
|
||||
* The final form can target a specific bookmark, regardless of whether a
|
||||
* human-readable interface has been designed. It allows developers to specify
|
||||
* a particular block by number.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#undef verify /* both libzfs.h and zfs_context.h want to define this */
|
||||
|
||||
#include "zinject.h"
|
||||
|
||||
libzfs_handle_t *g_zfs;
|
||||
int zfs_fd;
|
||||
|
||||
#define ECKSUM EBADE
|
||||
|
||||
static const char *errtable[TYPE_INVAL] = {
|
||||
"data",
|
||||
"dnode",
|
||||
"mos",
|
||||
"mosdir",
|
||||
"metaslab",
|
||||
"config",
|
||||
"bplist",
|
||||
"spacemap",
|
||||
"errlog",
|
||||
"uber",
|
||||
"nvlist"
|
||||
};
|
||||
|
||||
static err_type_t
|
||||
name_to_type(const char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < TYPE_INVAL; i++)
|
||||
if (strcmp(errtable[i], arg) == 0)
|
||||
return (i);
|
||||
|
||||
return (TYPE_INVAL);
|
||||
}
|
||||
|
||||
static const char *
|
||||
type_to_name(uint64_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case DMU_OT_OBJECT_DIRECTORY:
|
||||
return ("mosdir");
|
||||
case DMU_OT_OBJECT_ARRAY:
|
||||
return ("metaslab");
|
||||
case DMU_OT_PACKED_NVLIST:
|
||||
return ("config");
|
||||
case DMU_OT_BPLIST:
|
||||
return ("bplist");
|
||||
case DMU_OT_SPACE_MAP:
|
||||
return ("spacemap");
|
||||
case DMU_OT_ERROR_LOG:
|
||||
return ("errlog");
|
||||
default:
|
||||
return ("-");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print usage message.
|
||||
*/
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
(void) printf(
|
||||
"usage:\n"
|
||||
"\n"
|
||||
"\tzinject\n"
|
||||
"\n"
|
||||
"\t\tList all active injection records.\n"
|
||||
"\n"
|
||||
"\tzinject -c <id|all>\n"
|
||||
"\n"
|
||||
"\t\tClear the particular record (if given a numeric ID), or\n"
|
||||
"\t\tall records if 'all' is specificed.\n"
|
||||
"\n"
|
||||
"\tzinject -d device [-e errno] [-L <nvlist|uber>] pool\n"
|
||||
"\t\tInject a fault into a particular device or the device's\n"
|
||||
"\t\tlabel. Label injection can either be 'nvlist' or 'uber'.\n"
|
||||
"\t\t'errno' can either be 'nxio' (the default) or 'io'.\n"
|
||||
"\n"
|
||||
"\tzinject -b objset:object:level:blkid pool\n"
|
||||
"\n"
|
||||
"\t\tInject an error into pool 'pool' with the numeric bookmark\n"
|
||||
"\t\tspecified by the remaining tuple. Each number is in\n"
|
||||
"\t\thexidecimal, and only one block can be specified.\n"
|
||||
"\n"
|
||||
"\tzinject [-q] <-t type> [-e errno] [-l level] [-r range]\n"
|
||||
"\t [-a] [-m] [-u] [-f freq] <object>\n"
|
||||
"\n"
|
||||
"\t\tInject an error into the object specified by the '-t' option\n"
|
||||
"\t\tand the object descriptor. The 'object' parameter is\n"
|
||||
"\t\tinterperted depending on the '-t' option.\n"
|
||||
"\n"
|
||||
"\t\t-q\tQuiet mode. Only print out the handler number added.\n"
|
||||
"\t\t-e\tInject a specific error. Must be either 'io' or\n"
|
||||
"\t\t\t'checksum'. Default is 'io'.\n"
|
||||
"\t\t-l\tInject error at a particular block level. Default is "
|
||||
"0.\n"
|
||||
"\t\t-m\tAutomatically remount underlying filesystem.\n"
|
||||
"\t\t-r\tInject error over a particular logical range of an\n"
|
||||
"\t\t\tobject. Will be translated to the appropriate blkid\n"
|
||||
"\t\t\trange according to the object's properties.\n"
|
||||
"\t\t-a\tFlush the ARC cache. Can be specified without any\n"
|
||||
"\t\t\tassociated object.\n"
|
||||
"\t\t-u\tUnload the associated pool. Can be specified with only\n"
|
||||
"\t\t\ta pool object.\n"
|
||||
"\t\t-f\tOnly inject errors a fraction of the time. Expressed as\n"
|
||||
"\t\t\ta percentage between 1 and 100.\n"
|
||||
"\n"
|
||||
"\t-t data\t\tInject an error into the plain file contents of a\n"
|
||||
"\t\t\tfile. The object must be specified as a complete path\n"
|
||||
"\t\t\tto a file on a ZFS filesystem.\n"
|
||||
"\n"
|
||||
"\t-t dnode\tInject an error into the metadnode in the block\n"
|
||||
"\t\t\tcorresponding to the dnode for a file or directory. The\n"
|
||||
"\t\t\t'-r' option is incompatible with this mode. The object\n"
|
||||
"\t\t\tis specified as a complete path to a file or directory\n"
|
||||
"\t\t\ton a ZFS filesystem.\n"
|
||||
"\n"
|
||||
"\t-t <mos>\tInject errors into the MOS for objects of the given\n"
|
||||
"\t\t\ttype. Valid types are: mos, mosdir, config, bplist,\n"
|
||||
"\t\t\tspacemap, metaslab, errlog. The only valid <object> is\n"
|
||||
"\t\t\tthe poolname.\n");
|
||||
}
|
||||
|
||||
static int
|
||||
iter_handlers(int (*func)(int, const char *, zinject_record_t *, void *),
|
||||
void *data)
|
||||
{
|
||||
zfs_cmd_t zc;
|
||||
int ret;
|
||||
|
||||
zc.zc_guid = 0;
|
||||
|
||||
while (ioctl(zfs_fd, ZFS_IOC_INJECT_LIST_NEXT, &zc) == 0)
|
||||
if ((ret = func((int)zc.zc_guid, zc.zc_name,
|
||||
&zc.zc_inject_record, data)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
print_data_handler(int id, const char *pool, zinject_record_t *record,
|
||||
void *data)
|
||||
{
|
||||
int *count = data;
|
||||
|
||||
if (record->zi_guid != 0)
|
||||
return (0);
|
||||
|
||||
if (*count == 0) {
|
||||
(void) printf("%3s %-15s %-6s %-6s %-8s %3s %-15s\n",
|
||||
"ID", "POOL", "OBJSET", "OBJECT", "TYPE", "LVL", "RANGE");
|
||||
(void) printf("--- --------------- ------ "
|
||||
"------ -------- --- ---------------\n");
|
||||
}
|
||||
|
||||
*count += 1;
|
||||
|
||||
(void) printf("%3d %-15s %-6llu %-6llu %-8s %3d ", id, pool,
|
||||
(u_longlong_t)record->zi_objset, (u_longlong_t)record->zi_object,
|
||||
type_to_name(record->zi_type), record->zi_level);
|
||||
|
||||
if (record->zi_start == 0 &&
|
||||
record->zi_end == -1ULL)
|
||||
(void) printf("all\n");
|
||||
else
|
||||
(void) printf("[%llu, %llu]\n", (u_longlong_t)record->zi_start,
|
||||
(u_longlong_t)record->zi_end);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
print_device_handler(int id, const char *pool, zinject_record_t *record,
|
||||
void *data)
|
||||
{
|
||||
int *count = data;
|
||||
|
||||
if (record->zi_guid == 0)
|
||||
return (0);
|
||||
|
||||
if (*count == 0) {
|
||||
(void) printf("%3s %-15s %s\n", "ID", "POOL", "GUID");
|
||||
(void) printf("--- --------------- ----------------\n");
|
||||
}
|
||||
|
||||
*count += 1;
|
||||
|
||||
(void) printf("%3d %-15s %llx\n", id, pool,
|
||||
(u_longlong_t)record->zi_guid);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print all registered error handlers. Returns the number of handlers
|
||||
* registered.
|
||||
*/
|
||||
static int
|
||||
print_all_handlers(void)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
(void) iter_handlers(print_device_handler, &count);
|
||||
(void) printf("\n");
|
||||
count = 0;
|
||||
(void) iter_handlers(print_data_handler, &count);
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
cancel_one_handler(int id, const char *pool, zinject_record_t *record,
|
||||
void *data)
|
||||
{
|
||||
zfs_cmd_t zc;
|
||||
|
||||
zc.zc_guid = (uint64_t)id;
|
||||
|
||||
if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
|
||||
(void) fprintf(stderr, "failed to remove handler %d: %s\n",
|
||||
id, strerror(errno));
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all fault injection handlers.
|
||||
*/
|
||||
static int
|
||||
cancel_all_handlers(void)
|
||||
{
|
||||
int ret = iter_handlers(cancel_one_handler, NULL);
|
||||
|
||||
(void) printf("removed all registered handlers\n");
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a specific fault injection handler.
|
||||
*/
|
||||
static int
|
||||
cancel_handler(int id)
|
||||
{
|
||||
zfs_cmd_t zc;
|
||||
|
||||
zc.zc_guid = (uint64_t)id;
|
||||
|
||||
if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
|
||||
(void) fprintf(stderr, "failed to remove handler %d: %s\n",
|
||||
id, strerror(errno));
|
||||
return (1);
|
||||
}
|
||||
|
||||
(void) printf("removed handler %d\n", id);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a new fault injection handler.
|
||||
*/
|
||||
static int
|
||||
register_handler(const char *pool, int flags, zinject_record_t *record,
|
||||
int quiet)
|
||||
{
|
||||
zfs_cmd_t zc;
|
||||
|
||||
(void) strcpy(zc.zc_name, pool);
|
||||
zc.zc_inject_record = *record;
|
||||
zc.zc_guid = flags;
|
||||
|
||||
if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
|
||||
(void) fprintf(stderr, "failed to add handler: %s\n",
|
||||
strerror(errno));
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (flags & ZINJECT_NULL)
|
||||
return (0);
|
||||
|
||||
if (quiet) {
|
||||
(void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
|
||||
} else {
|
||||
(void) printf("Added handler %llu with the following "
|
||||
"properties:\n", (u_longlong_t)zc.zc_guid);
|
||||
(void) printf(" pool: %s\n", pool);
|
||||
if (record->zi_guid) {
|
||||
(void) printf(" vdev: %llx\n",
|
||||
(u_longlong_t)record->zi_guid);
|
||||
} else {
|
||||
(void) printf("objset: %llu\n",
|
||||
(u_longlong_t)record->zi_objset);
|
||||
(void) printf("object: %llu\n",
|
||||
(u_longlong_t)record->zi_object);
|
||||
(void) printf(" type: %llu\n",
|
||||
(u_longlong_t)record->zi_type);
|
||||
(void) printf(" level: %d\n", record->zi_level);
|
||||
if (record->zi_start == 0 &&
|
||||
record->zi_end == -1ULL)
|
||||
(void) printf(" range: all\n");
|
||||
else
|
||||
(void) printf(" range: [%llu, %llu)\n",
|
||||
(u_longlong_t)record->zi_start,
|
||||
(u_longlong_t)record->zi_end);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
char *range = NULL;
|
||||
char *cancel = NULL;
|
||||
char *end;
|
||||
char *raw = NULL;
|
||||
char *device = NULL;
|
||||
int level = 0;
|
||||
int quiet = 0;
|
||||
int error = 0;
|
||||
int domount = 0;
|
||||
err_type_t type = TYPE_INVAL;
|
||||
err_type_t label = TYPE_INVAL;
|
||||
zinject_record_t record = { 0 };
|
||||
char pool[MAXNAMELEN];
|
||||
char dataset[MAXNAMELEN];
|
||||
zfs_handle_t *zhp;
|
||||
int ret;
|
||||
int flags = 0;
|
||||
|
||||
if ((g_zfs = libzfs_init()) == NULL) {
|
||||
(void) fprintf(stderr, "internal error: failed to "
|
||||
"initialize ZFS library\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
libzfs_print_on_error(g_zfs, B_TRUE);
|
||||
|
||||
if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
|
||||
(void) fprintf(stderr, "failed to open ZFS device\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
/*
|
||||
* No arguments. Print the available handlers. If there are no
|
||||
* available handlers, direct the user to '-h' for help
|
||||
* information.
|
||||
*/
|
||||
if (print_all_handlers() == 0) {
|
||||
(void) printf("No handlers registered.\n");
|
||||
(void) printf("Run 'zinject -h' for usage "
|
||||
"information.\n");
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
while ((c = getopt(argc, argv, ":ab:d:f:qhc:t:l:mr:e:uL:")) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
flags |= ZINJECT_FLUSH_ARC;
|
||||
break;
|
||||
case 'b':
|
||||
raw = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
cancel = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
if (strcasecmp(optarg, "io") == 0) {
|
||||
error = EIO;
|
||||
} else if (strcasecmp(optarg, "checksum") == 0) {
|
||||
error = ECKSUM;
|
||||
} else if (strcasecmp(optarg, "nxio") == 0) {
|
||||
error = ENXIO;
|
||||
} else {
|
||||
(void) fprintf(stderr, "invalid error type "
|
||||
"'%s': must be 'io', 'checksum' or "
|
||||
"'nxio'\n", optarg);
|
||||
usage();
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
record.zi_freq = atoi(optarg);
|
||||
if (record.zi_freq < 1 || record.zi_freq > 100) {
|
||||
(void) fprintf(stderr, "frequency range must "
|
||||
"be in the range (0, 100]\n");
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
return (0);
|
||||
case 'l':
|
||||
level = (int)strtol(optarg, &end, 10);
|
||||
if (*end != '\0') {
|
||||
(void) fprintf(stderr, "invalid level '%s': "
|
||||
"must be an integer\n", optarg);
|
||||
usage();
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
domount = 1;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
case 'r':
|
||||
range = optarg;
|
||||
break;
|
||||
case 't':
|
||||
if ((type = name_to_type(optarg)) == TYPE_INVAL &&
|
||||
!MOS_TYPE(type)) {
|
||||
(void) fprintf(stderr, "invalid type '%s'\n",
|
||||
optarg);
|
||||
usage();
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
flags |= ZINJECT_UNLOAD_SPA;
|
||||
break;
|
||||
case 'L':
|
||||
if ((label = name_to_type(optarg)) == TYPE_INVAL &&
|
||||
!LABEL_TYPE(type)) {
|
||||
(void) fprintf(stderr, "invalid label type "
|
||||
"'%s'\n", optarg);
|
||||
usage();
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
(void) fprintf(stderr, "option -%c requires an "
|
||||
"operand\n", optopt);
|
||||
usage();
|
||||
return (1);
|
||||
case '?':
|
||||
(void) fprintf(stderr, "invalid option '%c'\n",
|
||||
optopt);
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (cancel != NULL) {
|
||||
/*
|
||||
* '-c' is invalid with any other options.
|
||||
*/
|
||||
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
|
||||
level != 0) {
|
||||
(void) fprintf(stderr, "cancel (-c) incompatible with "
|
||||
"any other options\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
if (argc != 0) {
|
||||
(void) fprintf(stderr, "extraneous argument to '-c'\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
if (strcmp(cancel, "all") == 0) {
|
||||
return (cancel_all_handlers());
|
||||
} else {
|
||||
int id = (int)strtol(cancel, &end, 10);
|
||||
if (*end != '\0') {
|
||||
(void) fprintf(stderr, "invalid handle id '%s':"
|
||||
" must be an integer or 'all'\n", cancel);
|
||||
usage();
|
||||
return (1);
|
||||
}
|
||||
return (cancel_handler(id));
|
||||
}
|
||||
}
|
||||
|
||||
if (device != NULL) {
|
||||
/*
|
||||
* Device (-d) injection uses a completely different mechanism
|
||||
* for doing injection, so handle it separately here.
|
||||
*/
|
||||
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
|
||||
level != 0) {
|
||||
(void) fprintf(stderr, "device (-d) incompatible with "
|
||||
"data error injection\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
if (argc != 1) {
|
||||
(void) fprintf(stderr, "device (-d) injection requires "
|
||||
"a single pool name\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
(void) strcpy(pool, argv[0]);
|
||||
dataset[0] = '\0';
|
||||
|
||||
if (error == ECKSUM) {
|
||||
(void) fprintf(stderr, "device error type must be "
|
||||
"'io' or 'nxio'\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (translate_device(pool, device, label, &record) != 0)
|
||||
return (1);
|
||||
if (!error)
|
||||
error = ENXIO;
|
||||
} else if (raw != NULL) {
|
||||
if (range != NULL || type != TYPE_INVAL || level != 0) {
|
||||
(void) fprintf(stderr, "raw (-b) format with "
|
||||
"any other options\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
if (argc != 1) {
|
||||
(void) fprintf(stderr, "raw (-b) format expects a "
|
||||
"single pool name\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
(void) strcpy(pool, argv[0]);
|
||||
dataset[0] = '\0';
|
||||
|
||||
if (error == ENXIO) {
|
||||
(void) fprintf(stderr, "data error type must be "
|
||||
"'checksum' or 'io'\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (translate_raw(raw, &record) != 0)
|
||||
return (1);
|
||||
if (!error)
|
||||
error = EIO;
|
||||
} else if (type == TYPE_INVAL) {
|
||||
if (flags == 0) {
|
||||
(void) fprintf(stderr, "at least one of '-b', '-d', "
|
||||
"'-t', '-a', or '-u' must be specified\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
if (argc == 1 && (flags & ZINJECT_UNLOAD_SPA)) {
|
||||
(void) strcpy(pool, argv[0]);
|
||||
dataset[0] = '\0';
|
||||
} else if (argc != 0) {
|
||||
(void) fprintf(stderr, "extraneous argument for "
|
||||
"'-f'\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
flags |= ZINJECT_NULL;
|
||||
} else {
|
||||
if (argc != 1) {
|
||||
(void) fprintf(stderr, "missing object\n");
|
||||
usage();
|
||||
return (2);
|
||||
}
|
||||
|
||||
if (error == ENXIO) {
|
||||
(void) fprintf(stderr, "data error type must be "
|
||||
"'checksum' or 'io'\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (translate_record(type, argv[0], range, level, &record, pool,
|
||||
dataset) != 0)
|
||||
return (1);
|
||||
if (!error)
|
||||
error = EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is pool-wide metadata, unmount everything. The ioctl() will
|
||||
* unload the pool, so that we trigger spa-wide reopen of metadata next
|
||||
* time we access the pool.
|
||||
*/
|
||||
if (dataset[0] != '\0' && domount) {
|
||||
if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
|
||||
return (1);
|
||||
|
||||
if (zfs_unmount(zhp, NULL, 0) != 0)
|
||||
return (1);
|
||||
}
|
||||
|
||||
record.zi_error = error;
|
||||
|
||||
ret = register_handler(pool, flags, &record, quiet);
|
||||
|
||||
if (dataset[0] != '\0' && domount)
|
||||
ret = (zfs_mount(zhp, NULL, 0) != 0);
|
||||
|
||||
libzfs_fini(g_zfs);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 _ZINJECT_H
|
||||
#define _ZINJECT_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/zfs_ioctl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
TYPE_DATA, /* plain file contents */
|
||||
TYPE_DNODE, /* metadnode contents */
|
||||
TYPE_MOS, /* all MOS data */
|
||||
TYPE_MOSDIR, /* MOS object directory */
|
||||
TYPE_METASLAB, /* metaslab objects */
|
||||
TYPE_CONFIG, /* MOS config */
|
||||
TYPE_BPLIST, /* block pointer list */
|
||||
TYPE_SPACEMAP, /* space map objects */
|
||||
TYPE_ERRLOG, /* persistent error log */
|
||||
TYPE_LABEL_UBERBLOCK, /* label specific uberblock */
|
||||
TYPE_LABEL_NVLIST, /* label specific nvlist */
|
||||
TYPE_INVAL
|
||||
} err_type_t;
|
||||
|
||||
#define MOS_TYPE(t) \
|
||||
((t) >= TYPE_MOS && (t) < TYPE_LABEL_UBERBLOCK)
|
||||
|
||||
#define LABEL_TYPE(t) \
|
||||
((t) >= TYPE_LABEL_UBERBLOCK && (t) < TYPE_INVAL)
|
||||
|
||||
int translate_record(err_type_t type, const char *object, const char *range,
|
||||
int level, zinject_record_t *record, char *poolname, char *dataset);
|
||||
int translate_raw(const char *raw, zinject_record_t *record);
|
||||
int translate_device(const char *pool, const char *device,
|
||||
err_type_t label_type, zinject_record_t *record);
|
||||
void usage(void);
|
||||
|
||||
extern libzfs_handle_t *g_zfs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZINJECT_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
+3475
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user