Update vendor/opensolaris to last OpenSolaris state (13149:b23a4dab3d50)

Add ZFS bits to vendor/opensolaris

Obtained from:	https://hg.openindiana.org/upstream/oracle/onnv-gate
This commit is contained in:
mm 2012-07-18 07:48:04 +00:00
parent 679969c11d
commit c998beca60
83 changed files with 52790 additions and 318 deletions

384
OPENSOLARIS.LICENSE Normal file
View File

@ -0,0 +1,384 @@
Unless otherwise noted, all files in this distribution are released
under the Common Development and Distribution License (CDDL).
Exceptions are noted within the associated source files.
--------------------------------------------------------------------
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0
1. Definitions.
1.1. "Contributor" means each individual or entity that creates
or contributes to the creation of Modifications.
1.2. "Contributor Version" means the combination of the Original
Software, prior Modifications used by a Contributor (if any),
and the Modifications made by that particular Contributor.
1.3. "Covered Software" means (a) the Original Software, or (b)
Modifications, or (c) the combination of files containing
Original Software with files containing Modifications, in
each case including portions thereof.
1.4. "Executable" means the Covered Software in any form other
than Source Code.
1.5. "Initial Developer" means the individual or entity that first
makes Original Software available under this License.
1.6. "Larger Work" means a work which combines Covered Software or
portions thereof with code not governed by the terms of this
License.
1.7. "License" means this document.
1.8. "Licensable" means having the right to grant, to the maximum
extent possible, whether at the time of the initial grant or
subsequently acquired, any and all of the rights conveyed
herein.
1.9. "Modifications" means the Source Code and Executable form of
any of the following:
A. Any file that results from an addition to, deletion from or
modification of the contents of a file containing Original
Software or previous Modifications;
B. Any new file that contains any part of the Original
Software or previous Modifications; or
C. Any new file that is contributed or otherwise made
available under the terms of this License.
1.10. "Original Software" means the Source Code and Executable
form of computer software code that is originally released
under this License.
1.11. "Patent Claims" means any patent claim(s), now owned or
hereafter acquired, including without limitation, method,
process, and apparatus claims, in any patent Licensable by
grantor.
1.12. "Source Code" means (a) the common form of computer software
code in which modifications are made and (b) associated
documentation included in or with such code.
1.13. "You" (or "Your") means an individual or a legal entity
exercising rights under, and complying with all of the terms
of, this License. For legal entities, "You" includes any
entity which controls, is controlled by, or is under common
control with You. For purposes of this definition,
"control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by
contract or otherwise, or (b) ownership of more than fifty
percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants.
2.1. The Initial Developer Grant.
Conditioned upon Your compliance with Section 3.1 below and
subject to third party intellectual property claims, the Initial
Developer hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Initial Developer, to use,
reproduce, modify, display, perform, sublicense and
distribute the Original Software (or portions thereof),
with or without Modifications, and/or as part of a Larger
Work; and
(b) under Patent Claims infringed by the making, using or
selling of Original Software, to make, have made, use,
practice, sell, and offer for sale, and/or otherwise
dispose of the Original Software (or portions thereof).
(c) The licenses granted in Sections 2.1(a) and (b) are
effective on the date Initial Developer first distributes
or otherwise makes the Original Software available to a
third party under the terms of this License.
(d) Notwithstanding Section 2.1(b) above, no patent license is
granted: (1) for code that You delete from the Original
Software, or (2) for infringements caused by: (i) the
modification of the Original Software, or (ii) the
combination of the Original Software with other software
or devices.
2.2. Contributor Grant.
Conditioned upon Your compliance with Section 3.1 below and
subject to third party intellectual property claims, each
Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Contributor to use, reproduce,
modify, display, perform, sublicense and distribute the
Modifications created by such Contributor (or portions
thereof), either on an unmodified basis, with other
Modifications, as Covered Software and/or as part of a
Larger Work; and
(b) under Patent Claims infringed by the making, using, or
selling of Modifications made by that Contributor either
alone and/or in combination with its Contributor Version
(or portions of such combination), to make, use, sell,
offer for sale, have made, and/or otherwise dispose of:
(1) Modifications made by that Contributor (or portions
thereof); and (2) the combination of Modifications made by
that Contributor with its Contributor Version (or portions
of such combination).
(c) The licenses granted in Sections 2.2(a) and 2.2(b) are
effective on the date Contributor first distributes or
otherwise makes the Modifications available to a third
party.
(d) Notwithstanding Section 2.2(b) above, no patent license is
granted: (1) for any code that Contributor has deleted
from the Contributor Version; (2) for infringements caused
by: (i) third party modifications of Contributor Version,
or (ii) the combination of Modifications made by that
Contributor with other software (except as part of the
Contributor Version) or other devices; or (3) under Patent
Claims infringed by Covered Software in the absence of
Modifications made by that Contributor.
3. Distribution Obligations.
3.1. Availability of Source Code.
Any Covered Software that You distribute or otherwise make
available in Executable form must also be made available in Source
Code form and that Source Code form must be distributed only under
the terms of this License. You must include a copy of this
License with every copy of the Source Code form of the Covered
Software You distribute or otherwise make available. You must
inform recipients of any such Covered Software in Executable form
as to how they can obtain such Covered Software in Source Code
form in a reasonable manner on or through a medium customarily
used for software exchange.
3.2. Modifications.
The Modifications that You create or to which You contribute are
governed by the terms of this License. You represent that You
believe Your Modifications are Your original creation(s) and/or
You have sufficient rights to grant the rights conveyed by this
License.
3.3. Required Notices.
You must include a notice in each of Your Modifications that
identifies You as the Contributor of the Modification. You may
not remove or alter any copyright, patent or trademark notices
contained within the Covered Software, or any notices of licensing
or any descriptive text giving attribution to any Contributor or
the Initial Developer.
3.4. Application of Additional Terms.
You may not offer or impose any terms on any Covered Software in
Source Code form that alters or restricts the applicable version
of this License or the recipients' rights hereunder. You may
choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of
Covered Software. However, you may do so only on Your own behalf,
and not on behalf of the Initial Developer or any Contributor.
You must make it absolutely clear that any such warranty, support,
indemnity or liability obligation is offered by You alone, and You
hereby agree to indemnify the Initial Developer and every
Contributor for any liability incurred by the Initial Developer or
such Contributor as a result of warranty, support, indemnity or
liability terms You offer.
3.5. Distribution of Executable Versions.
You may distribute the Executable form of the Covered Software
under the terms of this License or under the terms of a license of
Your choice, which may contain terms different from this License,
provided that You are in compliance with the terms of this License
and that the license for the Executable form does not attempt to
limit or alter the recipient's rights in the Source Code form from
the rights set forth in this License. If You distribute the
Covered Software in Executable form under a different license, You
must make it absolutely clear that any terms which differ from
this License are offered by You alone, not by the Initial
Developer or Contributor. You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred
by the Initial Developer or such Contributor as a result of any
such terms You offer.
3.6. Larger Works.
You may create a Larger Work by combining Covered Software with
other code not governed by the terms of this License and
distribute the Larger Work as a single product. In such a case,
You must make sure the requirements of this License are fulfilled
for the Covered Software.
4. Versions of the License.
4.1. New Versions.
Sun Microsystems, Inc. is the initial license steward and may
publish revised and/or new versions of this License from time to
time. Each version will be given a distinguishing version number.
Except as provided in Section 4.3, no one other than the license
steward has the right to modify this License.
4.2. Effect of New Versions.
You may always continue to use, distribute or otherwise make the
Covered Software available under the terms of the version of the
License under which You originally received the Covered Software.
If the Initial Developer includes a notice in the Original
Software prohibiting it from being distributed or otherwise made
available under any subsequent version of the License, You must
distribute and make the Covered Software available under the terms
of the version of the License under which You originally received
the Covered Software. Otherwise, You may also choose to use,
distribute or otherwise make the Covered Software available under
the terms of any subsequent version of the License published by
the license steward.
4.3. Modified Versions.
When You are an Initial Developer and You want to create a new
license for Your Original Software, You may create and use a
modified version of this License if You: (a) rename the license
and remove any references to the name of the license steward
(except to note that the license differs from this License); and
(b) otherwise make it clear that the license contains terms which
differ from this License.
5. DISCLAIMER OF WARRANTY.
COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY
COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
DISCLAIMER.
6. TERMINATION.
6.1. This License and the rights granted hereunder will terminate
automatically if You fail to comply with terms herein and fail to
cure such breach within 30 days of becoming aware of the breach.
Provisions which, by their nature, must remain in effect beyond
the termination of this License shall survive.
6.2. If You assert a patent infringement claim (excluding
declaratory judgment actions) against Initial Developer or a
Contributor (the Initial Developer or Contributor against whom You
assert such claim is referred to as "Participant") alleging that
the Participant Software (meaning the Contributor Version where
the Participant is a Contributor or the Original Software where
the Participant is the Initial Developer) directly or indirectly
infringes any patent, then any and all rights granted directly or
indirectly to You by such Participant, the Initial Developer (if
the Initial Developer is not the Participant) and all Contributors
under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
notice from Participant terminate prospectively and automatically
at the expiration of such 60 day notice period, unless if within
such 60 day period You withdraw Your claim with respect to the
Participant Software against such Participant either unilaterally
or pursuant to a written agreement with Participant.
6.3. In the event of termination under Sections 6.1 or 6.2 above,
all end user licenses that have been validly granted by You or any
distributor hereunder prior to termination (excluding licenses
granted to You by any distributor) shall survive termination.
7. LIMITATION OF LIABILITY.
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO
NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
APPLY TO YOU.
8. U.S. GOVERNMENT END USERS.
The Covered Software is a "commercial item," as that term is
defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
computer software" (as that term is defined at 48
C.F.R. 252.227-7014(a)(1)) and "commercial computer software
documentation" as such terms are used in 48 C.F.R. 12.212
(Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
U.S. Government End Users acquire Covered Software with only those
rights set forth herein. This U.S. Government Rights clause is in
lieu of, and supersedes, any other FAR, DFAR, or other clause or
provision that addresses Government rights in computer software
under this License.
9. MISCELLANEOUS.
This License represents the complete agreement concerning subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. This License shall be governed
by the law of the jurisdiction specified in a notice contained
within the Original Software (except to the extent applicable law,
if any, provides otherwise), excluding such jurisdiction's
conflict-of-law provisions. Any litigation relating to this
License shall be subject to the jurisdiction of the courts located
in the jurisdiction and venue specified in a notice contained
within the Original Software, with the losing party responsible
for costs, including, without limitation, court costs and
reasonable attorneys' fees and expenses. The application of the
United Nations Convention on Contracts for the International Sale
of Goods is expressly excluded. Any law or regulation which
provides that the language of a contract shall be construed
against the drafter shall not apply to this License. You agree
that You alone are responsible for compliance with the United
States export administration regulations (and the export control
laws and regulation of any other countries) when You use,
distribute or otherwise make available any Covered Software.
10. RESPONSIBILITY FOR CLAIMS.
As between Initial Developer and the Contributors, each party is
responsible for claims and damages arising, directly or
indirectly, out of its utilization of rights under this License
and You agree to work with Initial Developer and Contributors to
distribute such responsibility on an equitable basis. Nothing
herein is intended or shall be deemed to constitute any admission
of liability.
--------------------------------------------------------------------
NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND
DISTRIBUTION LICENSE (CDDL)
For Covered Software in this distribution, this License shall
be governed by the laws of the State of California (excluding
conflict-of-law provisions).
Any litigation relating to this License shall be subject to the
jurisdiction of the Federal Courts of the Northern District of
California and the state courts of the State of California, with
venue lying in Santa Clara County, California.

82
cmd/pyzfs/pyzfs.py Normal file
View File

@ -0,0 +1,82 @@
#! /usr/bin/python2.6 -S
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
# Note, we want SIGINT (control-c) to exit the process quietly, to mimic
# the standard behavior of C programs. The best we can do with pure
# Python is to run with -S (to disable "import site"), and start our
# program with a "try" statement. Hopefully nobody hits ^C before our
# try statement is executed.
try:
import site
import gettext
import zfs.util
import zfs.ioctl
import sys
import errno
import solaris.misc
"""This is the main script for doing zfs subcommands. It doesn't know
what subcommands there are, it just looks for a module zfs.<subcommand>
that implements that subcommand."""
try:
_ = gettext.translation("SUNW_OST_OSCMD", "/usr/lib/locale",
fallback=True).gettext
except:
_ = solaris.misc.gettext
if len(sys.argv) < 2:
sys.exit(_("missing subcommand argument"))
zfs.ioctl.set_cmdstr(" ".join(["zfs"] + sys.argv[1:]))
try:
# import zfs.<subcommand>
# subfunc = zfs.<subcommand>.do_<subcommand>
subcmd = sys.argv[1]
__import__("zfs." + subcmd)
submod = getattr(zfs, subcmd)
subfunc = getattr(submod, "do_" + subcmd)
except (ImportError, AttributeError):
sys.exit(_("invalid subcommand"))
try:
subfunc()
except zfs.util.ZFSError, e:
print(e)
sys.exit(1)
except IOError, e:
import errno
import sys
if e.errno == errno.EPIPE:
sys.exit(1)
raise
except KeyboardInterrupt:
import sys
sys.exit(1)

View File

@ -20,7 +20,7 @@
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Define an Alist, a list maintained as a reallocable array, and a for() loop
@ -32,8 +32,6 @@
#ifndef _ALIST_H
#define _ALIST_H
#pragma ident "%Z%%M% %I% %E% SMI"
#ifdef __cplusplus
extern "C" {
#endif
@ -138,6 +136,13 @@ typedef struct {
void *apl_data[1]; /* data area: (arrcnt * size) bytes */
} APlist;
#ifdef _SYSCALL32 /* required by librtld_db */
typedef struct {
Elf32_Word apl_arritems;
Elf32_Word apl_nitems;
Elf32_Addr apl_data[1];
} APlist32;
#endif /* _SYSCALL32 */
/*
* The ALIST_OFF_DATA and APLIST_OFF_DATA macros give the byte offset
@ -224,7 +229,7 @@ typedef struct {
* Possible values returned by aplist_test()
*/
typedef enum {
ALE_ALLOCFAIL = 0, /* Memory allocation error */
ALE_ALLOCFAIL = 0, /* memory allocation error */
ALE_EXISTS = 1, /* alist entry already exists */
ALE_NOTFND = 2, /* item not found and insert not required */
ALE_CREATE = 3 /* alist entry created */
@ -244,11 +249,14 @@ typedef enum {
((void *)((_off) + (char *)(_lp)))
/*
* # of items currently found in a list. These macros handle the case
* where the list has not been allocated yet.
* The number of items currently found in a list (nitems), and the total number
* of slots in the current data allocation (arritems). These macros handle the
* case where the list has not been allocated yet.
*/
#define alist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_nitems)
#define aplist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_nitems)
#define alist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_nitems)
#define aplist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_nitems)
#define alist_arritems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_arritems)
#define aplist_arritems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_arritems)
extern void *alist_append(Alist **, const void *, size_t, Aliste);

View File

@ -20,15 +20,12 @@
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _DEBUG_H
#define _DEBUG_H
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Global include file for lddbg debugging.
*
@ -45,6 +42,7 @@
* start with the `Elf_' prefix. These latter routines are the only
* routines used by the elfdump(1) utility.
*/
#include <sys/times.h>
#include <sgs.h>
#include <libld.h>
#include <rtld.h>
@ -55,13 +53,12 @@ extern "C" {
#endif
/*
* Define Dbg_*() interface flags. These flags direct the debugging routine to
* generate different diagnostics, thus the strings themselves are maintained
* Define Dbg_*() interface values. These values direct the debugging routine
* to generate different diagnostics, thus the strings themselves are maintained
* in the debugging library.
*/
#define DBG_SUP_ENVIRON 1
#define DBG_SUP_CMDLINE 2
#define DBG_SUP_DEFAULT 3
#define DBG_CONF_IGNORE 1 /* configuration processing errors */
#define DBG_CONF_VERSION 2
@ -87,18 +84,22 @@ extern "C" {
#define DBG_DLSYM_SELF 3
#define DBG_DLSYM_PROBE 4
#define DBG_DLSYM_SINGLETON 5
#define DBG_DLSYM_NUM DBG_DLSYM_SINGLETON + 1
#define DBG_DLCLOSE_NULL 0
#define DBG_DLCLOSE_IGNORE 1
#define DBG_DLCLOSE_RESCAN 2
#define DBG_WAIT_INIT 1
#define DBG_WAIT_FINI 2
#define DBG_WAIT_SYMBOL 3
#define DBG_SYM_REDUCE_GLOBAL 1 /* reporting global symbols to local */
#define DBG_SYM_REDUCE_RETAIN 2 /* reporting non reduced local syms */
#define DBG_AUD_CALL 1 /* original call to auditor */
#define DBG_AUD_RET 2 /* return from auditor diagnostic */
#define DBG_AUD_LOCAL 0 /* auditor is local */
#define DBG_AUD_GLOBAL 1 /* auditor is global */
#define DBG_AUD_PRELOAD 2 /* auditor is preloaded */
/*
* Group handle operations - passed to Dbg_file_hdl_title(). Indicate why
* handle dependencies are being manipulated.
@ -139,12 +140,18 @@ extern "C" {
#define DBG_BINFO_REF_PARENT 0x2000 /* reference to PARENT */
#define DBG_BINFO_REF_MSK 0xf000
#define DBG_CAP_INITIAL 0
#define DBG_CAP_IGNORE 1
#define DBG_CAP_OLD 2
#define DBG_CAP_NEW 3
#define DBG_CAP_RESOLVED 4
/*
* ld.so.1(1) symbol capabilities processing.
*/
#define DBG_CAP_DEFAULT 0
#define DBG_CAP_USED 1
#define DBG_CAP_CANDIDATE 2
#define DBG_CAP_REJECTED 3
#define DBG_CAP_HW_1 4
#define DBG_CAP_SF_1 5
#define DBG_CAP_HW_2 6
#define DBG_CAP_PLAT 7
#define DBG_CAP_MACH 8
#define DBG_REL_START 1
#define DBG_REL_FINISH 2
@ -153,23 +160,53 @@ extern "C" {
#define DBG_NL_STD 0 /* newline controllers - standard and */
#define DBG_NL_FRC 2 /* forced. */
#define DBG_BNDREJ_NODIR 0 /* bind rejected, direct to nodirect */
#define DBG_BNDREJ_SINGLE 1 /* bind rejected, singleton without */
#define DBG_BNDREJ_DIRECT 0 /* bind rejected, direct to nodirect */
#define DBG_BNDREJ_GROUP 1 /* bind rejected, group to nodirect */
#define DBG_BNDREJ_SINGLE 2 /* bind rejected, singleton without */
/* default search model */
#define DBG_BNDREJ_NUM DBG_BNDREJ_SINGLE
/*
* Dbg_state_str() is used to obtain commonly used "state transition"
* strings used in various debugging output.
*/
#define DBG_STATE_ADD 0 /* add */
#define DBG_STATE_CURRENT 1 /* current */
#define DBG_STATE_EXCLUDE 2 /* exclude */
#define DBG_STATE_IGNORED 3 /* ignored */
#define DBG_STATE_MOD_BEFORE 4 /* modify (before) */
#define DBG_STATE_MOD_AFTER 5 /* modify (after) */
#define DBG_STATE_NEW 6 /* new */
#define DBG_STATE_NEW_IMPLICIT 7 /* new (implicit) */
#define DBG_STATE_RESET 8 /* reset */
#define DBG_STATE_ORIGINAL 9 /* original */
#define DBG_STATE_RESOLVED 10 /* resolved */
#define DBG_STATE_NUM 11
typedef uint_t dbg_state_t;
extern const char *Dbg_state_str(dbg_state_t);
/*
* Define a debug descriptor, and a user macro that inspects the descriptor as
* a means of triggering a class of diagnostic output.
*/
typedef struct {
uint_t d_class; /* debugging classes */
uint_t d_extra; /* extra information for classes */
APlist *d_list; /* associated strings */
uint_t d_extra; /* extra public information */
APlist *d_list; /* accepted link-map list names */
struct timeval d_totaltime; /* total time since entry - */
/* gettimeofday(3c) */
struct timeval d_deltatime; /* delta time since last diagnostic - */
/* gettimeofday(3c) */
} Dbg_desc;
extern Dbg_desc *dbg_desc;
/*
* Macros used to avoid calls to liblddbg unless debugging is enabled.
* liblddbg is lazy loaded --- this prevents it from happening unless
* it will actually be used.
*/
#define DBG_ENABLED (dbg_desc->d_class)
#define DBG_CALL(func) if (DBG_ENABLED) func
@ -180,26 +217,51 @@ extern Dbg_desc *dbg_desc;
* may be interpreted by the debugging library itself or from the callers
* dbg_print() routine.
*/
#define DBG_E_DETAIL 0x0001 /* add detail to a class */
#define DBG_E_LONG 0x0002 /* use long names (ie. no truncation) */
#define DBG_E_DETAIL 0x00000001 /* add detail to a class */
#define DBG_E_LONG 0x00000002 /* use long names (ie. no truncation) */
#define DBG_E_DEMANGLE 0x00000004 /* demangle symbol names */
#define DBG_E_STDNL 0x00000008 /* standard newline indicator */
#define DBG_E_HELP 0x00000010 /* help requested */
#define DBG_E_HELP_EXIT 0x00000020 /* hint: user should exit after help */
#define DBG_E_TTIME 0x00000040 /* prepend total time */
#define DBG_E_DTIME 0x00000080 /* prepend delta time */
#define DBG_E_RESET 0x00000100 /* reset times */
#define DBG_E_STDNL 0x0010 /* standard newline indicator */
/* ld only */
#define DBG_E_SNAME 0x00001000 /* prepend simple name */
#define DBG_E_FNAME 0x00002000 /* prepend full name */
#define DBG_E_CLASS 0x00004000 /* prepend ELF class */
/* ld.so.1 only */
#define DBG_E_LMID 0x00100000 /* prepend link-map id */
#define DBG_E_LMID_LDSO 0x00200000 /* show ldso link-map list */
#define DBG_E_LMID_ALL 0x00400000 /* show all non-ldso link-map lists */
#define DBG_E_LMID_ALT 0x00800000 /* show all ALT link-map lists */
#define DBG_E_LMID_BASE 0x01000000 /* show BASE link-map list */
#define DBG_E_SNAME 0x0100 /* prepend simple name (ld only) */
#define DBG_E_FNAME 0x0200 /* prepend full name (ld only) */
#define DBG_E_CLASS 0x0400 /* prepend ELF class (ld only) */
#define DBG_E_LMID 0x0800 /* prepend link-map id (ld.so.1 only) */
#define DBG_E_DEMANGLE 0x1000 /* demangle symbol names */
#define DBG_NOTDETAIL() !(dbg_desc->d_extra & DBG_E_DETAIL)
#define DBG_NOTLONG() !(dbg_desc->d_extra & DBG_E_LONG)
#define DBG_ISDEMANGLE() \
(dbg_desc->d_extra & DBG_E_DEMANGLE)
#define DBG_TOTALTIME (dbg_desc->d_totaltime)
#define DBG_DELTATIME (dbg_desc->d_deltatime)
#define DBG_ISTTIME() (dbg_desc->d_extra & DBG_E_TTIME)
#define DBG_ISDTIME() (dbg_desc->d_extra & DBG_E_DTIME)
#define DBG_ISTIME() (dbg_desc->d_extra & (DBG_E_TTIME | DBG_E_DTIME))
#define DBG_NOTTIME() !(dbg_desc->d_extra & (DBG_E_TTIME | DBG_E_DTIME))
#define DBG_ISRESET() (dbg_desc->d_extra & DBG_E_RESET)
#define DBG_ONRESET() (dbg_desc->d_extra |= DBG_E_RESET)
#define DBG_OFFRESET() (dbg_desc->d_extra &= ~DBG_E_RESET)
#define DBG_ISSNAME() (dbg_desc->d_extra & DBG_E_SNAME)
#define DBG_ISFNAME() (dbg_desc->d_extra & DBG_E_FNAME)
#define DBG_ISCLASS() (dbg_desc->d_extra & DBG_E_CLASS)
#define DBG_ISLMID() (dbg_desc->d_extra & DBG_E_LMID)
#define DBG_ISDEMANGLE() \
(dbg_desc->d_extra & DBG_E_DEMANGLE)
/*
* Print routine, this must be supplied by the application. The initial
@ -209,7 +271,19 @@ extern Dbg_desc *dbg_desc;
/* PRINTFLIKE2 */
extern void dbg_print(Lm_list *, const char *, ...);
extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
/*
* Initialization routine, called before any other Dbg routines to
* establish the necessary state.
*/
typedef enum { DBG_CALLER_LD, DBG_CALLER_RTLD } dbg_setup_caller_t;
extern int Dbg_setup(dbg_setup_caller_t, const char *,
Dbg_desc *, const char **);
/* Call dbg_print() to produce linker version output */
extern void Dbg_version(void);
/* Call dbg_print() to produce help output */
extern void Dbg_help(void);
/*
* Establish ELF32 and ELF64 class Dbg_*() interfaces.
@ -225,12 +299,28 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_bind_reject Dbg64_bind_reject
#define Dbg_bind_weak Dbg64_bind_weak
#define Dbg_cap_val_hw1 Dbg64_cap_val_hw1
#define Dbg_cap_hw_candidate Dbg64_cap_hw_candidate
#define Dbg_cap_hw_filter Dbg64_cap_hw_filter
#define Dbg_cap_mapfile Dbg64_cap_mapfile
#define Dbg_cap_sec_entry Dbg64_cap_sec_entry
#define Dbg_cap_candidate Dbg64_cap_candidate
#define Dbg_cap_filter Dbg64_cap_filter
#define Dbg_cap_id Dbg64_cap_id
#define Dbg_cap_identical Dbg64_cap_identical
#define Dbg_cap_mapfile_title Dbg64_cap_mapfile_title
#define Dbg_cap_post_title Dbg64_cap_post_title
#define Dbg_cap_sec_title Dbg64_cap_sec_title
#define Dbg_cap_val Dbg64_cap_val
#define Dbg_cap_ptr_entry Dbg64_cap_ptr_entry
#define Dbg_cap_val_entry Dbg64_cap_val_entry
#define Dbg_dl_dladdr Dbg64_dl_dladdr
#define Dbg_dl_dlclose Dbg64_dl_dlclose
#define Dbg_dl_dldump Dbg64_dl_dldump
#define Dbg_dl_dlerror Dbg64_dl_dlerror
#define Dbg_dl_dlinfo Dbg64_dl_dlinfo
#define Dbg_dl_dlopen Dbg64_dl_dlopen
#define Dbg_dl_dlsym Dbg64_dl_dlsym
#define Dbg_dl_iphdr_enter Dbg64_dl_iphdr_enter
#define Dbg_dl_iphdr_callback Dbg64_dl_iphdr_callback
#define Dbg_dl_iphdr_mapchange Dbg64_dl_iphdr_mapchange
#define Dbg_dl_iphdr_unmap_ret Dbg64_dl_iphdr_unmap_ret
#define Dbg_ent_entry Dbg64_ent_entry
#define Dbg_ent_print Dbg64_ent_print
@ -241,15 +331,14 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_file_ar_rescan Dbg64_file_ar_rescan
#define Dbg_file_bind_entry Dbg64_file_bind_entry
#define Dbg_file_bindings Dbg64_file_bindings
#define Dbg_file_bindings_done Dbg64_file_bindings_done
#define Dbg_file_cleanup Dbg64_file_cleanup
#define Dbg_file_cntl Dbg64_file_cntl
#define Dbg_file_config_dis Dbg64_file_config_dis
#define Dbg_file_config_obj Dbg64_file_config_obj
#define Dbg_file_del_rescan Dbg64_file_del_rescan
#define Dbg_file_delete Dbg64_file_delete
#define Dbg_file_dlclose Dbg64_file_dlclose
#define Dbg_file_dldump Dbg64_file_dldump
#define Dbg_file_dlopen Dbg64_file_dlopen
#define Dbg_file_deferred Dbg64_file_deferred
#define Dbg_file_elf Dbg64_file_elf
#define Dbg_file_filtee Dbg64_file_filtee
#define Dbg_file_filter Dbg64_file_filter
@ -260,6 +349,7 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_file_hdl_title Dbg64_file_hdl_title
#define Dbg_file_lazyload Dbg64_file_lazyload
#define Dbg_file_ldso Dbg64_file_ldso
#define Dbg_file_mmapobj Dbg64_file_mmapobj
#define Dbg_file_mode_promote Dbg64_file_mode_promote
#define Dbg_file_modified Dbg64_file_modified
#define Dbg_file_needed Dbg64_file_needed
@ -275,7 +365,7 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_libs_audit Dbg64_libs_audit
#define Dbg_libs_find Dbg64_libs_find
#define Dbg_libs_found Dbg64_libs_found
#define Dbg_libs_ignore Dbg64_libs_ignore
#define Dbg_libs_insecure Dbg64_libs_insecure
#define Dbg_libs_init Dbg64_libs_init
#define Dbg_libs_l Dbg64_libs_l
#define Dbg_libs_path Dbg64_libs_path
@ -284,22 +374,28 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_libs_yp Dbg64_libs_yp
#define Dbg_libs_ylu Dbg64_libs_ylu
#define Dbg_map_dash Dbg64_map_dash
#define Dbg_map_cexp_id Dbg64_map_cexp_id
#define Dbg_map_dv Dbg64_map_dv
#define Dbg_map_dv_entry Dbg64_map_dv_entry
#define Dbg_map_ent Dbg64_map_ent
#define Dbg_map_ent_ord_title Dbg64_map_ent_ord_title
#define Dbg_map_hdr_noalloc Dbg64_map_hdr_noalloc
#define Dbg_map_parse Dbg64_map_parse
#define Dbg_map_pipe Dbg64_map_pipe
#define Dbg_map_pass Dbg64_map_pass
#define Dbg_map_post_title Dbg64_map_post_title
#define Dbg_map_seg Dbg64_map_seg
#define Dbg_map_set_atsign Dbg64_map_set_atsign
#define Dbg_map_set_equal Dbg64_map_set_equal
#define Dbg_map_seg_order Dbg64_map_seg_order
#define Dbg_map_seg_os_order Dbg64_map_seg_os_order
#define Dbg_map_size_new Dbg64_map_size_new
#define Dbg_map_size_old Dbg64_map_size_old
#define Dbg_map_sort_fini Dbg64_map_sort_fini
#define Dbg_map_sort_orig Dbg64_map_sort_orig
#define Dbg_map_sort_seg Dbg64_map_sort_seg
#define Dbg_map_sort_title Dbg64_map_sort_title
#define Dbg_map_symbol Dbg64_map_symbol
#define Dbg_map_version Dbg64_map_version
#define Dbg_move_adjexpandreloc Dbg64_move_adjexpandreloc
#define Dbg_move_adjmovereloc Dbg64_move_adjmovereloc
#define Dbg_move_bad Dbg64_move_bad
#define Dbg_move_data Dbg64_move_data
#define Dbg_move_entry1 Dbg64_move_entry1
#define Dbg_move_entry2 Dbg64_move_entry2
@ -329,13 +425,16 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_reloc_sloppycomdat Dbg64_reloc_sloppycomdat
#define Dbg_sec_added Dbg64_sec_added
#define Dbg_sec_backing Dbg64_sec_backing
#define Dbg_sec_created Dbg64_sec_created
#define Dbg_sec_discarded Dbg64_sec_discarded
#define Dbg_sec_genstr_compress Dbg64_sec_genstr_compress
#define Dbg_sec_group Dbg64_sec_group
#define Dbg_sec_gnu_comdat Dbg64_sec_gnu_comdat
#define Dbg_sec_in Dbg64_sec_in
#define Dbg_sec_order_error Dbg64_sec_order_error
#define Dbg_sec_order_list Dbg64_sec_order_list
#define Dbg_sec_redirected Dbg64_sec_redirected
#define Dbg_sec_strtab Dbg64_sec_strtab
#define Dbg_sec_unsup_strmerge Dbg64_sec_unsup_strmerge
@ -353,17 +452,24 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_support_action Dbg64_support_action
#define Dbg_support_load Dbg64_support_load
#define Dbg_support_req Dbg64_support_req
#define Dbg_support_vnone Dbg64_support_vnone
#define Dbg_syminfo_entry Dbg64_syminfo_entry
#define Dbg_syminfo_title Dbg64_syminfo_title
#define Dbg_syms_ar_checking Dbg64_syms_ar_checking
#define Dbg_syms_ar_entry Dbg64_syms_ar_entry
#define Dbg_syms_ar_force Dbg64_syms_ar_force
#define Dbg_syms_ar_resolve Dbg64_syms_ar_resolve
#define Dbg_syms_ar_skip Dbg64_syms_ar_skip
#define Dbg_syms_ar_title Dbg64_syms_ar_title
#define Dbg_syms_cap_convert Dbg64_syms_cap_convert
#define Dbg_syms_cap_local Dbg64_syms_cap_local
#define Dbg_syms_cap_lookup Dbg64_syms_cap_lookup
#define Dbg_syms_cap_title Dbg64_syms_cap_title
#define Dbg_syms_copy_reloc Dbg64_syms_copy_reloc
#define Dbg_syms_created Dbg64_syms_created
#define Dbg_syms_discarded Dbg64_syms_discarded
#define Dbg_syms_dlsym Dbg64_syms_dlsym
#define Dbg_syms_dup_discarded Dbg64_syms_dup_discarded
#define Dbg_syms_dup_sort_addr Dbg64_syms_dup_sort_addr
#define Dbg_syms_entered Dbg64_syms_entered
#define Dbg_syms_entry Dbg64_syms_entry
@ -376,7 +482,6 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_syms_old Dbg64_syms_old
#define Dbg_syms_process Dbg64_syms_process
#define Dbg_syms_reduce Dbg64_syms_reduce
#define Dbg_syms_reloc Dbg64_syms_reloc
#define Dbg_syms_resolved Dbg64_syms_resolved
#define Dbg_syms_resolving Dbg64_syms_resolving
#define Dbg_syms_sec_entry Dbg64_syms_sec_entry
@ -384,8 +489,8 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_syms_spec_title Dbg64_syms_spec_title
#define Dbg_syms_updated Dbg64_syms_updated
#define Dbg_syms_up_title Dbg64_syms_up_title
#define Dbg_syms_wrap Dbg64_syms_wrap
#define Dbg_util_broadcast Dbg64_util_broadcast
#define Dbg_util_call_array Dbg64_util_call_array
#define Dbg_util_call_fini Dbg64_util_call_fini
#define Dbg_util_call_init Dbg64_util_call_init
@ -401,7 +506,6 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_util_scc_entry Dbg64_util_scc_entry
#define Dbg_util_scc_title Dbg64_util_scc_title
#define Dbg_util_str Dbg64_util_str
#define Dbg_util_wait Dbg64_util_wait
#define Dbg_unused_file Dbg64_unused_file
#define Dbg_unused_lcinterface Dbg64_unused_lcinterface
@ -413,6 +517,7 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_ver_avail_title Dbg64_ver_avail_title
#define Dbg_ver_def_title Dbg64_ver_def_title
#define Dbg_ver_desc_entry Dbg64_ver_desc_entry
#define Dbg_ver_need_done Dbg64_ver_need_done
#define Dbg_ver_need_entry Dbg64_ver_need_entry
#define Dbg_ver_need_title Dbg64_ver_need_title
#define Dbg_ver_nointerface Dbg64_ver_nointerface
@ -427,12 +532,28 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_bind_reject Dbg32_bind_reject
#define Dbg_bind_weak Dbg32_bind_weak
#define Dbg_cap_val_hw1 Dbg32_cap_val_hw1
#define Dbg_cap_hw_candidate Dbg32_cap_hw_candidate
#define Dbg_cap_hw_filter Dbg32_cap_hw_filter
#define Dbg_cap_mapfile Dbg32_cap_mapfile
#define Dbg_cap_sec_entry Dbg32_cap_sec_entry
#define Dbg_cap_candidate Dbg32_cap_candidate
#define Dbg_cap_filter Dbg32_cap_filter
#define Dbg_cap_id Dbg32_cap_id
#define Dbg_cap_identical Dbg32_cap_identical
#define Dbg_cap_mapfile_title Dbg32_cap_mapfile_title
#define Dbg_cap_post_title Dbg32_cap_post_title
#define Dbg_cap_sec_title Dbg32_cap_sec_title
#define Dbg_cap_val Dbg32_cap_val
#define Dbg_cap_ptr_entry Dbg32_cap_ptr_entry
#define Dbg_cap_val_entry Dbg32_cap_val_entry
#define Dbg_dl_dladdr Dbg32_dl_dladdr
#define Dbg_dl_dlclose Dbg32_dl_dlclose
#define Dbg_dl_dldump Dbg32_dl_dldump
#define Dbg_dl_dlerror Dbg32_dl_dlerror
#define Dbg_dl_dlinfo Dbg32_dl_dlinfo
#define Dbg_dl_dlopen Dbg32_dl_dlopen
#define Dbg_dl_dlsym Dbg32_dl_dlsym
#define Dbg_dl_iphdr_enter Dbg32_dl_iphdr_enter
#define Dbg_dl_iphdr_callback Dbg32_dl_iphdr_callback
#define Dbg_dl_iphdr_mapchange Dbg32_dl_iphdr_mapchange
#define Dbg_dl_iphdr_unmap_ret Dbg32_dl_iphdr_unmap_ret
#define Dbg_ent_entry Dbg32_ent_entry
#define Dbg_ent_print Dbg32_ent_print
@ -443,15 +564,14 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_file_ar_rescan Dbg32_file_ar_rescan
#define Dbg_file_bind_entry Dbg32_file_bind_entry
#define Dbg_file_bindings Dbg32_file_bindings
#define Dbg_file_bindings_done Dbg32_file_bindings_done
#define Dbg_file_cleanup Dbg32_file_cleanup
#define Dbg_file_cntl Dbg32_file_cntl
#define Dbg_file_config_dis Dbg32_file_config_dis
#define Dbg_file_config_obj Dbg32_file_config_obj
#define Dbg_file_del_rescan Dbg32_file_del_rescan
#define Dbg_file_delete Dbg32_file_delete
#define Dbg_file_dlclose Dbg32_file_dlclose
#define Dbg_file_dldump Dbg32_file_dldump
#define Dbg_file_dlopen Dbg32_file_dlopen
#define Dbg_file_deferred Dbg32_file_deferred
#define Dbg_file_elf Dbg32_file_elf
#define Dbg_file_filtee Dbg32_file_filtee
#define Dbg_file_filter Dbg32_file_filter
@ -462,6 +582,7 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_file_hdl_title Dbg32_file_hdl_title
#define Dbg_file_lazyload Dbg32_file_lazyload
#define Dbg_file_ldso Dbg32_file_ldso
#define Dbg_file_mmapobj Dbg32_file_mmapobj
#define Dbg_file_mode_promote Dbg32_file_mode_promote
#define Dbg_file_modified Dbg32_file_modified
#define Dbg_file_needed Dbg32_file_needed
@ -477,7 +598,7 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_libs_audit Dbg32_libs_audit
#define Dbg_libs_find Dbg32_libs_find
#define Dbg_libs_found Dbg32_libs_found
#define Dbg_libs_ignore Dbg32_libs_ignore
#define Dbg_libs_insecure Dbg32_libs_insecure
#define Dbg_libs_init Dbg32_libs_init
#define Dbg_libs_l Dbg32_libs_l
#define Dbg_libs_path Dbg32_libs_path
@ -486,22 +607,28 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_libs_yp Dbg32_libs_yp
#define Dbg_libs_ylu Dbg32_libs_ylu
#define Dbg_map_dash Dbg32_map_dash
#define Dbg_map_cexp_id Dbg32_map_cexp_id
#define Dbg_map_dv Dbg32_map_dv
#define Dbg_map_dv_entry Dbg32_map_dv_entry
#define Dbg_map_ent Dbg32_map_ent
#define Dbg_map_ent_ord_title Dbg32_map_ent_ord_title
#define Dbg_map_hdr_noalloc Dbg32_map_hdr_noalloc
#define Dbg_map_parse Dbg32_map_parse
#define Dbg_map_pipe Dbg32_map_pipe
#define Dbg_map_pass Dbg32_map_pass
#define Dbg_map_post_title Dbg32_map_post_title
#define Dbg_map_seg Dbg32_map_seg
#define Dbg_map_set_atsign Dbg32_map_set_atsign
#define Dbg_map_set_equal Dbg32_map_set_equal
#define Dbg_map_seg_order Dbg32_map_seg_order
#define Dbg_map_seg_os_order Dbg32_map_seg_os_order
#define Dbg_map_size_new Dbg32_map_size_new
#define Dbg_map_size_old Dbg32_map_size_old
#define Dbg_map_sort_fini Dbg32_map_sort_fini
#define Dbg_map_sort_orig Dbg32_map_sort_orig
#define Dbg_map_sort_seg Dbg32_map_sort_seg
#define Dbg_map_sort_title Dbg32_map_sort_title
#define Dbg_map_symbol Dbg32_map_symbol
#define Dbg_map_version Dbg32_map_version
#define Dbg_move_adjexpandreloc Dbg32_move_adjexpandreloc
#define Dbg_move_adjmovereloc Dbg32_move_adjmovereloc
#define Dbg_move_bad Dbg32_move_bad
#define Dbg_move_data Dbg32_move_data
#define Dbg_move_entry1 Dbg32_move_entry1
#define Dbg_move_entry2 Dbg32_move_entry2
@ -531,13 +658,16 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_reloc_sloppycomdat Dbg32_reloc_sloppycomdat
#define Dbg_sec_added Dbg32_sec_added
#define Dbg_sec_backing Dbg32_sec_backing
#define Dbg_sec_created Dbg32_sec_created
#define Dbg_sec_discarded Dbg32_sec_discarded
#define Dbg_sec_genstr_compress Dbg32_sec_genstr_compress
#define Dbg_sec_group Dbg32_sec_group
#define Dbg_sec_gnu_comdat Dbg32_sec_gnu_comdat
#define Dbg_sec_in Dbg32_sec_in
#define Dbg_sec_order_error Dbg32_sec_order_error
#define Dbg_sec_order_list Dbg32_sec_order_list
#define Dbg_sec_redirected Dbg32_sec_redirected
#define Dbg_sec_strtab Dbg32_sec_strtab
#define Dbg_sec_unsup_strmerge Dbg32_sec_unsup_strmerge
@ -555,17 +685,24 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_support_action Dbg32_support_action
#define Dbg_support_load Dbg32_support_load
#define Dbg_support_req Dbg32_support_req
#define Dbg_support_vnone Dbg32_support_vnone
#define Dbg_syminfo_entry Dbg32_syminfo_entry
#define Dbg_syminfo_title Dbg32_syminfo_title
#define Dbg_syms_ar_checking Dbg32_syms_ar_checking
#define Dbg_syms_ar_entry Dbg32_syms_ar_entry
#define Dbg_syms_ar_force Dbg32_syms_ar_force
#define Dbg_syms_ar_resolve Dbg32_syms_ar_resolve
#define Dbg_syms_ar_skip Dbg32_syms_ar_skip
#define Dbg_syms_ar_title Dbg32_syms_ar_title
#define Dbg_syms_cap_convert Dbg32_syms_cap_convert
#define Dbg_syms_cap_local Dbg32_syms_cap_local
#define Dbg_syms_cap_lookup Dbg32_syms_cap_lookup
#define Dbg_syms_cap_title Dbg32_syms_cap_title
#define Dbg_syms_copy_reloc Dbg32_syms_copy_reloc
#define Dbg_syms_created Dbg32_syms_created
#define Dbg_syms_discarded Dbg32_syms_discarded
#define Dbg_syms_dlsym Dbg32_syms_dlsym
#define Dbg_syms_dup_discarded Dbg32_syms_dup_discarded
#define Dbg_syms_dup_sort_addr Dbg32_syms_dup_sort_addr
#define Dbg_syms_entered Dbg32_syms_entered
#define Dbg_syms_entry Dbg32_syms_entry
@ -579,7 +716,6 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_syms_old Dbg32_syms_old
#define Dbg_syms_process Dbg32_syms_process
#define Dbg_syms_reduce Dbg32_syms_reduce
#define Dbg_syms_reloc Dbg32_syms_reloc
#define Dbg_syms_resolved Dbg32_syms_resolved
#define Dbg_syms_resolving Dbg32_syms_resolving
#define Dbg_syms_sec_entry Dbg32_syms_sec_entry
@ -587,8 +723,8 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_syms_spec_title Dbg32_syms_spec_title
#define Dbg_syms_updated Dbg32_syms_updated
#define Dbg_syms_up_title Dbg32_syms_up_title
#define Dbg_syms_wrap Dbg32_syms_wrap
#define Dbg_util_broadcast Dbg32_util_broadcast
#define Dbg_util_call_array Dbg32_util_call_array
#define Dbg_util_call_fini Dbg32_util_call_fini
#define Dbg_util_call_init Dbg32_util_call_init
@ -604,7 +740,6 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_util_scc_entry Dbg32_util_scc_entry
#define Dbg_util_scc_title Dbg32_util_scc_title
#define Dbg_util_str Dbg32_util_str
#define Dbg_util_wait Dbg32_util_wait
#define Dbg_unused_file Dbg32_unused_file
#define Dbg_unused_lcinterface Dbg32_unused_lcinterface
@ -616,6 +751,7 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
#define Dbg_ver_avail_title Dbg32_ver_avail_title
#define Dbg_ver_def_title Dbg32_ver_def_title
#define Dbg_ver_desc_entry Dbg32_ver_desc_entry
#define Dbg_ver_need_done Dbg32_ver_need_done
#define Dbg_ver_need_entry Dbg32_ver_need_entry
#define Dbg_ver_need_title Dbg32_ver_need_title
#define Dbg_ver_nointerface Dbg32_ver_nointerface
@ -626,17 +762,40 @@ extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
/*
* External Dbg_*() interface routines.
*/
extern void Dbg_args_files(Lm_list *, int, char *);
extern void Dbg_args_flags(Lm_list *, int, int);
extern void Dbg_args_file(Lm_list *, int, char *);
extern void Dbg_args_guidance_unknown(Lm_list *, const char *);
extern void Dbg_args_option(Lm_list *, int, int, char *);
extern void Dbg_args_str2chr(Lm_list *, int, const char *, int);
extern void Dbg_args_Wldel(Lm_list *, int, const char *);
extern void Dbg_audit_activity(Lm_list *, const char *, const char *,
uint_t);
extern void Dbg_audit_ignore(Rt_map *);
extern void Dbg_audit_interface(Lm_list *, const char *, const char *);
extern void Dbg_audit_lib(Lm_list *, const char *);
extern void Dbg_audit_object(Lm_list *, const char *, const char *);
extern void Dbg_audit_symval(Lm_list *, const char *, const char *,
const char *, Addr, Addr);
extern void Dbg_audit_lib(Rt_map *, const char *, int);
extern void Dbg_audit_objclose(Lm_list *, const char *, const char *);
extern void Dbg_audit_objopen(Lm_list *, int, const char *, const char *,
uint_t, Boolean);
extern void Dbg_audit_objfilter(Lm_list *, int, const char *, const char *,
const char *, const char *);
extern void Dbg_audit_objsearch(Lm_list *, int, const char *, const char *,
uint_t, const char *);
extern void Dbg_audit_pltenter(Lm_list *, int, const char *, const char *,
Addr);
extern void Dbg_audit_pltexit(Lm_list *, const char *, const char *);
extern void Dbg_audit_preinit(Lm_list *, const char *, const char *);
extern void Dbg_audit_symbind(Lm_list *, int, const char *, const char *,
Addr, uint_t);
extern void Dbg_audit_skip(Lm_list *, const char *, const char *);
extern void Dbg_audit_terminate(Lm_list *, const char *);
extern void Dbg_audit_version(Lm_list *, const char *, ulong_t);
extern void Dbg_audit_version(Lm_list *, const char *, uint_t, uint_t);
extern void Dbg_basic_collect(Lm_list *);
extern void Dbg_basic_create(Lm_list *);
extern void Dbg_basic_finish(Lm_list *);
extern void Dbg_basic_files(Lm_list *);
extern void Dbg_basic_options(Lm_list *);
extern void Dbg_basic_relocate(Lm_list *);
extern void Dbg_basic_validate(Lm_list *);
extern void Dbg_bind_global(Rt_map *, Addr, Off, Xword, Pltbindtype,
Rt_map *, Addr, Off, const char *, uint_t);
@ -649,26 +808,43 @@ extern void Dbg_bind_pltpad_to(Rt_map *, Addr, const char *, const char *);
extern void Dbg_bind_reject(Rt_map *, Rt_map *, const char *, int);
extern void Dbg_bind_weak(Rt_map *, Addr, Addr, const char *);
extern void Dbg_cap_hw_candidate(Lm_list *, const char *);
extern void Dbg_cap_hw_filter(Lm_list *, const char *, Rt_map *);
extern void Dbg_cap_mapfile(Lm_list *, Xword, Xword, Half);
extern void Dbg_cap_sec_entry(Lm_list *, uint_t, Xword, Xword, Half);
extern void Dbg_cap_sec_title(Ofl_desc *);
extern void Dbg_cap_val_hw1(Lm_list *, Xword, Half);
extern void Dbg_cap_candidate(Lm_list *, const char *);
extern void Dbg_cap_filter(Lm_list *, const char *, Rt_map *);
extern void Dbg_cap_id(Lm_list *, Lineno, const char *, const char *);
extern void Dbg_cap_identical(Lm_list *, const char *, const char *);
extern void Dbg_cap_mapfile_title(Lm_list *, Lineno);
extern void Dbg_cap_post_title(Lm_list *, int *);
extern void Dbg_cap_sec_title(Lm_list *, const char *);
extern void Dbg_cap_val(Lm_list *, Syscapset *, Syscapset *, Half);
extern void Dbg_cap_ptr_entry(Lm_list *, dbg_state_t, Xword, const char *);
extern void Dbg_cap_val_entry(Lm_list *, dbg_state_t, Xword, Xword, Half);
extern void Dbg_dl_dladdr(Rt_map *, void *);
extern void Dbg_dl_dlclose(Rt_map *, const char *, int);
extern void Dbg_dl_dldump(Rt_map *, const char *, const char *, int);
extern void Dbg_dl_dlerror(Rt_map *, const char *);
extern void Dbg_dl_dlinfo(Rt_map *, const char *, int, void *);
extern void Dbg_dl_dlopen(Rt_map *, const char *, int *, int);
extern void Dbg_dl_dlsym(Rt_map *, const char *, int *, const char *, int);
extern void Dbg_dl_iphdr_enter(Rt_map *, u_longlong_t, u_longlong_t);
extern void Dbg_dl_iphdr_callback(Lm_list *, struct dl_phdr_info *);
extern void Dbg_dl_iphdr_mapchange(Lm_list *, u_longlong_t, u_longlong_t);
extern void Dbg_dl_iphdr_unmap_ret(Lm_list *);
extern const char *
Dbg_demangle_name(const char *);
extern void Dbg_ent_entry(Lm_list *, Half, Ent_desc *);
extern void Dbg_ent_print(Lm_list *, Half, List *, Boolean);
extern void Dbg_ent_entry(Lm_list *, uchar_t, Half, Ent_desc *);
extern void Dbg_ent_print(Lm_list *, uchar_t, Half, APlist *);
extern void Dbg_file_analyze(Rt_map *);
extern void Dbg_file_aout(Lm_list *, const char *, ulong_t, ulong_t,
ulong_t, const char *, Aliste);
extern void Dbg_file_ar(Lm_list *, const char *, int);
extern void Dbg_file_ar_rescan(Lm_list *);
extern void Dbg_file_aout(Lm_list *, const char *, Addr, size_t,
const char *, Aliste);
extern void Dbg_file_ar(Lm_list *, const char *, Boolean);
extern void Dbg_file_ar_rescan(Lm_list *, int, int);
extern void Dbg_file_bind_entry(Lm_list *, Bnd_desc *);
extern void Dbg_file_bindings(Rt_map *, int);
extern void Dbg_file_bindings_done(Lm_list *);
extern void Dbg_file_cleanup(Lm_list *, const char *, Aliste);
extern void Dbg_file_cntl(Lm_list *, Aliste, Aliste);
extern void Dbg_file_config_dis(Lm_list *, const char *, int);
@ -676,11 +852,9 @@ extern void Dbg_file_config_obj(Lm_list *, const char *, const char *,
const char *);
extern void Dbg_file_del_rescan(Lm_list *);
extern void Dbg_file_delete(Rt_map *);
extern void Dbg_file_dlclose(Lm_list *, const char *, int);
extern void Dbg_file_dldump(Rt_map *, const char *, int);
extern void Dbg_file_dlopen(Rt_map *, const char *, int *, int);
extern void Dbg_file_elf(Lm_list *, const char *, ulong_t, ulong_t,
ulong_t, ulong_t, const char *, Aliste);
extern void Dbg_file_deferred(Lm_list *, const char *, const char *);
extern void Dbg_file_elf(Lm_list *, const char *, Addr, size_t,
const char *, Aliste);
extern void Dbg_file_filtee(Lm_list *, const char *, const char *, int);
extern void Dbg_file_filter(Lm_list *, const char *, const char *, int);
extern void Dbg_file_fixname(Lm_list *, const char *, const char *);
@ -691,6 +865,8 @@ extern void Dbg_file_hdl_title(int);
extern void Dbg_file_lazyload(Rt_map *, const char *, const char *);
extern void Dbg_file_ldso(Rt_map *, char **, auxv_t *, const char *,
Aliste);
extern void Dbg_file_mmapobj(Lm_list *, const char *, mmapobj_result_t *,
uint_t);
extern void Dbg_file_mode_promote(Rt_map *, int);
extern void Dbg_file_modified(Lm_list *, const char *, const char *,
const char *, int, int, Elf *, Elf *);
@ -707,32 +883,42 @@ extern void Dbg_got_display(Ofl_desc *, Off, int, Word, size_t);
extern void Dbg_libs_audit(Lm_list *, const char *, const char *);
extern void Dbg_libs_find(Lm_list *, const char *);
extern void Dbg_libs_found(Lm_list *, const char *, int);
extern void Dbg_libs_ignore(Lm_list *, const char *);
extern void Dbg_libs_init(Lm_list *, List *, List *);
extern void Dbg_libs_insecure(Lm_list *, const char *, int);
extern void Dbg_libs_init(Lm_list *, APlist *, APlist *);
extern void Dbg_libs_l(Lm_list *, const char *, const char *);
extern void Dbg_libs_path(Lm_list *, const char *, uint_t, const char *);
extern void Dbg_libs_req(Lm_list *, const char *, const char *,
const char *);
extern void Dbg_libs_update(Lm_list *, List *, List *);
extern void Dbg_libs_update(Lm_list *, APlist *, APlist *);
extern void Dbg_libs_yp(Lm_list *, const char *);
extern void Dbg_libs_ylu(Lm_list *, const char *, const char *, int);
extern void Dbg_map_dash(Lm_list *, const char *, Sdf_desc *);
extern void Dbg_map_ent(Lm_list *, Boolean, Ent_desc *, Ofl_desc *);
extern void Dbg_map_parse(Lm_list *, const char *);
extern void Dbg_map_pipe(Lm_list *, Sg_desc *, const char *, const Word);
extern void Dbg_map_seg(Ofl_desc *, int, Sg_desc *);
extern void Dbg_map_set_atsign(Boolean);
extern void Dbg_map_set_equal(Boolean);
extern void Dbg_map_size_new(Lm_list *, const char *);
extern void Dbg_map_size_old(Ofl_desc *, Sym_desc *);
extern void Dbg_map_sort_fini(Lm_list *, Sg_desc *);
extern void Dbg_map_sort_orig(Lm_list *, Sg_desc *);
extern void Dbg_map_cexp_id(Lm_list *, Boolean, const char *, Lineno,
const char *);
extern void Dbg_map_dv(Lm_list *, const char *, Lineno);
extern void Dbg_map_dv_entry(Lm_list *, Lineno, int, const char *);
extern void Dbg_map_ent(Lm_list *, Ent_desc *, Ofl_desc *, Lineno);
extern void Dbg_map_ent_ord_title(Lm_list *, const char *);
extern void Dbg_map_hdr_noalloc(Lm_list *, Lineno);
extern void Dbg_map_parse(Lm_list *, const char *, int);
extern void Dbg_map_pass(Lm_list *, Boolean, const char *, Lineno,
const char *);
extern void Dbg_map_post_title(Lm_list *);
extern void Dbg_map_seg(Ofl_desc *, dbg_state_t, int, Sg_desc *, Lineno);
extern void Dbg_map_seg_order(Ofl_desc *, uchar_t, Half, dbg_state_t,
Lineno);
extern void Dbg_map_seg_os_order(Lm_list *, Sg_desc *, const char *,
Word, Lineno);
extern void Dbg_map_size_new(Lm_list *, const char *, const char *, Lineno);
extern void Dbg_map_size_old(Ofl_desc *, Sym_desc *, const char *, Lineno);
extern void Dbg_map_sort_title(Lm_list *, Boolean);
extern void Dbg_map_sort_seg(Lm_list *, uchar_t, Half, Sg_desc *);
extern void Dbg_map_symbol(Ofl_desc *, Sym_desc *);
extern void Dbg_map_version(Lm_list *, const char *, const char *, int);
extern void Dbg_move_adjexpandreloc(Lm_list *, Xword, const char *);
extern void Dbg_move_adjmovereloc(Lm_list *, Xword, Xword, const char *);
extern void Dbg_move_bad(Lm_list *, ulong_t, const char *, Addr);
extern void Dbg_move_data(Rt_map *);
extern void Dbg_move_entry1(Lm_list *, int, Move *, Sym_desc *);
extern void Dbg_move_entry2(Lm_list *, Move *, Word, const char *);
@ -747,8 +933,8 @@ extern void Dbg_reloc_apply_val(Lm_list *, int, Xword, Xword);
extern void Dbg_reloc_ars_entry(Lm_list *, int, Word, Half, Rel_desc *);
extern void Dbg_reloc_copy(Rt_map *, Rt_map *, const char *, int);
extern void Dbg_reloc_discard(Lm_list *, Half, Rel_desc *);
extern void Dbg_reloc_doact(Lm_list *, int, Half, Word, Word, Xword, Xword,
const char *, Os_desc *);
extern void Dbg_reloc_doact(Lm_list *, int, Half, Word, Rel_desc *,
Xword, Xword, rel_desc_sname_func_t);
extern void Dbg_reloc_doact_title(Lm_list *);
extern void Dbg_reloc_dooutrel(Lm_list *, Word);
extern void Dbg_reloc_entry(Lm_list *, const char *, Half, Word, void *,
@ -757,35 +943,40 @@ extern void Dbg_reloc_error(Lm_list *, int, Half, Word, void *,
const char *);
extern void Dbg_reloc_generate(Lm_list *, Os_desc *, Word);
extern void Dbg_reloc_in(Lm_list *, int, Half, Word, void *, const char *,
const char *);
Word, const char *);
extern void Dbg_reloc_ors_entry(Lm_list *, int, Word, Half, Rel_desc *);
extern void Dbg_reloc_out(Ofl_desc *, int, Word, void *, const char *,
const char *);
extern void Dbg_reloc_proc(Lm_list *, Os_desc *, Is_desc *, Is_desc *);
extern void Dbg_reloc_run(Rt_map *, uint_t, int, int);
extern void Dbg_reloc_transition(Lm_list *, Half, Word, Rel_desc *);
extern void Dbg_reloc_sloppycomdat(Lm_list *, const char *, Sym_desc *);
extern void Dbg_reloc_transition(Lm_list *, Half, Word, Rel_desc *,
rel_desc_sname_func_t);
extern void Dbg_reloc_sloppycomdat(Lm_list *, Sym_desc *);
extern void Dbg_sec_added(Lm_list *, Os_desc *, Sg_desc *);
extern void Dbg_sec_backing(Lm_list *);
extern void Dbg_sec_created(Lm_list *, Os_desc *, Sg_desc *);
extern void Dbg_sec_discarded(Lm_list *, Is_desc *, Is_desc *);
extern void Dbg_sec_genstr_compress(Lm_list *, const char *,
Xword, Xword);
extern void Dbg_sec_group(Lm_list *, Is_desc *, Group_desc *);
extern void Dbg_sec_gnu_comdat(Lm_list *, Is_desc *, Boolean, Boolean);
extern void Dbg_sec_in(Lm_list *, Is_desc *);
extern void Dbg_sec_order_error(Lm_list *, Ifl_desc *, Word, int);
extern void Dbg_sec_order_list(Ofl_desc *, int);
extern void Dbg_sec_redirected(Lm_list *, Is_desc *, const char *);
extern void Dbg_sec_strtab(Lm_list *, Os_desc *, Str_tbl *);
extern void Dbg_sec_unsup_strmerge(Lm_list *, Is_desc *);
extern void Dbg_seg_desc_entry(Lm_list *, Half, int, Sg_desc *);
extern void Dbg_seg_desc_entry(Lm_list *, uchar_t, Half, int, Sg_desc *,
Boolean);
extern void Dbg_seg_entry(Ofl_desc *, int, Sg_desc *);
extern void Dbg_seg_list(Lm_list *, Half, List *);
extern void Dbg_seg_list(Lm_list *, uchar_t, Half, APlist *);
extern void Dbg_seg_os(Ofl_desc *, Os_desc *, int);
extern void Dbg_seg_title(Lm_list *);
extern void Dbg_shdr_modified(Lm_list *, const char *, Half, Shdr *, Shdr *,
const char *);
extern void Dbg_shdr_modified(Lm_list *, const char *, uchar_t, Half,
Word, Shdr *, Shdr *, const char *);
extern void Dbg_statistics_ar(Ofl_desc *);
extern void Dbg_statistics_ld(Ofl_desc *);
@ -794,21 +985,29 @@ extern void Dbg_support_action(Lm_list *, const char *, const char *,
Support_ndx, const char *);
extern void Dbg_support_load(Lm_list *, const char *, const char *);
extern void Dbg_support_req(Lm_list *, const char *, int);
extern void Dbg_support_vnone(Lm_list *, const char *);
extern void Dbg_syminfo_entry(Lm_list *, Word, Syminfo *, Sym *,
const char *, Dyn *);
extern void Dbg_syminfo_title(Lm_list *);
extern void Dbg_syms_ar_checking(Lm_list *, Xword, Elf_Arsym *,
const char *);
extern void Dbg_syms_ar_entry(Lm_list *, Xword, Elf_Arsym *);
extern void Dbg_syms_ar_resolve(Lm_list *, Xword, Elf_Arsym *,
const char *, int);
extern void Dbg_syms_ar_title(Lm_list *, const char *, int);
extern void Dbg_syms_ar_checking(Lm_list *, const char *, const char *,
Elf_Arsym *);
extern void Dbg_syms_ar_force(Lm_list *, const char *, const char *);
extern void Dbg_syms_ar_resolve(Lm_list *, const char *, const char *,
Elf_Arsym *);
extern void Dbg_syms_ar_skip(Lm_list *, const char *, Elf_Arsym *);
extern void Dbg_syms_ar_title(Lm_list *, const char *, Boolean);
extern void Dbg_syms_cap_convert(Ofl_desc *, Word, const char *, Sym *);
extern void Dbg_syms_cap_local(Ofl_desc *, Word, const char *, Sym *,
Sym_desc *);
extern void Dbg_syms_cap_lookup(Rt_map *, uint_t, const char *, uint_t,
Half, Syscapset *);
extern void Dbg_syms_cap_title(Ofl_desc *);
extern void Dbg_syms_copy_reloc(Ofl_desc *, Sym_desc *, Word);
extern void Dbg_syms_created(Lm_list *, const char *);
extern void Dbg_syms_discarded(Lm_list *, Sym_desc *);
extern void Dbg_syms_dlsym(Rt_map *, const char *, int *, const char *,
int);
extern void Dbg_syms_dup_discarded(Lm_list *, Word ndx, Sym_desc *);
extern void Dbg_syms_dup_sort_addr(Lm_list *, const char *, const char *,
const char *, Addr);
extern void Dbg_syms_entered(Ofl_desc *, Sym *, Sym_desc *);
@ -826,7 +1025,6 @@ extern void Dbg_syms_old(Ofl_desc *, Sym_desc *);
extern void Dbg_syms_process(Lm_list *, Ifl_desc *);
extern void Dbg_syms_reduce(Ofl_desc *, int, Sym_desc *, int,
const char *);
extern void Dbg_syms_reloc(Ofl_desc *, Sym_desc *);
extern void Dbg_syms_resolved(Ofl_desc *, Sym_desc *);
extern void Dbg_syms_resolving(Ofl_desc *, Word, const char *, int, int,
Sym *, Sym *, Sym_desc *, Ifl_desc *);
@ -835,12 +1033,12 @@ extern void Dbg_syms_sec_title(Lm_list *);
extern void Dbg_syms_spec_title(Lm_list *);
extern void Dbg_syms_updated(Ofl_desc *, Sym_desc *, const char *);
extern void Dbg_syms_up_title(Lm_list *);
extern void Dbg_syms_wrap(Lm_list *, Word, const char *, const char *);
extern void Dbg_tls_modactivity(Lm_list *, void *, uint_t);
extern void Dbg_tls_static_block(Lm_list *, void *, ulong_t, ulong_t);
extern void Dbg_tls_static_resv(Rt_map *, ulong_t, ulong_t);
extern void Dbg_util_broadcast(Rt_map *);
extern void Dbg_util_call_array(Rt_map *, void *, int, Word);
extern void Dbg_util_call_fini(Rt_map *);
extern void Dbg_util_call_init(Rt_map *, int);
@ -854,10 +1052,9 @@ extern void Dbg_util_intoolate(Rt_map *);
extern void Dbg_util_lcinterface(Rt_map *, int, char *);
extern void Dbg_util_nl(Lm_list *, int);
extern void Dbg_util_no_init(Rt_map *);
extern void Dbg_util_str(Lm_list *, const char *);
extern void Dbg_util_scc_entry(Rt_map *, uint_t);
extern void Dbg_util_scc_title(Lm_list *, int);
extern void Dbg_util_wait(Rt_map *, Rt_map *, int);
extern void Dbg_util_str(Lm_list *, const char *);
extern void Dbg_unused_file(Lm_list *, const char *, int, uint_t);
extern void Dbg_unused_lcinterface(Rt_map *, Rt_map *, int);
@ -870,6 +1067,7 @@ extern void Dbg_ver_avail_entry(Lm_list *, Ver_index *, const char *);
extern void Dbg_ver_avail_title(Lm_list *, const char *);
extern void Dbg_ver_def_title(Lm_list *, const char *);
extern void Dbg_ver_desc_entry(Lm_list *, Ver_desc *);
extern void Dbg_ver_need_done(Lm_list *);
extern void Dbg_ver_need_entry(Lm_list *, Half, const char *,
const char *);
extern void Dbg_ver_need_title(Lm_list *, const char *);
@ -882,6 +1080,7 @@ extern void Dbg_ver_symbol(Lm_list *, const char *);
#define ELF_DBG_ELFDUMP 1
#define ELF_DBG_RTLD 2
#define ELF_DBG_LD 3
#define ELF_DBG_LD_ACT 4
/*
* Define generic Elf_*() interfaces.
@ -967,12 +1166,14 @@ extern void Elf_syminfo_title(Lm_list *);
#endif
extern void Elf_cap_entry(Lm_list *, Cap *, int, Half);
extern void Elf_cap_entry(Lm_list *, Cap *, int, const char *, size_t,
Half);
extern void Elf_cap_title(Lm_list *);
extern const char \
*Elf_demangle_name(const char *);
extern void Elf_dyn_entry(Lm_list *, Dyn *, int, const char *, Half);
extern void Elf_dyn_entry(Lm_list *, Dyn *, int, const char *,
uchar_t, Half);
extern void Elf_dyn_null_entry(Lm_list *, Dyn *, int, int);
extern void Elf_dyn_title(Lm_list *);
@ -982,7 +1183,7 @@ extern void Elf_got_entry(Lm_list *, Sword, Addr, Xword, Half,
uchar_t, uchar_t, Word, void *, const char *);
extern void Elf_got_title(Lm_list *);
extern void Elf_phdr(Lm_list *, Half, Phdr *);
extern void Elf_phdr(Lm_list *, uchar_t, Half, Phdr *);
extern void Elf_reloc_apply_val(Lm_list *, int, Xword, Xword);
extern void Elf_reloc_apply_reg(Lm_list *, int, Half, Xword, Xword);
@ -993,10 +1194,10 @@ extern void Elf_reloc_entry_2(Lm_list *, int, const char *, Word,
const char *);
extern void Elf_reloc_title(Lm_list *, int, Word);
extern void Elf_shdr(Lm_list *, Half, Shdr *);
extern void Elf_shdr(Lm_list *, uchar_t, Half, Shdr *);
extern void Elf_syms_table_entry(Lm_list *, int, const char *, Half, Sym *,
Versym, int, const char *, const char *);
extern void Elf_syms_table_entry(Lm_list *, int, const char *, uchar_t,
Half, Sym *, Versym, int, const char *, const char *);
extern void Elf_syms_table_title(Lm_list *, int);
extern void Elf_ver_def_title(Lm_list *);

View File

@ -24,8 +24,7 @@
* All Rights Reserved
*
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Global include file for all sgs.
*/
@ -33,9 +32,6 @@
#ifndef _SGS_H
#define _SGS_H
#pragma ident "%Z%%M% %I% %E% SMI"
#ifdef __cplusplus
extern "C" {
#endif
@ -51,6 +47,7 @@ extern "C" {
#include <sys/types.h>
#include <sys/machelf.h>
#include <stdlib.h>
#include <stdarg.h>
#include <libelf.h>
#include <assert.h>
#include <alist.h>
@ -122,17 +119,27 @@ typedef enum {
} Boolean;
/*
* Types of errors (used by eprintf()), together with a generic error return
* Types of errors (used by veprintf()), together with a generic error return
* value.
*/
typedef enum {
ERR_NONE,
ERR_WARNING,
ERR_FATAL,
ERR_ELF,
ERR_NUM /* Must be last */
ERR_NONE, /* plain message */
ERR_WARNING_NF, /* warning that cannot be promoted to fatal */
ERR_WARNING, /* warning that can be promoted to fatal */
ERR_GUIDANCE, /* guidance warning that can be promoted */
ERR_FATAL, /* fatal error */
ERR_ELF, /* fatal libelf error */
ERR_NUM /* # of Error codes. Must be last */
} Error;
/*
* Type used to represent line numbers within files, and a corresponding
* printing macro for it.
*/
typedef ulong_t Lineno;
#define EC_LINENO(_x) EC_XWORD(_x) /* "llu" */
#if defined(_LP64) && !defined(_ELF64)
#define S_ERROR (~(uint_t)0)
#else
@ -140,48 +147,16 @@ typedef enum {
#endif
/*
* LIST_TRAVERSE() is used as the only "argument" of a "for" loop to
* traverse a linked list. The node pointer `node' is set to each node in
* turn and the corresponding data pointer is copied to `data'. The macro
* is used as in
* for (LIST_TRAVERSE(List *list, Listnode *node, void *data)) {
* process(data);
* }
* CTF currently does not handle automatic array variables sized via function
* arguments (VLA arrays) properly, when the code is compiled with gcc.
* Adding 1 to the size is a workaround. VLA_SIZE, and its use, should be
* pulled when CTF is fixed or replaced.
*/
#define LIST_TRAVERSE(L, N, D) \
(void) (((N) = (L)->head) != NULL && ((D) = (N)->data) != NULL); \
(N) != NULL; \
(void) (((N) = (N)->next) != NULL && ((D) = (N)->data) != NULL)
typedef struct listnode Listnode;
typedef struct list List;
struct listnode { /* a node on a linked list */
void *data; /* the data item */
Listnode *next; /* the next element */
};
struct list { /* a linked list */
Listnode *head; /* the first element */
Listnode *tail; /* the last element */
};
#ifdef _SYSCALL32
typedef struct listnode32 Listnode32;
typedef struct list32 List32;
struct listnode32 { /* a node on a linked list */
Elf32_Addr data; /* the data item */
Elf32_Addr next; /* the next element */
};
struct list32 { /* a linked list */
Elf32_Addr head; /* the first element */
Elf32_Addr tail; /* the last element */
};
#endif /* _SYSCALL32 */
#ifdef __GNUC__
#define VLA_SIZE(_arg) ((_arg) + 1)
#else
#define VLA_SIZE(_arg) (_arg)
#endif
/*
* Structure to maintain rejected files elf information. Files that are not
@ -192,7 +167,7 @@ struct list32 { /* a linked list */
*/
typedef struct {
ushort_t rej_type; /* SGS_REJ_ value */
ushort_t rej_flag; /* additional information */
ushort_t rej_flags; /* additional information */
uint_t rej_info; /* numeric and string information */
const char *rej_str; /* associated with error */
const char *rej_name; /* object name - expanded library */
@ -212,7 +187,16 @@ typedef struct {
/* required */
#define SGS_REJ_STR 10 /* generic error - info is a string */
#define SGS_REJ_UNKFILE 11 /* unknown file type */
#define SGS_REJ_HWCAP_1 12 /* hardware capabilities mismatch */
#define SGS_REJ_UNKCAP 12 /* unknown capabilities */
#define SGS_REJ_HWCAP_1 13 /* hardware capabilities mismatch */
#define SGS_REJ_SFCAP_1 14 /* software capabilities mismatch */
#define SGS_REJ_MACHCAP 15 /* machine capability mismatch */
#define SGS_REJ_PLATCAP 16 /* platform capability mismatch */
#define SGS_REJ_HWCAP_2 17 /* hardware capabilities mismatch */
#define SGS_REJ_ARCHIVE 18 /* archive used in invalid context */
#define SGS_REJ_NUM 19
#define FLG_REJ_ALTER 0x01 /* object name is an alternative */
/*
* For those source files used both inside and outside of the
@ -227,44 +211,42 @@ typedef struct {
#define realloc libld_realloc
#define libld_calloc(x, a) libld_malloc(((size_t)x) * ((size_t)a))
extern void libld_free(void *);
extern void *libld_malloc(size_t);
extern void *libld_realloc(void *, size_t);
extern void libld_free(void *);
extern void *libld_malloc(size_t);
extern void *libld_realloc(void *, size_t);
#endif
/*
* Data structures (defined in libld.h).
*/
typedef struct audit_desc Audit_desc;
typedef struct audit_info Audit_info;
typedef struct audit_list Audit_list;
typedef struct cap_desc Cap_desc;
typedef struct ent_desc Ent_desc;
typedef struct group_desc Group_desc;
typedef struct ifl_desc Ifl_desc;
typedef struct is_desc Is_desc;
typedef struct isa_desc Isa_desc;
typedef struct isa_opt Isa_opt;
typedef struct mv_desc Mv_desc;
typedef struct ofl_desc Ofl_desc;
typedef struct os_desc Os_desc;
typedef struct ofl_desc Ofl_desc;
typedef struct rel_cache Rel_cache;
typedef struct rel_cachebuf Rel_cachebuf;
typedef struct rel_aux_cachebuf Rel_aux_cachebuf;
typedef struct rel_aux Rel_aux;
typedef struct rel_desc Rel_desc;
typedef struct sdf_desc Sdf_desc;
typedef struct sdv_desc Sdv_desc;
typedef struct sec_order Sec_order;
typedef struct sg_desc Sg_desc;
typedef struct sort_desc Sort_desc;
typedef struct sec_order Sec_order;
typedef struct sym_desc Sym_desc;
typedef struct sym_aux Sym_aux;
typedef struct sym_avlnode Sym_avlnode;
typedef struct sym_aux Sym_aux;
typedef struct sym_desc Sym_desc;
typedef struct uts_desc Uts_desc;
typedef struct ver_desc Ver_desc;
typedef struct ver_index Ver_index;
typedef struct audit_desc Audit_desc;
typedef struct audit_info Audit_info;
typedef struct audit_list Audit_list;
/*
* Data structures defined in machrel.h.
*/
typedef struct rel_desc Rel_desc;
/*
* Data structures defined in rtld.h.
@ -279,7 +261,7 @@ typedef struct lm_list32 Lm_list32;
*/
extern int assfail(const char *, const char *, int);
extern void eprintf(Lm_list *, Error, const char *, ...);
extern char *sgs_demangle(char *);
extern void veprintf(Lm_list *, Error, const char *, va_list);
extern uint_t sgs_str_hash(const char *);
extern uint_t findprime(uint_t);

View File

@ -1,6 +1,5 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
#
# CDDL HEADER START
#
@ -21,8 +20,6 @@
#
# CDDL HEADER END
#
# ident "%Z%%M% %I% %E% SMI"
#
#
# Global message identifiers for the sgs utilities. This information is read
# by sgsmsg(1l) using the -i option.
@ -37,7 +34,7 @@ MSG_ID_RTLD 1 SUNW_OST_SGS /* sgs/rtld */
MSG_ID_LIBRTLD 2 SUNW_OST_SGS /* sgs/librtld */
MSG_ID_LIBLD 3 SUNW_OST_SGS /* sgs/libld */
MSG_ID_LIBLDDBG 4 SUNW_OST_SGS /* sgs/liblddbg */
MSG_ID_LIBLDSTAB 5 SUNW_OST_SGS /* sgs/libldstab */
MSG_ID_LIBRTLD_DB 6 SUNW_OST_SGS /* sgs/librtld_db */
MSG_ID_LIBPROF 7 SUNW_OST_SGS /* sgs/libprof */
MSG_ID_LIBCRLE 8 SUNW_OST_SGS /* sgs/libcrle */
@ -60,3 +57,4 @@ MSG_ID_ELFEDIT_STR 27 SUNW_OST_SGS /* str: */
MSG_ID_ELFEDIT_SYM 27 SUNW_OST_SGS /* sym: */
MSG_ID_ELFEDIT_SYMINFO 27 SUNW_OST_SGS /* syminfo: */
MSG_ID_ELFWRAP 28 SUNW_OST_SGS /* sgs/elfwrap */
MSG_ID_AR 29 SUNW_OST_SGS /* sgs/ar */

View File

@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
*
* sgsmsg generates several message files from an input template file. Messages
* are constructed for use with gettext(3i) - the default - or catgets(3c). The
@ -66,7 +65,6 @@
* the data array being built in msg.c. The index into this array
* becomes the `message' identifier created in the msg.h file.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <fcntl.h>
#include <stdlib.h>
@ -393,15 +391,22 @@ init_defs(void)
return (1);
}
if (fprintf(fddefs, "#include <sgsmsg.h>\t/* Msg typedef */\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
/*
* add "typedef int Msg;"
* The MSG_SGS_ARRAY_NAME macro supplies a generic way to
* reference the string table regardless of its name.
*/
if (fprintf(fddefs, "typedef int\tMsg;\n\n") < 0) {
if (fprintf(fddefs, "#define\tMSG_SGS_LOCAL_ARRAY\t__%s\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
@ -418,7 +423,13 @@ init_defs(void)
return (1);
}
}
if (fprintf(fddefs, "#define\tMSG_ORIG(x)\t&__%s[x]\n\n",
if (fprintf(fddefs,
"#define\tMSG_ORIG_STRTAB(_x, _s)\t&_s[_x]\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs,
"#define\tMSG_ORIG(x)\tMSG_ORIG_STRTAB(x, __%s)\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
@ -453,17 +464,14 @@ fini_defs(void)
return (1);
}
/*
* When __lint is defined, Msg is a char *. This allows lint to
* check our format strings against it's arguments.
*/
if (fprintf(fddefs, "\ntypedef char *\tMsg;\n\n") < 0) {
if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
interface) < 0) {
if (fprintf(fddefs, "#ifndef MSG_SGS_LOCAL_ARRAY\n"
"#define\tMSG_SGS_LOCAL_ARRAY\t\"\"\n#endif\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
@ -478,11 +486,24 @@ fini_defs(void)
}
if (fprintf(fddefs,
"#define MSG_ORIG_STRTAB(_x, _s)\t_x\n"
"#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
/*
* Provide a way to get the array and function declarations above
* without also getting the actual messages. This is useful in
* our lintsup.c files that include more than one message header.
* lintsup doesn't need the actual messages, and this prevents
* macro name collisions.
*/
if (fprintf(fddefs, "\n#ifndef LINTSUP_SUPPRESS_STRINGS\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
/*
* Copy the temporary lint defs file into the new header.
*/
@ -510,6 +531,11 @@ fini_defs(void)
(void) free(buf);
}
if (fprintf(fddefs, "\n#endif\t/* LINTSUP_SUPPRESS_STRINGS */\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
@ -1102,8 +1128,9 @@ main(int argc, char ** argv)
}
}
if (fddefs && fddata) {
(void) sprintf(fllint, "%s.%d", nmlint, (int)getpid());
if ((fdlint = fopen(fllint, "w+")) == NULL) {
(void) sprintf(fllint, "%s.%d.XXXXXX", nmlint, (int)getpid());
if ((mkstemp(fllint) == -1) ||
((fdlint = fopen(fllint, "w+")) == NULL)) {
(void) fprintf(stderr, Errmsg_opne, fllint,
strerror(errno));
return (1);

View File

@ -20,12 +20,10 @@
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <_string_table.h>
#include <strings.h>
#include <sgs.h>
@ -160,7 +158,7 @@ st_new(uint_t flags)
{
Str_tbl *stp;
if ((stp = calloc(sizeof (Str_tbl), 1)) == NULL)
if ((stp = calloc(sizeof (*stp), 1)) == NULL)
return (NULL);
/*
@ -175,7 +173,7 @@ st_new(uint_t flags)
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
return (stp);
if ((stp->st_lentree = calloc(sizeof (avl_tree_t), 1)) == NULL)
if ((stp->st_lentree = calloc(sizeof (*stp->st_lentree), 1)) == NULL)
return (NULL);
avl_create(stp->st_lentree, &avl_len_compare, sizeof (LenNode),
@ -187,9 +185,9 @@ st_new(uint_t flags)
/*
* Insert a new string into the Str_tbl. There are two AVL trees used.
*
* . The first LenNode AVL tree maintains a tree of nodes based on string
* - The first LenNode AVL tree maintains a tree of nodes based on string
* sizes.
* . Each LenNode maintains a StrNode AVL tree for each string. Large
* - Each LenNode maintains a StrNode AVL tree for each string. Large
* applications have been known to contribute thousands of strings of
* the same size. Should strings need to be removed (-z ignore), then
* the string AVL tree makes this removal efficient and scalable.
@ -227,12 +225,13 @@ st_insert(Str_tbl *stp, const char *str)
*/
ln.ln_strlen = len;
if ((lnp = avl_find(stp->st_lentree, &ln, &where)) == NULL) {
if ((lnp = calloc(sizeof (LenNode), 1)) == NULL)
if ((lnp = calloc(sizeof (*lnp), 1)) == NULL)
return (-1);
lnp->ln_strlen = len;
avl_insert(stp->st_lentree, lnp, where);
if ((lnp->ln_strtree = calloc(sizeof (avl_tree_t), 1)) == NULL)
if ((lnp->ln_strtree = calloc(sizeof (*lnp->ln_strtree), 1)) ==
NULL)
return (0);
avl_create(lnp->ln_strtree, &avl_str_compare, sizeof (StrNode),
@ -246,7 +245,7 @@ st_insert(Str_tbl *stp, const char *str)
*/
sn.sn_str = str;
if ((snp = avl_find(lnp->ln_strtree, &sn, &where)) == NULL) {
if ((snp = calloc(sizeof (StrNode), 1)) == NULL)
if ((snp = calloc(sizeof (*snp), 1)) == NULL)
return (-1);
snp->sn_str = str;
avl_insert(lnp->ln_strtree, snp, where);
@ -513,7 +512,7 @@ st_hash_insert(Str_tbl *stp, const char *str, size_t len)
/*
* allocate a new master string
*/
if ((mstr = calloc(sizeof (Str_hash), 1)) == 0)
if ((mstr = calloc(sizeof (*mstr), 1)) == 0)
return (-1);
mstr->sm_next = stp->st_mstrlist;
stp->st_mstrlist = mstr;
@ -528,7 +527,7 @@ st_hash_insert(Str_tbl *stp, const char *str, size_t len)
stp->st_strsize += len - mstr->sm_strlen;
}
if ((sthash = calloc(sizeof (Str_hash), 1)) == 0)
if ((sthash = calloc(sizeof (*sthash), 1)) == 0)
return (-1);
mstr->sm_hashval = sthash->hi_hashval = hashval;
@ -569,8 +568,8 @@ st_getstrtab_sz(Str_tbl *stp)
* strings input.
*/
stp->st_hbckcnt = findprime(stp->st_strcnt);
if ((stp->st_hashbcks =
calloc(sizeof (Str_hash), stp->st_hbckcnt)) == NULL)
if ((stp->st_hashbcks = calloc(sizeof (*stp->st_hashbcks),
stp->st_hbckcnt)) == NULL)
return (0);
/*
@ -673,8 +672,8 @@ st_setstrbuf(Str_tbl *stp, char *stbuf, size_t bufsize)
#ifdef DEBUG
/*
* for debug builds - start with a stringtable filled in
* with '0xff'. This makes it very easy to find wholes
* which we failed to fill in - in the strtab.
* with '0xff'. This makes it very easy to spot unfilled
* holes in the strtab.
*/
memset(stbuf, 0xff, bufsize);
stbuf[0] = '\0';

View File

@ -0,0 +1,328 @@
/*
* 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Common routines for acquiring snapshots of kstats for
* iostat, mpstat, and vmstat.
*/
#ifndef _STATCOMMON_H
#define _STATCOMMON_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <kstat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/buf.h>
#include <sys/dnlc.h>
#include <sys/sysinfo.h>
#include <sys/processor.h>
#include <sys/pset.h>
#include <sys/avl.h>
/* No CPU present at this CPU position */
#define ID_NO_CPU -1
/* CPU belongs to no pset (we number this as "pset 0") */
#define ID_NO_PSET 0
/* CPU is usable */
#define CPU_ONLINE(s) ((s) == P_ONLINE || (s) == P_NOINTR)
/* will the CPU have kstats */
#define CPU_ACTIVE(c) (CPU_ONLINE((c)->cs_state) && (c)->cs_id != ID_NO_CPU)
/* IO device has no identified ID */
#define IODEV_NO_ID -1
/* no limit to iodevs to collect */
#define UNLIMITED_IODEVS ((size_t)-1)
#define NODATE 0 /* Default: No time stamp */
#define DDATE 1 /* Standard date format */
#define UDATE 2 /* Internal representation of Unix time */
enum snapshot_types {
/* All CPUs separately */
SNAP_CPUS = 1 << 0,
/* Aggregated processor sets */
SNAP_PSETS = 1 << 1,
/* sys-wide stats including aggregated CPU stats */
SNAP_SYSTEM = 1 << 2,
/* interrupt sources and counts */
SNAP_INTERRUPTS = 1 << 3,
/* disk etc. stats */
SNAP_IODEVS = 1 << 4,
/* disk controller aggregates */
SNAP_CONTROLLERS = 1 << 5,
/* mpxio L I (multipath) paths: -X: Lun,LunInitiator */
SNAP_IOPATHS_LI = 1 << 6,
/* mpxio LTI (multipath) paths: -Y: Lun,LunTarget,LunTargetInitiator */
SNAP_IOPATHS_LTI = 1 << 7,
/* disk error stats */
SNAP_IODEV_ERRORS = 1 << 8,
/* pretty names for iodevs */
SNAP_IODEV_PRETTY = 1 << 9,
/* devid for iodevs */
SNAP_IODEV_DEVID = 1 << 10
};
struct cpu_snapshot {
/* may be ID_NO_CPU if no CPU present */
processorid_t cs_id;
/* may be ID_NO_PSET if no pset */
psetid_t cs_pset_id;
/* as in p_online(2) */
int cs_state;
/* stats for this CPU */
kstat_t cs_vm;
kstat_t cs_sys;
};
struct pset_snapshot {
/* ID may be zero to indicate the "none set" */
psetid_t ps_id;
/* number of CPUs in set */
size_t ps_nr_cpus;
/* the CPUs in this set */
struct cpu_snapshot **ps_cpus;
};
struct intr_snapshot {
/* name of interrupt source */
char is_name[KSTAT_STRLEN];
/* total number of interrupts from this source */
ulong_t is_total;
};
struct sys_snapshot {
sysinfo_t ss_sysinfo;
vminfo_t ss_vminfo;
struct nc_stats ss_nc;
/* vm/sys stats aggregated across all CPUs */
kstat_t ss_agg_vm;
kstat_t ss_agg_sys;
/* ticks since boot */
ulong_t ss_ticks;
long ss_deficit;
};
/* order is significant (see sort_before()) */
enum iodev_type {
IODEV_CONTROLLER = 1 << 0,
IODEV_DISK = 1 << 1,
IODEV_PARTITION = 1 << 2,
IODEV_TAPE = 1 << 3,
IODEV_NFS = 1 << 4,
IODEV_IOPATH_LT = 1 << 5, /* synthetic LunTarget */
IODEV_IOPATH_LI = 1 << 6, /* synthetic LunInitiator */
IODEV_IOPATH_LTI = 1 << 7, /* LunTgtInitiator (pathinfo) */
IODEV_UNKNOWN = 1 << 8
};
/* identify a disk, partition, etc. */
struct iodev_id {
int id;
/* target id (for disks) */
char tid[KSTAT_STRLEN];
};
/*
* Used for disks, partitions, tapes, nfs, controllers, iopaths
* Each entry can be a branch of a tree; for example, the disks
* of a controller constitute the children of the controller
* iodev_snapshot. This relationship is not strictly maintained
* if is_pretty can't be found.
*/
struct iodev_snapshot {
/* original kstat name */
char is_name[KSTAT_STRLEN];
/* type of kstat */
enum iodev_type is_type;
/* ID if meaningful */
struct iodev_id is_id;
/* parent ID if meaningful */
struct iodev_id is_parent_id;
/* user-friendly name if found */
char *is_pretty;
/* device ID if applicable */
char *is_devid;
/* mount-point if applicable */
char *is_dname;
/* number of direct children */
int is_nr_children;
/* children of this I/O device */
struct iodev_snapshot *is_children;
/* standard I/O stats */
kstat_io_t is_stats;
/* iodev error stats */
kstat_t is_errors;
/* creation time of the stats */
hrtime_t is_crtime;
/* time at which iodev snapshot was taken */
hrtime_t is_snaptime;
/* kstat module */
char is_module[KSTAT_STRLEN];
/* kstat instance */
int is_instance;
/* kstat (only used temporarily) */
kstat_t *is_ksp;
struct iodev_snapshot *is_prev;
struct iodev_snapshot *is_next;
/* AVL structures to speedup insertion */
avl_tree_t *avl_list; /* list this element belongs to */
avl_node_t avl_link;
};
/* which iodevs to show. */
struct iodev_filter {
/* nr. of iodevs to choose */
size_t if_max_iodevs;
/* bit mask of enum io_types to allow */
int if_allowed_types;
/* should we show floppy ? if_names can override this */
int if_skip_floppy;
/* nr. of named iodevs */
size_t if_nr_names;
char **if_names;
};
/* The primary structure of a system snapshot. */
struct snapshot {
/* what types were *requested* */
enum snapshot_types s_types;
size_t s_nr_cpus;
struct cpu_snapshot *s_cpus;
size_t s_nr_psets;
struct pset_snapshot *s_psets;
size_t s_nr_intrs;
struct intr_snapshot *s_intrs;
size_t s_nr_iodevs;
struct iodev_snapshot *s_iodevs;
size_t s_iodevs_is_name_maxlen;
struct sys_snapshot s_sys;
struct biostats s_biostats;
};
/* print a message and exit with failure */
void fail(int do_perror, char *message, ...);
/* strdup str, or exit with failure */
char *safe_strdup(char *str);
/* malloc successfully, or exit with failure */
void *safe_alloc(size_t size);
/*
* Copy a kstat from src to dst. If the source kstat contains no data,
* then set the destination kstat data to NULL and size to zero.
* Returns 0 on success.
*/
int kstat_copy(const kstat_t *src, kstat_t *dst);
/*
* Look up the named kstat, and give the ui64 difference i.e.
* new - old, or if old is NULL, return new.
*/
uint64_t kstat_delta(kstat_t *old, kstat_t *new, char *name);
/* Return the number of ticks delta between two hrtime_t values. */
uint64_t hrtime_delta(hrtime_t old, hrtime_t new);
/*
* Add the integer-valued stats from "src" to the
* existing ones in "dst". If "dst" does not contain
* stats, then a kstat_copy() is performed.
*/
int kstat_add(const kstat_t *src, kstat_t *dst);
/* return the number of CPUs with kstats (i.e. present and online) */
int nr_active_cpus(struct snapshot *ss);
/*
* Return the difference in CPU ticks between the two sys
* kstats.
*/
uint64_t cpu_ticks_delta(kstat_t *old, kstat_t *new);
/*
* Open the kstat chain. Cannot fail.
*/
kstat_ctl_t *open_kstat(void);
/*
* Return a struct snapshot based on the snapshot_types parameter
* passed in. iodev_filter may be NULL in which case all iodevs
* are selected if SNAP_IODEVS is passed.
*/
struct snapshot *acquire_snapshot(kstat_ctl_t *, int, struct iodev_filter *);
/* free a snapshot */
void free_snapshot(struct snapshot *ss);
typedef void (*snapshot_cb)(void *old, void *new, void *data);
/*
* Call the call back for each pair of data items of the given type,
* passing the data pointer passed in as well. If an item has been
* added, the first pointer will be NULL; if removed, the second pointer
* will be NULL.
*
* A non-zero return value indicates configuration has changed.
*/
int snapshot_walk(enum snapshot_types type, struct snapshot *old,
struct snapshot *new, snapshot_cb cb, void *data);
/*
* Output a line detailing any configuration changes such as a CPU
* brought online, etc, bracketed by << >>.
*/
void snapshot_report_changes(struct snapshot *old, struct snapshot *new);
/* Return non-zero if configuration has changed. */
int snapshot_has_changed(struct snapshot *old, struct snapshot *new);
/* free the given iodev */
void free_iodev(struct iodev_snapshot *iodev);
/* acquire the I/O devices */
int acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc,
struct iodev_filter *df);
/* strcmp-style I/O device comparator */
int iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2);
/* sleep until *wakeup + interval, keeping cadence where desired */
void sleep_until(hrtime_t *wakeup, hrtime_t interval, int forever,
int *caught_cont);
/* signal handler - so we can be aware of SIGCONT */
void cont_handler(int sig_number);
/* Print a timestamp in either Unix or standard format. */
void print_timestamp(uint_t);
#ifdef __cplusplus
}
#endif
#endif /* _STATCOMMON_H */

View File

@ -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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "statcommon.h"
#include <langinfo.h>
/*
* Print timestamp as decimal reprentation of time_t value (-T u was specified)
* or in date(1) format (-T d was specified).
*/
void
print_timestamp(uint_t timestamp_fmt)
{
time_t t = time(NULL);
static char *fmt = NULL;
/* We only need to retrieve this once per invocation */
if (fmt == NULL)
fmt = nl_langinfo(_DATE_FMT);
if (timestamp_fmt == UDATE) {
(void) printf("%ld\n", t);
} else if (timestamp_fmt == DDATE) {
char dstr[64];
int len;
len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
if (len > 0)
(void) printf("%s\n", dstr);
}
}

3158
cmd/zdb/zdb.c Normal file

File diff suppressed because it is too large Load Diff

384
cmd/zdb/zdb_il.c Normal file
View File

@ -0,0 +1,384 @@
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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 char prefix[4] = "\t\t\t";
static void
print_log_bp(const blkptr_t *bp, const char *prefix)
{
char blkbuf[BP_SPRINTF_LEN];
sprintf_blkptr(blkbuf, 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, *link;
lr_attr_t *lrattr;
name = (char *)(lr + 1);
if (lr->lr_common.lrc_txtype == TX_CREATE_ATTR ||
lr->lr_common.lrc_txtype == TX_MKDIR_ATTR) {
lrattr = (lr_attr_t *)(lr + 1);
name += ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
}
if (txtype == TX_SYMLINK) {
link = name + strlen(name) + 1;
(void) printf("%s%s -> %s\n", prefix, name, link);
} else if (txtype != TX_MKXATTR) {
(void) printf("%s%s\n", prefix, name);
}
(void) printf("%s%s", prefix, ctime(&crtime));
(void) printf("%sdoid %llu, foid %llu, mode %llo\n", prefix,
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_foid,
(longlong_t)lr->lr_mode);
(void) printf("%suid %llu, gid %llu, gen %llu, rdev 0x%llx\n", prefix,
(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("%sdoid %llu, name %s\n", prefix,
(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("%sdoid %llu, link_obj %llu, name %s\n", prefix,
(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("%ssdoid %llu, tdoid %llu\n", prefix,
(u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid);
(void) printf("%ssrc %s tgt %s\n", prefix, 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;
zbookmark_t zb;
char buf[SPA_MAXBLOCKSIZE];
int verbose = MAX(dump_opt['d'], dump_opt['i']);
int error;
(void) printf("%sfoid %llu, offset %llx, length %llx\n", prefix,
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_offset,
(u_longlong_t)lr->lr_length);
if (txtype == TX_WRITE2 || verbose < 5)
return;
if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
(void) printf("%shas blkptr, %s\n", prefix,
bp->blk_birth >= spa_first_txg(zilog->zl_spa) ?
"will claim" : "won't claim");
print_log_bp(bp, prefix);
if (BP_IS_HOLE(bp)) {
(void) printf("\t\t\tLSIZE 0x%llx\n",
(u_longlong_t)BP_GET_LSIZE(bp));
}
if (bp->blk_birth == 0) {
bzero(buf, sizeof (buf));
(void) printf("%s<hole>\n", prefix);
return;
}
if (bp->blk_birth < zilog->zl_header->zh_claim_txg) {
(void) printf("%s<block already committed>\n", prefix);
return;
}
SET_BOOKMARK(&zb, dmu_objset_id(zilog->zl_os),
lr->lr_foid, ZB_ZIL_LEVEL,
lr->lr_offset / BP_GET_LSIZE(bp));
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;
} else {
data = (char *)(lr + 1);
}
dlimit = data + MIN(lr->lr_length,
(verbose < 6 ? 20 : SPA_MAXBLOCKSIZE));
(void) printf("%s", prefix);
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("%sfoid %llu, offset 0x%llx, length 0x%llx\n", prefix,
(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("%sfoid %llu, mask 0x%llx\n", prefix,
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_mask);
if (lr->lr_mask & AT_MODE) {
(void) printf("%sAT_MODE %llo\n", prefix,
(longlong_t)lr->lr_mode);
}
if (lr->lr_mask & AT_UID) {
(void) printf("%sAT_UID %llu\n", prefix,
(u_longlong_t)lr->lr_uid);
}
if (lr->lr_mask & AT_GID) {
(void) printf("%sAT_GID %llu\n", prefix,
(u_longlong_t)lr->lr_gid);
}
if (lr->lr_mask & AT_SIZE) {
(void) printf("%sAT_SIZE %llu\n", prefix,
(u_longlong_t)lr->lr_size);
}
if (lr->lr_mask & AT_ATIME) {
(void) printf("%sAT_ATIME %llu.%09llu %s", prefix,
(u_longlong_t)lr->lr_atime[0],
(u_longlong_t)lr->lr_atime[1],
ctime(&atime));
}
if (lr->lr_mask & AT_MTIME) {
(void) printf("%sAT_MTIME %llu.%09llu %s", prefix,
(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("%sfoid %llu, aclcnt %llu\n", prefix,
(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 " },
{ zil_prt_rec_write, "TX_WRITE2 " },
};
/* ARGSUSED */
static int
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++;
return (0);
}
/* ARGSUSED */
static int
print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
{
char blkbuf[BP_SPRINTF_LEN + 10];
int verbose = MAX(dump_opt['d'], dump_opt['i']);
char *claim;
if (verbose <= 3)
return (0);
if (verbose >= 5) {
(void) strcpy(blkbuf, ", ");
sprintf_blkptr(blkbuf + 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);
return (0);
}
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 < 1)
return;
(void) printf("\n ZIL header: claim_txg %llu, "
"claim_blk_seq %llu, claim_lr_seq %llu",
(u_longlong_t)zh->zh_claim_txg,
(u_longlong_t)zh->zh_claim_blk_seq,
(u_longlong_t)zh->zh_claim_lr_seq);
(void) printf(" replay_seq %llu, flags 0x%llx\n",
(u_longlong_t)zh->zh_replay_seq, (u_longlong_t)zh->zh_flags);
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);
}
}

464
cmd/zfs/zfs_iter.c Normal file
View File

@ -0,0 +1,464 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#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;
int cb_depth_limit;
int cb_depth;
uint8_t cb_props_table[ZFS_NUM_PROPS];
} 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) {
if ((*cb->cb_proplist) &&
!(*cb->cb_proplist)->pl_all)
zfs_prune_proplist(zhp,
cb->cb_props_table);
if (zfs_expand_proplist(zhp, cb->cb_proplist,
(cb->cb_flags & ZFS_ITER_RECVD_PROPS))
!= 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 &&
((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
cb->cb_depth < cb->cb_depth_limit)) {
cb->cb_depth++;
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);
cb->cb_depth--;
}
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, int limit,
zfs_iter_f callback, void *data)
{
callback_data_t cb = {0};
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)
nomem();
cb.cb_sortcol = sortcol;
cb.cb_flags = flags;
cb.cb_proplist = proplist;
cb.cb_types = types;
cb.cb_depth_limit = limit;
/*
* If cb_proplist is provided then in the zfs_handles created we
* retain only those properties listed in cb_proplist and sortcol.
* The rest are pruned. So, the caller should make sure that no other
* properties other than those listed in cb_proplist/sortcol are
* accessed.
*
* If cb_proplist is NULL then we retain all the properties. We
* always retain the zoned property, which some other properties
* need (userquota & friends), and the createtxg property, which
* we need to sort snapshots.
*/
if (cb.cb_proplist && *cb.cb_proplist) {
zprop_list_t *p = *cb.cb_proplist;
while (p) {
if (p->pl_prop >= ZFS_PROP_TYPE &&
p->pl_prop < ZFS_NUM_PROPS) {
cb.cb_props_table[p->pl_prop] = B_TRUE;
}
p = p->pl_next;
}
while (sortcol) {
if (sortcol->sc_prop >= ZFS_PROP_TYPE &&
sortcol->sc_prop < ZFS_NUM_PROPS) {
cb.cb_props_table[sortcol->sc_prop] = B_TRUE;
}
sortcol = sortcol->sc_next;
}
cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE;
cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE;
} else {
(void) memset(cb.cb_props_table, B_TRUE,
sizeof (cb.cb_props_table));
}
if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
nomem();
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)
nomem();
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);
}

56
cmd/zfs/zfs_iter.h Normal file
View File

@ -0,0 +1,56 @@
/*
* 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 2009 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)
#define ZFS_ITER_DEPTH_LIMIT (1 << 3)
#define ZFS_ITER_RECVD_PROPS (1 << 4)
int zfs_for_each(int, char **, int options, zfs_type_t,
zfs_sort_column_t *, zprop_list_t **, int, 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 */

4160
cmd/zfs/zfs_main.c Normal file

File diff suppressed because it is too large Load Diff

42
cmd/zfs/zfs_util.h Normal file
View File

@ -0,0 +1,42 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _ZFS_UTIL_H
#define _ZFS_UTIL_H
#include <libzfs.h>
#ifdef __cplusplus
extern "C" {
#endif
void * safe_malloc(size_t size);
void nomem(void);
libzfs_handle_t *g_zfs;
#ifdef __cplusplus
}
#endif
#endif /* _ZFS_UTIL_H */

494
cmd/zinject/translate.c Normal file
View File

@ -0,0 +1,494 @@
/*
* 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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <libzfs.h>
#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);
}
static void
compress_slashes(const char *src, char *dest)
{
while (*src != '\0') {
*dest = *src++;
while (*dest == '/' && *src == '/')
++src;
++dest;
}
*dest = '\0';
}
/*
* 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 *inpath, char *dataset, char *relpath,
struct stat64 *statbuf)
{
struct extmnttab mp;
FILE *fp;
int match;
const char *rel;
char fullpath[MAXPATHLEN];
compress_slashes(inpath, fullpath);
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();
err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, FTAG, &os);
if (err != 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_disown(os, FTAG);
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_own(dataset, DMU_OST_ANY,
B_TRUE, FTAG, &os)) != 0) {
(void) fprintf(stderr, "cannot open dataset '%s': %s\n",
dataset, strerror(err));
goto out;
}
if (record->zi_object == 0) {
dn = DMU_META_DNODE(os);
} else {
err = dnode_hold(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 != DMU_META_DNODE(os))
dnode_rele(dn, FTAG);
}
if (os)
dmu_objset_disown(os, FTAG);
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_BPOBJ:
record->zi_type = DMU_OT_BPOBJ;
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;
case TYPE_LABEL_PAD1:
record->zi_start = offsetof(vdev_label_t, vl_pad1);
record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
break;
case TYPE_LABEL_PAD2:
record->zi_start = offsetof(vdev_label_t, vl_pad2);
record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
break;
}
return (0);
}

972
cmd/zinject/zinject.c Normal file
View File

@ -0,0 +1,972 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* 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, uberblock, pad1, or pad2 region
* of all the labels for the specified device.
*
* This form of the command looks like:
*
* zinject -d device [-e errno] [-L <uber | nvlist | pad1 | pad2>] 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
* bpobj 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",
"bpobj",
"spacemap",
"errlog",
"uber",
"nvlist",
"pad1",
"pad2"
};
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_BPOBJ:
return ("bpobj");
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 -p <function name> pool\n"
"\t\tInject a panic fault at the specified function. Only \n"
"\t\tfunctions which call spa_vdev_config_exit(), or \n"
"\t\tspa_vdev_exit() will trigger a panic.\n"
"\n"
"\tzinject -d device [-e errno] [-L <nvlist|uber|pad1|pad2>] [-F]\n"
"\t [-T <read|write|free|claim|all> pool\n"
"\t\tInject a fault into a particular device or the device's\n"
"\t\tlabel. Label injection can either be 'nvlist', 'uber',\n "
"\t\t'pad1', or 'pad2'.\n"
"\t\t'errno' can be 'nxio' (the default), 'io', or 'dtl'.\n"
"\n"
"\tzinject -d device -A <degrade|fault> pool\n"
"\t\tPerform a specific action on a particular device\n"
"\n"
"\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
"\t\tCause the pool to stop writing blocks yet not\n"
"\t\treport errors for a duration. Simulates buggy hardware\n"
"\t\tthat fails to honor cache flush requests.\n"
"\t\tDefault duration is 30 seconds. The machine is panicked\n"
"\t\tat the end of the duration.\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, bpobj,\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);
if (errno != ENOENT) {
(void) fprintf(stderr, "Unable to list handlers: %s\n",
strerror(errno));
return (-1);
}
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 || record->zi_func[0] != '\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 || record->zi_func[0] != '\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);
}
static int
print_panic_handler(int id, const char *pool, zinject_record_t *record,
void *data)
{
int *count = data;
if (record->zi_func[0] == '\0')
return (0);
if (*count == 0) {
(void) printf("%3s %-15s %s\n", "ID", "POOL", "FUNCTION");
(void) printf("--- --------------- ----------------\n");
}
*count += 1;
(void) printf("%3d %-15s %s\n", id, pool, record->zi_func);
return (0);
}
/*
* Print all registered error handlers. Returns the number of handlers
* registered.
*/
static int
print_all_handlers(void)
{
int count = 0, total = 0;
(void) iter_handlers(print_device_handler, &count);
if (count > 0) {
total += count;
(void) printf("\n");
count = 0;
}
(void) iter_handlers(print_data_handler, &count);
if (count > 0) {
total += count;
(void) printf("\n");
count = 0;
}
(void) iter_handlers(print_panic_handler, &count);
return (count + total);
}
/* 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);
if (ret == 0)
(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 if (record->zi_func[0] != '\0') {
(void) printf(" panic function: %s\n",
record->zi_func);
} else if (record->zi_duration > 0) {
(void) printf(" time: %lld seconds\n",
(u_longlong_t)record->zi_duration);
} else if (record->zi_duration < 0) {
(void) printf(" txgs: %lld \n",
(u_longlong_t)-record->zi_duration);
} 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
perform_action(const char *pool, zinject_record_t *record, int cmd)
{
zfs_cmd_t zc;
ASSERT(cmd == VDEV_STATE_DEGRADED || cmd == VDEV_STATE_FAULTED);
(void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
zc.zc_guid = record->zi_guid;
zc.zc_cookie = cmd;
if (ioctl(zfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
return (0);
return (1);
}
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;
int io_type = ZIO_TYPES;
int action = VDEV_STATE_UNKNOWN;
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 nowrites = 0;
int dur_txg = 0;
int dur_secs = 0;
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,
":aA:b:d:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:")) != -1) {
switch (c) {
case 'a':
flags |= ZINJECT_FLUSH_ARC;
break;
case 'A':
if (strcasecmp(optarg, "degrade") == 0) {
action = VDEV_STATE_DEGRADED;
} else if (strcasecmp(optarg, "fault") == 0) {
action = VDEV_STATE_FAULTED;
} else {
(void) fprintf(stderr, "invalid action '%s': "
"must be 'degrade' or 'fault'\n", optarg);
usage();
return (1);
}
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 if (strcasecmp(optarg, "dtl") == 0) {
error = ECHILD;
} 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 'F':
record.zi_failfast = B_TRUE;
break;
case 'g':
dur_txg = 1;
record.zi_duration = (int)strtol(optarg, &end, 10);
if (record.zi_duration <= 0 || *end != '\0') {
(void) fprintf(stderr, "invalid duration '%s': "
"must be a positive integer\n", optarg);
usage();
return (1);
}
/* store duration of txgs as its negative */
record.zi_duration *= -1;
break;
case 'h':
usage();
return (0);
case 'I':
/* default duration, if one hasn't yet been defined */
nowrites = 1;
if (dur_secs == 0 && dur_txg == 0)
record.zi_duration = 30;
break;
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 'p':
(void) strlcpy(record.zi_func, optarg,
sizeof (record.zi_func));
break;
case 'q':
quiet = 1;
break;
case 'r':
range = optarg;
break;
case 's':
dur_secs = 1;
record.zi_duration = (int)strtol(optarg, &end, 10);
if (record.zi_duration <= 0 || *end != '\0') {
(void) fprintf(stderr, "invalid duration '%s': "
"must be a positive integer\n", optarg);
usage();
return (1);
}
break;
case 'T':
if (strcasecmp(optarg, "read") == 0) {
io_type = ZIO_TYPE_READ;
} else if (strcasecmp(optarg, "write") == 0) {
io_type = ZIO_TYPE_WRITE;
} else if (strcasecmp(optarg, "free") == 0) {
io_type = ZIO_TYPE_FREE;
} else if (strcasecmp(optarg, "claim") == 0) {
io_type = ZIO_TYPE_CLAIM;
} else if (strcasecmp(optarg, "all") == 0) {
io_type = ZIO_TYPES;
} else {
(void) fprintf(stderr, "invalid I/O type "
"'%s': must be 'read', 'write', 'free', "
"'claim' or 'all'\n", optarg);
usage();
return (1);
}
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 || record.zi_func[0] != '\0' ||
record.zi_duration != 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 || record.zi_func[0] != '\0' ||
record.zi_duration != 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);
}
record.zi_iotype = io_type;
if (translate_device(pool, device, label, &record) != 0)
return (1);
if (!error)
error = ENXIO;
if (action != VDEV_STATE_UNKNOWN)
return (perform_action(pool, &record, action));
} else if (raw != NULL) {
if (range != NULL || type != TYPE_INVAL || level != 0 ||
record.zi_func[0] != '\0' || record.zi_duration != 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 (record.zi_func[0] != '\0') {
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
level != 0 || device != NULL || record.zi_duration != 0) {
(void) fprintf(stderr, "panic (-p) incompatible with "
"other options\n");
usage();
return (2);
}
if (argc < 1 || argc > 2) {
(void) fprintf(stderr, "panic (-p) injection requires "
"a single pool name and an optional id\n");
usage();
return (2);
}
(void) strcpy(pool, argv[0]);
if (argv[1] != NULL)
record.zi_type = atoi(argv[1]);
dataset[0] = '\0';
} else if (record.zi_duration != 0) {
if (nowrites == 0) {
(void) fprintf(stderr, "-s or -g meaningless "
"without -I (ignore writes)\n");
usage();
return (2);
} else if (dur_secs && dur_txg) {
(void) fprintf(stderr, "choose a duration either "
"in seconds (-s) or a number of txgs (-g) "
"but not both\n");
usage();
return (2);
} else if (argc != 1) {
(void) fprintf(stderr, "ignore writes (-I) "
"injection requires a single pool name\n");
usage();
return (2);
}
(void) strcpy(pool, argv[0]);
dataset[0] = '\0';
} else if (type == TYPE_INVAL) {
if (flags == 0) {
(void) fprintf(stderr, "at least one of '-b', '-d', "
"'-t', '-a', '-p', '-I' 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);
}

70
cmd/zinject/zinject.h Normal file
View File

@ -0,0 +1,70 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _ZINJECT_H
#define _ZINJECT_H
#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_BPOBJ, /* 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_LABEL_PAD1, /* label specific 8K pad1 area */
TYPE_LABEL_PAD2, /* label specific 8K pad2 area */
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 */

411
cmd/zlook/zlook.c Normal file
View File

@ -0,0 +1,411 @@
/*
* 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* This is a test program that uses ioctls to the ZFS Unit Test driver
* to perform readdirs or lookups using flags not normally available
* to user-land programs. This allows testing of the flags'
* behavior outside of a complicated consumer, such as the SMB driver.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stropts.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/dirent.h>
#include <sys/attr.h>
#include <stddef.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#define _KERNEL
#include <sys/fs/zut.h>
#include <sys/extdirent.h>
#undef _KERNEL
#define MAXBUF (64 * 1024)
#define BIGBUF 4096
#define LILBUF (sizeof (dirent_t))
#define DIRENT_NAMELEN(reclen) \
((reclen) - (offsetof(dirent_t, d_name[0])))
static void
usage(char *pnam)
{
(void) fprintf(stderr, "Usage:\n %s -l [-is] dir-to-look-in "
"file-in-dir [xfile-on-file]\n", pnam);
(void) fprintf(stderr, " %s -i [-ls] dir-to-look-in "
"file-in-dir [xfile-on-file]\n", pnam);
(void) fprintf(stderr, " %s -s [-il] dir-to-look-in "
"file-in-dir [xfile-on-file]\n", pnam);
(void) fprintf(stderr, "\t Perform a lookup\n");
(void) fprintf(stderr, "\t -l == lookup\n");
(void) fprintf(stderr, "\t -i == request FIGNORECASE\n");
(void) fprintf(stderr, "\t -s == request stat(2) and xvattr info\n");
(void) fprintf(stderr, " %s -r [-ea] [-b buffer-size-in-bytes] "
"dir-to-look-in [file-in-dir]\n", pnam);
(void) fprintf(stderr, " %s -e [-ra] [-b buffer-size-in-bytes] "
"dir-to-look-in [file-in-dir]\n", pnam);
(void) fprintf(stderr, " %s -a [-re] [-b buffer-size-in-bytes] "
"dir-to-look-in [file-in-dir]\n", pnam);
(void) fprintf(stderr, "\t Perform a readdir\n");
(void) fprintf(stderr, "\t -r == readdir\n");
(void) fprintf(stderr, "\t -e == request extended entries\n");
(void) fprintf(stderr, "\t -a == request access filtering\n");
(void) fprintf(stderr, "\t -b == buffer size (default 4K)\n");
(void) fprintf(stderr, " %s -A path\n", pnam);
(void) fprintf(stderr, "\t Look up _PC_ACCESS_FILTERING "
"for path with pathconf(2)\n");
(void) fprintf(stderr, " %s -E path\n", pnam);
(void) fprintf(stderr, "\t Look up _PC_SATTR_EXISTS "
"for path with pathconf(2)\n");
(void) fprintf(stderr, " %s -S path\n", pnam);
(void) fprintf(stderr, "\t Look up _PC_SATTR_EXISTS "
"for path with pathconf(2)\n");
exit(EINVAL);
}
static void
print_extd_entries(zut_readdir_t *r)
{
struct edirent *eodp;
char *bufstart;
eodp = (edirent_t *)(uintptr_t)r->zr_buf;
bufstart = (char *)eodp;
while ((char *)eodp < bufstart + r->zr_bytes) {
char *blanks = " ";
int i = 0;
while (i < EDIRENT_NAMELEN(eodp->ed_reclen)) {
if (!eodp->ed_name[i])
break;
(void) printf("%c", eodp->ed_name[i++]);
}
if (i < 16)
(void) printf("%.*s", 16 - i, blanks);
(void) printf("\t%x\n", eodp->ed_eflags);
eodp = (edirent_t *)((intptr_t)eodp + eodp->ed_reclen);
}
}
static void
print_entries(zut_readdir_t *r)
{
dirent64_t *dp;
char *bufstart;
dp = (dirent64_t *)(intptr_t)r->zr_buf;
bufstart = (char *)dp;
while ((char *)dp < bufstart + r->zr_bytes) {
int i = 0;
while (i < DIRENT_NAMELEN(dp->d_reclen)) {
if (!dp->d_name[i])
break;
(void) printf("%c", dp->d_name[i++]);
}
(void) printf("\n");
dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen);
}
}
static void
print_stats(struct stat64 *sb)
{
char timebuf[512];
(void) printf("st_mode\t\t\t%04lo\n", (unsigned long)sb->st_mode);
(void) printf("st_ino\t\t\t%llu\n", (unsigned long long)sb->st_ino);
(void) printf("st_nlink\t\t%lu\n", (unsigned long)sb->st_nlink);
(void) printf("st_uid\t\t\t%d\n", sb->st_uid);
(void) printf("st_gid\t\t\t%d\n", sb->st_gid);
(void) printf("st_size\t\t\t%lld\n", (long long)sb->st_size);
(void) printf("st_blksize\t\t%ld\n", (long)sb->st_blksize);
(void) printf("st_blocks\t\t%lld\n", (long long)sb->st_blocks);
timebuf[0] = 0;
if (ctime_r(&sb->st_atime, timebuf, 512)) {
(void) printf("st_atime\t\t");
(void) printf("%s", timebuf);
}
timebuf[0] = 0;
if (ctime_r(&sb->st_mtime, timebuf, 512)) {
(void) printf("st_mtime\t\t");
(void) printf("%s", timebuf);
}
timebuf[0] = 0;
if (ctime_r(&sb->st_ctime, timebuf, 512)) {
(void) printf("st_ctime\t\t");
(void) printf("%s", timebuf);
}
}
static void
print_xvs(uint64_t xvs)
{
uint_t bits;
int idx = 0;
if (xvs == 0)
return;
(void) printf("-------------------\n");
(void) printf("Attribute bit(s) set:\n");
(void) printf("-------------------\n");
bits = xvs & ((1 << F_ATTR_ALL) - 1);
while (bits) {
uint_t rest = bits >> 1;
if (bits & 1) {
(void) printf("%s", attr_to_name((f_attr_t)idx));
if (rest)
(void) printf(", ");
}
idx++;
bits = rest;
}
(void) printf("\n");
}
int
main(int argc, char **argv)
{
zut_lookup_t lk = {0};
zut_readdir_t rd = {0};
boolean_t checking = B_FALSE;
boolean_t looking = B_FALSE;
boolean_t reading = B_FALSE;
boolean_t bflag = B_FALSE;
long rddir_bufsize = BIGBUF;
int error = 0;
int check;
int fd;
int c;
while ((c = getopt(argc, argv, "lisaerb:ASE")) != -1) {
switch (c) {
case 'l':
looking = B_TRUE;
break;
case 'i':
lk.zl_reqflags |= ZUT_IGNORECASE;
looking = B_TRUE;
break;
case 's':
lk.zl_reqflags |= ZUT_GETSTAT;
looking = B_TRUE;
break;
case 'a':
rd.zr_reqflags |= ZUT_ACCFILTER;
reading = B_TRUE;
break;
case 'e':
rd.zr_reqflags |= ZUT_EXTRDDIR;
reading = B_TRUE;
break;
case 'r':
reading = B_TRUE;
break;
case 'b':
reading = B_TRUE;
bflag = B_TRUE;
rddir_bufsize = strtol(optarg, NULL, 0);
break;
case 'A':
checking = B_TRUE;
check = _PC_ACCESS_FILTERING;
break;
case 'S':
checking = B_TRUE;
check = _PC_SATTR_ENABLED;
break;
case 'E':
checking = B_TRUE;
check = _PC_SATTR_EXISTS;
break;
case '?':
default:
usage(argv[0]); /* no return */
}
}
if ((checking && looking) || (checking && reading) ||
(looking && reading) || (!reading && bflag) ||
(!checking && !reading && !looking))
usage(argv[0]); /* no return */
if (rddir_bufsize < LILBUF || rddir_bufsize > MAXBUF) {
(void) fprintf(stderr, "Sorry, buffer size "
"must be >= %d and less than or equal to %d bytes.\n",
(int)LILBUF, MAXBUF);
exit(EINVAL);
}
if (checking) {
char pathbuf[MAXPATHLEN];
long result;
if (argc - optind < 1)
usage(argv[0]); /* no return */
(void) strlcpy(pathbuf, argv[optind], MAXPATHLEN);
result = pathconf(pathbuf, check);
(void) printf("pathconf(2) check for %s\n", pathbuf);
switch (check) {
case _PC_SATTR_ENABLED:
(void) printf("System attributes ");
if (result != 0)
(void) printf("Enabled\n");
else
(void) printf("Not enabled\n");
break;
case _PC_SATTR_EXISTS:
(void) printf("System attributes ");
if (result != 0)
(void) printf("Exist\n");
else
(void) printf("Do not exist\n");
break;
case _PC_ACCESS_FILTERING:
(void) printf("Access filtering ");
if (result != 0)
(void) printf("Available\n");
else
(void) printf("Not available\n");
break;
}
return (result);
}
if ((fd = open(ZUT_DEV, O_RDONLY)) < 0) {
perror(ZUT_DEV);
return (ENXIO);
}
if (reading) {
char *buf;
if (argc - optind < 1)
usage(argv[0]); /* no return */
(void) strlcpy(rd.zr_dir, argv[optind], MAXPATHLEN);
if (argc - optind > 1) {
(void) strlcpy(rd.zr_file, argv[optind + 1],
MAXNAMELEN);
rd.zr_reqflags |= ZUT_XATTR;
}
if ((buf = malloc(rddir_bufsize)) == NULL) {
error = errno;
perror("malloc");
(void) close(fd);
return (error);
}
rd.zr_buf = (uint64_t)(uintptr_t)buf;
rd.zr_buflen = rddir_bufsize;
while (!rd.zr_eof) {
int ierr;
if ((ierr = ioctl(fd, ZUT_IOC_READDIR, &rd)) != 0) {
(void) fprintf(stderr,
"IOCTL error: %s (%d)\n",
strerror(ierr), ierr);
free(buf);
(void) close(fd);
return (ierr);
}
if (rd.zr_retcode) {
(void) fprintf(stderr,
"readdir result: %s (%d)\n",
strerror(rd.zr_retcode), rd.zr_retcode);
free(buf);
(void) close(fd);
return (rd.zr_retcode);
}
if (rd.zr_reqflags & ZUT_EXTRDDIR)
print_extd_entries(&rd);
else
print_entries(&rd);
}
free(buf);
} else {
int ierr;
if (argc - optind < 2)
usage(argv[0]); /* no return */
(void) strlcpy(lk.zl_dir, argv[optind], MAXPATHLEN);
(void) strlcpy(lk.zl_file, argv[optind + 1], MAXNAMELEN);
if (argc - optind > 2) {
(void) strlcpy(lk.zl_xfile,
argv[optind + 2], MAXNAMELEN);
lk.zl_reqflags |= ZUT_XATTR;
}
if ((ierr = ioctl(fd, ZUT_IOC_LOOKUP, &lk)) != 0) {
(void) fprintf(stderr,
"IOCTL error: %s (%d)\n",
strerror(ierr), ierr);
(void) close(fd);
return (ierr);
}
(void) printf("\nLookup of ");
if (lk.zl_reqflags & ZUT_XATTR) {
(void) printf("extended attribute \"%s\" of ",
lk.zl_xfile);
}
(void) printf("file \"%s\" ", lk.zl_file);
(void) printf("in directory \"%s\" ", lk.zl_dir);
if (lk.zl_retcode) {
(void) printf("failed: %s (%d)\n",
strerror(lk.zl_retcode), lk.zl_retcode);
(void) close(fd);
return (lk.zl_retcode);
}
(void) printf("succeeded.\n");
if (lk.zl_reqflags & ZUT_IGNORECASE) {
(void) printf("----------------------------\n");
(void) printf("dirent flags: 0x%0x\n", lk.zl_deflags);
(void) printf("real name: %s\n", lk.zl_real);
}
if (lk.zl_reqflags & ZUT_GETSTAT) {
(void) printf("----------------------------\n");
print_stats(&lk.zl_statbuf);
print_xvs(lk.zl_xvattrs);
}
}
(void) close(fd);
return (0);
}

252
cmd/zpool/zpool_iter.c Normal file
View File

@ -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);
}

4467
cmd/zpool/zpool_main.c Normal file

File diff suppressed because it is too large Load Diff

86
cmd/zpool/zpool_util.c Normal file
View File

@ -0,0 +1,86 @@
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#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);
}
/*
* 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);
}

72
cmd/zpool/zpool_util.h Normal file
View File

@ -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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#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);
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 replacing, boolean_t dryrun, int argc, char **argv);
nvlist_t *split_mirror_vdev(zpool_handle_t *zhp, char *newname,
nvlist_t *props, splitflags_t flags, 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 */

1469
cmd/zpool/zpool_vdev.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,429 @@
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <libnvpair.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/dmu.h>
#include <sys/zfs_ioctl.h>
#include <zfs_fletcher.h>
uint64_t drr_record_count[DRR_NUMTYPES];
uint64_t total_write_size = 0;
uint64_t total_stream_len = 0;
FILE *send_stream = 0;
boolean_t do_byteswap = B_FALSE;
boolean_t do_cksum = B_TRUE;
#define INITIAL_BUFLEN (1<<20)
static void
usage(void)
{
(void) fprintf(stderr, "usage: zstreamdump [-v] [-C] < file\n");
(void) fprintf(stderr, "\t -v -- verbose\n");
(void) fprintf(stderr, "\t -C -- suppress checksum verification\n");
exit(1);
}
/*
* ssread - send stream read.
*
* Read while computing incremental checksum
*/
static size_t
ssread(void *buf, size_t len, zio_cksum_t *cksum)
{
size_t outlen;
if ((outlen = fread(buf, len, 1, send_stream)) == 0)
return (0);
if (do_cksum && cksum) {
if (do_byteswap)
fletcher_4_incremental_byteswap(buf, len, cksum);
else
fletcher_4_incremental_native(buf, len, cksum);
}
total_stream_len += len;
return (outlen);
}
int
main(int argc, char *argv[])
{
char *buf = malloc(INITIAL_BUFLEN);
dmu_replay_record_t thedrr;
dmu_replay_record_t *drr = &thedrr;
struct drr_begin *drrb = &thedrr.drr_u.drr_begin;
struct drr_end *drre = &thedrr.drr_u.drr_end;
struct drr_object *drro = &thedrr.drr_u.drr_object;
struct drr_freeobjects *drrfo = &thedrr.drr_u.drr_freeobjects;
struct drr_write *drrw = &thedrr.drr_u.drr_write;
struct drr_write_byref *drrwbr = &thedrr.drr_u.drr_write_byref;
struct drr_free *drrf = &thedrr.drr_u.drr_free;
struct drr_spill *drrs = &thedrr.drr_u.drr_spill;
char c;
boolean_t verbose = B_FALSE;
boolean_t first = B_TRUE;
int err;
zio_cksum_t zc = { 0 };
zio_cksum_t pcksum = { 0 };
while ((c = getopt(argc, argv, ":vC")) != -1) {
switch (c) {
case 'C':
do_cksum = B_FALSE;
break;
case 'v':
verbose = B_TRUE;
break;
case ':':
(void) fprintf(stderr,
"missing argument for '%c' option\n", optopt);
usage();
break;
case '?':
(void) fprintf(stderr, "invalid option '%c'\n",
optopt);
usage();
}
}
if (isatty(STDIN_FILENO)) {
(void) fprintf(stderr,
"Error: Backup stream can not be read "
"from a terminal.\n"
"You must redirect standard input.\n");
exit(1);
}
send_stream = stdin;
pcksum = zc;
while (ssread(drr, sizeof (dmu_replay_record_t), &zc)) {
if (first) {
if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
do_byteswap = B_TRUE;
if (do_cksum) {
ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
/*
* recalculate header checksum now
* that we know it needs to be
* byteswapped.
*/
fletcher_4_incremental_byteswap(drr,
sizeof (dmu_replay_record_t), &zc);
}
} else if (drrb->drr_magic != DMU_BACKUP_MAGIC) {
(void) fprintf(stderr, "Invalid stream "
"(bad magic number)\n");
exit(1);
}
first = B_FALSE;
}
if (do_byteswap) {
drr->drr_type = BSWAP_32(drr->drr_type);
drr->drr_payloadlen =
BSWAP_32(drr->drr_payloadlen);
}
/*
* At this point, the leading fields of the replay record
* (drr_type and drr_payloadlen) have been byte-swapped if
* necessary, but the rest of the data structure (the
* union of type-specific structures) is still in its
* original state.
*/
if (drr->drr_type >= DRR_NUMTYPES) {
(void) printf("INVALID record found: type 0x%x\n",
drr->drr_type);
(void) printf("Aborting.\n");
exit(1);
}
drr_record_count[drr->drr_type]++;
switch (drr->drr_type) {
case DRR_BEGIN:
if (do_byteswap) {
drrb->drr_magic = BSWAP_64(drrb->drr_magic);
drrb->drr_versioninfo =
BSWAP_64(drrb->drr_versioninfo);
drrb->drr_creation_time =
BSWAP_64(drrb->drr_creation_time);
drrb->drr_type = BSWAP_32(drrb->drr_type);
drrb->drr_flags = BSWAP_32(drrb->drr_flags);
drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
drrb->drr_fromguid =
BSWAP_64(drrb->drr_fromguid);
}
(void) printf("BEGIN record\n");
(void) printf("\thdrtype = %lld\n",
DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo));
(void) printf("\tfeatures = %llx\n",
DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo));
(void) printf("\tmagic = %llx\n",
(u_longlong_t)drrb->drr_magic);
(void) printf("\tcreation_time = %llx\n",
(u_longlong_t)drrb->drr_creation_time);
(void) printf("\ttype = %u\n", drrb->drr_type);
(void) printf("\tflags = 0x%x\n", drrb->drr_flags);
(void) printf("\ttoguid = %llx\n",
(u_longlong_t)drrb->drr_toguid);
(void) printf("\tfromguid = %llx\n",
(u_longlong_t)drrb->drr_fromguid);
(void) printf("\ttoname = %s\n", drrb->drr_toname);
if (verbose)
(void) printf("\n");
if ((DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
DMU_COMPOUNDSTREAM) && drr->drr_payloadlen != 0) {
nvlist_t *nv;
int sz = drr->drr_payloadlen;
if (sz > 1<<20) {
free(buf);
buf = malloc(sz);
}
(void) ssread(buf, sz, &zc);
if (ferror(send_stream))
perror("fread");
err = nvlist_unpack(buf, sz, &nv, 0);
if (err)
perror(strerror(err));
nvlist_print(stdout, nv);
nvlist_free(nv);
}
break;
case DRR_END:
if (do_byteswap) {
drre->drr_checksum.zc_word[0] =
BSWAP_64(drre->drr_checksum.zc_word[0]);
drre->drr_checksum.zc_word[1] =
BSWAP_64(drre->drr_checksum.zc_word[1]);
drre->drr_checksum.zc_word[2] =
BSWAP_64(drre->drr_checksum.zc_word[2]);
drre->drr_checksum.zc_word[3] =
BSWAP_64(drre->drr_checksum.zc_word[3]);
}
/*
* We compare against the *previous* checksum
* value, because the stored checksum is of
* everything before the DRR_END record.
*/
if (do_cksum && !ZIO_CHECKSUM_EQUAL(drre->drr_checksum,
pcksum)) {
(void) printf("Expected checksum differs from "
"checksum in stream.\n");
(void) printf("Expected checksum = "
"%llx/%llx/%llx/%llx\n",
pcksum.zc_word[0],
pcksum.zc_word[1],
pcksum.zc_word[2],
pcksum.zc_word[3]);
}
(void) printf("END checksum = %llx/%llx/%llx/%llx\n",
drre->drr_checksum.zc_word[0],
drre->drr_checksum.zc_word[1],
drre->drr_checksum.zc_word[2],
drre->drr_checksum.zc_word[3]);
ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
break;
case DRR_OBJECT:
if (do_byteswap) {
drro->drr_object = BSWAP_64(drro->drr_object);
drro->drr_type = BSWAP_32(drro->drr_type);
drro->drr_bonustype =
BSWAP_32(drro->drr_bonustype);
drro->drr_blksz = BSWAP_32(drro->drr_blksz);
drro->drr_bonuslen =
BSWAP_32(drro->drr_bonuslen);
drro->drr_toguid = BSWAP_64(drro->drr_toguid);
}
if (verbose) {
(void) printf("OBJECT object = %llu type = %u "
"bonustype = %u blksz = %u bonuslen = %u\n",
(u_longlong_t)drro->drr_object,
drro->drr_type,
drro->drr_bonustype,
drro->drr_blksz,
drro->drr_bonuslen);
}
if (drro->drr_bonuslen > 0) {
(void) ssread(buf, P2ROUNDUP(drro->drr_bonuslen,
8), &zc);
}
break;
case DRR_FREEOBJECTS:
if (do_byteswap) {
drrfo->drr_firstobj =
BSWAP_64(drrfo->drr_firstobj);
drrfo->drr_numobjs =
BSWAP_64(drrfo->drr_numobjs);
drrfo->drr_toguid = BSWAP_64(drrfo->drr_toguid);
}
if (verbose) {
(void) printf("FREEOBJECTS firstobj = %llu "
"numobjs = %llu\n",
(u_longlong_t)drrfo->drr_firstobj,
(u_longlong_t)drrfo->drr_numobjs);
}
break;
case DRR_WRITE:
if (do_byteswap) {
drrw->drr_object = BSWAP_64(drrw->drr_object);
drrw->drr_type = BSWAP_32(drrw->drr_type);
drrw->drr_offset = BSWAP_64(drrw->drr_offset);
drrw->drr_length = BSWAP_64(drrw->drr_length);
drrw->drr_toguid = BSWAP_64(drrw->drr_toguid);
drrw->drr_key.ddk_prop =
BSWAP_64(drrw->drr_key.ddk_prop);
}
if (verbose) {
(void) printf("WRITE object = %llu type = %u "
"checksum type = %u\n"
"offset = %llu length = %llu "
"props = %llx\n",
(u_longlong_t)drrw->drr_object,
drrw->drr_type,
drrw->drr_checksumtype,
(u_longlong_t)drrw->drr_offset,
(u_longlong_t)drrw->drr_length,
(u_longlong_t)drrw->drr_key.ddk_prop);
}
(void) ssread(buf, drrw->drr_length, &zc);
total_write_size += drrw->drr_length;
break;
case DRR_WRITE_BYREF:
if (do_byteswap) {
drrwbr->drr_object =
BSWAP_64(drrwbr->drr_object);
drrwbr->drr_offset =
BSWAP_64(drrwbr->drr_offset);
drrwbr->drr_length =
BSWAP_64(drrwbr->drr_length);
drrwbr->drr_toguid =
BSWAP_64(drrwbr->drr_toguid);
drrwbr->drr_refguid =
BSWAP_64(drrwbr->drr_refguid);
drrwbr->drr_refobject =
BSWAP_64(drrwbr->drr_refobject);
drrwbr->drr_refoffset =
BSWAP_64(drrwbr->drr_refoffset);
drrwbr->drr_key.ddk_prop =
BSWAP_64(drrwbr->drr_key.ddk_prop);
}
if (verbose) {
(void) printf("WRITE_BYREF object = %llu "
"checksum type = %u props = %llx\n"
"offset = %llu length = %llu\n"
"toguid = %llx refguid = %llx\n"
"refobject = %llu refoffset = %llu\n",
(u_longlong_t)drrwbr->drr_object,
drrwbr->drr_checksumtype,
(u_longlong_t)drrwbr->drr_key.ddk_prop,
(u_longlong_t)drrwbr->drr_offset,
(u_longlong_t)drrwbr->drr_length,
(u_longlong_t)drrwbr->drr_toguid,
(u_longlong_t)drrwbr->drr_refguid,
(u_longlong_t)drrwbr->drr_refobject,
(u_longlong_t)drrwbr->drr_refoffset);
}
break;
case DRR_FREE:
if (do_byteswap) {
drrf->drr_object = BSWAP_64(drrf->drr_object);
drrf->drr_offset = BSWAP_64(drrf->drr_offset);
drrf->drr_length = BSWAP_64(drrf->drr_length);
}
if (verbose) {
(void) printf("FREE object = %llu "
"offset = %llu length = %lld\n",
(u_longlong_t)drrf->drr_object,
(u_longlong_t)drrf->drr_offset,
(longlong_t)drrf->drr_length);
}
break;
case DRR_SPILL:
if (do_byteswap) {
drrs->drr_object = BSWAP_64(drrs->drr_object);
drrs->drr_length = BSWAP_64(drrs->drr_length);
}
if (verbose) {
(void) printf("SPILL block for object = %llu "
"length = %llu\n", drrs->drr_object,
drrs->drr_length);
}
(void) ssread(buf, drrs->drr_length, &zc);
break;
}
pcksum = zc;
}
free(buf);
/* Print final summary */
(void) printf("SUMMARY:\n");
(void) printf("\tTotal DRR_BEGIN records = %lld\n",
(u_longlong_t)drr_record_count[DRR_BEGIN]);
(void) printf("\tTotal DRR_END records = %lld\n",
(u_longlong_t)drr_record_count[DRR_END]);
(void) printf("\tTotal DRR_OBJECT records = %lld\n",
(u_longlong_t)drr_record_count[DRR_OBJECT]);
(void) printf("\tTotal DRR_FREEOBJECTS records = %lld\n",
(u_longlong_t)drr_record_count[DRR_FREEOBJECTS]);
(void) printf("\tTotal DRR_WRITE records = %lld\n",
(u_longlong_t)drr_record_count[DRR_WRITE]);
(void) printf("\tTotal DRR_FREE records = %lld\n",
(u_longlong_t)drr_record_count[DRR_FREE]);
(void) printf("\tTotal DRR_SPILL records = %lld\n",
(u_longlong_t)drr_record_count[DRR_SPILL]);
(void) printf("\tTotal records = %lld\n",
(u_longlong_t)(drr_record_count[DRR_BEGIN] +
drr_record_count[DRR_OBJECT] +
drr_record_count[DRR_FREEOBJECTS] +
drr_record_count[DRR_WRITE] +
drr_record_count[DRR_FREE] +
drr_record_count[DRR_SPILL] +
drr_record_count[DRR_END]));
(void) printf("\tTotal write size = %lld (0x%llx)\n",
(u_longlong_t)total_write_size, (u_longlong_t)total_write_size);
(void) printf("\tTotal stream length = %lld (0x%llx)\n",
(u_longlong_t)total_stream_len, (u_longlong_t)total_stream_len);
return (0);
}

5604
cmd/ztest/ztest.c Normal file

File diff suppressed because it is too large Load Diff

34
head/atomic.h Normal file
View File

@ -0,0 +1,34 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _ATOMIC_H
#define _ATOMIC_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/atomic.h>
#endif /* _ATOMIC_H */

125
head/libintl.h Normal file
View File

@ -0,0 +1,125 @@
/*
* 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 _LIBINTL_H
#define _LIBINTL_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/isa_defs.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* wchar_t is a built-in type in standard C++ and as such is not
* defined here when using standard C++. However, the GNU compiler
* fixincludes utility nonetheless creates its own version of this
* header for use by gcc and g++. In that version it adds a redundant
* guard for __cplusplus. To avoid the creation of a gcc/g++ specific
* header we need to include the following magic comment:
*
* we must use the C++ compiler's type
*
* The above comment should not be removed or changed until GNU
* gcc/fixinc/inclhack.def is updated to bypass this header.
*/
#if !defined(__cplusplus) || (__cplusplus < 199711L && !defined(__GNUG__))
#ifndef _WCHAR_T
#define _WCHAR_T
#if defined(_LP64)
typedef int wchar_t;
#else
typedef long wchar_t;
#endif
#endif /* !_WCHAR_T */
#endif /* !defined(__cplusplus) ... */
#define TEXTDOMAINMAX 256
#define __GNU_GETTEXT_SUPPORTED_REVISION(m) \
((((m) == 0) || ((m) == 1)) ? 1 : -1)
#ifdef __STDC__
extern char *dcgettext(const char *, const char *, const int);
extern char *dgettext(const char *, const char *);
extern char *gettext(const char *);
extern char *textdomain(const char *);
extern char *bindtextdomain(const char *, const char *);
/*
* LI18NUX 2000 Globalization Specification Version 1.0
* with Amendment 2
*/
extern char *dcngettext(const char *, const char *,
const char *, unsigned long int, int);
extern char *dngettext(const char *, const char *,
const char *, unsigned long int);
extern char *ngettext(const char *, const char *, unsigned long int);
extern char *bind_textdomain_codeset(const char *, const char *);
/* Word handling functions --- requires dynamic linking */
/* Warning: these are experimental and subject to change. */
extern int wdinit(void);
extern int wdchkind(wchar_t);
extern int wdbindf(wchar_t, wchar_t, int);
extern wchar_t *wddelim(wchar_t, wchar_t, int);
extern wchar_t mcfiller(void);
extern int mcwrap(void);
#else
extern char *dcgettext();
extern char *dgettext();
extern char *gettext();
extern char *textdomain();
extern char *bindtextdomain();
/*
* LI18NUX 2000 Globalization Specification Version 1.0
* with Amendment 2
*/
extern char *dcngettext();
extern char *dngettext();
extern char *ngettext();
extern char *bind_textdomain_codeset();
/* Word handling functions --- requires dynamic linking */
/* Warning: these are experimental and subject to change. */
extern int wdinit();
extern int wdchkind();
extern int wdbindf();
extern wchar_t *wddelim();
extern wchar_t mcfiller();
extern int mcwrap();
#endif
#ifdef __cplusplus
}
#endif
#endif /* _LIBINTL_H */

88
head/stdio_ext.h Normal file
View File

@ -0,0 +1,88 @@
/*
* 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.
*/
/*
* Extensions to the stdio package
*/
#ifndef _STDIO_EXT_H
#define _STDIO_EXT_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Even though the contents of the stdio FILE structure have always been
* private to the stdio implementation, over the years some programs have
* needed to get information about a stdio stream that was not accessible
* through a supported interface. These programs have resorted to accessing
* fields of the FILE structure directly, rendering them possibly non-portable
* to new implementations of stdio, or more likely, preventing enhancements
* to stdio because those programs will break.
*
* In the 64-bit world, the FILE structure is opaque. The routines here
* are provided as a way to get the information that used to be retrieved
* directly from the FILE structure. They are based on the needs of
* existing programs (such as 'mh' and 'emacs'), so they may be extended
* as other programs are ported. Though they may still be non-portable to
* other operating systems, they will work from each Solaris release to
* the next. More portable interfaces are being developed.
*/
#define FSETLOCKING_QUERY 0
#define FSETLOCKING_INTERNAL 1
#define FSETLOCKING_BYCALLER 2
extern size_t __fbufsize(FILE *stream);
extern int __freading(FILE *stream);
extern int __fwriting(FILE *stream);
extern int __freadable(FILE *stream);
extern int __fwritable(FILE *stream);
extern int __flbf(FILE *stream);
extern void __fpurge(FILE *stream);
extern size_t __fpending(FILE *stream);
extern void _flushlbf(void);
extern int __fsetlocking(FILE *stream, int type);
/*
* Extended FILE enabling function.
*/
#if defined(_LP64) && !defined(__lint)
#define enable_extended_FILE_stdio(fd, act) (0)
#else
extern int enable_extended_FILE_stdio(int, int);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _STDIO_EXT_H */

277
head/synch.h Normal file
View File

@ -0,0 +1,277 @@
/*
* 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 (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _SYNCH_H
#define _SYNCH_H
/*
* synch.h:
* definitions needed to use the thread synchronization interface
*/
#ifndef _ASM
#include <sys/machlock.h>
#include <sys/time_impl.h>
#include <sys/synch.h>
#endif /* _ASM */
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ASM
/*
* Semaphores
*/
typedef struct _sema {
/* this structure must be the same as sem_t in <semaphore.h> */
uint32_t count; /* semaphore count */
uint16_t type;
uint16_t magic;
upad64_t pad1[3]; /* reserved for a mutex_t */
upad64_t pad2[2]; /* reserved for a cond_t */
} sema_t;
/*
* POSIX.1c Note:
* POSIX.1c requires that <pthread.h> define the structures pthread_mutex_t
* and pthread_cond_t. These structures are identical to mutex_t (lwp_mutex_t)
* and cond_t (lwp_cond_t) which are defined in <synch.h>. A nested included
* of <synch.h> (to allow a "#typedef mutex_t pthread_mutex_t") would pull in
* non-posix symbols/constants violating the namespace restrictions. Hence,
* pthread_mutex_t/pthread_cond_t have been redefined in <pthread.h> (actually
* in <sys/types.h>). Any modifications done to mutex_t/lwp_mutex_t or
* cond_t/lwp_cond_t should also be done to pthread_mutex_t/pthread_cond_t.
*/
typedef lwp_mutex_t mutex_t;
typedef lwp_cond_t cond_t;
/*
* Readers/writer locks
*
* NOTE: The layout of this structure should be kept in sync with the layout
* of the correponding structure of pthread_rwlock_t in sys/types.h.
* Also, there is an identical structure for lwp_rwlock_t in <sys/synch.h>.
* Because we have to deal with C++, we cannot redefine this one as that one.
*/
typedef struct _rwlock {
int32_t readers; /* rwstate word */
uint16_t type;
uint16_t magic;
mutex_t mutex; /* used with process-shared rwlocks */
cond_t readercv; /* used only to indicate ownership */
cond_t writercv; /* used only to indicate ownership */
} rwlock_t;
#ifdef __STDC__
int _lwp_mutex_lock(lwp_mutex_t *);
int _lwp_mutex_unlock(lwp_mutex_t *);
int _lwp_mutex_trylock(lwp_mutex_t *);
int _lwp_cond_wait(lwp_cond_t *, lwp_mutex_t *);
int _lwp_cond_timedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
int _lwp_cond_reltimedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
int _lwp_cond_signal(lwp_cond_t *);
int _lwp_cond_broadcast(lwp_cond_t *);
int _lwp_sema_init(lwp_sema_t *, int);
int _lwp_sema_wait(lwp_sema_t *);
int _lwp_sema_trywait(lwp_sema_t *);
int _lwp_sema_post(lwp_sema_t *);
int cond_init(cond_t *, int, void *);
int cond_destroy(cond_t *);
int cond_wait(cond_t *, mutex_t *);
int cond_timedwait(cond_t *, mutex_t *, const timespec_t *);
int cond_reltimedwait(cond_t *, mutex_t *, const timespec_t *);
int cond_signal(cond_t *);
int cond_broadcast(cond_t *);
int mutex_init(mutex_t *, int, void *);
int mutex_destroy(mutex_t *);
int mutex_consistent(mutex_t *);
int mutex_lock(mutex_t *);
int mutex_trylock(mutex_t *);
int mutex_unlock(mutex_t *);
int rwlock_init(rwlock_t *, int, void *);
int rwlock_destroy(rwlock_t *);
int rw_rdlock(rwlock_t *);
int rw_wrlock(rwlock_t *);
int rw_unlock(rwlock_t *);
int rw_tryrdlock(rwlock_t *);
int rw_trywrlock(rwlock_t *);
int sema_init(sema_t *, unsigned int, int, void *);
int sema_destroy(sema_t *);
int sema_wait(sema_t *);
int sema_timedwait(sema_t *, const timespec_t *);
int sema_reltimedwait(sema_t *, const timespec_t *);
int sema_post(sema_t *);
int sema_trywait(sema_t *);
#else /* __STDC__ */
int _lwp_mutex_lock();
int _lwp_mutex_unlock();
int _lwp_mutex_trylock();
int _lwp_cond_wait();
int _lwp_cond_timedwait();
int _lwp_cond_reltimedwait();
int _lwp_cond_signal();
int _lwp_cond_broadcast();
int _lwp_sema_init();
int _lwp_sema_wait();
int _lwp_sema_trywait();
int _lwp_sema_post();
int cond_init();
int cond_destroy();
int cond_wait();
int cond_timedwait();
int cond_reltimedwait();
int cond_signal();
int cond_broadcast();
int mutex_init();
int mutex_destroy();
int mutex_consistent();
int mutex_lock();
int mutex_trylock();
int mutex_unlock();
int rwlock_init();
int rwlock_destroy();
int rw_rdlock();
int rw_wrlock();
int rw_unlock();
int rw_tryrdlock();
int rw_trywrlock();
int sema_init();
int sema_destroy();
int sema_wait();
int sema_timedwait();
int sema_reltimedwait();
int sema_post();
int sema_trywait();
#endif /* __STDC__ */
#endif /* _ASM */
/* "Magic numbers" tagging synchronization object types */
#define MUTEX_MAGIC _MUTEX_MAGIC
#define SEMA_MAGIC _SEMA_MAGIC
#define COND_MAGIC _COND_MAGIC
#define RWL_MAGIC _RWL_MAGIC
/*
* POSIX.1c Note:
* DEFAULTMUTEX is defined same as PTHREAD_MUTEX_INITIALIZER in <pthread.h>.
* DEFAULTCV is defined same as PTHREAD_COND_INITIALIZER in <pthread.h>.
* DEFAULTRWLOCK is defined same as PTHREAD_RWLOCK_INITIALIZER in <pthread.h>.
* Any changes to these macros should be reflected in <pthread.h>
*/
#define DEFAULTMUTEX \
{{0, 0, 0, {USYNC_THREAD}, MUTEX_MAGIC}, \
{{{0, 0, 0, 0, 0, 0, 0, 0}}}, 0}
#define SHAREDMUTEX \
{{0, 0, 0, {USYNC_PROCESS}, MUTEX_MAGIC}, \
{{{0, 0, 0, 0, 0, 0, 0, 0}}}, 0}
#define RECURSIVEMUTEX \
{{0, 0, 0, {USYNC_THREAD|LOCK_RECURSIVE}, MUTEX_MAGIC}, \
{{{0, 0, 0, 0, 0, 0, 0, 0}}}, 0}
#define ERRORCHECKMUTEX \
{{0, 0, 0, {USYNC_THREAD|LOCK_ERRORCHECK}, MUTEX_MAGIC}, \
{{{0, 0, 0, 0, 0, 0, 0, 0}}}, 0}
#define RECURSIVE_ERRORCHECKMUTEX \
{{0, 0, 0, {USYNC_THREAD|LOCK_RECURSIVE|LOCK_ERRORCHECK}, \
MUTEX_MAGIC}, {{{0, 0, 0, 0, 0, 0, 0, 0}}}, 0}
#define DEFAULTCV \
{{{0, 0, 0, 0}, USYNC_THREAD, COND_MAGIC}, 0}
#define SHAREDCV \
{{{0, 0, 0, 0}, USYNC_PROCESS, COND_MAGIC}, 0}
#define DEFAULTSEMA \
{0, USYNC_THREAD, SEMA_MAGIC, {0, 0, 0}, {0, 0}}
#define SHAREDSEMA \
{0, USYNC_PROCESS, SEMA_MAGIC, {0, 0, 0}, {0, 0}}
#define DEFAULTRWLOCK \
{0, USYNC_THREAD, RWL_MAGIC, DEFAULTMUTEX, DEFAULTCV, DEFAULTCV}
#define SHAREDRWLOCK \
{0, USYNC_PROCESS, RWL_MAGIC, SHAREDMUTEX, SHAREDCV, SHAREDCV}
/*
* Tests on lock states.
*/
#define SEMA_HELD(x) _sema_held(x)
#define RW_READ_HELD(x) _rw_read_held(x)
#define RW_WRITE_HELD(x) _rw_write_held(x)
#define RW_LOCK_HELD(x) (RW_READ_HELD(x) || RW_WRITE_HELD(x))
#define MUTEX_HELD(x) _mutex_held(x)
/*
* The following definitions are for assertions which can be checked
* statically by tools like lock_lint. You can also define your own
* run-time test for each. If you don't, we define them to 1 so that
* such assertions simply pass.
*/
#ifndef NO_LOCKS_HELD
#define NO_LOCKS_HELD 1
#endif
#ifndef NO_COMPETING_THREADS
#define NO_COMPETING_THREADS 1
#endif
#ifndef _ASM
#ifdef __STDC__
/*
* The *_held() functions apply equally well to Solaris threads
* and to Posix threads synchronization objects, but the formal
* type declarations are different, so we just declare the argument
* to each *_held() function to be a void *, expecting that they will
* be called with the proper type of argument in each case.
*/
int _sema_held(void *); /* sema_t or sem_t */
int _rw_read_held(void *); /* rwlock_t or pthread_rwlock_t */
int _rw_write_held(void *); /* rwlock_t or pthread_rwlock_t */
int _mutex_held(void *); /* mutex_t or pthread_mutex_t */
#else /* __STDC__ */
int _sema_held();
int _rw_read_held();
int _rw_write_held();
int _mutex_held();
#endif /* __STDC__ */
/* Pause API */
#ifdef __STDC__
void smt_pause(void);
#else /* __STDC__ */
void smt_pause();
#endif /* __STDC__ */
#endif /* _ASM */
#ifdef __cplusplus
}
#endif
#endif /* _SYNCH_H */

156
head/thread.h Normal file
View File

@ -0,0 +1,156 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _THREAD_H
#define _THREAD_H
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* thread.h:
* definitions needed to use the thread interface except synchronization.
* use <synch.h> for thread synchronization.
*/
#ifndef _ASM
#include <sys/signal.h>
#include <sys/time.h>
#include <synch.h>
#endif /* _ASM */
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ASM
typedef unsigned int thread_t;
typedef unsigned int thread_key_t;
#endif /* _ASM */
#ifndef _ASM
#ifdef __STDC__
extern int thr_create(void *, size_t, void *(*)(void *), void *, long,
thread_t *);
extern int thr_join(thread_t, thread_t *, void **);
extern int thr_setconcurrency(int);
extern int thr_getconcurrency(void);
extern void thr_exit(void *) __NORETURN;
extern thread_t thr_self(void);
/*
* the definition of thr_sigsetmask() is not strict ansi-c since sigset_t is
* not in the strict ansi-c name space. Hence, include the prototype for
* thr_sigsetmask() only if strict ansi-c conformance is not turned on.
*/
#if !defined(_STRICT_STDC) || defined(__EXTENSIONS__)
extern int thr_sigsetmask(int, const sigset_t *, sigset_t *);
#endif
/*
* the definition of thr_stksegment() is not strict ansi-c since stack_t is
* not in the strict ansi-c name space. Hence, include the prototype for
* thr_stksegment() only if strict ansi-c conformance is not turned on.
*/
#if !defined(_STRICT_STDC) || defined(__EXTENSIONS__)
extern int thr_stksegment(stack_t *);
#endif
extern int thr_main(void);
extern int thr_kill(thread_t, int);
extern int thr_suspend(thread_t);
extern int thr_continue(thread_t);
extern void thr_yield(void);
extern int thr_setprio(thread_t, int);
extern int thr_getprio(thread_t, int *);
extern int thr_keycreate(thread_key_t *, void(*)(void *));
extern int thr_keycreate_once(thread_key_t *, void(*)(void *));
extern int thr_setspecific(thread_key_t, void *);
extern int thr_getspecific(thread_key_t, void **);
extern size_t thr_min_stack(void);
#else /* __STDC */
extern int thr_create();
extern int thr_join();
extern int thr_setconcurrency();
extern int thr_getconcurrency();
extern void thr_exit();
extern thread_t thr_self();
extern int thr_sigsetmask();
extern int thr_stksegment();
extern int thr_main();
extern int thr_kill();
extern int thr_suspend();
extern int thr_continue();
extern void thr_yield();
extern int thr_setprio();
extern int thr_getprio();
extern int thr_keycreate();
extern int thr_keycreate_once();
extern int thr_setspecific();
extern int thr_getspecific();
extern size_t thr_min_stack();
#endif /* __STDC */
#endif /* _ASM */
#define THR_MIN_STACK thr_min_stack()
/*
* thread flags (one word bit mask)
*/
/*
* POSIX.1c Note:
* THR_BOUND is defined same as PTHREAD_SCOPE_SYSTEM in <pthread.h>
* THR_DETACHED is defined same as PTHREAD_CREATE_DETACHED in <pthread.h>
* Any changes in these definitions should be reflected in <pthread.h>
*/
#define THR_BOUND 0x00000001 /* = PTHREAD_SCOPE_SYSTEM */
#define THR_NEW_LWP 0x00000002
#define THR_DETACHED 0x00000040 /* = PTHREAD_CREATE_DETACHED */
#define THR_SUSPENDED 0x00000080
#define THR_DAEMON 0x00000100
/*
* The key to be created by thr_keycreate_once()
* must be statically initialized with THR_ONCE_KEY.
* This must be the same as PTHREAD_ONCE_KEY_NP in <pthread.h>
*/
#define THR_ONCE_KEY (thread_key_t)(-1)
/*
* The available register states returned by thr_getstate().
*/
#define TRS_VALID 0
#define TRS_NONVOLATILE 1
#define TRS_LWPID 2
#define TRS_INVALID 3
#ifdef __cplusplus
}
#endif
#endif /* _THREAD_H */

View File

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* 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.
@ -19,12 +18,10 @@
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <strings.h>
#include <stdlib.h>
@ -703,8 +700,7 @@ dt_decl_enumerator(char *s, dt_node_t *dnp)
char *name;
int value;
name = alloca(strlen(s) + 1);
(void) strcpy(name, s);
name = strdupa(s);
free(s);
if (dsp == NULL)

View File

@ -20,12 +20,9 @@
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <strings.h>
#include <stdlib.h>
@ -181,8 +178,7 @@ dt_idcook_func(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args)
int i = 0;
assert(idp->di_iarg != NULL);
s = alloca(strlen(idp->di_iarg) + 1);
(void) strcpy(s, idp->di_iarg);
s = strdupa(idp->di_iarg);
if ((p2 = strrchr(s, ')')) != NULL)
*p2 = '\0'; /* mark end of parameter list string */

View File

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* 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.
@ -21,12 +20,9 @@
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* DTrace D Language Parser
*
@ -472,9 +468,9 @@ dt_node_name(const dt_node_t *dnp, char *buf, size_t len)
case DT_NODE_XLATOR:
(void) snprintf(buf, len, "translator <%s> (%s)",
dt_type_name(dnp->dn_xlator->dx_dst_ctfp,
dnp->dn_xlator->dx_dst_type, n1, sizeof (n1)),
dnp->dn_xlator->dx_dst_type, n1, sizeof (n1)),
dt_type_name(dnp->dn_xlator->dx_src_ctfp,
dnp->dn_xlator->dx_src_type, n2, sizeof (n2)));
dnp->dn_xlator->dx_src_type, n2, sizeof (n2)));
break;
case DT_NODE_PROG:
(void) snprintf(buf, len, "%s", "program");
@ -1440,9 +1436,9 @@ dt_node_decl(void)
"\t current: %s\n\tprevious: %s\n",
dmp->dm_name, dsp->ds_ident,
dt_type_name(dtt.dtt_ctfp, dtt.dtt_type,
n1, sizeof (n1)),
n1, sizeof (n1)),
dt_type_name(ott.dtt_ctfp, ott.dtt_type,
n2, sizeof (n2)));
n2, sizeof (n2)));
} else if (!exists && dt_module_extern(dtp, dmp,
dsp->ds_ident, &dtt) == NULL) {
xyerror(D_UNKNOWN,
@ -1452,7 +1448,7 @@ dt_node_decl(void)
dt_dprintf("extern %s`%s type=<%s>\n",
dmp->dm_name, dsp->ds_ident,
dt_type_name(dtt.dtt_ctfp, dtt.dtt_type,
n1, sizeof (n1)));
n1, sizeof (n1)));
}
break;
}
@ -1756,8 +1752,7 @@ dt_node_offsetof(dt_decl_t *ddp, char *s)
ctf_id_t type;
uint_t kind;
name = alloca(strlen(s) + 1);
(void) strcpy(name, s);
name = strdupa(s);
free(s);
err = dt_decl_type(ddp, &dtt);

View File

@ -20,12 +20,9 @@
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <strings.h>
#include <alloca.h>
@ -337,8 +334,7 @@ dt_pragma_option(const char *prname, dt_node_t *dnp)
"superfluous arguments specified for #pragma %s\n", prname);
}
opt = alloca(strlen(dnp->dn_string) + 1);
(void) strcpy(opt, dnp->dn_string);
opt = strdupa(dnp->dn_string);
if ((val = strchr(opt, '=')) != NULL)
*val++ = '\0';

View File

@ -29,23 +29,6 @@
#include <ctype.h>
#include <dt_string.h>
#include <dt_impl.h>
/*
* Create a copy of string s, but only duplicate the first n bytes.
*/
char *
strndup(const char *s, size_t n)
{
char *s2 = malloc(n + 1);
if (s2 == NULL)
longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
(void) strncpy(s2, s, n);
s2[n] = '\0';
return (s2);
}
/*
* Transform string s inline, converting each embedded C escape sequence string

View File

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* 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.
@ -19,16 +18,14 @@
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _DT_STRING_H
#define _DT_STRING_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <strings.h>
@ -36,7 +33,6 @@
extern "C" {
#endif
extern char *strndup(const char *, size_t);
extern size_t stresc2chr(char *);
extern char *strchr2esc(const char *, size_t);
extern const char *strbasename(const char *);

View File

@ -20,8 +20,7 @@
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/sysmacros.h>
@ -222,8 +221,7 @@ dtrace_str2attr(const char *str, dtrace_attribute_t *attr)
return (-1); /* invalid function arguments */
*attr = _dtrace_maxattr;
p = alloca(strlen(str) + 1);
(void) strcpy(p, str);
p = strdupa(str);
if ((p = dt_getstrattr(p, &q)) == NULL)
return (0);

1269
lib/libnvpair/libnvpair.c Normal file

File diff suppressed because it is too large Load Diff

194
lib/libnvpair/libnvpair.h Normal file
View File

@ -0,0 +1,194 @@
/*
* 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 (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _LIBNVPAIR_H
#define _LIBNVPAIR_H
#include <sys/nvpair.h>
#include <stdlib.h>
#include <stdio.h>
#include <regex.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* All interfaces described in this file are private to Solaris, and
* are subject to change at any time and without notice. The public
* nvlist/nvpair interfaces, as documented in manpage sections 3NVPAIR,
* are all imported from <sys/nvpair.h> included above.
*/
extern int nvpair_value_match(nvpair_t *, int, char *, char **);
extern int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *,
char **);
extern void nvlist_print(FILE *, nvlist_t *);
extern void dump_nvlist(nvlist_t *, int);
/*
* Private nvlist printing interface that allows the caller some control
* over output rendering (as opposed to nvlist_print and dump_nvlist).
*
* Obtain an opaque nvlist_prtctl_t cookie using nvlist_prtctl_alloc
* (NULL on failure); on return the cookie is set up for default formatting
* and rendering. Quote the cookie in subsequent customisation functions and
* then pass the cookie to nvlist_prt to render the nvlist. Finally,
* use nvlist_prtctl_free to release the cookie.
*
* For all nvlist_lookup_xxx and nvlist_lookup_xxx_array functions
* we have a corresponding brace of functions that appoint replacement
* rendering functions:
*
* extern void nvlist_prtctl_xxx(nvlist_prtctl_t,
* void (*)(nvlist_prtctl_t ctl, void *private, const char *name,
* xxxtype value))
*
* and
*
* extern void nvlist_prtctl_xxx_array(nvlist_prtctl_t,
* void (*)(nvlist_prtctl_t ctl, void *private, const char *name,
* xxxtype value, uint_t count))
*
* where xxxtype is the C datatype corresponding to xxx, eg int8_t for "int8"
* and char * for "string". The function that is appointed to render the
* specified datatype receives as arguments the cookie, the nvlist
* member name, the value of that member (or a pointer for array function),
* and (for array rendering functions) a count of the number of elements.
*/
typedef struct nvlist_prtctl *nvlist_prtctl_t; /* opaque */
enum nvlist_indent_mode {
NVLIST_INDENT_ABS, /* Absolute indentation */
NVLIST_INDENT_TABBED /* Indent with tabstops */
};
extern nvlist_prtctl_t nvlist_prtctl_alloc(void);
extern void nvlist_prtctl_free(nvlist_prtctl_t);
extern void nvlist_prt(nvlist_t *, nvlist_prtctl_t);
/* Output stream */
extern void nvlist_prtctl_setdest(nvlist_prtctl_t, FILE *);
extern FILE *nvlist_prtctl_getdest(nvlist_prtctl_t);
/* Indentation mode, start indent, indent increment; default tabbed/0/1 */
extern void nvlist_prtctl_setindent(nvlist_prtctl_t, enum nvlist_indent_mode,
int, int);
extern void nvlist_prtctl_doindent(nvlist_prtctl_t, int);
enum nvlist_prtctl_fmt {
NVLIST_FMT_MEMBER_NAME, /* name fmt; default "%s = " */
NVLIST_FMT_MEMBER_POSTAMBLE, /* after nvlist member; default "\n" */
NVLIST_FMT_BTWN_ARRAY /* between array members; default " " */
};
extern void nvlist_prtctl_setfmt(nvlist_prtctl_t, enum nvlist_prtctl_fmt,
const char *);
extern void nvlist_prtctl_dofmt(nvlist_prtctl_t, enum nvlist_prtctl_fmt, ...);
/*
* Function prototypes for interfaces that appoint a new rendering function
* for single-valued nvlist members.
*
* A replacement function receives arguments as follows:
*
* nvlist_prtctl_t Print control structure; do not change preferences
* for this object from a print callback function.
*
* void * The function-private cookie argument registered
* when the replacement function was appointed.
*
* nvlist_t * The full nvlist that is being processed. The
* rendering function is called to render a single
* member (name and value passed as below) but it may
* want to reference or incorporate other aspects of
* the full nvlist.
*
* const char * Member name to render
*
* valtype Value of the member to render
*
* The function must return non-zero if it has rendered output for this
* member, or 0 if it wants to default to standard rendering for this
* one member.
*/
#define NVLIST_PRINTCTL_SVDECL(funcname, valtype) \
extern void funcname(nvlist_prtctl_t, \
int (*)(nvlist_prtctl_t, void *, nvlist_t *, const char *, valtype), \
void *)
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_boolean, int);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_boolean_value, boolean_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_byte, uchar_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int8, int8_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint8, uint8_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int16, int16_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint16, uint16_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int32, int32_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint32, uint32_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int64, int64_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint64, uint64_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_double, double);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_string, char *);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_hrtime, hrtime_t);
NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_nvlist, nvlist_t *);
#undef NVLIST_PRINTCTL_SVDECL /* was just for "clarity" above */
/*
* Function prototypes for interfaces that appoint a new rendering function
* for array-valued nvlist members.
*
* One additional argument is taken: uint_t for the number of array elements
*
* Return values as above.
*/
#define NVLIST_PRINTCTL_AVDECL(funcname, vtype) \
extern void funcname(nvlist_prtctl_t, \
int (*)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, uint_t), \
void *)
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_boolean_array, boolean_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_byte_array, uchar_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int8_array, int8_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint8_array, uint8_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int16_array, int16_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint16_array, uint16_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int32_array, int32_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint32_array, uint32_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int64_array, int64_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint64_array, uint64_t *);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_string_array, char **);
NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_nvlist_array, nvlist_t **);
#undef NVLIST_PRINTCTL_AVDECL /* was just for "clarity" above */
#ifdef __cplusplus
}
#endif
#endif /* _LIBNVPAIR_H */

View File

@ -0,0 +1,59 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/nvpair.h>
#include <stdlib.h>
/*ARGSUSED*/
static void *
nv_alloc_sys(nv_alloc_t *nva, size_t size)
{
return (malloc(size));
}
/*ARGSUSED*/
static void
nv_free_sys(nv_alloc_t *nva, void *buf, size_t size)
{
free(buf);
}
const nv_alloc_ops_t system_ops_def = {
NULL, /* nv_ao_init() */
NULL, /* nv_ao_fini() */
nv_alloc_sys, /* nv_ao_alloc() */
nv_free_sys, /* nv_ao_free() */
NULL /* nv_ao_reset() */
};
nv_alloc_t nv_alloc_nosleep_def = {
&system_ops_def,
NULL
};
nv_alloc_t *nv_alloc_nosleep = &nv_alloc_nosleep_def;

View File

@ -0,0 +1,390 @@
/*
* 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _LIBUUTIL_H
#define _LIBUUTIL_H
#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Standard flags codes.
*/
#define UU_DEFAULT 0
/*
* Standard error codes.
*/
#define UU_ERROR_NONE 0 /* no error */
#define UU_ERROR_INVALID_ARGUMENT 1 /* invalid argument */
#define UU_ERROR_UNKNOWN_FLAG 2 /* passed flag invalid */
#define UU_ERROR_NO_MEMORY 3 /* out of memory */
#define UU_ERROR_CALLBACK_FAILED 4 /* callback-initiated error */
#define UU_ERROR_NOT_SUPPORTED 5 /* operation not supported */
#define UU_ERROR_EMPTY 6 /* no value provided */
#define UU_ERROR_UNDERFLOW 7 /* value is too small */
#define UU_ERROR_OVERFLOW 8 /* value is too value */
#define UU_ERROR_INVALID_CHAR 9 /* value contains unexpected char */
#define UU_ERROR_INVALID_DIGIT 10 /* value contains digit not in base */
#define UU_ERROR_SYSTEM 99 /* underlying system error */
#define UU_ERROR_UNKNOWN 100 /* error status not known */
/*
* Standard program exit codes.
*/
#define UU_EXIT_OK (*(uu_exit_ok()))
#define UU_EXIT_FATAL (*(uu_exit_fatal()))
#define UU_EXIT_USAGE (*(uu_exit_usage()))
/*
* Exit status profiles.
*/
#define UU_PROFILE_DEFAULT 0
#define UU_PROFILE_LAUNCHER 1
/*
* Error reporting functions.
*/
uint32_t uu_error(void);
const char *uu_strerror(uint32_t);
/*
* Program notification functions.
*/
extern void uu_alt_exit(int);
extern const char *uu_setpname(char *);
extern const char *uu_getpname(void);
/*PRINTFLIKE1*/
extern void uu_warn(const char *, ...);
extern void uu_vwarn(const char *, va_list);
/*PRINTFLIKE1*/
extern void uu_die(const char *, ...) __NORETURN;
extern void uu_vdie(const char *, va_list) __NORETURN;
/*PRINTFLIKE2*/
extern void uu_xdie(int, const char *, ...) __NORETURN;
extern void uu_vxdie(int, const char *, va_list) __NORETURN;
/*
* Exit status functions (not to be used directly)
*/
extern int *uu_exit_ok(void);
extern int *uu_exit_fatal(void);
extern int *uu_exit_usage(void);
/*
* string->number conversions
*/
extern int uu_strtoint(const char *, void *, size_t, int, int64_t, int64_t);
extern int uu_strtouint(const char *, void *, size_t, int, uint64_t, uint64_t);
/*
* Debug print facility functions.
*/
typedef struct uu_dprintf uu_dprintf_t;
typedef enum {
UU_DPRINTF_SILENT,
UU_DPRINTF_FATAL,
UU_DPRINTF_WARNING,
UU_DPRINTF_NOTICE,
UU_DPRINTF_INFO,
UU_DPRINTF_DEBUG
} uu_dprintf_severity_t;
extern uu_dprintf_t *uu_dprintf_create(const char *, uu_dprintf_severity_t,
uint_t);
/*PRINTFLIKE3*/
extern void uu_dprintf(uu_dprintf_t *, uu_dprintf_severity_t,
const char *, ...);
extern void uu_dprintf_destroy(uu_dprintf_t *);
extern const char *uu_dprintf_getname(uu_dprintf_t *);
/*
* Identifier test flags and function.
*/
#define UU_NAME_DOMAIN 0x1 /* allow SUNW, or com.sun, prefix */
#define UU_NAME_PATH 0x2 /* allow '/'-delimited paths */
int uu_check_name(const char *, uint_t);
/*
* File creation functions.
*/
extern int uu_open_tmp(const char *dir, uint_t uflags);
/*
* Convenience functions.
*/
#define UU_NELEM(a) (sizeof (a) / sizeof ((a)[0]))
/*PRINTFLIKE1*/
extern char *uu_msprintf(const char *format, ...);
extern void *uu_zalloc(size_t);
extern char *uu_strdup(const char *);
extern void uu_free(void *);
extern boolean_t uu_strcaseeq(const char *a, const char *b);
extern boolean_t uu_streq(const char *a, const char *b);
extern char *uu_strndup(const char *s, size_t n);
extern boolean_t uu_strbw(const char *a, const char *b);
extern void *uu_memdup(const void *buf, size_t sz);
extern void uu_dump(FILE *out, const char *prefix, const void *buf, size_t len);
/*
* Comparison function type definition.
* Developers should be careful in their use of the _private argument. If you
* break interface guarantees, you get undefined behavior.
*/
typedef int uu_compare_fn_t(const void *__left, const void *__right,
void *__private);
/*
* Walk variant flags.
* A data structure need not provide support for all variants and
* combinations. Refer to the appropriate documentation.
*/
#define UU_WALK_ROBUST 0x00000001 /* walk can survive removes */
#define UU_WALK_REVERSE 0x00000002 /* reverse walk order */
#define UU_WALK_PREORDER 0x00000010 /* walk tree in pre-order */
#define UU_WALK_POSTORDER 0x00000020 /* walk tree in post-order */
/*
* Walk callback function return codes.
*/
#define UU_WALK_ERROR -1
#define UU_WALK_NEXT 0
#define UU_WALK_DONE 1
/*
* Walk callback function type definition.
*/
typedef int uu_walk_fn_t(void *_elem, void *_private);
/*
* lists: opaque structures
*/
typedef struct uu_list_pool uu_list_pool_t;
typedef struct uu_list uu_list_t;
typedef struct uu_list_node {
uintptr_t uln_opaque[2];
} uu_list_node_t;
typedef struct uu_list_walk uu_list_walk_t;
typedef uintptr_t uu_list_index_t;
/*
* lists: interface
*
* basic usage:
* typedef struct foo {
* ...
* uu_list_node_t foo_node;
* ...
* } foo_t;
*
* static int
* foo_compare(void *l_arg, void *r_arg, void *private)
* {
* foo_t *l = l_arg;
* foo_t *r = r_arg;
*
* if (... l greater than r ...)
* return (1);
* if (... l less than r ...)
* return (-1);
* return (0);
* }
*
* ...
* // at initialization time
* foo_pool = uu_list_pool_create("foo_pool",
* sizeof (foo_t), offsetof(foo_t, foo_node), foo_compare,
* debugging? 0 : UU_AVL_POOL_DEBUG);
* ...
*/
uu_list_pool_t *uu_list_pool_create(const char *, size_t, size_t,
uu_compare_fn_t *, uint32_t);
#define UU_LIST_POOL_DEBUG 0x00000001
void uu_list_pool_destroy(uu_list_pool_t *);
/*
* usage:
*
* foo_t *a;
* a = malloc(sizeof(*a));
* uu_list_node_init(a, &a->foo_list, pool);
* ...
* uu_list_node_fini(a, &a->foo_list, pool);
* free(a);
*/
void uu_list_node_init(void *, uu_list_node_t *, uu_list_pool_t *);
void uu_list_node_fini(void *, uu_list_node_t *, uu_list_pool_t *);
uu_list_t *uu_list_create(uu_list_pool_t *, void *_parent, uint32_t);
#define UU_LIST_DEBUG 0x00000001
#define UU_LIST_SORTED 0x00000002 /* list is sorted */
void uu_list_destroy(uu_list_t *); /* list must be empty */
size_t uu_list_numnodes(uu_list_t *);
void *uu_list_first(uu_list_t *);
void *uu_list_last(uu_list_t *);
void *uu_list_next(uu_list_t *, void *);
void *uu_list_prev(uu_list_t *, void *);
int uu_list_walk(uu_list_t *, uu_walk_fn_t *, void *, uint32_t);
uu_list_walk_t *uu_list_walk_start(uu_list_t *, uint32_t);
void *uu_list_walk_next(uu_list_walk_t *);
void uu_list_walk_end(uu_list_walk_t *);
void *uu_list_find(uu_list_t *, void *, void *, uu_list_index_t *);
void uu_list_insert(uu_list_t *, void *, uu_list_index_t);
void *uu_list_nearest_next(uu_list_t *, uu_list_index_t);
void *uu_list_nearest_prev(uu_list_t *, uu_list_index_t);
void *uu_list_teardown(uu_list_t *, void **);
void uu_list_remove(uu_list_t *, void *);
/*
* lists: interfaces for non-sorted lists only
*/
int uu_list_insert_before(uu_list_t *, void *_target, void *_elem);
int uu_list_insert_after(uu_list_t *, void *_target, void *_elem);
/*
* avl trees: opaque structures
*/
typedef struct uu_avl_pool uu_avl_pool_t;
typedef struct uu_avl uu_avl_t;
typedef struct uu_avl_node {
#ifdef _LP64
uintptr_t uan_opaque[3];
#else
uintptr_t uan_opaque[4];
#endif
} uu_avl_node_t;
typedef struct uu_avl_walk uu_avl_walk_t;
typedef uintptr_t uu_avl_index_t;
/*
* avl trees: interface
*
* basic usage:
* typedef struct foo {
* ...
* uu_avl_node_t foo_node;
* ...
* } foo_t;
*
* static int
* foo_compare(void *l_arg, void *r_arg, void *private)
* {
* foo_t *l = l_arg;
* foo_t *r = r_arg;
*
* if (... l greater than r ...)
* return (1);
* if (... l less than r ...)
* return (-1);
* return (0);
* }
*
* ...
* // at initialization time
* foo_pool = uu_avl_pool_create("foo_pool",
* sizeof (foo_t), offsetof(foo_t, foo_node), foo_compare,
* debugging? 0 : UU_AVL_POOL_DEBUG);
* ...
*/
uu_avl_pool_t *uu_avl_pool_create(const char *, size_t, size_t,
uu_compare_fn_t *, uint32_t);
#define UU_AVL_POOL_DEBUG 0x00000001
void uu_avl_pool_destroy(uu_avl_pool_t *);
/*
* usage:
*
* foo_t *a;
* a = malloc(sizeof(*a));
* uu_avl_node_init(a, &a->foo_avl, pool);
* ...
* uu_avl_node_fini(a, &a->foo_avl, pool);
* free(a);
*/
void uu_avl_node_init(void *, uu_avl_node_t *, uu_avl_pool_t *);
void uu_avl_node_fini(void *, uu_avl_node_t *, uu_avl_pool_t *);
uu_avl_t *uu_avl_create(uu_avl_pool_t *, void *_parent, uint32_t);
#define UU_AVL_DEBUG 0x00000001
void uu_avl_destroy(uu_avl_t *); /* list must be empty */
size_t uu_avl_numnodes(uu_avl_t *);
void *uu_avl_first(uu_avl_t *);
void *uu_avl_last(uu_avl_t *);
void *uu_avl_next(uu_avl_t *, void *);
void *uu_avl_prev(uu_avl_t *, void *);
int uu_avl_walk(uu_avl_t *, uu_walk_fn_t *, void *, uint32_t);
uu_avl_walk_t *uu_avl_walk_start(uu_avl_t *, uint32_t);
void *uu_avl_walk_next(uu_avl_walk_t *);
void uu_avl_walk_end(uu_avl_walk_t *);
void *uu_avl_find(uu_avl_t *, void *, void *, uu_avl_index_t *);
void uu_avl_insert(uu_avl_t *, void *, uu_avl_index_t);
void *uu_avl_nearest_next(uu_avl_t *, uu_avl_index_t);
void *uu_avl_nearest_prev(uu_avl_t *, uu_avl_index_t);
void *uu_avl_teardown(uu_avl_t *, void **);
void uu_avl_remove(uu_avl_t *, void *);
#ifdef __cplusplus
}
#endif
#endif /* _LIBUUTIL_H */

View File

@ -0,0 +1,35 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBUUTIL_COMMON_H
#define _LIBUUTIL_COMMON_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libuutil.h>
#include <libuutil_impl.h>
#endif /* _LIBUUTIL_COMMON_H */

View File

@ -0,0 +1,181 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBUUTIL_IMPL_H
#define _LIBUUTIL_IMPL_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libuutil.h>
#include <pthread.h>
#include <sys/avl_impl.h>
#include <sys/byteorder.h>
#ifdef __cplusplus
extern "C" {
#endif
void uu_set_error(uint_t);
#pragma rarely_called(uu_set_error)
/*PRINTFLIKE1*/
void uu_panic(const char *format, ...);
#pragma rarely_called(uu_panic)
struct uu_dprintf {
char *uud_name;
uu_dprintf_severity_t uud_severity;
uint_t uud_flags;
};
/*
* For debugging purposes, libuutil keeps around linked lists of all uu_lists
* and uu_avls, along with pointers to their parents. These can cause false
* negatives when looking for memory leaks, so we encode the pointers by
* storing them with swapped endianness; this is not perfect, but it's about
* the best we can do without wasting a lot of space.
*/
#ifdef _LP64
#define UU_PTR_ENCODE(ptr) BSWAP_64((uintptr_t)(void *)(ptr))
#else
#define UU_PTR_ENCODE(ptr) BSWAP_32((uintptr_t)(void *)(ptr))
#endif
#define UU_PTR_DECODE(ptr) ((void *)UU_PTR_ENCODE(ptr))
/*
* uu_list structures
*/
typedef struct uu_list_node_impl {
struct uu_list_node_impl *uln_next;
struct uu_list_node_impl *uln_prev;
} uu_list_node_impl_t;
struct uu_list_walk {
uu_list_walk_t *ulw_next;
uu_list_walk_t *ulw_prev;
uu_list_t *ulw_list;
int8_t ulw_dir;
uint8_t ulw_robust;
uu_list_node_impl_t *ulw_next_result;
};
struct uu_list {
uintptr_t ul_next_enc;
uintptr_t ul_prev_enc;
uu_list_pool_t *ul_pool;
uintptr_t ul_parent_enc; /* encoded parent pointer */
size_t ul_offset;
size_t ul_numnodes;
uint8_t ul_debug;
uint8_t ul_sorted;
uint8_t ul_index; /* mark for uu_list_index_ts */
uu_list_node_impl_t ul_null_node;
uu_list_walk_t ul_null_walk; /* for robust walkers */
};
#define UU_LIST_PTR(ptr) ((uu_list_t *)UU_PTR_DECODE(ptr))
#define UU_LIST_POOL_MAXNAME 64
struct uu_list_pool {
uu_list_pool_t *ulp_next;
uu_list_pool_t *ulp_prev;
char ulp_name[UU_LIST_POOL_MAXNAME];
size_t ulp_nodeoffset;
size_t ulp_objsize;
uu_compare_fn_t *ulp_cmp;
uint8_t ulp_debug;
uint8_t ulp_last_index;
pthread_mutex_t ulp_lock; /* protects null_list */
uu_list_t ulp_null_list;
};
/*
* uu_avl structures
*/
typedef struct avl_node uu_avl_node_impl_t;
struct uu_avl_walk {
uu_avl_walk_t *uaw_next;
uu_avl_walk_t *uaw_prev;
uu_avl_t *uaw_avl;
void *uaw_next_result;
int8_t uaw_dir;
uint8_t uaw_robust;
};
struct uu_avl {
uintptr_t ua_next_enc;
uintptr_t ua_prev_enc;
uu_avl_pool_t *ua_pool;
uintptr_t ua_parent_enc;
uint8_t ua_debug;
uint8_t ua_index; /* mark for uu_avl_index_ts */
struct avl_tree ua_tree;
uu_avl_walk_t ua_null_walk;
};
#define UU_AVL_PTR(x) ((uu_avl_t *)UU_PTR_DECODE(x))
#define UU_AVL_POOL_MAXNAME 64
struct uu_avl_pool {
uu_avl_pool_t *uap_next;
uu_avl_pool_t *uap_prev;
char uap_name[UU_AVL_POOL_MAXNAME];
size_t uap_nodeoffset;
size_t uap_objsize;
uu_compare_fn_t *uap_cmp;
uint8_t uap_debug;
uint8_t uap_last_index;
pthread_mutex_t uap_lock; /* protects null_avl */
uu_avl_t uap_null_avl;
};
/*
* atfork() handlers
*/
void uu_avl_lockup(void);
void uu_avl_release(void);
void uu_list_lockup(void);
void uu_list_release(void);
#ifdef __cplusplus
}
#endif
#endif /* _LIBUUTIL_IMPL_H */

View File

@ -0,0 +1,135 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include "libuutil_common.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *
uu_zalloc(size_t n)
{
void *p = malloc(n);
if (p == NULL) {
uu_set_error(UU_ERROR_SYSTEM);
return (NULL);
}
(void) memset(p, 0, n);
return (p);
}
void
uu_free(void *p)
{
free(p);
}
char *
uu_strdup(const char *str)
{
char *buf = NULL;
if (str != NULL) {
size_t sz;
sz = strlen(str) + 1;
buf = uu_zalloc(sz);
if (buf != NULL)
(void) memcpy(buf, str, sz);
}
return (buf);
}
/*
* Duplicate up to n bytes of a string. Kind of sort of like
* strdup(strlcpy(s, n)).
*/
char *
uu_strndup(const char *s, size_t n)
{
size_t len;
char *p;
len = strnlen(s, n);
p = uu_zalloc(len + 1);
if (p == NULL)
return (NULL);
if (len > 0)
(void) memcpy(p, s, len);
p[len] = '\0';
return (p);
}
/*
* Duplicate a block of memory. Combines malloc with memcpy, much as
* strdup combines malloc, strlen, and strcpy.
*/
void *
uu_memdup(const void *buf, size_t sz)
{
void *p;
p = uu_zalloc(sz);
if (p == NULL)
return (NULL);
(void) memcpy(p, buf, sz);
return (p);
}
char *
uu_msprintf(const char *format, ...)
{
va_list args;
char attic[1];
uint_t M, m;
char *b;
va_start(args, format);
M = vsnprintf(attic, 1, format, args);
va_end(args);
for (;;) {
m = M;
if ((b = uu_zalloc(m + 1)) == NULL)
return (NULL);
va_start(args, format);
M = vsnprintf(b, m + 1, format, args);
va_end(args);
if (M == m)
break; /* sizes match */
uu_free(b);
}
return (b);
}

View File

@ -0,0 +1,569 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/avl.h>
static uu_avl_pool_t uu_null_apool = { &uu_null_apool, &uu_null_apool };
static pthread_mutex_t uu_apool_list_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* The index mark change on every insert and delete, to catch stale
* references.
*
* We leave the low bit alone, since the avl code uses it.
*/
#define INDEX_MAX (sizeof (uintptr_t) - 2)
#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 2 : ((m) + 2) & INDEX_MAX)
#define INDEX_DECODE(i) ((i) & ~INDEX_MAX)
#define INDEX_ENCODE(p, n) (((n) & ~INDEX_MAX) | (p)->ua_index)
#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ua_index)
#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)
/*
* When an element is inactive (not in a tree), we keep a marked pointer to
* its containing pool in its first word, and a NULL pointer in its second.
*
* On insert, we use these to verify that it comes from the correct pool.
*/
#define NODE_ARRAY(p, n) ((uintptr_t *)((uintptr_t)(n) + \
(pp)->uap_nodeoffset))
#define POOL_TO_MARKER(pp) (((uintptr_t)(pp) | 1))
#define DEAD_MARKER 0xc4
uu_avl_pool_t *
uu_avl_pool_create(const char *name, size_t objsize, size_t nodeoffset,
uu_compare_fn_t *compare_func, uint32_t flags)
{
uu_avl_pool_t *pp, *next, *prev;
if (name == NULL ||
uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
nodeoffset + sizeof (uu_avl_node_t) > objsize ||
compare_func == NULL) {
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (NULL);
}
if (flags & ~UU_AVL_POOL_DEBUG) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (NULL);
}
pp = uu_zalloc(sizeof (uu_avl_pool_t));
if (pp == NULL) {
uu_set_error(UU_ERROR_NO_MEMORY);
return (NULL);
}
(void) strlcpy(pp->uap_name, name, sizeof (pp->uap_name));
pp->uap_nodeoffset = nodeoffset;
pp->uap_objsize = objsize;
pp->uap_cmp = compare_func;
if (flags & UU_AVL_POOL_DEBUG)
pp->uap_debug = 1;
pp->uap_last_index = 0;
(void) pthread_mutex_init(&pp->uap_lock, NULL);
pp->uap_null_avl.ua_next_enc = UU_PTR_ENCODE(&pp->uap_null_avl);
pp->uap_null_avl.ua_prev_enc = UU_PTR_ENCODE(&pp->uap_null_avl);
(void) pthread_mutex_lock(&uu_apool_list_lock);
pp->uap_next = next = &uu_null_apool;
pp->uap_prev = prev = next->uap_prev;
next->uap_prev = pp;
prev->uap_next = pp;
(void) pthread_mutex_unlock(&uu_apool_list_lock);
return (pp);
}
void
uu_avl_pool_destroy(uu_avl_pool_t *pp)
{
if (pp->uap_debug) {
if (pp->uap_null_avl.ua_next_enc !=
UU_PTR_ENCODE(&pp->uap_null_avl) ||
pp->uap_null_avl.ua_prev_enc !=
UU_PTR_ENCODE(&pp->uap_null_avl)) {
uu_panic("uu_avl_pool_destroy: Pool \"%.*s\" (%p) has "
"outstanding avls, or is corrupt.\n",
(int)sizeof (pp->uap_name), pp->uap_name,
(void *)pp);
}
}
(void) pthread_mutex_lock(&uu_apool_list_lock);
pp->uap_next->uap_prev = pp->uap_prev;
pp->uap_prev->uap_next = pp->uap_next;
(void) pthread_mutex_unlock(&uu_apool_list_lock);
pp->uap_prev = NULL;
pp->uap_next = NULL;
uu_free(pp);
}
void
uu_avl_node_init(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
{
uintptr_t *na = (uintptr_t *)np;
if (pp->uap_debug) {
uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
if (offset + sizeof (*np) > pp->uap_objsize) {
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't fit in object (size %ld)\n",
base, (void *)np, (void *)pp, pp->uap_name,
(long)offset, (long)pp->uap_objsize);
}
if (offset != pp->uap_nodeoffset) {
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't match pool's offset (%ld)\n",
base, (void *)np, (void *)pp, pp->uap_name,
(long)offset, (long)pp->uap_objsize);
}
}
na[0] = POOL_TO_MARKER(pp);
na[1] = 0;
}
void
uu_avl_node_fini(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
{
uintptr_t *na = (uintptr_t *)np;
if (pp->uap_debug) {
if (na[0] == DEAD_MARKER && na[1] == DEAD_MARKER) {
uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
"node already finied\n",
base, (void *)np, (void *)pp, pp->uap_name);
}
if (na[0] != POOL_TO_MARKER(pp) || na[1] != 0) {
uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
"node corrupt, in tree, or in different pool\n",
base, (void *)np, (void *)pp, pp->uap_name);
}
}
na[0] = DEAD_MARKER;
na[1] = DEAD_MARKER;
na[2] = DEAD_MARKER;
}
struct uu_avl_node_compare_info {
uu_compare_fn_t *ac_compare;
void *ac_private;
void *ac_right;
void *ac_found;
};
static int
uu_avl_node_compare(const void *l, const void *r)
{
struct uu_avl_node_compare_info *info =
(struct uu_avl_node_compare_info *)l;
int res = info->ac_compare(r, info->ac_right, info->ac_private);
if (res == 0) {
if (info->ac_found == NULL)
info->ac_found = (void *)r;
return (-1);
}
if (res < 0)
return (1);
return (-1);
}
uu_avl_t *
uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags)
{
uu_avl_t *ap, *next, *prev;
if (flags & ~UU_AVL_DEBUG) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (NULL);
}
ap = uu_zalloc(sizeof (*ap));
if (ap == NULL) {
uu_set_error(UU_ERROR_NO_MEMORY);
return (NULL);
}
ap->ua_pool = pp;
ap->ua_parent_enc = UU_PTR_ENCODE(parent);
ap->ua_debug = pp->uap_debug || (flags & UU_AVL_DEBUG);
ap->ua_index = (pp->uap_last_index = INDEX_NEXT(pp->uap_last_index));
avl_create(&ap->ua_tree, &uu_avl_node_compare, pp->uap_objsize,
pp->uap_nodeoffset);
ap->ua_null_walk.uaw_next = &ap->ua_null_walk;
ap->ua_null_walk.uaw_prev = &ap->ua_null_walk;
(void) pthread_mutex_lock(&pp->uap_lock);
next = &pp->uap_null_avl;
prev = UU_PTR_DECODE(next->ua_prev_enc);
ap->ua_next_enc = UU_PTR_ENCODE(next);
ap->ua_prev_enc = UU_PTR_ENCODE(prev);
next->ua_prev_enc = UU_PTR_ENCODE(ap);
prev->ua_next_enc = UU_PTR_ENCODE(ap);
(void) pthread_mutex_unlock(&pp->uap_lock);
return (ap);
}
void
uu_avl_destroy(uu_avl_t *ap)
{
uu_avl_pool_t *pp = ap->ua_pool;
if (ap->ua_debug) {
if (avl_numnodes(&ap->ua_tree) != 0) {
uu_panic("uu_avl_destroy(%p): tree not empty\n",
(void *)ap);
}
if (ap->ua_null_walk.uaw_next != &ap->ua_null_walk ||
ap->ua_null_walk.uaw_prev != &ap->ua_null_walk) {
uu_panic("uu_avl_destroy(%p): outstanding walkers\n",
(void *)ap);
}
}
(void) pthread_mutex_lock(&pp->uap_lock);
UU_AVL_PTR(ap->ua_next_enc)->ua_prev_enc = ap->ua_prev_enc;
UU_AVL_PTR(ap->ua_prev_enc)->ua_next_enc = ap->ua_next_enc;
(void) pthread_mutex_unlock(&pp->uap_lock);
ap->ua_prev_enc = UU_PTR_ENCODE(NULL);
ap->ua_next_enc = UU_PTR_ENCODE(NULL);
ap->ua_pool = NULL;
avl_destroy(&ap->ua_tree);
uu_free(ap);
}
size_t
uu_avl_numnodes(uu_avl_t *ap)
{
return (avl_numnodes(&ap->ua_tree));
}
void *
uu_avl_first(uu_avl_t *ap)
{
return (avl_first(&ap->ua_tree));
}
void *
uu_avl_last(uu_avl_t *ap)
{
return (avl_last(&ap->ua_tree));
}
void *
uu_avl_next(uu_avl_t *ap, void *node)
{
return (AVL_NEXT(&ap->ua_tree, node));
}
void *
uu_avl_prev(uu_avl_t *ap, void *node)
{
return (AVL_PREV(&ap->ua_tree, node));
}
static void
_avl_walk_init(uu_avl_walk_t *wp, uu_avl_t *ap, uint32_t flags)
{
uu_avl_walk_t *next, *prev;
int robust = (flags & UU_WALK_ROBUST);
int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
(void) memset(wp, 0, sizeof (*wp));
wp->uaw_avl = ap;
wp->uaw_robust = robust;
wp->uaw_dir = direction;
if (direction > 0)
wp->uaw_next_result = avl_first(&ap->ua_tree);
else
wp->uaw_next_result = avl_last(&ap->ua_tree);
if (ap->ua_debug || robust) {
wp->uaw_next = next = &ap->ua_null_walk;
wp->uaw_prev = prev = next->uaw_prev;
next->uaw_prev = wp;
prev->uaw_next = wp;
}
}
static void *
_avl_walk_advance(uu_avl_walk_t *wp, uu_avl_t *ap)
{
void *np = wp->uaw_next_result;
avl_tree_t *t = &ap->ua_tree;
if (np == NULL)
return (NULL);
wp->uaw_next_result = (wp->uaw_dir > 0)? AVL_NEXT(t, np) :
AVL_PREV(t, np);
return (np);
}
static void
_avl_walk_fini(uu_avl_walk_t *wp)
{
if (wp->uaw_next != NULL) {
wp->uaw_next->uaw_prev = wp->uaw_prev;
wp->uaw_prev->uaw_next = wp->uaw_next;
wp->uaw_next = NULL;
wp->uaw_prev = NULL;
}
wp->uaw_avl = NULL;
wp->uaw_next_result = NULL;
}
uu_avl_walk_t *
uu_avl_walk_start(uu_avl_t *ap, uint32_t flags)
{
uu_avl_walk_t *wp;
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (NULL);
}
wp = uu_zalloc(sizeof (*wp));
if (wp == NULL) {
uu_set_error(UU_ERROR_NO_MEMORY);
return (NULL);
}
_avl_walk_init(wp, ap, flags);
return (wp);
}
void *
uu_avl_walk_next(uu_avl_walk_t *wp)
{
return (_avl_walk_advance(wp, wp->uaw_avl));
}
void
uu_avl_walk_end(uu_avl_walk_t *wp)
{
_avl_walk_fini(wp);
uu_free(wp);
}
int
uu_avl_walk(uu_avl_t *ap, uu_walk_fn_t *func, void *private, uint32_t flags)
{
void *e;
uu_avl_walk_t my_walk;
int status = UU_WALK_NEXT;
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (-1);
}
_avl_walk_init(&my_walk, ap, flags);
while (status == UU_WALK_NEXT &&
(e = _avl_walk_advance(&my_walk, ap)) != NULL)
status = (*func)(e, private);
_avl_walk_fini(&my_walk);
if (status >= 0)
return (0);
uu_set_error(UU_ERROR_CALLBACK_FAILED);
return (-1);
}
void
uu_avl_remove(uu_avl_t *ap, void *elem)
{
uu_avl_walk_t *wp;
uu_avl_pool_t *pp = ap->ua_pool;
uintptr_t *na = NODE_ARRAY(pp, elem);
if (ap->ua_debug) {
/*
* invalidate outstanding uu_avl_index_ts.
*/
ap->ua_index = INDEX_NEXT(ap->ua_index);
}
/*
* Robust walkers most be advanced, if we are removing the node
* they are currently using. In debug mode, non-robust walkers
* are also on the walker list.
*/
for (wp = ap->ua_null_walk.uaw_next; wp != &ap->ua_null_walk;
wp = wp->uaw_next) {
if (wp->uaw_robust) {
if (elem == wp->uaw_next_result)
(void) _avl_walk_advance(wp, ap);
} else if (wp->uaw_next_result != NULL) {
uu_panic("uu_avl_remove(%p, %p): active non-robust "
"walker\n", (void *)ap, elem);
}
}
avl_remove(&ap->ua_tree, elem);
na[0] = POOL_TO_MARKER(pp);
na[1] = 0;
}
void *
uu_avl_teardown(uu_avl_t *ap, void **cookie)
{
void *elem = avl_destroy_nodes(&ap->ua_tree, cookie);
if (elem != NULL) {
uu_avl_pool_t *pp = ap->ua_pool;
uintptr_t *na = NODE_ARRAY(pp, elem);
na[0] = POOL_TO_MARKER(pp);
na[1] = 0;
}
return (elem);
}
void *
uu_avl_find(uu_avl_t *ap, void *elem, void *private, uu_avl_index_t *out)
{
struct uu_avl_node_compare_info info;
void *result;
info.ac_compare = ap->ua_pool->uap_cmp;
info.ac_private = private;
info.ac_right = elem;
info.ac_found = NULL;
result = avl_find(&ap->ua_tree, &info, out);
if (out != NULL)
*out = INDEX_ENCODE(ap, *out);
if (ap->ua_debug && result != NULL)
uu_panic("uu_avl_find: internal error: avl_find succeeded\n");
return (info.ac_found);
}
void
uu_avl_insert(uu_avl_t *ap, void *elem, uu_avl_index_t idx)
{
if (ap->ua_debug) {
uu_avl_pool_t *pp = ap->ua_pool;
uintptr_t *na = NODE_ARRAY(pp, elem);
if (na[1] != 0)
uu_panic("uu_avl_insert(%p, %p, %p): node already "
"in tree, or corrupt\n",
(void *)ap, elem, (void *)idx);
if (na[0] == 0)
uu_panic("uu_avl_insert(%p, %p, %p): node not "
"initialized\n",
(void *)ap, elem, (void *)idx);
if (na[0] != POOL_TO_MARKER(pp))
uu_panic("uu_avl_insert(%p, %p, %p): node from "
"other pool, or corrupt\n",
(void *)ap, elem, (void *)idx);
if (!INDEX_VALID(ap, idx))
uu_panic("uu_avl_insert(%p, %p, %p): %s\n",
(void *)ap, elem, (void *)idx,
INDEX_CHECK(idx)? "outdated index" :
"invalid index");
/*
* invalidate outstanding uu_avl_index_ts.
*/
ap->ua_index = INDEX_NEXT(ap->ua_index);
}
avl_insert(&ap->ua_tree, elem, INDEX_DECODE(idx));
}
void *
uu_avl_nearest_next(uu_avl_t *ap, uu_avl_index_t idx)
{
if (ap->ua_debug && !INDEX_VALID(ap, idx))
uu_panic("uu_avl_nearest_next(%p, %p): %s\n",
(void *)ap, (void *)idx, INDEX_CHECK(idx)?
"outdated index" : "invalid index");
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_AFTER));
}
void *
uu_avl_nearest_prev(uu_avl_t *ap, uu_avl_index_t idx)
{
if (ap->ua_debug && !INDEX_VALID(ap, idx))
uu_panic("uu_avl_nearest_prev(%p, %p): %s\n",
(void *)ap, (void *)idx, INDEX_CHECK(idx)?
"outdated index" : "invalid index");
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_BEFORE));
}
/*
* called from uu_lockup() and uu_release(), as part of our fork1()-safety.
*/
void
uu_avl_lockup(void)
{
uu_avl_pool_t *pp;
(void) pthread_mutex_lock(&uu_apool_list_lock);
for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
pp = pp->uap_next)
(void) pthread_mutex_lock(&pp->uap_lock);
}
void
uu_avl_release(void)
{
uu_avl_pool_t *pp;
for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
pp = pp->uap_next)
(void) pthread_mutex_unlock(&pp->uap_lock);
(void) pthread_mutex_unlock(&uu_apool_list_lock);
}

View File

@ -0,0 +1,128 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <errno.h>
#include <libintl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define FACILITY_FMT "%s (%s): "
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
static const char *
strseverity(uu_dprintf_severity_t severity)
{
switch (severity) {
case UU_DPRINTF_SILENT:
return (dgettext(TEXT_DOMAIN, "silent"));
case UU_DPRINTF_FATAL:
return (dgettext(TEXT_DOMAIN, "FATAL"));
case UU_DPRINTF_WARNING:
return (dgettext(TEXT_DOMAIN, "WARNING"));
case UU_DPRINTF_NOTICE:
return (dgettext(TEXT_DOMAIN, "note"));
case UU_DPRINTF_INFO:
return (dgettext(TEXT_DOMAIN, "info"));
case UU_DPRINTF_DEBUG:
return (dgettext(TEXT_DOMAIN, "debug"));
default:
return (dgettext(TEXT_DOMAIN, "unspecified"));
}
}
uu_dprintf_t *
uu_dprintf_create(const char *name, uu_dprintf_severity_t severity,
uint_t flags)
{
uu_dprintf_t *D;
if (uu_check_name(name, UU_NAME_DOMAIN) == -1) {
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (NULL);
}
if ((D = uu_zalloc(sizeof (uu_dprintf_t))) == NULL)
return (NULL);
if (name != NULL) {
D->uud_name = strdup(name);
if (D->uud_name == NULL) {
uu_free(D);
return (NULL);
}
} else {
D->uud_name = NULL;
}
D->uud_severity = severity;
D->uud_flags = flags;
return (D);
}
/*PRINTFLIKE3*/
void
uu_dprintf(uu_dprintf_t *D, uu_dprintf_severity_t severity,
const char *format, ...)
{
va_list alist;
/* XXX Assert that severity is not UU_DPRINTF_SILENT. */
if (severity > D->uud_severity)
return;
(void) fprintf(stderr, FACILITY_FMT, D->uud_name,
strseverity(severity));
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
}
void
uu_dprintf_destroy(uu_dprintf_t *D)
{
if (D->uud_name)
free(D->uud_name);
uu_free(D);
}
const char *
uu_dprintf_getname(uu_dprintf_t *D)
{
return (D->uud_name);
}

View File

@ -0,0 +1,122 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <string.h>
/*
* We require names of the form:
* [provider,]identifier[/[provider,]identifier]...
*
* Where provider is either a stock symbol (SUNW) or a java-style reversed
* domain name (com.sun).
*
* Both providers and identifiers must start with a letter, and may
* only contain alphanumerics, dashes, and underlines. Providers
* may also contain periods.
*
* Note that we do _not_ use the macros in <ctype.h>, since they are affected
* by the current locale settings.
*/
#define IS_ALPHA(c) \
(((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
#define IS_DIGIT(c) \
((c) >= '0' && (c) <= '9')
static int
is_valid_ident(const char *s, const char *e, int allowdot)
{
char c;
if (s >= e)
return (0); /* name is empty */
c = *s++;
if (!IS_ALPHA(c))
return (0); /* does not start with letter */
while (s < e && (c = *s++) != 0) {
if (IS_ALPHA(c) || IS_DIGIT(c) || c == '-' || c == '_' ||
(allowdot && c == '.'))
continue;
return (0); /* invalid character */
}
return (1);
}
static int
is_valid_component(const char *b, const char *e, uint_t flags)
{
char *sp;
if (flags & UU_NAME_DOMAIN) {
sp = strchr(b, ',');
if (sp != NULL && sp < e) {
if (!is_valid_ident(b, sp, 1))
return (0);
b = sp + 1;
}
}
return (is_valid_ident(b, e, 0));
}
int
uu_check_name(const char *name, uint_t flags)
{
const char *end = name + strlen(name);
const char *p;
if (flags & ~(UU_NAME_DOMAIN | UU_NAME_PATH)) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (-1);
}
if (!(flags & UU_NAME_PATH)) {
if (!is_valid_component(name, end, flags))
goto bad;
return (0);
}
while ((p = strchr(name, '/')) != NULL) {
if (!is_valid_component(name, p - 1, flags))
goto bad;
name = p + 1;
}
if (!is_valid_component(name, end, flags))
goto bad;
return (0);
bad:
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (-1);
}

View File

@ -0,0 +1,718 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#define ELEM_TO_NODE(lp, e) \
((uu_list_node_impl_t *)((uintptr_t)(e) + (lp)->ul_offset))
#define NODE_TO_ELEM(lp, n) \
((void *)((uintptr_t)(n) - (lp)->ul_offset))
/*
* uu_list_index_ts define a location for insertion. They are simply a
* pointer to the object after the insertion point. We store a mark
* in the low-bits of the index, to help prevent mistakes.
*
* When debugging, the index mark changes on every insert and delete, to
* catch stale references.
*/
#define INDEX_MAX (sizeof (uintptr_t) - 1)
#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 1 : ((m) + 1) & INDEX_MAX)
#define INDEX_TO_NODE(i) ((uu_list_node_impl_t *)((i) & ~INDEX_MAX))
#define NODE_TO_INDEX(p, n) (((uintptr_t)(n) & ~INDEX_MAX) | (p)->ul_index)
#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ul_index)
#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)
#define POOL_TO_MARKER(pp) ((void *)((uintptr_t)(pp) | 1))
static uu_list_pool_t uu_null_lpool = { &uu_null_lpool, &uu_null_lpool };
static pthread_mutex_t uu_lpool_list_lock = PTHREAD_MUTEX_INITIALIZER;
uu_list_pool_t *
uu_list_pool_create(const char *name, size_t objsize,
size_t nodeoffset, uu_compare_fn_t *compare_func, uint32_t flags)
{
uu_list_pool_t *pp, *next, *prev;
if (name == NULL ||
uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
nodeoffset + sizeof (uu_list_node_t) > objsize) {
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (NULL);
}
if (flags & ~UU_LIST_POOL_DEBUG) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (NULL);
}
pp = uu_zalloc(sizeof (uu_list_pool_t));
if (pp == NULL) {
uu_set_error(UU_ERROR_NO_MEMORY);
return (NULL);
}
(void) strlcpy(pp->ulp_name, name, sizeof (pp->ulp_name));
pp->ulp_nodeoffset = nodeoffset;
pp->ulp_objsize = objsize;
pp->ulp_cmp = compare_func;
if (flags & UU_LIST_POOL_DEBUG)
pp->ulp_debug = 1;
pp->ulp_last_index = 0;
(void) pthread_mutex_init(&pp->ulp_lock, NULL);
pp->ulp_null_list.ul_next_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
pp->ulp_null_list.ul_prev_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
(void) pthread_mutex_lock(&uu_lpool_list_lock);
pp->ulp_next = next = &uu_null_lpool;
pp->ulp_prev = prev = next->ulp_prev;
next->ulp_prev = pp;
prev->ulp_next = pp;
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
return (pp);
}
void
uu_list_pool_destroy(uu_list_pool_t *pp)
{
if (pp->ulp_debug) {
if (pp->ulp_null_list.ul_next_enc !=
UU_PTR_ENCODE(&pp->ulp_null_list) ||
pp->ulp_null_list.ul_prev_enc !=
UU_PTR_ENCODE(&pp->ulp_null_list)) {
uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has "
"outstanding lists, or is corrupt.\n",
(int)sizeof (pp->ulp_name), pp->ulp_name,
(void *)pp);
}
}
(void) pthread_mutex_lock(&uu_lpool_list_lock);
pp->ulp_next->ulp_prev = pp->ulp_prev;
pp->ulp_prev->ulp_next = pp->ulp_next;
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
pp->ulp_prev = NULL;
pp->ulp_next = NULL;
uu_free(pp);
}
void
uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
{
uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
if (pp->ulp_debug) {
uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
if (offset + sizeof (*np) > pp->ulp_objsize) {
uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't fit in object (size %ld)\n",
base, (void *)np, (void *)pp, pp->ulp_name,
(long)offset, (long)pp->ulp_objsize);
}
if (offset != pp->ulp_nodeoffset) {
uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't match pool's offset (%ld)\n",
base, (void *)np, (void *)pp, pp->ulp_name,
(long)offset, (long)pp->ulp_objsize);
}
}
np->uln_next = POOL_TO_MARKER(pp);
np->uln_prev = NULL;
}
void
uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
{
uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
if (pp->ulp_debug) {
if (np->uln_next == NULL &&
np->uln_prev == NULL) {
uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
"node already finied\n",
base, (void *)np_arg, (void *)pp, pp->ulp_name);
}
if (np->uln_next != POOL_TO_MARKER(pp) ||
np->uln_prev != NULL) {
uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
"node corrupt or on list\n",
base, (void *)np_arg, (void *)pp, pp->ulp_name);
}
}
np->uln_next = NULL;
np->uln_prev = NULL;
}
uu_list_t *
uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)
{
uu_list_t *lp, *next, *prev;
if (flags & ~(UU_LIST_DEBUG | UU_LIST_SORTED)) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (NULL);
}
if ((flags & UU_LIST_SORTED) && pp->ulp_cmp == NULL) {
if (pp->ulp_debug)
uu_panic("uu_list_create(%p, ...): requested "
"UU_LIST_SORTED, but pool has no comparison func\n",
(void *)pp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (NULL);
}
lp = uu_zalloc(sizeof (*lp));
if (lp == NULL) {
uu_set_error(UU_ERROR_NO_MEMORY);
return (NULL);
}
lp->ul_pool = pp;
lp->ul_parent_enc = UU_PTR_ENCODE(parent);
lp->ul_offset = pp->ulp_nodeoffset;
lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG);
lp->ul_sorted = (flags & UU_LIST_SORTED);
lp->ul_numnodes = 0;
lp->ul_index = (pp->ulp_last_index = INDEX_NEXT(pp->ulp_last_index));
lp->ul_null_node.uln_next = &lp->ul_null_node;
lp->ul_null_node.uln_prev = &lp->ul_null_node;
lp->ul_null_walk.ulw_next = &lp->ul_null_walk;
lp->ul_null_walk.ulw_prev = &lp->ul_null_walk;
(void) pthread_mutex_lock(&pp->ulp_lock);
next = &pp->ulp_null_list;
prev = UU_PTR_DECODE(next->ul_prev_enc);
lp->ul_next_enc = UU_PTR_ENCODE(next);
lp->ul_prev_enc = UU_PTR_ENCODE(prev);
next->ul_prev_enc = UU_PTR_ENCODE(lp);
prev->ul_next_enc = UU_PTR_ENCODE(lp);
(void) pthread_mutex_unlock(&pp->ulp_lock);
return (lp);
}
void
uu_list_destroy(uu_list_t *lp)
{
uu_list_pool_t *pp = lp->ul_pool;
if (lp->ul_debug) {
if (lp->ul_null_node.uln_next != &lp->ul_null_node ||
lp->ul_null_node.uln_prev != &lp->ul_null_node) {
uu_panic("uu_list_destroy(%p): list not empty\n",
(void *)lp);
}
if (lp->ul_numnodes != 0) {
uu_panic("uu_list_destroy(%p): numnodes is nonzero, "
"but list is empty\n", (void *)lp);
}
if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||
lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {
uu_panic("uu_list_destroy(%p): outstanding walkers\n",
(void *)lp);
}
}
(void) pthread_mutex_lock(&pp->ulp_lock);
UU_LIST_PTR(lp->ul_next_enc)->ul_prev_enc = lp->ul_prev_enc;
UU_LIST_PTR(lp->ul_prev_enc)->ul_next_enc = lp->ul_next_enc;
(void) pthread_mutex_unlock(&pp->ulp_lock);
lp->ul_prev_enc = UU_PTR_ENCODE(NULL);
lp->ul_next_enc = UU_PTR_ENCODE(NULL);
lp->ul_pool = NULL;
uu_free(lp);
}
static void
list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,
uu_list_node_impl_t *next)
{
if (lp->ul_debug) {
if (next->uln_prev != prev || prev->uln_next != next)
uu_panic("insert(%p): internal error: %p and %p not "
"neighbors\n", (void *)lp, (void *)next,
(void *)prev);
if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||
np->uln_prev != NULL) {
uu_panic("insert(%p): elem %p node %p corrupt, "
"not initialized, or already in a list.\n",
(void *)lp, NODE_TO_ELEM(lp, np), (void *)np);
}
/*
* invalidate outstanding uu_list_index_ts.
*/
lp->ul_index = INDEX_NEXT(lp->ul_index);
}
np->uln_next = next;
np->uln_prev = prev;
next->uln_prev = np;
prev->uln_next = np;
lp->ul_numnodes++;
}
void
uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)
{
uu_list_node_impl_t *np;
np = INDEX_TO_NODE(idx);
if (np == NULL)
np = &lp->ul_null_node;
if (lp->ul_debug) {
if (!INDEX_VALID(lp, idx))
uu_panic("uu_list_insert(%p, %p, %p): %s\n",
(void *)lp, elem, (void *)idx,
INDEX_CHECK(idx)? "outdated index" :
"invalid index");
if (np->uln_prev == NULL)
uu_panic("uu_list_insert(%p, %p, %p): out-of-date "
"index\n", (void *)lp, elem, (void *)idx);
}
list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
}
void *
uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
{
int sorted = lp->ul_sorted;
uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
uu_list_node_impl_t *np;
if (func == NULL) {
if (out != NULL)
*out = 0;
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (NULL);
}
for (np = lp->ul_null_node.uln_next; np != &lp->ul_null_node;
np = np->uln_next) {
void *ep = NODE_TO_ELEM(lp, np);
int cmp = func(ep, elem, private);
if (cmp == 0) {
if (out != NULL)
*out = NODE_TO_INDEX(lp, np);
return (ep);
}
if (sorted && cmp > 0) {
if (out != NULL)
*out = NODE_TO_INDEX(lp, np);
return (NULL);
}
}
if (out != NULL)
*out = NODE_TO_INDEX(lp, 0);
return (NULL);
}
void *
uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)
{
uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
if (np == NULL)
np = &lp->ul_null_node;
if (lp->ul_debug) {
if (!INDEX_VALID(lp, idx))
uu_panic("uu_list_nearest_next(%p, %p): %s\n",
(void *)lp, (void *)idx,
INDEX_CHECK(idx)? "outdated index" :
"invalid index");
if (np->uln_prev == NULL)
uu_panic("uu_list_nearest_next(%p, %p): out-of-date "
"index\n", (void *)lp, (void *)idx);
}
if (np == &lp->ul_null_node)
return (NULL);
else
return (NODE_TO_ELEM(lp, np));
}
void *
uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)
{
uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
if (np == NULL)
np = &lp->ul_null_node;
if (lp->ul_debug) {
if (!INDEX_VALID(lp, idx))
uu_panic("uu_list_nearest_prev(%p, %p): %s\n",
(void *)lp, (void *)idx, INDEX_CHECK(idx)?
"outdated index" : "invalid index");
if (np->uln_prev == NULL)
uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "
"index\n", (void *)lp, (void *)idx);
}
if ((np = np->uln_prev) == &lp->ul_null_node)
return (NULL);
else
return (NODE_TO_ELEM(lp, np));
}
static void
list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)
{
uu_list_walk_t *next, *prev;
int robust = (flags & UU_WALK_ROBUST);
int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
(void) memset(wp, 0, sizeof (*wp));
wp->ulw_list = lp;
wp->ulw_robust = robust;
wp->ulw_dir = direction;
if (direction > 0)
wp->ulw_next_result = lp->ul_null_node.uln_next;
else
wp->ulw_next_result = lp->ul_null_node.uln_prev;
if (lp->ul_debug || robust) {
/*
* Add this walker to the list's list of walkers so
* uu_list_remove() can advance us if somebody tries to
* remove ulw_next_result.
*/
wp->ulw_next = next = &lp->ul_null_walk;
wp->ulw_prev = prev = next->ulw_prev;
next->ulw_prev = wp;
prev->ulw_next = wp;
}
}
static uu_list_node_impl_t *
list_walk_advance(uu_list_walk_t *wp, uu_list_t *lp)
{
uu_list_node_impl_t *np = wp->ulw_next_result;
uu_list_node_impl_t *next;
if (np == &lp->ul_null_node)
return (NULL);
next = (wp->ulw_dir > 0)? np->uln_next : np->uln_prev;
wp->ulw_next_result = next;
return (np);
}
static void
list_walk_fini(uu_list_walk_t *wp)
{
/* GLXXX debugging? */
if (wp->ulw_next != NULL) {
wp->ulw_next->ulw_prev = wp->ulw_prev;
wp->ulw_prev->ulw_next = wp->ulw_next;
wp->ulw_next = NULL;
wp->ulw_prev = NULL;
}
wp->ulw_list = NULL;
wp->ulw_next_result = NULL;
}
uu_list_walk_t *
uu_list_walk_start(uu_list_t *lp, uint32_t flags)
{
uu_list_walk_t *wp;
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (NULL);
}
wp = uu_zalloc(sizeof (*wp));
if (wp == NULL) {
uu_set_error(UU_ERROR_NO_MEMORY);
return (NULL);
}
list_walk_init(wp, lp, flags);
return (wp);
}
void *
uu_list_walk_next(uu_list_walk_t *wp)
{
uu_list_t *lp = wp->ulw_list;
uu_list_node_impl_t *np = list_walk_advance(wp, lp);
if (np == NULL)
return (NULL);
return (NODE_TO_ELEM(lp, np));
}
void
uu_list_walk_end(uu_list_walk_t *wp)
{
list_walk_fini(wp);
uu_free(wp);
}
int
uu_list_walk(uu_list_t *lp, uu_walk_fn_t *func, void *private, uint32_t flags)
{
uu_list_node_impl_t *np;
int status = UU_WALK_NEXT;
int robust = (flags & UU_WALK_ROBUST);
int reverse = (flags & UU_WALK_REVERSE);
if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
uu_set_error(UU_ERROR_UNKNOWN_FLAG);
return (-1);
}
if (lp->ul_debug || robust) {
uu_list_walk_t my_walk;
void *e;
list_walk_init(&my_walk, lp, flags);
while (status == UU_WALK_NEXT &&
(e = uu_list_walk_next(&my_walk)) != NULL)
status = (*func)(e, private);
list_walk_fini(&my_walk);
} else {
if (!reverse) {
for (np = lp->ul_null_node.uln_next;
status == UU_WALK_NEXT && np != &lp->ul_null_node;
np = np->uln_next) {
status = (*func)(NODE_TO_ELEM(lp, np), private);
}
} else {
for (np = lp->ul_null_node.uln_prev;
status == UU_WALK_NEXT && np != &lp->ul_null_node;
np = np->uln_prev) {
status = (*func)(NODE_TO_ELEM(lp, np), private);
}
}
}
if (status >= 0)
return (0);
uu_set_error(UU_ERROR_CALLBACK_FAILED);
return (-1);
}
void
uu_list_remove(uu_list_t *lp, void *elem)
{
uu_list_node_impl_t *np = ELEM_TO_NODE(lp, elem);
uu_list_walk_t *wp;
if (lp->ul_debug) {
if (np->uln_prev == NULL)
uu_panic("uu_list_remove(%p, %p): elem not on list\n",
(void *)lp, elem);
/*
* invalidate outstanding uu_list_index_ts.
*/
lp->ul_index = INDEX_NEXT(lp->ul_index);
}
/*
* robust walkers must be advanced. In debug mode, non-robust
* walkers are also on the list. If there are any, it's an error.
*/
for (wp = lp->ul_null_walk.ulw_next; wp != &lp->ul_null_walk;
wp = wp->ulw_next) {
if (wp->ulw_robust) {
if (np == wp->ulw_next_result)
(void) list_walk_advance(wp, lp);
} else if (wp->ulw_next_result != NULL) {
uu_panic("uu_list_remove(%p, %p): active non-robust "
"walker\n", (void *)lp, elem);
}
}
np->uln_next->uln_prev = np->uln_prev;
np->uln_prev->uln_next = np->uln_next;
lp->ul_numnodes--;
np->uln_next = POOL_TO_MARKER(lp->ul_pool);
np->uln_prev = NULL;
}
void *
uu_list_teardown(uu_list_t *lp, void **cookie)
{
void *ep;
/*
* XXX: disable list modification until list is empty
*/
if (lp->ul_debug && *cookie != NULL)
uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n",
(void *)lp, (void *)cookie);
ep = uu_list_first(lp);
if (ep)
uu_list_remove(lp, ep);
return (ep);
}
int
uu_list_insert_before(uu_list_t *lp, void *target, void *elem)
{
uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
if (target == NULL)
np = &lp->ul_null_node;
if (lp->ul_debug) {
if (np->uln_prev == NULL)
uu_panic("uu_list_insert_before(%p, %p, %p): %p is "
"not currently on a list\n",
(void *)lp, target, elem, target);
}
if (lp->ul_sorted) {
if (lp->ul_debug)
uu_panic("uu_list_insert_before(%p, ...): list is "
"UU_LIST_SORTED\n", (void *)lp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (-1);
}
list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
return (0);
}
int
uu_list_insert_after(uu_list_t *lp, void *target, void *elem)
{
uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
if (target == NULL)
np = &lp->ul_null_node;
if (lp->ul_debug) {
if (np->uln_prev == NULL)
uu_panic("uu_list_insert_after(%p, %p, %p): %p is "
"not currently on a list\n",
(void *)lp, target, elem, target);
}
if (lp->ul_sorted) {
if (lp->ul_debug)
uu_panic("uu_list_insert_after(%p, ...): list is "
"UU_LIST_SORTED\n", (void *)lp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (-1);
}
list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);
return (0);
}
size_t
uu_list_numnodes(uu_list_t *lp)
{
return (lp->ul_numnodes);
}
void *
uu_list_first(uu_list_t *lp)
{
uu_list_node_impl_t *n = lp->ul_null_node.uln_next;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
void *
uu_list_last(uu_list_t *lp)
{
uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
void *
uu_list_next(uu_list_t *lp, void *elem)
{
uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
n = n->uln_next;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
void *
uu_list_prev(uu_list_t *lp, void *elem)
{
uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
n = n->uln_prev;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
/*
* called from uu_lockup() and uu_release(), as part of our fork1()-safety.
*/
void
uu_list_lockup(void)
{
uu_list_pool_t *pp;
(void) pthread_mutex_lock(&uu_lpool_list_lock);
for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
pp = pp->ulp_next)
(void) pthread_mutex_lock(&pp->ulp_lock);
}
void
uu_list_release(void)
{
uu_list_pool_t *pp;
for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
pp = pp->ulp_next)
(void) pthread_mutex_unlock(&pp->ulp_lock);
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
}

View File

@ -0,0 +1,280 @@
/*
* 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include "libuutil_common.h"
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/debug.h>
#include <thread.h>
#include <unistd.h>
#include <ctype.h>
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
/*
* All of the old code under !defined(PTHREAD_ONCE_KEY_NP)
* is here to enable the building of a native version of
* libuutil.so when the build machine has not yet been upgraded
* to a version of libc that provides pthread_key_create_once_np().
* It should all be deleted when solaris_nevada ships.
* The code is not MT-safe in a relaxed memory model.
*/
#if defined(PTHREAD_ONCE_KEY_NP)
static pthread_key_t uu_error_key = PTHREAD_ONCE_KEY_NP;
#else /* PTHREAD_ONCE_KEY_NP */
static pthread_key_t uu_error_key = 0;
static pthread_mutex_t uu_key_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* PTHREAD_ONCE_KEY_NP */
static int uu_error_key_setup = 0;
static pthread_mutex_t uu_panic_lock = PTHREAD_MUTEX_INITIALIZER;
/* LINTED static unused */
static const char *uu_panic_format;
/* LINTED static unused */
static va_list uu_panic_args;
static pthread_t uu_panic_thread;
static uint32_t _uu_main_error;
void
uu_set_error(uint_t code)
{
if (thr_main() != 0) {
_uu_main_error = code;
return;
}
#if defined(PTHREAD_ONCE_KEY_NP)
if (pthread_key_create_once_np(&uu_error_key, NULL) != 0)
uu_error_key_setup = -1;
else
uu_error_key_setup = 1;
#else /* PTHREAD_ONCE_KEY_NP */
if (uu_error_key_setup == 0) {
(void) pthread_mutex_lock(&uu_key_lock);
if (uu_error_key_setup == 0) {
if (pthread_key_create(&uu_error_key, NULL) != 0)
uu_error_key_setup = -1;
else
uu_error_key_setup = 1;
}
(void) pthread_mutex_unlock(&uu_key_lock);
}
#endif /* PTHREAD_ONCE_KEY_NP */
if (uu_error_key_setup > 0)
(void) pthread_setspecific(uu_error_key,
(void *)(uintptr_t)code);
}
uint32_t
uu_error(void)
{
if (thr_main() != 0)
return (_uu_main_error);
if (uu_error_key_setup < 0) /* can't happen? */
return (UU_ERROR_UNKNOWN);
/*
* Because UU_ERROR_NONE == 0, if uu_set_error() was
* never called, then this will return UU_ERROR_NONE:
*/
return ((uint32_t)(uintptr_t)pthread_getspecific(uu_error_key));
}
const char *
uu_strerror(uint32_t code)
{
const char *str;
switch (code) {
case UU_ERROR_NONE:
str = dgettext(TEXT_DOMAIN, "No error");
break;
case UU_ERROR_INVALID_ARGUMENT:
str = dgettext(TEXT_DOMAIN, "Invalid argument");
break;
case UU_ERROR_UNKNOWN_FLAG:
str = dgettext(TEXT_DOMAIN, "Unknown flag passed");
break;
case UU_ERROR_NO_MEMORY:
str = dgettext(TEXT_DOMAIN, "Out of memory");
break;
case UU_ERROR_CALLBACK_FAILED:
str = dgettext(TEXT_DOMAIN, "Callback-initiated failure");
break;
case UU_ERROR_NOT_SUPPORTED:
str = dgettext(TEXT_DOMAIN, "Operation not supported");
break;
case UU_ERROR_EMPTY:
str = dgettext(TEXT_DOMAIN, "No value provided");
break;
case UU_ERROR_UNDERFLOW:
str = dgettext(TEXT_DOMAIN, "Value too small");
break;
case UU_ERROR_OVERFLOW:
str = dgettext(TEXT_DOMAIN, "Value too large");
break;
case UU_ERROR_INVALID_CHAR:
str = dgettext(TEXT_DOMAIN,
"Value contains unexpected character");
break;
case UU_ERROR_INVALID_DIGIT:
str = dgettext(TEXT_DOMAIN,
"Value contains digit not in base");
break;
case UU_ERROR_SYSTEM:
str = dgettext(TEXT_DOMAIN, "Underlying system error");
break;
case UU_ERROR_UNKNOWN:
str = dgettext(TEXT_DOMAIN, "Error status not known");
break;
default:
errno = ESRCH;
str = NULL;
break;
}
return (str);
}
void
uu_panic(const char *format, ...)
{
va_list args;
va_start(args, format);
(void) pthread_mutex_lock(&uu_panic_lock);
if (uu_panic_thread == 0) {
uu_panic_thread = pthread_self();
uu_panic_format = format;
va_copy(uu_panic_args, args);
}
(void) pthread_mutex_unlock(&uu_panic_lock);
(void) vfprintf(stderr, format, args);
if (uu_panic_thread == pthread_self())
abort();
else
for (;;)
(void) pause();
}
int
assfail(const char *astring, const char *file, int line)
{
__assert(astring, file, line);
/*NOTREACHED*/
return (0);
}
static void
uu_lockup(void)
{
(void) pthread_mutex_lock(&uu_panic_lock);
#if !defined(PTHREAD_ONCE_KEY_NP)
(void) pthread_mutex_lock(&uu_key_lock);
#endif
uu_avl_lockup();
uu_list_lockup();
}
static void
uu_release(void)
{
(void) pthread_mutex_unlock(&uu_panic_lock);
#if !defined(PTHREAD_ONCE_KEY_NP)
(void) pthread_mutex_unlock(&uu_key_lock);
#endif
uu_avl_release();
uu_list_release();
}
static void
uu_release_child(void)
{
uu_panic_format = NULL;
uu_panic_thread = 0;
uu_release();
}
#pragma init(uu_init)
static void
uu_init(void)
{
(void) pthread_atfork(uu_lockup, uu_release, uu_release_child);
}
/*
* Dump a block of memory in hex+ascii, for debugging
*/
void
uu_dump(FILE *out, const char *prefix, const void *buf, size_t len)
{
const unsigned char *p = buf;
int i;
for (i = 0; i < len; i += 16) {
int j;
(void) fprintf(out, "%s", prefix);
for (j = 0; j < 16 && i + j < len; j++) {
(void) fprintf(out, "%2.2x ", p[i + j]);
}
for (; j < 16; j++) {
(void) fprintf(out, " ");
}
for (j = 0; j < 16 && i + j < len; j++) {
(void) fprintf(out, "%c",
isprint(p[i + j]) ? p[i + j] : '.');
}
(void) fprintf(out, "\n");
}
}

View File

@ -0,0 +1,70 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#ifdef _LP64
#define TMPPATHFMT "%s/uu%ld"
#else /* _LP64 */
#define TMPPATHFMT "%s/uu%lld"
#endif /* _LP64 */
/*ARGSUSED*/
int
uu_open_tmp(const char *dir, uint_t uflags)
{
int f;
char *fname = uu_zalloc(PATH_MAX);
if (fname == NULL)
return (-1);
for (;;) {
(void) snprintf(fname, PATH_MAX, "%s/uu%lld", dir, gethrtime());
f = open(fname, O_CREAT | O_EXCL | O_RDWR, 0600);
if (f >= 0 || errno != EEXIST)
break;
}
if (f >= 0)
(void) unlink(fname);
uu_free(fname);
return (f);
}

View File

@ -0,0 +1,207 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <libintl.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <wchar.h>
#include <unistd.h>
static const char PNAME_FMT[] = "%s: ";
static const char ERRNO_FMT[] = ": %s\n";
static const char *pname;
static void
uu_die_internal(int status, const char *format, va_list alist) __NORETURN;
int uu_exit_ok_value = EXIT_SUCCESS;
int uu_exit_fatal_value = EXIT_FAILURE;
int uu_exit_usage_value = 2;
int *
uu_exit_ok(void)
{
return (&uu_exit_ok_value);
}
int *
uu_exit_fatal(void)
{
return (&uu_exit_fatal_value);
}
int *
uu_exit_usage(void)
{
return (&uu_exit_usage_value);
}
void
uu_alt_exit(int profile)
{
switch (profile) {
case UU_PROFILE_DEFAULT:
uu_exit_ok_value = EXIT_SUCCESS;
uu_exit_fatal_value = EXIT_FAILURE;
uu_exit_usage_value = 2;
break;
case UU_PROFILE_LAUNCHER:
uu_exit_ok_value = EXIT_SUCCESS;
uu_exit_fatal_value = 124;
uu_exit_usage_value = 125;
break;
}
}
static void
uu_warn_internal(int err, const char *format, va_list alist)
{
if (pname != NULL)
(void) fprintf(stderr, PNAME_FMT, pname);
(void) vfprintf(stderr, format, alist);
if (strrchr(format, '\n') == NULL)
(void) fprintf(stderr, ERRNO_FMT, strerror(err));
}
void
uu_vwarn(const char *format, va_list alist)
{
uu_warn_internal(errno, format, alist);
}
/*PRINTFLIKE1*/
void
uu_warn(const char *format, ...)
{
va_list alist;
va_start(alist, format);
uu_warn_internal(errno, format, alist);
va_end(alist);
}
static void
uu_die_internal(int status, const char *format, va_list alist)
{
uu_warn_internal(errno, format, alist);
#ifdef DEBUG
{
char *cp;
if (!issetugid()) {
cp = getenv("UU_DIE_ABORTS");
if (cp != NULL && *cp != '\0')
abort();
}
}
#endif
exit(status);
}
void
uu_vdie(const char *format, va_list alist)
{
uu_die_internal(UU_EXIT_FATAL, format, alist);
}
/*PRINTFLIKE1*/
void
uu_die(const char *format, ...)
{
va_list alist;
va_start(alist, format);
uu_die_internal(UU_EXIT_FATAL, format, alist);
va_end(alist);
}
void
uu_vxdie(int status, const char *format, va_list alist)
{
uu_die_internal(status, format, alist);
}
/*PRINTFLIKE2*/
void
uu_xdie(int status, const char *format, ...)
{
va_list alist;
va_start(alist, format);
uu_die_internal(status, format, alist);
va_end(alist);
}
const char *
uu_setpname(char *arg0)
{
/*
* Having a NULL argv[0], while uncommon, is possible. It
* makes more sense to handle this event in uu_setpname rather
* than in each of its consumers.
*/
if (arg0 == NULL) {
pname = getexecname();
if (pname == NULL)
pname = "unknown_command";
return (pname);
}
/*
* Guard against '/' at end of command invocation.
*/
for (;;) {
char *p = strrchr(arg0, '/');
if (p == NULL) {
pname = arg0;
break;
} else {
if (*(p + 1) == '\0') {
*p = '\0';
continue;
}
pname = p + 1;
break;
}
}
return (pname);
}
const char *
uu_getpname(void)
{
return (pname);
}

View File

@ -0,0 +1,56 @@
/*
* 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* String helper functions
*/
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <malloc.h>
#include <ctype.h>
#include "libuutil.h"
/* Return true if strings are equal */
boolean_t
uu_streq(const char *a, const char *b)
{
return (strcmp(a, b) == 0);
}
/* Return true if strings are equal, case-insensitively */
boolean_t
uu_strcaseeq(const char *a, const char *b)
{
return (strcasecmp(a, b) == 0);
}
/* Return true if string a Begins With string b */
boolean_t
uu_strbw(const char *a, const char *b)
{
return (strncmp(a, b, strlen(b)) == 0);
}

View File

@ -0,0 +1,300 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <limits.h>
#include <ctype.h>
#define MAX_BASE 36
#define IS_DIGIT(x) ((x) >= '0' && (x) <= '9')
#define CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \
((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A')
static int
strtoint(const char *s_arg, uint64_t *out, uint32_t base, int sign)
{
const unsigned char *s = (const unsigned char *)s_arg;
uint64_t val = 0;
uint64_t multmax;
unsigned c, i;
int neg = 0;
int bad_digit = 0;
int bad_char = 0;
int overflow = 0;
if (s == NULL || base == 1 || base > MAX_BASE) {
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (-1);
}
while ((c = *s) != 0 && isspace(c))
s++;
switch (c) {
case '-':
if (!sign)
overflow = 1; /* becomes underflow below */
neg = 1;
/*FALLTHRU*/
case '+':
c = *++s;
break;
default:
break;
}
if (c == '\0') {
uu_set_error(UU_ERROR_EMPTY);
return (-1);
}
if (base == 0) {
if (c != '0')
base = 10;
else if (s[1] == 'x' || s[1] == 'X')
base = 16;
else
base = 8;
}
if (base == 16 && c == '0' && (s[1] == 'x' || s[1] == 'X'))
c = *(s += 2);
if ((val = CTOI(c)) >= base) {
if (IS_DIGIT(c))
bad_digit = 1;
else
bad_char = 1;
val = 0;
}
multmax = (uint64_t)UINT64_MAX / (uint64_t)base;
for (c = *++s; c != '\0'; c = *++s) {
if ((i = CTOI(c)) >= base) {
if (isspace(c))
break;
if (IS_DIGIT(c))
bad_digit = 1;
else
bad_char = 1;
i = 0;
}
if (val > multmax)
overflow = 1;
val *= base;
if ((uint64_t)UINT64_MAX - val < (uint64_t)i)
overflow = 1;
val += i;
}
while ((c = *s) != 0) {
if (!isspace(c))
bad_char = 1;
s++;
}
if (sign) {
if (neg) {
if (val > -(uint64_t)INT64_MIN)
overflow = 1;
} else {
if (val > INT64_MAX)
overflow = 1;
}
}
if (neg)
val = -val;
if (bad_char | bad_digit | overflow) {
if (bad_char)
uu_set_error(UU_ERROR_INVALID_CHAR);
else if (bad_digit)
uu_set_error(UU_ERROR_INVALID_DIGIT);
else if (overflow) {
if (neg)
uu_set_error(UU_ERROR_UNDERFLOW);
else
uu_set_error(UU_ERROR_OVERFLOW);
}
return (-1);
}
*out = val;
return (0);
}
int
uu_strtoint(const char *s, void *v, size_t sz, int base,
int64_t min, int64_t max)
{
uint64_t val_u;
int64_t val;
if (min > max)
goto bad_argument;
switch (sz) {
case 1:
if (max > INT8_MAX || min < INT8_MIN)
goto bad_argument;
break;
case 2:
if (max > INT16_MAX || min < INT16_MIN)
goto bad_argument;
break;
case 4:
if (max > INT32_MAX || min < INT32_MIN)
goto bad_argument;
break;
case 8:
if (max > INT64_MAX || min < INT64_MIN)
goto bad_argument;
break;
default:
goto bad_argument;
}
if (min == 0 && max == 0) {
min = -(1ULL << (8 * sz - 1));
max = (1ULL << (8 * sz - 1)) - 1;
}
if (strtoint(s, &val_u, base, 1) == -1)
return (-1);
val = (int64_t)val_u;
if (val < min) {
uu_set_error(UU_ERROR_UNDERFLOW);
return (-1);
} else if (val > max) {
uu_set_error(UU_ERROR_OVERFLOW);
return (-1);
}
switch (sz) {
case 1:
*(int8_t *)v = val;
return (0);
case 2:
*(int16_t *)v = val;
return (0);
case 4:
*(int32_t *)v = val;
return (0);
case 8:
*(int64_t *)v = val;
return (0);
default:
break; /* fall through to bad_argument */
}
bad_argument:
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (-1);
}
int
uu_strtouint(const char *s, void *v, size_t sz, int base,
uint64_t min, uint64_t max)
{
uint64_t val;
if (min > max)
goto bad_argument;
switch (sz) {
case 1:
if (max > UINT8_MAX)
goto bad_argument;
break;
case 2:
if (max > UINT16_MAX)
goto bad_argument;
break;
case 4:
if (max > UINT32_MAX)
goto bad_argument;
break;
case 8:
if (max > UINT64_MAX)
goto bad_argument;
break;
default:
goto bad_argument;
}
if (min == 0 && max == 0) {
/* we have to be careful, since << can overflow */
max = (1ULL << (8 * sz - 1)) * 2 - 1;
}
if (strtoint(s, &val, base, 0) == -1)
return (-1);
if (val < min) {
uu_set_error(UU_ERROR_UNDERFLOW);
return (-1);
} else if (val > max) {
uu_set_error(UU_ERROR_OVERFLOW);
return (-1);
}
switch (sz) {
case 1:
*(uint8_t *)v = val;
return (0);
case 2:
*(uint16_t *)v = val;
return (0);
case 4:
*(uint32_t *)v = val;
return (0);
case 8:
*(uint64_t *)v = val;
return (0);
default:
break; /* shouldn't happen, fall through */
}
bad_argument:
uu_set_error(UU_ERROR_INVALID_ARGUMENT);
return (-1);
}

705
lib/libzfs/common/libzfs.h Normal file
View File

@ -0,0 +1,705 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _LIBZFS_H
#define _LIBZFS_H
#include <assert.h>
#include <libnvpair.h>
#include <sys/mnttab.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/fs/zfs.h>
#include <sys/avl.h>
#include <ucred.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Miscellaneous ZFS constants
*/
#define ZFS_MAXNAMELEN MAXNAMELEN
#define ZPOOL_MAXNAMELEN MAXNAMELEN
#define ZFS_MAXPROPLEN MAXPATHLEN
#define ZPOOL_MAXPROPLEN MAXPATHLEN
/*
* libzfs errors
*/
enum {
EZFS_NOMEM = 2000, /* out of memory */
EZFS_BADPROP, /* invalid property value */
EZFS_PROPREADONLY, /* cannot set readonly property */
EZFS_PROPTYPE, /* property does not apply to dataset type */
EZFS_PROPNONINHERIT, /* property is not inheritable */
EZFS_PROPSPACE, /* bad quota or reservation */
EZFS_BADTYPE, /* dataset is not of appropriate type */
EZFS_BUSY, /* pool or dataset is busy */
EZFS_EXISTS, /* pool or dataset already exists */
EZFS_NOENT, /* no such pool or dataset */
EZFS_BADSTREAM, /* bad backup stream */
EZFS_DSREADONLY, /* dataset is readonly */
EZFS_VOLTOOBIG, /* volume is too large for 32-bit system */
EZFS_INVALIDNAME, /* invalid dataset name */
EZFS_BADRESTORE, /* unable to restore to destination */
EZFS_BADBACKUP, /* backup failed */
EZFS_BADTARGET, /* bad attach/detach/replace target */
EZFS_NODEVICE, /* no such device in pool */
EZFS_BADDEV, /* invalid device to add */
EZFS_NOREPLICAS, /* no valid replicas */
EZFS_RESILVERING, /* currently resilvering */
EZFS_BADVERSION, /* unsupported version */
EZFS_POOLUNAVAIL, /* pool is currently unavailable */
EZFS_DEVOVERFLOW, /* too many devices in one vdev */
EZFS_BADPATH, /* must be an absolute path */
EZFS_CROSSTARGET, /* rename or clone across pool or dataset */
EZFS_ZONED, /* used improperly in local zone */
EZFS_MOUNTFAILED, /* failed to mount dataset */
EZFS_UMOUNTFAILED, /* failed to unmount dataset */
EZFS_UNSHARENFSFAILED, /* unshare(1M) failed */
EZFS_SHARENFSFAILED, /* share(1M) failed */
EZFS_PERM, /* permission denied */
EZFS_NOSPC, /* out of space */
EZFS_FAULT, /* bad address */
EZFS_IO, /* I/O error */
EZFS_INTR, /* signal received */
EZFS_ISSPARE, /* device is a hot spare */
EZFS_INVALCONFIG, /* invalid vdev configuration */
EZFS_RECURSIVE, /* recursive dependency */
EZFS_NOHISTORY, /* no history object */
EZFS_POOLPROPS, /* couldn't retrieve pool props */
EZFS_POOL_NOTSUP, /* ops not supported for this type of pool */
EZFS_POOL_INVALARG, /* invalid argument for this pool operation */
EZFS_NAMETOOLONG, /* dataset name is too long */
EZFS_OPENFAILED, /* open of device failed */
EZFS_NOCAP, /* couldn't get capacity */
EZFS_LABELFAILED, /* write of label failed */
EZFS_BADWHO, /* invalid permission who */
EZFS_BADPERM, /* invalid permission */
EZFS_BADPERMSET, /* invalid permission set name */
EZFS_NODELEGATION, /* delegated administration is disabled */
EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
EZFS_SHARESMBFAILED, /* failed to share over smb */
EZFS_BADCACHE, /* bad cache file */
EZFS_ISL2CACHE, /* device is for the level 2 ARC */
EZFS_VDEVNOTSUP, /* unsupported vdev type */
EZFS_NOTSUP, /* ops not supported on this dataset */
EZFS_ACTIVE_SPARE, /* pool has active shared spare devices */
EZFS_UNPLAYED_LOGS, /* log device has unplayed logs */
EZFS_REFTAG_RELE, /* snapshot release: tag not found */
EZFS_REFTAG_HOLD, /* snapshot hold: tag already exists */
EZFS_TAGTOOLONG, /* snapshot hold/rele: tag too long */
EZFS_PIPEFAILED, /* pipe create failed */
EZFS_THREADCREATEFAILED, /* thread create failed */
EZFS_POSTSPLIT_ONLINE, /* onlining a disk after splitting it */
EZFS_SCRUBBING, /* currently scrubbing */
EZFS_NO_SCRUB, /* no active scrub */
EZFS_DIFF, /* general failure of zfs diff */
EZFS_DIFFDATA, /* bad zfs diff data */
EZFS_POOLREADONLY, /* pool is in read-only mode */
EZFS_UNKNOWN
};
/*
* The following data structures are all part
* of the zfs_allow_t data structure which is
* used for printing 'allow' permissions.
* It is a linked list of zfs_allow_t's which
* then contain avl tree's for user/group/sets/...
* and each one of the entries in those trees have
* avl tree's for the permissions they belong to and
* whether they are local,descendent or local+descendent
* permissions. The AVL trees are used primarily for
* sorting purposes, but also so that we can quickly find
* a given user and or permission.
*/
typedef struct zfs_perm_node {
avl_node_t z_node;
char z_pname[MAXPATHLEN];
} zfs_perm_node_t;
typedef struct zfs_allow_node {
avl_node_t z_node;
char z_key[MAXPATHLEN]; /* name, such as joe */
avl_tree_t z_localdescend; /* local+descendent perms */
avl_tree_t z_local; /* local permissions */
avl_tree_t z_descend; /* descendent permissions */
} zfs_allow_node_t;
typedef struct zfs_allow {
struct zfs_allow *z_next;
char z_setpoint[MAXPATHLEN];
avl_tree_t z_sets;
avl_tree_t z_crperms;
avl_tree_t z_user;
avl_tree_t z_group;
avl_tree_t z_everyone;
} zfs_allow_t;
/*
* Basic handle types
*/
typedef struct zfs_handle zfs_handle_t;
typedef struct zpool_handle zpool_handle_t;
typedef struct libzfs_handle libzfs_handle_t;
/*
* Library initialization
*/
extern libzfs_handle_t *libzfs_init(void);
extern void libzfs_fini(libzfs_handle_t *);
extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *);
extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
extern int libzfs_errno(libzfs_handle_t *);
extern const char *libzfs_error_action(libzfs_handle_t *);
extern const char *libzfs_error_description(libzfs_handle_t *);
extern void libzfs_mnttab_init(libzfs_handle_t *);
extern void libzfs_mnttab_fini(libzfs_handle_t *);
extern void libzfs_mnttab_cache(libzfs_handle_t *, boolean_t);
extern int libzfs_mnttab_find(libzfs_handle_t *, const char *,
struct mnttab *);
extern void libzfs_mnttab_add(libzfs_handle_t *, const char *,
const char *, const char *);
extern void libzfs_mnttab_remove(libzfs_handle_t *, const char *);
/*
* Basic handle functions
*/
extern zpool_handle_t *zpool_open(libzfs_handle_t *, const char *);
extern zpool_handle_t *zpool_open_canfail(libzfs_handle_t *, const char *);
extern void zpool_close(zpool_handle_t *);
extern const char *zpool_get_name(zpool_handle_t *);
extern int zpool_get_state(zpool_handle_t *);
extern char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
extern void zpool_free_handles(libzfs_handle_t *);
/*
* Iterate over all active pools in the system.
*/
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
/*
* Functions to create and destroy pools
*/
extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
nvlist_t *, nvlist_t *);
extern int zpool_destroy(zpool_handle_t *);
extern int zpool_add(zpool_handle_t *, nvlist_t *);
typedef struct splitflags {
/* do not split, but return the config that would be split off */
int dryrun : 1;
/* after splitting, import the pool */
int import : 1;
} splitflags_t;
/*
* Functions to manipulate pool and vdev state
*/
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t);
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
vdev_state_t *);
extern int zpool_vdev_offline(zpool_handle_t *, const char *, boolean_t);
extern int zpool_vdev_attach(zpool_handle_t *, const char *,
const char *, nvlist_t *, int);
extern int zpool_vdev_detach(zpool_handle_t *, const char *);
extern int zpool_vdev_remove(zpool_handle_t *, const char *);
extern int zpool_vdev_split(zpool_handle_t *, char *, nvlist_t **, nvlist_t *,
splitflags_t);
extern int zpool_vdev_fault(zpool_handle_t *, uint64_t, vdev_aux_t);
extern int zpool_vdev_degrade(zpool_handle_t *, uint64_t, vdev_aux_t);
extern int zpool_vdev_clear(zpool_handle_t *, uint64_t);
extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
boolean_t *, boolean_t *);
extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
boolean_t *, boolean_t *, boolean_t *);
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
/*
* Functions to manage pool properties
*/
extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
size_t proplen, zprop_source_t *);
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
zprop_source_t *);
extern const char *zpool_prop_to_name(zpool_prop_t);
extern const char *zpool_prop_values(zpool_prop_t);
/*
* Pool health statistics.
*/
typedef enum {
/*
* The following correspond to faults as defined in the (fault.fs.zfs.*)
* event namespace. Each is associated with a corresponding message ID.
*/
ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */
ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */
ZPOOL_STATUS_MISSING_DEV_NR, /* missing device with no replicas */
ZPOOL_STATUS_CORRUPT_LABEL_R, /* bad device label with replicas */
ZPOOL_STATUS_CORRUPT_LABEL_NR, /* bad device label with no replicas */
ZPOOL_STATUS_BAD_GUID_SUM, /* sum of device guids didn't match */
ZPOOL_STATUS_CORRUPT_POOL, /* pool metadata is corrupted */
ZPOOL_STATUS_CORRUPT_DATA, /* data errors in user (meta)data */
ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */
ZPOOL_STATUS_HOSTID_MISMATCH, /* last accessed by another system */
ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */
ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
/*
* These faults have no corresponding message ID. At the time we are
* checking the status, the original reason for the FMA fault (I/O or
* checksum errors) has been lost.
*/
ZPOOL_STATUS_FAULTED_DEV_R, /* faulted device with replicas */
ZPOOL_STATUS_FAULTED_DEV_NR, /* faulted device with no replicas */
/*
* The following are not faults per se, but still an error possibly
* requiring administrative attention. There is no corresponding
* message ID.
*/
ZPOOL_STATUS_VERSION_OLDER, /* older on-disk version */
ZPOOL_STATUS_RESILVERING, /* device being resilvered */
ZPOOL_STATUS_OFFLINE_DEV, /* device online */
ZPOOL_STATUS_REMOVED_DEV, /* removed device */
/*
* Finally, the following indicates a healthy pool.
*/
ZPOOL_STATUS_OK
} zpool_status_t;
extern zpool_status_t zpool_get_status(zpool_handle_t *, char **);
extern zpool_status_t zpool_import_status(nvlist_t *, char **);
extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh);
/*
* Statistics and configuration functions.
*/
extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
/*
* Import and export functions
*/
extern int zpool_export(zpool_handle_t *, boolean_t);
extern int zpool_export_force(zpool_handle_t *);
extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
char *altroot);
extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
nvlist_t *, int);
/*
* Search for pools to import
*/
typedef struct importargs {
char **path; /* a list of paths to search */
int paths; /* number of paths to search */
char *poolname; /* name of a pool to find */
uint64_t guid; /* guid of a pool to find */
char *cachefile; /* cachefile to use for import */
int can_be_active : 1; /* can the pool be active? */
int unique : 1; /* does 'poolname' already exist? */
int exists : 1; /* set on return if pool already exists */
} importargs_t;
extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *);
/* legacy pool search routines */
extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *,
char *, uint64_t);
/*
* Miscellaneous pool functions
*/
struct zfs_cmd;
extern const char *zfs_history_event_names[LOG_END];
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
boolean_t verbose);
extern int zpool_upgrade(zpool_handle_t *, uint64_t);
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
nvlist_t ***, uint_t *);
extern void zpool_set_history_str(const char *subcommand, int argc,
char **argv, char *history_str);
extern int zpool_stage_history(libzfs_handle_t *, const char *);
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
size_t len);
extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
extern int zpool_get_physpath(zpool_handle_t *, char *, size_t);
extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
nvlist_t *);
/*
* Basic handle manipulations. These functions do not create or destroy the
* underlying datasets, only the references to them.
*/
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
extern void zfs_close(zfs_handle_t *);
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
extern const char *zfs_get_name(const zfs_handle_t *);
extern zpool_handle_t *zfs_get_pool_handle(const zfs_handle_t *);
/*
* Property management functions. Some functions are shared with the kernel,
* and are found in sys/fs/zfs.h.
*/
/*
* zfs dataset property management
*/
extern const char *zfs_prop_default_string(zfs_prop_t);
extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
extern const char *zfs_prop_column_name(zfs_prop_t);
extern boolean_t zfs_prop_align_right(zfs_prop_t);
extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t,
nvlist_t *, uint64_t, zfs_handle_t *, const char *);
extern const char *zfs_prop_to_name(zfs_prop_t);
extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
zprop_source_t *, char *, size_t, boolean_t);
extern int zfs_prop_get_recvd(zfs_handle_t *, const char *, char *, size_t,
boolean_t);
extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
zprop_source_t *, char *, size_t);
extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
uint64_t *propvalue);
extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
char *propbuf, int proplen, boolean_t literal);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
extern const char *zfs_prop_values(zfs_prop_t);
extern int zfs_prop_is_string(zfs_prop_t prop);
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
typedef struct zprop_list {
int pl_prop;
char *pl_user_prop;
struct zprop_list *pl_next;
boolean_t pl_all;
size_t pl_width;
size_t pl_recvd_width;
boolean_t pl_fixed;
} zprop_list_t;
extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **, boolean_t);
extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *);
#define ZFS_MOUNTPOINT_NONE "none"
#define ZFS_MOUNTPOINT_LEGACY "legacy"
/*
* zpool property management
*/
extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
extern const char *zpool_prop_default_string(zpool_prop_t);
extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
extern const char *zpool_prop_column_name(zpool_prop_t);
extern boolean_t zpool_prop_align_right(zpool_prop_t);
/*
* Functions shared by zfs and zpool property management.
*/
extern int zprop_iter(zprop_func func, void *cb, boolean_t show_all,
boolean_t ordered, zfs_type_t type);
extern int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **,
zfs_type_t);
extern void zprop_free_list(zprop_list_t *);
#define ZFS_GET_NCOLS 5
typedef enum {
GET_COL_NONE,
GET_COL_NAME,
GET_COL_PROPERTY,
GET_COL_VALUE,
GET_COL_RECVD,
GET_COL_SOURCE
} zfs_get_column_t;
/*
* Functions for printing zfs or zpool properties
*/
typedef struct zprop_get_cbdata {
int cb_sources;
zfs_get_column_t cb_columns[ZFS_GET_NCOLS];
int cb_colwidths[ZFS_GET_NCOLS + 1];
boolean_t cb_scripted;
boolean_t cb_literal;
boolean_t cb_first;
zprop_list_t *cb_proplist;
zfs_type_t cb_type;
} zprop_get_cbdata_t;
void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
const char *, const char *, zprop_source_t, const char *,
const char *);
/*
* Iterator functions.
*/
typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
typedef struct get_all_cb {
zfs_handle_t **cb_handles;
size_t cb_alloc;
size_t cb_used;
boolean_t cb_verbose;
int (*cb_getone)(zfs_handle_t *, void *);
} get_all_cb_t;
void libzfs_add_handle(get_all_cb_t *, zfs_handle_t *);
int libzfs_dataset_cmp(const void *, const void *);
/*
* Functions to create and destroy datasets.
*/
extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
nvlist_t *);
extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
extern int zfs_destroy(zfs_handle_t *, boolean_t);
extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
typedef struct sendflags {
/* print informational messages (ie, -v was specified) */
int verbose : 1;
/* recursive send (ie, -R) */
int replicate : 1;
/* for incrementals, do all intermediate snapshots */
int doall : 1; /* (ie, -I) */
/* if dataset is a clone, do incremental from its origin */
int fromorigin : 1;
/* do deduplication */
int dedup : 1;
/* send properties (ie, -p) */
int props : 1;
} sendflags_t;
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
extern int zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
void *cb_arg, nvlist_t **debugnvp);
extern int zfs_promote(zfs_handle_t *);
extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t,
boolean_t, boolean_t, int, uint64_t, uint64_t);
extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
extern uint64_t zvol_volsize_to_reservation(uint64_t, nvlist_t *);
typedef int (*zfs_userspace_cb_t)(void *arg, const char *domain,
uid_t rid, uint64_t space);
extern int zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
zfs_userspace_cb_t func, void *arg);
typedef struct recvflags {
/* print informational messages (ie, -v was specified) */
int verbose : 1;
/* the destination is a prefix, not the exact fs (ie, -d) */
int isprefix : 1;
/*
* Only the tail of the sent snapshot path is appended to the
* destination to determine the received snapshot name (ie, -e).
*/
int istail : 1;
/* do not actually do the recv, just check if it would work (ie, -n) */
int dryrun : 1;
/* rollback/destroy filesystems as necessary (eg, -F) */
int force : 1;
/* set "canmount=off" on all modified filesystems */
int canmountoff : 1;
/* byteswap flag is used internally; callers need not specify */
int byteswap : 1;
/* do not mount file systems as they are extracted (private) */
int nomount : 1;
} recvflags_t;
extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
int, avl_tree_t *);
typedef enum diff_flags {
ZFS_DIFF_PARSEABLE = 0x1,
ZFS_DIFF_TIMESTAMP = 0x2,
ZFS_DIFF_CLASSIFY = 0x4
} diff_flags_t;
extern int zfs_show_diffs(zfs_handle_t *, int, const char *, const char *,
int);
/*
* Miscellaneous functions.
*/
extern const char *zfs_type_to_name(zfs_type_t);
extern void zfs_refresh_properties(zfs_handle_t *);
extern int zfs_name_valid(const char *, zfs_type_t);
extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
zfs_type_t);
extern int zfs_spa_version(zfs_handle_t *, int *);
/*
* Mount support functions.
*/
extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
extern boolean_t zfs_is_mounted(zfs_handle_t *, char **);
extern int zfs_mount(zfs_handle_t *, const char *, int);
extern int zfs_unmount(zfs_handle_t *, const char *, int);
extern int zfs_unmountall(zfs_handle_t *, int);
/*
* Share support functions.
*/
extern boolean_t zfs_is_shared(zfs_handle_t *);
extern int zfs_share(zfs_handle_t *);
extern int zfs_unshare(zfs_handle_t *);
/*
* Protocol-specific share support functions.
*/
extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **);
extern int zfs_share_nfs(zfs_handle_t *);
extern int zfs_share_smb(zfs_handle_t *);
extern int zfs_shareall(zfs_handle_t *);
extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
extern int zfs_unshare_smb(zfs_handle_t *, const char *);
extern int zfs_unshareall_nfs(zfs_handle_t *);
extern int zfs_unshareall_smb(zfs_handle_t *);
extern int zfs_unshareall_bypath(zfs_handle_t *, const char *);
extern int zfs_unshareall(zfs_handle_t *);
extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *,
void *, void *, int, zfs_share_op_t);
/*
* When dealing with nvlists, verify() is extremely useful
*/
#ifdef NDEBUG
#define verify(EX) ((void)(EX))
#else
#define verify(EX) assert(EX)
#endif
/*
* Utility function to convert a number to a human-readable form.
*/
extern void zfs_nicenum(uint64_t, char *, size_t);
extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
/*
* Given a device or file, determine if it is part of a pool.
*/
extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
boolean_t *);
/*
* Label manipulation.
*/
extern int zpool_read_label(int, nvlist_t **);
extern int zpool_clear_label(int);
/* is this zvol valid for use as a dump device? */
extern int zvol_check_dump_config(char *);
/*
* Management interfaces for SMB ACL files
*/
int zfs_smb_acl_add(libzfs_handle_t *, char *, char *, char *);
int zfs_smb_acl_remove(libzfs_handle_t *, char *, char *, char *);
int zfs_smb_acl_purge(libzfs_handle_t *, char *, char *);
int zfs_smb_acl_rename(libzfs_handle_t *, char *, char *, char *, char *);
/*
* Enable and disable datasets within a pool by mounting/unmounting and
* sharing/unsharing them.
*/
extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
/*
* Mappings between vdev and FRU.
*/
extern void libzfs_fru_refresh(libzfs_handle_t *);
extern const char *libzfs_fru_lookup(libzfs_handle_t *, const char *);
extern const char *libzfs_fru_devpath(libzfs_handle_t *, const char *);
extern boolean_t libzfs_fru_compare(libzfs_handle_t *, const char *,
const char *);
extern boolean_t libzfs_fru_notself(libzfs_handle_t *, const char *);
extern int zpool_fru_set(zpool_handle_t *, uint64_t, const char *);
#ifdef __cplusplus
}
#endif
#endif /* _LIBZFS_H */

View File

@ -0,0 +1,693 @@
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Portions Copyright 2007 Ramprakash Jelari
*/
#include <libintl.h>
#include <libuutil.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zone.h>
#include <libzfs.h>
#include "libzfs_impl.h"
/*
* Structure to keep track of dataset state. Before changing the 'sharenfs' or
* 'mountpoint' property, we record whether the filesystem was previously
* mounted/shared. This prior state dictates whether we remount/reshare the
* dataset after the property has been changed.
*
* The interface consists of the following sequence of functions:
*
* changelist_gather()
* changelist_prefix()
* < change property >
* changelist_postfix()
* changelist_free()
*
* Other interfaces:
*
* changelist_remove() - remove a node from a gathered list
* changelist_rename() - renames all datasets appropriately when doing a rename
* changelist_unshare() - unshares all the nodes in a given changelist
* changelist_haszonedchild() - check if there is any child exported to
* a local zone
*/
typedef struct prop_changenode {
zfs_handle_t *cn_handle;
int cn_shared;
int cn_mounted;
int cn_zoned;
boolean_t cn_needpost; /* is postfix() needed? */
uu_list_node_t cn_listnode;
} prop_changenode_t;
struct prop_changelist {
zfs_prop_t cl_prop;
zfs_prop_t cl_realprop;
zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
uu_list_pool_t *cl_pool;
uu_list_t *cl_list;
boolean_t cl_waslegacy;
boolean_t cl_allchildren;
boolean_t cl_alldependents;
int cl_mflags; /* Mount flags */
int cl_gflags; /* Gather request flags */
boolean_t cl_haszonedchild;
boolean_t cl_sorted;
};
/*
* If the property is 'mountpoint', go through and unmount filesystems as
* necessary. We don't do the same for 'sharenfs', because we can just re-share
* with different options without interrupting service. We do handle 'sharesmb'
* since there may be old resource names that need to be removed.
*/
int
changelist_prefix(prop_changelist_t *clp)
{
prop_changenode_t *cn;
int ret = 0;
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
clp->cl_prop != ZFS_PROP_SHARESMB)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
/* if a previous loop failed, set the remaining to false */
if (ret == -1) {
cn->cn_needpost = B_FALSE;
continue;
}
/*
* If we are in the global zone, but this dataset is exported
* to a local zone, do nothing.
*/
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
continue;
if (!ZFS_IS_VOLUME(cn->cn_handle)) {
/*
* Do the property specific processing.
*/
switch (clp->cl_prop) {
case ZFS_PROP_MOUNTPOINT:
if (zfs_unmount(cn->cn_handle, NULL,
clp->cl_mflags) != 0) {
ret = -1;
cn->cn_needpost = B_FALSE;
}
break;
case ZFS_PROP_SHARESMB:
(void) zfs_unshare_smb(cn->cn_handle, NULL);
break;
}
}
}
if (ret == -1)
(void) changelist_postfix(clp);
return (ret);
}
/*
* If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
* reshare the filesystems as necessary. In changelist_gather() we recorded
* whether the filesystem was previously shared or mounted. The action we take
* depends on the previous state, and whether the value was previously 'legacy'.
* For non-legacy properties, we only remount/reshare the filesystem if it was
* previously mounted/shared. Otherwise, we always remount/reshare the
* filesystem.
*/
int
changelist_postfix(prop_changelist_t *clp)
{
prop_changenode_t *cn;
char shareopts[ZFS_MAXPROPLEN];
int errors = 0;
libzfs_handle_t *hdl;
/*
* If we're changing the mountpoint, attempt to destroy the underlying
* mountpoint. All other datasets will have inherited from this dataset
* (in which case their mountpoints exist in the filesystem in the new
* location), or have explicit mountpoints set (in which case they won't
* be in the changelist).
*/
if ((cn = uu_list_last(clp->cl_list)) == NULL)
return (0);
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT)
remove_mountpoint(cn->cn_handle);
/*
* It is possible that the changelist_prefix() used libshare
* to unshare some entries. Since libshare caches data, an
* attempt to reshare during postfix can fail unless libshare
* is uninitialized here so that it will reinitialize later.
*/
if (cn->cn_handle != NULL) {
hdl = cn->cn_handle->zfs_hdl;
assert(hdl != NULL);
zfs_uninit_libshare(hdl);
}
/*
* We walk the datasets in reverse, because we want to mount any parent
* datasets before mounting the children. We walk all datasets even if
* there are errors.
*/
for (cn = uu_list_last(clp->cl_list); cn != NULL;
cn = uu_list_prev(clp->cl_list, cn)) {
boolean_t sharenfs;
boolean_t sharesmb;
boolean_t mounted;
/*
* If we are in the global zone, but this dataset is exported
* to a local zone, do nothing.
*/
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
continue;
/* Only do post-processing if it's required */
if (!cn->cn_needpost)
continue;
cn->cn_needpost = B_FALSE;
zfs_refresh_properties(cn->cn_handle);
if (ZFS_IS_VOLUME(cn->cn_handle))
continue;
/*
* Remount if previously mounted or mountpoint was legacy,
* or sharenfs or sharesmb property is set.
*/
sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
shareopts, sizeof (shareopts), NULL, NULL, 0,
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
shareopts, sizeof (shareopts), NULL, NULL, 0,
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
mounted = zfs_is_mounted(cn->cn_handle, NULL);
if (!mounted && (cn->cn_mounted ||
((sharenfs || sharesmb || clp->cl_waslegacy) &&
(zfs_prop_get_int(cn->cn_handle,
ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
errors++;
else
mounted = TRUE;
}
/*
* If the file system is mounted we always re-share even
* if the filesystem is currently shared, so that we can
* adopt any new options.
*/
if (sharenfs && mounted)
errors += zfs_share_nfs(cn->cn_handle);
else if (cn->cn_shared || clp->cl_waslegacy)
errors += zfs_unshare_nfs(cn->cn_handle, NULL);
if (sharesmb && mounted)
errors += zfs_share_smb(cn->cn_handle);
else if (cn->cn_shared || clp->cl_waslegacy)
errors += zfs_unshare_smb(cn->cn_handle, NULL);
}
return (errors ? -1 : 0);
}
/*
* Is this "dataset" a child of "parent"?
*/
boolean_t
isa_child_of(const char *dataset, const char *parent)
{
int len;
len = strlen(parent);
if (strncmp(dataset, parent, len) == 0 &&
(dataset[len] == '@' || dataset[len] == '/' ||
dataset[len] == '\0'))
return (B_TRUE);
else
return (B_FALSE);
}
/*
* If we rename a filesystem, child filesystem handles are no longer valid
* since we identify each dataset by its name in the ZFS namespace. As a
* result, we have to go through and fix up all the names appropriately. We
* could do this automatically if libzfs kept track of all open handles, but
* this is a lot less work.
*/
void
changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
{
prop_changenode_t *cn;
char newname[ZFS_MAXNAMELEN];
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
/*
* Do not rename a clone that's not in the source hierarchy.
*/
if (!isa_child_of(cn->cn_handle->zfs_name, src))
continue;
/*
* Destroy the previous mountpoint if needed.
*/
remove_mountpoint(cn->cn_handle);
(void) strlcpy(newname, dst, sizeof (newname));
(void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
(void) strlcpy(cn->cn_handle->zfs_name, newname,
sizeof (cn->cn_handle->zfs_name));
}
}
/*
* Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
* unshare all the datasets in the list.
*/
int
changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
{
prop_changenode_t *cn;
int ret = 0;
if (clp->cl_prop != ZFS_PROP_SHARENFS &&
clp->cl_prop != ZFS_PROP_SHARESMB)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
ret = -1;
}
return (ret);
}
/*
* Check if there is any child exported to a local zone in a given changelist.
* This information has already been recorded while gathering the changelist
* via changelist_gather().
*/
int
changelist_haszonedchild(prop_changelist_t *clp)
{
return (clp->cl_haszonedchild);
}
/*
* Remove a node from a gathered list.
*/
void
changelist_remove(prop_changelist_t *clp, const char *name)
{
prop_changenode_t *cn;
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
uu_list_remove(clp->cl_list, cn);
zfs_close(cn->cn_handle);
free(cn);
return;
}
}
}
/*
* Release any memory associated with a changelist.
*/
void
changelist_free(prop_changelist_t *clp)
{
prop_changenode_t *cn;
void *cookie;
if (clp->cl_list) {
cookie = NULL;
while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) {
zfs_close(cn->cn_handle);
free(cn);
}
uu_list_destroy(clp->cl_list);
}
if (clp->cl_pool)
uu_list_pool_destroy(clp->cl_pool);
free(clp);
}
static int
change_one(zfs_handle_t *zhp, void *data)
{
prop_changelist_t *clp = data;
char property[ZFS_MAXPROPLEN];
char where[64];
prop_changenode_t *cn;
zprop_source_t sourcetype;
zprop_source_t share_sourcetype;
/*
* We only want to unmount/unshare those filesystems that may inherit
* from the target filesystem. If we find any filesystem with a
* locally set mountpoint, we ignore any children since changing the
* property will not affect them. If this is a rename, we iterate
* over all children regardless, since we need them unmounted in
* order to do the rename. Also, if this is a volume and we're doing
* a rename, then always add it to the changelist.
*/
if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
zfs_prop_get(zhp, clp->cl_prop, property,
sizeof (property), &sourcetype, where, sizeof (where),
B_FALSE) != 0) {
zfs_close(zhp);
return (0);
}
/*
* If we are "watching" sharenfs or sharesmb
* then check out the companion property which is tracked
* in cl_shareprop
*/
if (clp->cl_shareprop != ZPROP_INVAL &&
zfs_prop_get(zhp, clp->cl_shareprop, property,
sizeof (property), &share_sourcetype, where, sizeof (where),
B_FALSE) != 0) {
zfs_close(zhp);
return (0);
}
if (clp->cl_alldependents || clp->cl_allchildren ||
sourcetype == ZPROP_SRC_DEFAULT ||
sourcetype == ZPROP_SRC_INHERITED ||
(clp->cl_shareprop != ZPROP_INVAL &&
(share_sourcetype == ZPROP_SRC_DEFAULT ||
share_sourcetype == ZPROP_SRC_INHERITED))) {
if ((cn = zfs_alloc(zfs_get_handle(zhp),
sizeof (prop_changenode_t))) == NULL) {
zfs_close(zhp);
return (-1);
}
cn->cn_handle = zhp;
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
zfs_is_mounted(zhp, NULL);
cn->cn_shared = zfs_is_shared(zhp);
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
cn->cn_needpost = B_TRUE;
/* Indicate if any child is exported to a local zone. */
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
clp->cl_haszonedchild = B_TRUE;
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
if (clp->cl_sorted) {
uu_list_index_t idx;
(void) uu_list_find(clp->cl_list, cn, NULL,
&idx);
uu_list_insert(clp->cl_list, cn, idx);
} else {
/*
* Add this child to beginning of the list. Children
* below this one in the hierarchy will get added above
* this one in the list. This produces a list in
* reverse dataset name order.
* This is necessary when the original mountpoint
* is legacy or none.
*/
ASSERT(!clp->cl_alldependents);
verify(uu_list_insert_before(clp->cl_list,
uu_list_first(clp->cl_list), cn) == 0);
}
if (!clp->cl_alldependents)
return (zfs_iter_children(zhp, change_one, data));
} else {
zfs_close(zhp);
}
return (0);
}
/*ARGSUSED*/
static int
compare_mountpoints(const void *a, const void *b, void *unused)
{
const prop_changenode_t *ca = a;
const prop_changenode_t *cb = b;
char mounta[MAXPATHLEN];
char mountb[MAXPATHLEN];
boolean_t hasmounta, hasmountb;
/*
* When unsharing or unmounting filesystems, we need to do it in
* mountpoint order. This allows the user to have a mountpoint
* hierarchy that is different from the dataset hierarchy, and still
* allow it to be changed. However, if either dataset doesn't have a
* mountpoint (because it is a volume or a snapshot), we place it at the
* end of the list, because it doesn't affect our change at all.
*/
hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
if (!hasmounta && hasmountb)
return (-1);
else if (hasmounta && !hasmountb)
return (1);
else if (!hasmounta && !hasmountb)
return (0);
else
return (strcmp(mountb, mounta));
}
/*
* Given a ZFS handle and a property, construct a complete list of datasets
* that need to be modified as part of this process. For anything but the
* 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
* Otherwise, we iterate over all children and look for any datasets that
* inherit the property. For each such dataset, we add it to the list and
* mark whether it was shared beforehand.
*/
prop_changelist_t *
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
int mnt_flags)
{
prop_changelist_t *clp;
prop_changenode_t *cn;
zfs_handle_t *temp;
char property[ZFS_MAXPROPLEN];
uu_compare_fn_t *compare = NULL;
boolean_t legacy = B_FALSE;
if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
return (NULL);
/*
* For mountpoint-related tasks, we want to sort everything by
* mountpoint, so that we mount and unmount them in the appropriate
* order, regardless of their position in the hierarchy.
*/
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
prop == ZFS_PROP_SHARESMB) {
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
property, sizeof (property),
NULL, NULL, 0, B_FALSE) == 0 &&
(strcmp(property, "legacy") == 0 ||
strcmp(property, "none") == 0)) {
legacy = B_TRUE;
}
if (!legacy) {
compare = compare_mountpoints;
clp->cl_sorted = B_TRUE;
}
}
clp->cl_pool = uu_list_pool_create("changelist_pool",
sizeof (prop_changenode_t),
offsetof(prop_changenode_t, cn_listnode),
compare, 0);
if (clp->cl_pool == NULL) {
assert(uu_error() == UU_ERROR_NO_MEMORY);
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
changelist_free(clp);
return (NULL);
}
clp->cl_list = uu_list_create(clp->cl_pool, NULL,
clp->cl_sorted ? UU_LIST_SORTED : 0);
clp->cl_gflags = gather_flags;
clp->cl_mflags = mnt_flags;
if (clp->cl_list == NULL) {
assert(uu_error() == UU_ERROR_NO_MEMORY);
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
changelist_free(clp);
return (NULL);
}
/*
* If this is a rename or the 'zoned' property, we pretend we're
* changing the mountpoint and flag it so we can catch all children in
* change_one().
*
* Flag cl_alldependents to catch all children plus the dependents
* (clones) that are not in the hierarchy.
*/
if (prop == ZFS_PROP_NAME) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
clp->cl_alldependents = B_TRUE;
} else if (prop == ZFS_PROP_ZONED) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
clp->cl_allchildren = B_TRUE;
} else if (prop == ZFS_PROP_CANMOUNT) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
} else if (prop == ZFS_PROP_VOLSIZE) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
} else {
clp->cl_prop = prop;
}
clp->cl_realprop = prop;
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
clp->cl_prop != ZFS_PROP_SHARENFS &&
clp->cl_prop != ZFS_PROP_SHARESMB)
return (clp);
/*
* If watching SHARENFS or SHARESMB then
* also watch its companion property.
*/
if (clp->cl_prop == ZFS_PROP_SHARENFS)
clp->cl_shareprop = ZFS_PROP_SHARESMB;
else if (clp->cl_prop == ZFS_PROP_SHARESMB)
clp->cl_shareprop = ZFS_PROP_SHARENFS;
if (clp->cl_alldependents) {
if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
changelist_free(clp);
return (NULL);
}
} else if (zfs_iter_children(zhp, change_one, clp) != 0) {
changelist_free(clp);
return (NULL);
}
/*
* We have to re-open ourselves because we auto-close all the handles
* and can't tell the difference.
*/
if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
ZFS_TYPE_DATASET)) == NULL) {
changelist_free(clp);
return (NULL);
}
/*
* Always add ourself to the list. We add ourselves to the end so that
* we're the last to be unmounted.
*/
if ((cn = zfs_alloc(zhp->zfs_hdl,
sizeof (prop_changenode_t))) == NULL) {
zfs_close(temp);
changelist_free(clp);
return (NULL);
}
cn->cn_handle = temp;
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
zfs_is_mounted(temp, NULL);
cn->cn_shared = zfs_is_shared(temp);
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
cn->cn_needpost = B_TRUE;
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
if (clp->cl_sorted) {
uu_list_index_t idx;
(void) uu_list_find(clp->cl_list, cn, NULL, &idx);
uu_list_insert(clp->cl_list, cn, idx);
} else {
/*
* Add the target dataset to the end of the list.
* The list is not really unsorted. The list will be
* in reverse dataset name order. This is necessary
* when the original mountpoint is legacy or none.
*/
verify(uu_list_insert_after(clp->cl_list,
uu_list_last(clp->cl_list), cn) == 0);
}
/*
* If the mountpoint property was previously 'legacy', or 'none',
* record it as the behavior of changelist_postfix() will be different.
*/
if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
/*
* do not automatically mount ex-legacy datasets if
* we specifically set canmount to noauto
*/
if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
ZFS_CANMOUNT_NOAUTO)
clp->cl_waslegacy = B_TRUE;
}
return (clp);
}

View File

@ -0,0 +1,370 @@
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* The pool configuration repository is stored in /etc/zfs/zpool.cache as a
* single packed nvlist. While it would be nice to just read in this
* file from userland, this wouldn't work from a local zone. So we have to have
* a zpool ioctl to return the complete configuration for all pools. In the
* global zone, this will be identical to reading the file and unpacking it in
* userland.
*/
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <libintl.h>
#include <libuutil.h>
#include "libzfs_impl.h"
typedef struct config_node {
char *cn_name;
nvlist_t *cn_config;
uu_avl_node_t cn_avl;
} config_node_t;
/* ARGSUSED */
static int
config_node_compare(const void *a, const void *b, void *unused)
{
int ret;
const config_node_t *ca = (config_node_t *)a;
const config_node_t *cb = (config_node_t *)b;
ret = strcmp(ca->cn_name, cb->cn_name);
if (ret < 0)
return (-1);
else if (ret > 0)
return (1);
else
return (0);
}
void
namespace_clear(libzfs_handle_t *hdl)
{
if (hdl->libzfs_ns_avl) {
config_node_t *cn;
void *cookie = NULL;
while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl,
&cookie)) != NULL) {
nvlist_free(cn->cn_config);
free(cn->cn_name);
free(cn);
}
uu_avl_destroy(hdl->libzfs_ns_avl);
hdl->libzfs_ns_avl = NULL;
}
if (hdl->libzfs_ns_avlpool) {
uu_avl_pool_destroy(hdl->libzfs_ns_avlpool);
hdl->libzfs_ns_avlpool = NULL;
}
}
/*
* Loads the pool namespace, or re-loads it if the cache has changed.
*/
static int
namespace_reload(libzfs_handle_t *hdl)
{
nvlist_t *config;
config_node_t *cn;
nvpair_t *elem;
zfs_cmd_t zc = { 0 };
void *cookie;
if (hdl->libzfs_ns_gen == 0) {
/*
* This is the first time we've accessed the configuration
* cache. Initialize the AVL tree and then fall through to the
* common code.
*/
if ((hdl->libzfs_ns_avlpool = uu_avl_pool_create("config_pool",
sizeof (config_node_t),
offsetof(config_node_t, cn_avl),
config_node_compare, UU_DEFAULT)) == NULL)
return (no_memory(hdl));
if ((hdl->libzfs_ns_avl = uu_avl_create(hdl->libzfs_ns_avlpool,
NULL, UU_DEFAULT)) == NULL)
return (no_memory(hdl));
}
if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
return (-1);
for (;;) {
zc.zc_cookie = hdl->libzfs_ns_gen;
if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
switch (errno) {
case EEXIST:
/*
* The namespace hasn't changed.
*/
zcmd_free_nvlists(&zc);
return (0);
case ENOMEM:
if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
break;
default:
zcmd_free_nvlists(&zc);
return (zfs_standard_error(hdl, errno,
dgettext(TEXT_DOMAIN, "failed to read "
"pool configuration")));
}
} else {
hdl->libzfs_ns_gen = zc.zc_cookie;
break;
}
}
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
zcmd_free_nvlists(&zc);
/*
* Clear out any existing configuration information.
*/
cookie = NULL;
while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl, &cookie)) != NULL) {
nvlist_free(cn->cn_config);
free(cn->cn_name);
free(cn);
}
elem = NULL;
while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {
nvlist_t *child;
uu_avl_index_t where;
if ((cn = zfs_alloc(hdl, sizeof (config_node_t))) == NULL) {
nvlist_free(config);
return (-1);
}
if ((cn->cn_name = zfs_strdup(hdl,
nvpair_name(elem))) == NULL) {
free(cn);
nvlist_free(config);
return (-1);
}
verify(nvpair_value_nvlist(elem, &child) == 0);
if (nvlist_dup(child, &cn->cn_config, 0) != 0) {
free(cn->cn_name);
free(cn);
nvlist_free(config);
return (no_memory(hdl));
}
verify(uu_avl_find(hdl->libzfs_ns_avl, cn, NULL, &where)
== NULL);
uu_avl_insert(hdl->libzfs_ns_avl, cn, where);
}
nvlist_free(config);
return (0);
}
/*
* Retrieve the configuration for the given pool. The configuration is a nvlist
* describing the vdevs, as well as the statistics associated with each one.
*/
nvlist_t *
zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
{
if (oldconfig)
*oldconfig = zhp->zpool_old_config;
return (zhp->zpool_config);
}
/*
* Refresh the vdev statistics associated with the given pool. This is used in
* iostat to show configuration changes and determine the delta from the last
* time the function was called. This function can fail, in case the pool has
* been destroyed.
*/
int
zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
{
zfs_cmd_t zc = { 0 };
int error;
nvlist_t *config;
libzfs_handle_t *hdl = zhp->zpool_hdl;
*missing = B_FALSE;
(void) strcpy(zc.zc_name, zhp->zpool_name);
if (zhp->zpool_config_size == 0)
zhp->zpool_config_size = 1 << 16;
if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0)
return (-1);
for (;;) {
if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_STATS,
&zc) == 0) {
/*
* The real error is returned in the zc_cookie field.
*/
error = zc.zc_cookie;
break;
}
if (errno == ENOMEM) {
if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
} else {
zcmd_free_nvlists(&zc);
if (errno == ENOENT || errno == EINVAL)
*missing = B_TRUE;
zhp->zpool_state = POOL_STATE_UNAVAIL;
return (0);
}
}
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
zcmd_free_nvlists(&zc);
zhp->zpool_config_size = zc.zc_nvlist_dst_size;
if (zhp->zpool_config != NULL) {
uint64_t oldtxg, newtxg;
verify(nvlist_lookup_uint64(zhp->zpool_config,
ZPOOL_CONFIG_POOL_TXG, &oldtxg) == 0);
verify(nvlist_lookup_uint64(config,
ZPOOL_CONFIG_POOL_TXG, &newtxg) == 0);
if (zhp->zpool_old_config != NULL)
nvlist_free(zhp->zpool_old_config);
if (oldtxg != newtxg) {
nvlist_free(zhp->zpool_config);
zhp->zpool_old_config = NULL;
} else {
zhp->zpool_old_config = zhp->zpool_config;
}
}
zhp->zpool_config = config;
if (error)
zhp->zpool_state = POOL_STATE_UNAVAIL;
else
zhp->zpool_state = POOL_STATE_ACTIVE;
return (0);
}
/*
* Iterate over all pools in the system.
*/
int
zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
{
config_node_t *cn;
zpool_handle_t *zhp;
int ret;
/*
* If someone makes a recursive call to zpool_iter(), we want to avoid
* refreshing the namespace because that will invalidate the parent
* context. We allow recursive calls, but simply re-use the same
* namespace AVL tree.
*/
if (!hdl->libzfs_pool_iter && namespace_reload(hdl) != 0)
return (-1);
hdl->libzfs_pool_iter++;
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) {
hdl->libzfs_pool_iter--;
return (-1);
}
if (zhp == NULL)
continue;
if ((ret = func(zhp, data)) != 0) {
hdl->libzfs_pool_iter--;
return (ret);
}
}
hdl->libzfs_pool_iter--;
return (0);
}
/*
* Iterate over root datasets, calling the given function for each. The zfs
* handle passed each time must be explicitly closed by the callback.
*/
int
zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
{
config_node_t *cn;
zfs_handle_t *zhp;
int ret;
if (namespace_reload(hdl) != 0)
return (-1);
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
continue;
if ((ret = func(zhp, data)) != 0)
return (ret);
}
return (0);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,826 @@
/*
* 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* zfs diff support
*/
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <attr.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stropts.h>
#include <pthread.h>
#include <sys/zfs_ioctl.h>
#include <libzfs.h>
#include "libzfs_impl.h"
#define ZDIFF_SNAPDIR "/.zfs/snapshot/"
#define ZDIFF_SHARESDIR "/.zfs/shares/"
#define ZDIFF_PREFIX "zfs-diff-%d"
#define ZDIFF_ADDED '+'
#define ZDIFF_MODIFIED 'M'
#define ZDIFF_REMOVED '-'
#define ZDIFF_RENAMED 'R'
static boolean_t
do_name_cmp(const char *fpath, const char *tpath)
{
char *fname, *tname;
fname = strrchr(fpath, '/') + 1;
tname = strrchr(tpath, '/') + 1;
return (strcmp(fname, tname) == 0);
}
typedef struct differ_info {
zfs_handle_t *zhp;
char *fromsnap;
char *frommnt;
char *tosnap;
char *tomnt;
char *ds;
char *dsmnt;
char *tmpsnap;
char errbuf[1024];
boolean_t isclone;
boolean_t scripted;
boolean_t classify;
boolean_t timestamped;
uint64_t shares;
int zerr;
int cleanupfd;
int outputfd;
int datafd;
} differ_info_t;
/*
* Given a {dsname, object id}, get the object path
*/
static int
get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
char *pn, int maxlen, zfs_stat_t *sb)
{
zfs_cmd_t zc = { 0 };
int error;
(void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
zc.zc_obj = obj;
errno = 0;
error = ioctl(di->zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc);
di->zerr = errno;
/* we can get stats even if we failed to get a path */
(void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t));
if (error == 0) {
ASSERT(di->zerr == 0);
(void) strlcpy(pn, zc.zc_value, maxlen);
return (0);
}
if (di->zerr == EPERM) {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"The sys_config privilege or diff delegated permission "
"is needed\nto discover path names"));
return (-1);
} else {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"Unable to determine path or stats for "
"object %lld in %s"), obj, dsname);
return (-1);
}
}
/*
* stream_bytes
*
* Prints a file name out a character at a time. If the character is
* not in the range of what we consider "printable" ASCII, display it
* as an escaped 3-digit octal value. ASCII values less than a space
* are all control characters and we declare the upper end as the
* DELete character. This also is the last 7-bit ASCII character.
* We choose to treat all 8-bit ASCII as not printable for this
* application.
*/
static void
stream_bytes(FILE *fp, const char *string)
{
while (*string) {
if (*string > ' ' && *string != '\\' && *string < '\177')
(void) fprintf(fp, "%c", *string++);
else
(void) fprintf(fp, "\\%03o", *string++);
}
}
static void
print_what(FILE *fp, mode_t what)
{
char symbol;
switch (what & S_IFMT) {
case S_IFBLK:
symbol = 'B';
break;
case S_IFCHR:
symbol = 'C';
break;
case S_IFDIR:
symbol = '/';
break;
case S_IFDOOR:
symbol = '>';
break;
case S_IFIFO:
symbol = '|';
break;
case S_IFLNK:
symbol = '@';
break;
case S_IFPORT:
symbol = 'P';
break;
case S_IFSOCK:
symbol = '=';
break;
case S_IFREG:
symbol = 'F';
break;
default:
symbol = '?';
break;
}
(void) fprintf(fp, "%c", symbol);
}
static void
print_cmn(FILE *fp, differ_info_t *di, const char *file)
{
stream_bytes(fp, di->dsmnt);
stream_bytes(fp, file);
}
static void
print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new,
zfs_stat_t *isb)
{
if (di->timestamped)
(void) fprintf(fp, "%10lld.%09lld\t",
(longlong_t)isb->zs_ctime[0],
(longlong_t)isb->zs_ctime[1]);
(void) fprintf(fp, "%c\t", ZDIFF_RENAMED);
if (di->classify) {
print_what(fp, isb->zs_mode);
(void) fprintf(fp, "\t");
}
print_cmn(fp, di, old);
if (di->scripted)
(void) fprintf(fp, "\t");
else
(void) fprintf(fp, " -> ");
print_cmn(fp, di, new);
(void) fprintf(fp, "\n");
}
static void
print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file,
zfs_stat_t *isb)
{
if (di->timestamped)
(void) fprintf(fp, "%10lld.%09lld\t",
(longlong_t)isb->zs_ctime[0],
(longlong_t)isb->zs_ctime[1]);
(void) fprintf(fp, "%c\t", ZDIFF_MODIFIED);
if (di->classify) {
print_what(fp, isb->zs_mode);
(void) fprintf(fp, "\t");
}
print_cmn(fp, di, file);
(void) fprintf(fp, "\t(%+d)", delta);
(void) fprintf(fp, "\n");
}
static void
print_file(FILE *fp, differ_info_t *di, char type, const char *file,
zfs_stat_t *isb)
{
if (di->timestamped)
(void) fprintf(fp, "%10lld.%09lld\t",
(longlong_t)isb->zs_ctime[0],
(longlong_t)isb->zs_ctime[1]);
(void) fprintf(fp, "%c\t", type);
if (di->classify) {
print_what(fp, isb->zs_mode);
(void) fprintf(fp, "\t");
}
print_cmn(fp, di, file);
(void) fprintf(fp, "\n");
}
static int
write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj)
{
struct zfs_stat fsb, tsb;
boolean_t same_name;
mode_t fmode, tmode;
char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN];
int fobjerr, tobjerr;
int change;
if (dobj == di->shares)
return (0);
/*
* Check the from and to snapshots for info on the object. If
* we get ENOENT, then the object just didn't exist in that
* snapshot. If we get ENOTSUP, then we tried to get
* info on a non-ZPL object, which we don't care about anyway.
*/
fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname,
MAXPATHLEN, &fsb);
if (fobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
return (-1);
tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname,
MAXPATHLEN, &tsb);
if (tobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
return (-1);
/*
* Unallocated object sharing the same meta dnode block
*/
if (fobjerr && tobjerr) {
ASSERT(di->zerr == ENOENT || di->zerr == ENOTSUP);
di->zerr = 0;
return (0);
}
di->zerr = 0; /* negate get_stats_for_obj() from side that failed */
fmode = fsb.zs_mode & S_IFMT;
tmode = tsb.zs_mode & S_IFMT;
if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 ||
tsb.zs_links == 0)
change = 0;
else
change = tsb.zs_links - fsb.zs_links;
if (fobjerr) {
if (change) {
print_link_change(fp, di, change, tobjname, &tsb);
return (0);
}
print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
return (0);
} else if (tobjerr) {
if (change) {
print_link_change(fp, di, change, fobjname, &fsb);
return (0);
}
print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
return (0);
}
if (fmode != tmode && fsb.zs_gen == tsb.zs_gen)
tsb.zs_gen++; /* Force a generational difference */
same_name = do_name_cmp(fobjname, tobjname);
/* Simple modification or no change */
if (fsb.zs_gen == tsb.zs_gen) {
/* No apparent changes. Could we assert !this? */
if (fsb.zs_ctime[0] == tsb.zs_ctime[0] &&
fsb.zs_ctime[1] == tsb.zs_ctime[1])
return (0);
if (change) {
print_link_change(fp, di, change,
change > 0 ? fobjname : tobjname, &tsb);
} else if (same_name) {
print_file(fp, di, ZDIFF_MODIFIED, fobjname, &tsb);
} else {
print_rename(fp, di, fobjname, tobjname, &tsb);
}
return (0);
} else {
/* file re-created or object re-used */
print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
return (0);
}
}
static int
write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
{
uint64_t o;
int err;
for (o = dr->ddr_first; o <= dr->ddr_last; o++) {
if (err = write_inuse_diffs_one(fp, di, o))
return (err);
}
return (0);
}
static int
describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf,
int maxlen)
{
struct zfs_stat sb;
if (get_stats_for_obj(di, di->fromsnap, object, namebuf,
maxlen, &sb) != 0) {
/* Let it slide, if in the delete queue on from side */
if (di->zerr == ENOENT && sb.zs_links == 0) {
di->zerr = 0;
return (0);
}
return (-1);
}
print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb);
return (0);
}
static int
write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
{
zfs_cmd_t zc = { 0 };
libzfs_handle_t *lhdl = di->zhp->zfs_hdl;
char fobjname[MAXPATHLEN];
(void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name));
zc.zc_obj = dr->ddr_first - 1;
ASSERT(di->zerr == 0);
while (zc.zc_obj < dr->ddr_last) {
int err;
err = ioctl(lhdl->libzfs_fd, ZFS_IOC_NEXT_OBJ, &zc);
if (err == 0) {
if (zc.zc_obj == di->shares) {
zc.zc_obj++;
continue;
}
if (zc.zc_obj > dr->ddr_last) {
break;
}
err = describe_free(fp, di, zc.zc_obj, fobjname,
MAXPATHLEN);
if (err)
break;
} else if (errno == ESRCH) {
break;
} else {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"next allocated object (> %lld) find failure"),
zc.zc_obj);
di->zerr = errno;
break;
}
}
if (di->zerr)
return (-1);
return (0);
}
static void *
differ(void *arg)
{
differ_info_t *di = arg;
dmu_diff_record_t dr;
FILE *ofp;
int err = 0;
if ((ofp = fdopen(di->outputfd, "w")) == NULL) {
di->zerr = errno;
(void) strerror_r(errno, di->errbuf, sizeof (di->errbuf));
(void) close(di->datafd);
return ((void *)-1);
}
for (;;) {
char *cp = (char *)&dr;
int len = sizeof (dr);
int rv;
do {
rv = read(di->datafd, cp, len);
cp += rv;
len -= rv;
} while (len > 0 && rv > 0);
if (rv < 0 || (rv == 0 && len != sizeof (dr))) {
di->zerr = EPIPE;
break;
} else if (rv == 0) {
/* end of file at a natural breaking point */
break;
}
switch (dr.ddr_type) {
case DDR_FREE:
err = write_free_diffs(ofp, di, &dr);
break;
case DDR_INUSE:
err = write_inuse_diffs(ofp, di, &dr);
break;
default:
di->zerr = EPIPE;
break;
}
if (err || di->zerr)
break;
}
(void) fclose(ofp);
(void) close(di->datafd);
if (err)
return ((void *)-1);
if (di->zerr) {
ASSERT(di->zerr == EINVAL);
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"Internal error: bad data from diff IOCTL"));
return ((void *)-1);
}
return ((void *)0);
}
static int
find_shares_object(differ_info_t *di)
{
char fullpath[MAXPATHLEN];
struct stat64 sb = { 0 };
(void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
(void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);
if (stat64(fullpath, &sb) != 0) {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
}
di->shares = (uint64_t)sb.st_ino;
return (0);
}
static int
make_temp_snapshot(differ_info_t *di)
{
libzfs_handle_t *hdl = di->zhp->zfs_hdl;
zfs_cmd_t zc = { 0 };
(void) snprintf(zc.zc_value, sizeof (zc.zc_value),
ZDIFF_PREFIX, getpid());
(void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name));
zc.zc_cleanup_fd = di->cleanupfd;
if (ioctl(hdl->libzfs_fd, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) {
int err = errno;
if (err == EPERM) {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN, "The diff delegated "
"permission is needed in order\nto create a "
"just-in-time snapshot for diffing\n"));
return (zfs_error(hdl, EZFS_DIFF, di->errbuf));
} else {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN, "Cannot create just-in-time "
"snapshot of '%s'"), zc.zc_name);
return (zfs_standard_error(hdl, err, di->errbuf));
}
}
di->tmpsnap = zfs_strdup(hdl, zc.zc_value);
di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap);
return (0);
}
static void
teardown_differ_info(differ_info_t *di)
{
free(di->ds);
free(di->dsmnt);
free(di->fromsnap);
free(di->frommnt);
free(di->tosnap);
free(di->tmpsnap);
free(di->tomnt);
(void) close(di->cleanupfd);
}
static int
get_snapshot_names(differ_info_t *di, const char *fromsnap,
const char *tosnap)
{
libzfs_handle_t *hdl = di->zhp->zfs_hdl;
char *atptrf = NULL;
char *atptrt = NULL;
int fdslen, fsnlen;
int tdslen, tsnlen;
/*
* Can accept
* dataset@snap1
* dataset@snap1 dataset@snap2
* dataset@snap1 @snap2
* dataset@snap1 dataset
* @snap1 dataset@snap2
*/
if (tosnap == NULL) {
/* only a from snapshot given, must be valid */
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"Badly formed snapshot name %s"), fromsnap);
if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT,
B_FALSE)) {
return (zfs_error(hdl, EZFS_INVALIDNAME,
di->errbuf));
}
atptrf = strchr(fromsnap, '@');
ASSERT(atptrf != NULL);
fdslen = atptrf - fromsnap;
di->fromsnap = zfs_strdup(hdl, fromsnap);
di->ds = zfs_strdup(hdl, fromsnap);
di->ds[fdslen] = '\0';
/* the to snap will be a just-in-time snap of the head */
return (make_temp_snapshot(di));
}
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"Unable to determine which snapshots to compare"));
atptrf = strchr(fromsnap, '@');
atptrt = strchr(tosnap, '@');
fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap);
tdslen = atptrt ? atptrt - tosnap : strlen(tosnap);
fsnlen = strlen(fromsnap) - fdslen; /* includes @ sign */
tsnlen = strlen(tosnap) - tdslen; /* includes @ sign */
if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0) ||
(fsnlen == 0 && tsnlen == 0)) {
return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
} else if ((fdslen > 0 && tdslen > 0) &&
((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) {
/*
* not the same dataset name, might be okay if
* tosnap is a clone of a fromsnap descendant.
*/
char origin[ZFS_MAXNAMELEN];
zprop_source_t src;
zfs_handle_t *zhp;
di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1);
(void) strncpy(di->ds, tosnap, tdslen);
di->ds[tdslen] = '\0';
zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM);
while (zhp != NULL) {
(void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
origin, sizeof (origin), &src, NULL, 0, B_FALSE);
if (strncmp(origin, fromsnap, fsnlen) == 0)
break;
(void) zfs_close(zhp);
zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM);
}
if (zhp == NULL) {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"Not an earlier snapshot from the same fs"));
return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
} else {
(void) zfs_close(zhp);
}
di->isclone = B_TRUE;
di->fromsnap = zfs_strdup(hdl, fromsnap);
if (tsnlen) {
di->tosnap = zfs_strdup(hdl, tosnap);
} else {
return (make_temp_snapshot(di));
}
} else {
int dslen = fdslen ? fdslen : tdslen;
di->ds = zfs_alloc(hdl, dslen + 1);
(void) strncpy(di->ds, fdslen ? fromsnap : tosnap, dslen);
di->ds[dslen] = '\0';
di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf);
if (tsnlen) {
di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt);
} else {
return (make_temp_snapshot(di));
}
}
return (0);
}
static int
get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt)
{
boolean_t mounted;
mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt);
if (mounted == B_FALSE) {
(void) snprintf(di->errbuf, sizeof (di->errbuf),
dgettext(TEXT_DOMAIN,
"Cannot diff an unmounted snapshot"));
return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf));
}
/* Avoid a double slash at the beginning of root-mounted datasets */
if (**mntpt == '/' && *(*mntpt + 1) == '\0')
**mntpt = '\0';
return (0);
}
static int
get_mountpoints(differ_info_t *di)
{
char *strptr;
char *frommntpt;
/*
* first get the mountpoint for the parent dataset
*/
if (get_mountpoint(di, di->ds, &di->dsmnt) != 0)
return (-1);
strptr = strchr(di->tosnap, '@');
ASSERT3P(strptr, !=, NULL);
di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt,
ZDIFF_SNAPDIR, ++strptr);
strptr = strchr(di->fromsnap, '@');
ASSERT3P(strptr, !=, NULL);
frommntpt = di->dsmnt;
if (di->isclone) {
char *mntpt;
int err;
*strptr = '\0';
err = get_mountpoint(di, di->fromsnap, &mntpt);
*strptr = '@';
if (err != 0)
return (-1);
frommntpt = mntpt;
}
di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt,
ZDIFF_SNAPDIR, ++strptr);
if (di->isclone)
free(frommntpt);
return (0);
}
static int
setup_differ_info(zfs_handle_t *zhp, const char *fromsnap,
const char *tosnap, differ_info_t *di)
{
di->zhp = zhp;
di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL);
VERIFY(di->cleanupfd >= 0);
if (get_snapshot_names(di, fromsnap, tosnap) != 0)
return (-1);
if (get_mountpoints(di) != 0)
return (-1);
if (find_shares_object(di) != 0)
return (-1);
return (0);
}
int
zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap,
const char *tosnap, int flags)
{
zfs_cmd_t zc = { 0 };
char errbuf[1024];
differ_info_t di = { 0 };
pthread_t tid;
int pipefd[2];
int iocerr;
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "zfs diff failed"));
if (setup_differ_info(zhp, fromsnap, tosnap, &di)) {
teardown_differ_info(&di);
return (-1);
}
if (pipe(pipefd)) {
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
teardown_differ_info(&di);
return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf));
}
di.scripted = (flags & ZFS_DIFF_PARSEABLE);
di.classify = (flags & ZFS_DIFF_CLASSIFY);
di.timestamped = (flags & ZFS_DIFF_TIMESTAMP);
di.outputfd = outfd;
di.datafd = pipefd[0];
if (pthread_create(&tid, NULL, differ, &di)) {
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
(void) close(pipefd[0]);
(void) close(pipefd[1]);
teardown_differ_info(&di);
return (zfs_error(zhp->zfs_hdl,
EZFS_THREADCREATEFAILED, errbuf));
}
/* do the ioctl() */
(void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1);
(void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1);
zc.zc_cookie = pipefd[1];
iocerr = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DIFF, &zc);
if (iocerr != 0) {
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "Unable to obtain diffs"));
if (errno == EPERM) {
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"\n The sys_mount privilege or diff delegated "
"permission is needed\n to execute the "
"diff ioctl"));
} else if (errno == EXDEV) {
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"\n Not an earlier snapshot from the same fs"));
} else if (errno != EPIPE || di.zerr == 0) {
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
}
(void) close(pipefd[1]);
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
teardown_differ_info(&di);
if (di.zerr != 0 && di.zerr != EPIPE) {
zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
} else {
return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf));
}
}
(void) close(pipefd[1]);
(void) pthread_join(tid, NULL);
if (di.zerr != 0) {
zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
}
teardown_differ_info(&di);
return (0);
}

View File

@ -0,0 +1,452 @@
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <dlfcn.h>
#include <errno.h>
#include <libintl.h>
#include <link.h>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>
#include <libzfs.h>
#include <fm/libtopo.h>
#include <sys/fm/protocol.h>
#include <sys/systeminfo.h>
#include "libzfs_impl.h"
/*
* This file is responsible for determining the relationship between I/O
* devices paths and physical locations. In the world of MPxIO and external
* enclosures, the device path is not synonymous with the physical location.
* If you remove a drive and insert it into a different slot, it will end up
* with the same path under MPxIO. If you recable storage enclosures, the
* device paths may change. All of this makes it difficult to implement the
* 'autoreplace' property, which is supposed to automatically manage disk
* replacement based on physical slot.
*
* In order to work around these limitations, we have a per-vdev FRU property
* that is the libtopo path (minus disk-specific authority information) to the
* physical location of the device on the system. This is an optional
* property, and is only needed when using the 'autoreplace' property or when
* generating FMA faults against vdevs.
*/
/*
* Because the FMA packages depend on ZFS, we have to dlopen() libtopo in case
* it is not present. We only need this once per library instance, so it is
* not part of the libzfs handle.
*/
static void *_topo_dlhandle;
static topo_hdl_t *(*_topo_open)(int, const char *, int *);
static void (*_topo_close)(topo_hdl_t *);
static char *(*_topo_snap_hold)(topo_hdl_t *, const char *, int *);
static void (*_topo_snap_release)(topo_hdl_t *);
static topo_walk_t *(*_topo_walk_init)(topo_hdl_t *, const char *,
topo_walk_cb_t, void *, int *);
static int (*_topo_walk_step)(topo_walk_t *, int);
static void (*_topo_walk_fini)(topo_walk_t *);
static void (*_topo_hdl_strfree)(topo_hdl_t *, char *);
static char *(*_topo_node_name)(tnode_t *);
static int (*_topo_prop_get_string)(tnode_t *, const char *, const char *,
char **, int *);
static int (*_topo_node_fru)(tnode_t *, nvlist_t **, nvlist_t *, int *);
static int (*_topo_fmri_nvl2str)(topo_hdl_t *, nvlist_t *, char **, int *);
static int (*_topo_fmri_strcmp_noauth)(topo_hdl_t *, const char *,
const char *);
#define ZFS_FRU_HASH_SIZE 257
static size_t
fru_strhash(const char *key)
{
ulong_t g, h = 0;
const char *p;
for (p = key; *p != '\0'; p++) {
h = (h << 4) + *p;
if ((g = (h & 0xf0000000)) != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h % ZFS_FRU_HASH_SIZE);
}
static int
libzfs_fru_gather(topo_hdl_t *thp, tnode_t *tn, void *arg)
{
libzfs_handle_t *hdl = arg;
nvlist_t *fru;
char *devpath, *frustr;
int err;
libzfs_fru_t *frup;
size_t idx;
/*
* If this is the chassis node, and we don't yet have the system
* chassis ID, then fill in this value now.
*/
if (hdl->libzfs_chassis_id[0] == '\0' &&
strcmp(_topo_node_name(tn), "chassis") == 0) {
if (_topo_prop_get_string(tn, FM_FMRI_AUTHORITY,
FM_FMRI_AUTH_CHASSIS, &devpath, &err) == 0)
(void) strlcpy(hdl->libzfs_chassis_id, devpath,
sizeof (hdl->libzfs_chassis_id));
}
/*
* Skip non-disk nodes.
*/
if (strcmp(_topo_node_name(tn), "disk") != 0)
return (TOPO_WALK_NEXT);
/*
* Get the devfs path and FRU.
*/
if (_topo_prop_get_string(tn, "io", "devfs-path", &devpath, &err) != 0)
return (TOPO_WALK_NEXT);
if (libzfs_fru_lookup(hdl, devpath) != NULL) {
_topo_hdl_strfree(thp, devpath);
return (TOPO_WALK_NEXT);
}
if (_topo_node_fru(tn, &fru, NULL, &err) != 0) {
_topo_hdl_strfree(thp, devpath);
return (TOPO_WALK_NEXT);
}
/*
* Convert the FRU into a string.
*/
if (_topo_fmri_nvl2str(thp, fru, &frustr, &err) != 0) {
nvlist_free(fru);
_topo_hdl_strfree(thp, devpath);
return (TOPO_WALK_NEXT);
}
nvlist_free(fru);
/*
* Finally, we have a FRU string and device path. Add it to the hash.
*/
if ((frup = calloc(sizeof (libzfs_fru_t), 1)) == NULL) {
_topo_hdl_strfree(thp, devpath);
_topo_hdl_strfree(thp, frustr);
return (TOPO_WALK_NEXT);
}
if ((frup->zf_device = strdup(devpath)) == NULL ||
(frup->zf_fru = strdup(frustr)) == NULL) {
free(frup->zf_device);
free(frup);
_topo_hdl_strfree(thp, devpath);
_topo_hdl_strfree(thp, frustr);
return (TOPO_WALK_NEXT);
}
_topo_hdl_strfree(thp, devpath);
_topo_hdl_strfree(thp, frustr);
idx = fru_strhash(frup->zf_device);
frup->zf_chain = hdl->libzfs_fru_hash[idx];
hdl->libzfs_fru_hash[idx] = frup;
frup->zf_next = hdl->libzfs_fru_list;
hdl->libzfs_fru_list = frup;
return (TOPO_WALK_NEXT);
}
/*
* Called during initialization to setup the dynamic libtopo connection.
*/
#pragma init(libzfs_init_fru)
static void
libzfs_init_fru(void)
{
char path[MAXPATHLEN];
char isa[257];
#if defined(_LP64)
if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
isa[0] = '\0';
#else
isa[0] = '\0';
#endif
(void) snprintf(path, sizeof (path),
"/usr/lib/fm/%s/libtopo.so", isa);
if ((_topo_dlhandle = dlopen(path, RTLD_LAZY)) == NULL)
return;
_topo_open = (topo_hdl_t *(*)())
dlsym(_topo_dlhandle, "topo_open");
_topo_close = (void (*)())
dlsym(_topo_dlhandle, "topo_close");
_topo_snap_hold = (char *(*)())
dlsym(_topo_dlhandle, "topo_snap_hold");
_topo_snap_release = (void (*)())
dlsym(_topo_dlhandle, "topo_snap_release");
_topo_walk_init = (topo_walk_t *(*)())
dlsym(_topo_dlhandle, "topo_walk_init");
_topo_walk_step = (int (*)())
dlsym(_topo_dlhandle, "topo_walk_step");
_topo_walk_fini = (void (*)())
dlsym(_topo_dlhandle, "topo_walk_fini");
_topo_hdl_strfree = (void (*)())
dlsym(_topo_dlhandle, "topo_hdl_strfree");
_topo_node_name = (char *(*)())
dlsym(_topo_dlhandle, "topo_node_name");
_topo_prop_get_string = (int (*)())
dlsym(_topo_dlhandle, "topo_prop_get_string");
_topo_node_fru = (int (*)())
dlsym(_topo_dlhandle, "topo_node_fru");
_topo_fmri_nvl2str = (int (*)())
dlsym(_topo_dlhandle, "topo_fmri_nvl2str");
_topo_fmri_strcmp_noauth = (int (*)())
dlsym(_topo_dlhandle, "topo_fmri_strcmp_noauth");
if (_topo_open == NULL || _topo_close == NULL ||
_topo_snap_hold == NULL || _topo_snap_release == NULL ||
_topo_walk_init == NULL || _topo_walk_step == NULL ||
_topo_walk_fini == NULL || _topo_hdl_strfree == NULL ||
_topo_node_name == NULL || _topo_prop_get_string == NULL ||
_topo_node_fru == NULL || _topo_fmri_nvl2str == NULL ||
_topo_fmri_strcmp_noauth == NULL) {
(void) dlclose(_topo_dlhandle);
_topo_dlhandle = NULL;
}
}
/*
* Refresh the mappings from device path -> FMRI. We do this by walking the
* hc topology looking for disk nodes, and recording the io/devfs-path and FRU.
* Note that we strip out the disk-specific authority information (serial,
* part, revision, etc) so that we are left with only the identifying
* characteristics of the slot (hc path and chassis-id).
*/
void
libzfs_fru_refresh(libzfs_handle_t *hdl)
{
int err;
char *uuid;
topo_hdl_t *thp;
topo_walk_t *twp;
if (_topo_dlhandle == NULL)
return;
/*
* Clear the FRU hash and initialize our basic structures.
*/
libzfs_fru_clear(hdl, B_FALSE);
if ((hdl->libzfs_topo_hdl = _topo_open(TOPO_VERSION,
NULL, &err)) == NULL)
return;
thp = hdl->libzfs_topo_hdl;
if ((uuid = _topo_snap_hold(thp, NULL, &err)) == NULL)
return;
_topo_hdl_strfree(thp, uuid);
if (hdl->libzfs_fru_hash == NULL &&
(hdl->libzfs_fru_hash =
calloc(ZFS_FRU_HASH_SIZE * sizeof (void *), 1)) == NULL)
return;
/*
* We now have a topo snapshot, so iterate over the hc topology looking
* for disks to add to the hash.
*/
twp = _topo_walk_init(thp, FM_FMRI_SCHEME_HC,
libzfs_fru_gather, hdl, &err);
if (twp != NULL) {
(void) _topo_walk_step(twp, TOPO_WALK_CHILD);
_topo_walk_fini(twp);
}
}
/*
* Given a devfs path, return the FRU for the device, if known. This will
* automatically call libzfs_fru_refresh() if it hasn't already been called by
* the consumer. The string returned is valid until the next call to
* libzfs_fru_refresh().
*/
const char *
libzfs_fru_lookup(libzfs_handle_t *hdl, const char *devpath)
{
size_t idx = fru_strhash(devpath);
libzfs_fru_t *frup;
if (hdl->libzfs_fru_hash == NULL)
libzfs_fru_refresh(hdl);
if (hdl->libzfs_fru_hash == NULL)
return (NULL);
for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL;
frup = frup->zf_chain) {
if (strcmp(devpath, frup->zf_device) == 0)
return (frup->zf_fru);
}
return (NULL);
}
/*
* Given a fru path, return the device path. This will automatically call
* libzfs_fru_refresh() if it hasn't already been called by the consumer. The
* string returned is valid until the next call to libzfs_fru_refresh().
*/
const char *
libzfs_fru_devpath(libzfs_handle_t *hdl, const char *fru)
{
libzfs_fru_t *frup;
size_t idx;
if (hdl->libzfs_fru_hash == NULL)
libzfs_fru_refresh(hdl);
if (hdl->libzfs_fru_hash == NULL)
return (NULL);
for (idx = 0; idx < ZFS_FRU_HASH_SIZE; idx++) {
for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL;
frup = frup->zf_next) {
if (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl,
fru, frup->zf_fru))
return (frup->zf_device);
}
}
return (NULL);
}
/*
* Change the stored FRU for the given vdev.
*/
int
zpool_fru_set(zpool_handle_t *zhp, uint64_t vdev_guid, const char *fru)
{
zfs_cmd_t zc = { 0 };
(void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
(void) strncpy(zc.zc_value, fru, sizeof (zc.zc_value));
zc.zc_guid = vdev_guid;
if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SETFRU, &zc) != 0)
return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot set FRU")));
return (0);
}
/*
* Compare to two FRUs, ignoring any authority information.
*/
boolean_t
libzfs_fru_compare(libzfs_handle_t *hdl, const char *a, const char *b)
{
if (hdl->libzfs_fru_hash == NULL)
libzfs_fru_refresh(hdl);
if (hdl->libzfs_fru_hash == NULL)
return (strcmp(a, b) == 0);
return (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl, a, b));
}
/*
* This special function checks to see whether the FRU indicates it's supposed
* to be in the system chassis, but the chassis-id doesn't match. This can
* happen in a clustered case, where both head nodes have the same logical
* disk, but opening the device on the other head node is meaningless.
*/
boolean_t
libzfs_fru_notself(libzfs_handle_t *hdl, const char *fru)
{
const char *chassisid;
size_t len;
if (hdl->libzfs_fru_hash == NULL)
libzfs_fru_refresh(hdl);
if (hdl->libzfs_chassis_id[0] == '\0')
return (B_FALSE);
if (strstr(fru, "/chassis=0/") == NULL)
return (B_FALSE);
if ((chassisid = strstr(fru, ":chassis-id=")) == NULL)
return (B_FALSE);
chassisid += 12;
len = strlen(hdl->libzfs_chassis_id);
if (strncmp(chassisid, hdl->libzfs_chassis_id, len) == 0 &&
(chassisid[len] == '/' || chassisid[len] == ':'))
return (B_FALSE);
return (B_TRUE);
}
/*
* Clear memory associated with the FRU hash.
*/
void
libzfs_fru_clear(libzfs_handle_t *hdl, boolean_t final)
{
libzfs_fru_t *frup;
while ((frup = hdl->libzfs_fru_list) != NULL) {
hdl->libzfs_fru_list = frup->zf_next;
free(frup->zf_device);
free(frup->zf_fru);
free(frup);
}
hdl->libzfs_fru_list = NULL;
if (hdl->libzfs_topo_hdl != NULL) {
_topo_snap_release(hdl->libzfs_topo_hdl);
_topo_close(hdl->libzfs_topo_hdl);
hdl->libzfs_topo_hdl = NULL;
}
if (final) {
free(hdl->libzfs_fru_hash);
} else if (hdl->libzfs_fru_hash != NULL) {
bzero(hdl->libzfs_fru_hash,
ZFS_FRU_HASH_SIZE * sizeof (void *));
}
}

View File

@ -0,0 +1,653 @@
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Iterate over all children of the current object. This includes the normal
* dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
* walk all datasets in the pool, and construct a directed graph of the form:
*
* home
* |
* +----+----+
* | |
* v v ws
* bar baz |
* | |
* v v
* @yesterday ----> foo
*
* In order to construct this graph, we have to walk every dataset in the pool,
* because the clone parent is stored as a property of the child, not the
* parent. The parent only keeps track of the number of clones.
*
* In the normal case (without clones) this would be rather expensive. To avoid
* unnecessary computation, we first try a walk of the subtree hierarchy
* starting from the initial node. At each dataset, we construct a node in the
* graph and an edge leading from its parent. If we don't see any snapshots
* with a non-zero clone count, then we are finished.
*
* If we do find a cloned snapshot, then we finish the walk of the current
* subtree, but indicate that we need to do a complete walk. We then perform a
* global walk of all datasets, avoiding the subtree we already processed.
*
* At the end of this, we'll end up with a directed graph of all relevant (and
* possible some irrelevant) datasets in the system. We need to both find our
* limiting subgraph and determine a safe ordering in which to destroy the
* datasets. We do a topological ordering of our graph starting at our target
* dataset, and then walk the results in reverse.
*
* It's possible for the graph to have cycles if, for example, the user renames
* a clone to be the parent of its origin snapshot. The user can request to
* generate an error in this case, or ignore the cycle and continue.
*
* When removing datasets, we want to destroy the snapshots in chronological
* order (because this is the most efficient method). In order to accomplish
* this, we store the creation transaction group with each vertex and keep each
* vertex's edges sorted according to this value. The topological sort will
* automatically walk the snapshots in the correct order.
*/
#include <assert.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <libzfs.h>
#include "libzfs_impl.h"
#include "zfs_namecheck.h"
#define MIN_EDGECOUNT 4
/*
* Vertex structure. Indexed by dataset name, this structure maintains a list
* of edges to other vertices.
*/
struct zfs_edge;
typedef struct zfs_vertex {
char zv_dataset[ZFS_MAXNAMELEN];
struct zfs_vertex *zv_next;
int zv_visited;
uint64_t zv_txg;
struct zfs_edge **zv_edges;
int zv_edgecount;
int zv_edgealloc;
} zfs_vertex_t;
enum {
VISIT_SEEN = 1,
VISIT_SORT_PRE,
VISIT_SORT_POST
};
/*
* Edge structure. Simply maintains a pointer to the destination vertex. There
* is no need to store the source vertex, since we only use edges in the context
* of the source vertex.
*/
typedef struct zfs_edge {
zfs_vertex_t *ze_dest;
struct zfs_edge *ze_next;
} zfs_edge_t;
#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
/*
* Graph structure. Vertices are maintained in a hash indexed by dataset name.
*/
typedef struct zfs_graph {
zfs_vertex_t **zg_hash;
size_t zg_size;
size_t zg_nvertex;
const char *zg_root;
int zg_clone_count;
} zfs_graph_t;
/*
* Allocate a new edge pointing to the target vertex.
*/
static zfs_edge_t *
zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest)
{
zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t));
if (zep == NULL)
return (NULL);
zep->ze_dest = dest;
return (zep);
}
/*
* Destroy an edge.
*/
static void
zfs_edge_destroy(zfs_edge_t *zep)
{
free(zep);
}
/*
* Allocate a new vertex with the given name.
*/
static zfs_vertex_t *
zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset)
{
zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t));
if (zvp == NULL)
return (NULL);
assert(strlen(dataset) < ZFS_MAXNAMELEN);
(void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
if ((zvp->zv_edges = zfs_alloc(hdl,
MIN_EDGECOUNT * sizeof (void *))) == NULL) {
free(zvp);
return (NULL);
}
zvp->zv_edgealloc = MIN_EDGECOUNT;
return (zvp);
}
/*
* Destroy a vertex. Frees up any associated edges.
*/
static void
zfs_vertex_destroy(zfs_vertex_t *zvp)
{
int i;
for (i = 0; i < zvp->zv_edgecount; i++)
zfs_edge_destroy(zvp->zv_edges[i]);
free(zvp->zv_edges);
free(zvp);
}
/*
* Given a vertex, add an edge to the destination vertex.
*/
static int
zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp,
zfs_vertex_t *dest)
{
zfs_edge_t *zep = zfs_edge_create(hdl, dest);
if (zep == NULL)
return (-1);
if (zvp->zv_edgecount == zvp->zv_edgealloc) {
void *ptr;
if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
zvp->zv_edgealloc * sizeof (void *),
zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
return (-1);
zvp->zv_edges = ptr;
zvp->zv_edgealloc *= 2;
}
zvp->zv_edges[zvp->zv_edgecount++] = zep;
return (0);
}
static int
zfs_edge_compare(const void *a, const void *b)
{
const zfs_edge_t *ea = *((zfs_edge_t **)a);
const zfs_edge_t *eb = *((zfs_edge_t **)b);
if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
return (-1);
if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
return (1);
return (0);
}
/*
* Sort the given vertex edges according to the creation txg of each vertex.
*/
static void
zfs_vertex_sort_edges(zfs_vertex_t *zvp)
{
if (zvp->zv_edgecount == 0)
return;
qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
zfs_edge_compare);
}
/*
* Construct a new graph object. We allow the size to be specified as a
* parameter so in the future we can size the hash according to the number of
* datasets in the pool.
*/
static zfs_graph_t *
zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
{
zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
if (zgp == NULL)
return (NULL);
zgp->zg_size = size;
if ((zgp->zg_hash = zfs_alloc(hdl,
size * sizeof (zfs_vertex_t *))) == NULL) {
free(zgp);
return (NULL);
}
zgp->zg_root = dataset;
zgp->zg_clone_count = 0;
return (zgp);
}
/*
* Destroy a graph object. We have to iterate over all the hash chains,
* destroying each vertex in the process.
*/
static void
zfs_graph_destroy(zfs_graph_t *zgp)
{
int i;
zfs_vertex_t *current, *next;
for (i = 0; i < zgp->zg_size; i++) {
current = zgp->zg_hash[i];
while (current != NULL) {
next = current->zv_next;
zfs_vertex_destroy(current);
current = next;
}
}
free(zgp->zg_hash);
free(zgp);
}
/*
* Graph hash function. Classic bernstein k=33 hash function, taken from
* usr/src/cmd/sgs/tools/common/strhash.c
*/
static size_t
zfs_graph_hash(zfs_graph_t *zgp, const char *str)
{
size_t hash = 5381;
int c;
while ((c = *str++) != 0)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return (hash % zgp->zg_size);
}
/*
* Given a dataset name, finds the associated vertex, creating it if necessary.
*/
static zfs_vertex_t *
zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset,
uint64_t txg)
{
size_t idx = zfs_graph_hash(zgp, dataset);
zfs_vertex_t *zvp;
for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
if (strcmp(zvp->zv_dataset, dataset) == 0) {
if (zvp->zv_txg == 0)
zvp->zv_txg = txg;
return (zvp);
}
}
if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL)
return (NULL);
zvp->zv_next = zgp->zg_hash[idx];
zvp->zv_txg = txg;
zgp->zg_hash[idx] = zvp;
zgp->zg_nvertex++;
return (zvp);
}
/*
* Given two dataset names, create an edge between them. For the source vertex,
* mark 'zv_visited' to indicate that we have seen this vertex, and not simply
* created it as a destination of another edge. If 'dest' is NULL, then this
* is an individual vertex (i.e. the starting vertex), so don't add an edge.
*/
static int
zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
const char *dest, uint64_t txg)
{
zfs_vertex_t *svp, *dvp;
if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
return (-1);
svp->zv_visited = VISIT_SEEN;
if (dest != NULL) {
dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
if (dvp == NULL)
return (-1);
if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
return (-1);
}
return (0);
}
/*
* Iterate over all children of the given dataset, adding any vertices
* as necessary. Returns -1 if there was an error, or 0 otherwise.
* This is a simple recursive algorithm - the ZFS namespace typically
* is very flat. We manually invoke the necessary ioctl() calls to
* avoid the overhead and additional semantics of zfs_open().
*/
static int
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
zfs_cmd_t zc = { 0 };
zfs_vertex_t *zvp;
/*
* Look up the source vertex, and avoid it if we've seen it before.
*/
zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
if (zvp == NULL)
return (-1);
if (zvp->zv_visited == VISIT_SEEN)
return (0);
/*
* Iterate over all children
*/
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
/*
* Get statistics for this dataset, to determine the type of the
* dataset and clone statistics. If this fails, the dataset has
* since been removed, and we're pretty much screwed anyway.
*/
zc.zc_objset_stats.dds_origin[0] = '\0';
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
continue;
if (zc.zc_objset_stats.dds_origin[0] != '\0') {
if (zfs_graph_add(hdl, zgp,
zc.zc_objset_stats.dds_origin, zc.zc_name,
zc.zc_objset_stats.dds_creation_txg) != 0)
return (-1);
/*
* Count origins only if they are contained in the graph
*/
if (isa_child_of(zc.zc_objset_stats.dds_origin,
zgp->zg_root))
zgp->zg_clone_count--;
}
/*
* Add an edge between the parent and the child.
*/
if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
zc.zc_objset_stats.dds_creation_txg) != 0)
return (-1);
/*
* Recursively visit child
*/
if (iterate_children(hdl, zgp, zc.zc_name))
return (-1);
}
/*
* Now iterate over all snapshots.
*/
bzero(&zc, sizeof (zc));
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
/*
* Get statistics for this dataset, to determine the type of the
* dataset and clone statistics. If this fails, the dataset has
* since been removed, and we're pretty much screwed anyway.
*/
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
continue;
/*
* Add an edge between the parent and the child.
*/
if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
zc.zc_objset_stats.dds_creation_txg) != 0)
return (-1);
zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
}
zvp->zv_visited = VISIT_SEEN;
return (0);
}
/*
* Returns false if there are no snapshots with dependent clones in this
* subtree or if all of those clones are also in this subtree. Returns
* true if there is an error or there are external dependents.
*/
static boolean_t
external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
zfs_cmd_t zc = { 0 };
/*
* Check whether this dataset is a clone or has clones since
* iterate_children() only checks the children.
*/
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
return (B_TRUE);
if (zc.zc_objset_stats.dds_origin[0] != '\0') {
if (zfs_graph_add(hdl, zgp,
zc.zc_objset_stats.dds_origin, zc.zc_name,
zc.zc_objset_stats.dds_creation_txg) != 0)
return (B_TRUE);
if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
zgp->zg_clone_count--;
}
if ((zc.zc_objset_stats.dds_num_clones) ||
iterate_children(hdl, zgp, dataset))
return (B_TRUE);
return (zgp->zg_clone_count != 0);
}
/*
* Construct a complete graph of all necessary vertices. First, iterate over
* only our object's children. If no cloned snapshots are found, or all of
* the cloned snapshots are in this subtree then return a graph of the subtree.
* Otherwise, start at the root of the pool and iterate over all datasets.
*/
static zfs_graph_t *
construct_graph(libzfs_handle_t *hdl, const char *dataset)
{
zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
int ret = 0;
if (zgp == NULL)
return (zgp);
if ((strchr(dataset, '/') == NULL) ||
(external_dependents(hdl, zgp, dataset))) {
/*
* Determine pool name and try again.
*/
int len = strcspn(dataset, "/@") + 1;
char *pool = zfs_alloc(hdl, len);
if (pool == NULL) {
zfs_graph_destroy(zgp);
return (NULL);
}
(void) strlcpy(pool, dataset, len);
if (iterate_children(hdl, zgp, pool) == -1 ||
zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
free(pool);
zfs_graph_destroy(zgp);
return (NULL);
}
free(pool);
}
if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
zfs_graph_destroy(zgp);
return (NULL);
}
return (zgp);
}
/*
* Given a graph, do a recursive topological sort into the given array. This is
* really just a depth first search, so that the deepest nodes appear first.
* hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
*/
static int
topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
size_t *idx, zfs_vertex_t *zgv)
{
int i;
if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
/*
* If we've already seen this vertex as part of our depth-first
* search, then we have a cyclic dependency, and we must return
* an error.
*/
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"recursive dependency at '%s'"),
zgv->zv_dataset);
return (zfs_error(hdl, EZFS_RECURSIVE,
dgettext(TEXT_DOMAIN,
"cannot determine dependent datasets")));
} else if (zgv->zv_visited >= VISIT_SORT_PRE) {
/*
* If we've already processed this as part of the topological
* sort, then don't bother doing so again.
*/
return (0);
}
zgv->zv_visited = VISIT_SORT_PRE;
/* avoid doing a search if we don't have to */
zfs_vertex_sort_edges(zgv);
for (i = 0; i < zgv->zv_edgecount; i++) {
if (topo_sort(hdl, allowrecursion, result, idx,
zgv->zv_edges[i]->ze_dest) != 0)
return (-1);
}
/* we may have visited this in the course of the above */
if (zgv->zv_visited == VISIT_SORT_POST)
return (0);
if ((result[*idx] = zfs_alloc(hdl,
strlen(zgv->zv_dataset) + 1)) == NULL)
return (-1);
(void) strcpy(result[*idx], zgv->zv_dataset);
*idx += 1;
zgv->zv_visited = VISIT_SORT_POST;
return (0);
}
/*
* The only public interface for this file. Do the dirty work of constructing a
* child list for the given object. Construct the graph, do the toplogical
* sort, and then return the array of strings to the caller.
*
* The 'allowrecursion' parameter controls behavior when cycles are found. If
* it is set, the the cycle is ignored and the results returned as if the cycle
* did not exist. If it is not set, then the routine will generate an error if
* a cycle is found.
*/
int
get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
const char *dataset, char ***result, size_t *count)
{
zfs_graph_t *zgp;
zfs_vertex_t *zvp;
if ((zgp = construct_graph(hdl, dataset)) == NULL)
return (-1);
if ((*result = zfs_alloc(hdl,
zgp->zg_nvertex * sizeof (char *))) == NULL) {
zfs_graph_destroy(zgp);
return (-1);
}
if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
free(*result);
zfs_graph_destroy(zgp);
return (-1);
}
*count = 0;
if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
free(*result);
zfs_graph_destroy(zgp);
return (-1);
}
/*
* Get rid of the last entry, which is our starting vertex and not
* strictly a dependent.
*/
assert(*count > 0);
free((*result)[*count - 1]);
(*count)--;
zfs_graph_destroy(zgp);
return (0);
}

View File

@ -0,0 +1,214 @@
/*
* CDDL HEADER SART
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _LIBFS_IMPL_H
#define _LIBFS_IMPL_H
#include <sys/dmu.h>
#include <sys/fs/zfs.h>
#include <sys/zfs_ioctl.h>
#include <sys/spa.h>
#include <sys/nvpair.h>
#include <libuutil.h>
#include <libzfs.h>
#include <libshare.h>
#include <fm/libtopo.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef VERIFY
#undef VERIFY
#endif
#define VERIFY verify
typedef struct libzfs_fru {
char *zf_device;
char *zf_fru;
struct libzfs_fru *zf_chain;
struct libzfs_fru *zf_next;
} libzfs_fru_t;
struct libzfs_handle {
int libzfs_error;
int libzfs_fd;
FILE *libzfs_mnttab;
FILE *libzfs_sharetab;
zpool_handle_t *libzfs_pool_handles;
uu_avl_pool_t *libzfs_ns_avlpool;
uu_avl_t *libzfs_ns_avl;
uint64_t libzfs_ns_gen;
int libzfs_desc_active;
char libzfs_action[1024];
char libzfs_desc[1024];
char *libzfs_log_str;
int libzfs_printerr;
int libzfs_storeerr; /* stuff error messages into buffer */
void *libzfs_sharehdl; /* libshare handle */
uint_t libzfs_shareflags;
boolean_t libzfs_mnttab_enable;
avl_tree_t libzfs_mnttab_cache;
int libzfs_pool_iter;
topo_hdl_t *libzfs_topo_hdl;
libzfs_fru_t **libzfs_fru_hash;
libzfs_fru_t *libzfs_fru_list;
char libzfs_chassis_id[256];
};
#define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */
struct zfs_handle {
libzfs_handle_t *zfs_hdl;
zpool_handle_t *zpool_hdl;
char zfs_name[ZFS_MAXNAMELEN];
zfs_type_t zfs_type; /* type including snapshot */
zfs_type_t zfs_head_type; /* type excluding snapshot */
dmu_objset_stats_t zfs_dmustats;
nvlist_t *zfs_props;
nvlist_t *zfs_user_props;
nvlist_t *zfs_recvd_props;
boolean_t zfs_mntcheck;
char *zfs_mntopts;
uint8_t *zfs_props_table;
};
/*
* This is different from checking zfs_type, because it will also catch
* snapshots of volumes.
*/
#define ZFS_IS_VOLUME(zhp) ((zhp)->zfs_head_type == ZFS_TYPE_VOLUME)
struct zpool_handle {
libzfs_handle_t *zpool_hdl;
zpool_handle_t *zpool_next;
char zpool_name[ZPOOL_MAXNAMELEN];
int zpool_state;
size_t zpool_config_size;
nvlist_t *zpool_config;
nvlist_t *zpool_old_config;
nvlist_t *zpool_props;
diskaddr_t zpool_start_block;
};
typedef enum {
PROTO_NFS = 0,
PROTO_SMB = 1,
PROTO_END = 2
} zfs_share_proto_t;
/*
* The following can be used as a bitmask and any new values
* added must preserve that capability.
*/
typedef enum {
SHARED_NOT_SHARED = 0x0,
SHARED_NFS = 0x2,
SHARED_SMB = 0x4
} zfs_share_type_t;
int zfs_error(libzfs_handle_t *, int, const char *);
int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
void zfs_error_aux(libzfs_handle_t *, const char *, ...);
void *zfs_alloc(libzfs_handle_t *, size_t);
void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t);
char *zfs_asprintf(libzfs_handle_t *, const char *, ...);
char *zfs_strdup(libzfs_handle_t *, const char *);
int no_memory(libzfs_handle_t *);
int zfs_standard_error(libzfs_handle_t *, int, const char *);
int zfs_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int zpool_standard_error(libzfs_handle_t *, int, const char *);
int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
nvlist_t *, char **, uint64_t *, const char *);
int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
zfs_type_t type);
/*
* Use this changelist_gather() flag to force attempting mounts
* on each change node regardless of whether or not it is currently
* mounted.
*/
#define CL_GATHER_MOUNT_ALWAYS 1
typedef struct prop_changelist prop_changelist_t;
int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t);
int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
int zcmd_write_conf_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *);
int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **);
void zcmd_free_nvlists(zfs_cmd_t *);
int changelist_prefix(prop_changelist_t *);
int changelist_postfix(prop_changelist_t *);
void changelist_rename(prop_changelist_t *, const char *, const char *);
void changelist_remove(prop_changelist_t *, const char *);
void changelist_free(prop_changelist_t *);
prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int, int);
int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *);
int changelist_haszonedchild(prop_changelist_t *);
void remove_mountpoint(zfs_handle_t *);
int create_parents(libzfs_handle_t *, char *, int);
boolean_t isa_child_of(const char *dataset, const char *parent);
zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
boolean_t modifying);
void namespace_clear(libzfs_handle_t *);
/*
* libshare (sharemgr) interfaces used internally.
*/
extern int zfs_init_libshare(libzfs_handle_t *, int);
extern void zfs_uninit_libshare(libzfs_handle_t *);
extern int zfs_parse_options(char *, zfs_share_proto_t);
extern int zfs_unshare_proto(zfs_handle_t *,
const char *, zfs_share_proto_t *);
extern void libzfs_fru_clear(libzfs_handle_t *, boolean_t);
#ifdef __cplusplus
}
#endif
#endif /* _LIBFS_IMPL_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,398 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* This file contains the functions which analyze the status of a pool. This
* include both the status of an active pool, as well as the status exported
* pools. Returns one of the ZPOOL_STATUS_* defines describing the status of
* the pool. This status is independent (to a certain degree) from the state of
* the pool. A pool's state describes only whether or not it is capable of
* providing the necessary fault tolerance for data. The status describes the
* overall status of devices. A pool that is online can still have a device
* that is experiencing errors.
*
* Only a subset of the possible faults can be detected using 'zpool status',
* and not all possible errors correspond to a FMA message ID. The explanation
* is left up to the caller, depending on whether it is a live pool or an
* import.
*/
#include <libzfs.h>
#include <string.h>
#include <unistd.h>
#include "libzfs_impl.h"
/*
* Message ID table. This must be kept in sync with the ZPOOL_STATUS_* defines
* in libzfs.h. Note that there are some status results which go past the end
* of this table, and hence have no associated message ID.
*/
static char *zfs_msgid_table[] = {
"ZFS-8000-14",
"ZFS-8000-2Q",
"ZFS-8000-3C",
"ZFS-8000-4J",
"ZFS-8000-5E",
"ZFS-8000-6X",
"ZFS-8000-72",
"ZFS-8000-8A",
"ZFS-8000-9P",
"ZFS-8000-A5",
"ZFS-8000-EY",
"ZFS-8000-HC",
"ZFS-8000-JQ",
"ZFS-8000-K4",
};
#define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))
/* ARGSUSED */
static int
vdev_missing(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_CANT_OPEN &&
aux == VDEV_AUX_OPEN_FAILED);
}
/* ARGSUSED */
static int
vdev_faulted(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_FAULTED);
}
/* ARGSUSED */
static int
vdev_errors(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_DEGRADED || errs != 0);
}
/* ARGSUSED */
static int
vdev_broken(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_CANT_OPEN);
}
/* ARGSUSED */
static int
vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_OFFLINE);
}
/* ARGSUSED */
static int
vdev_removed(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_REMOVED);
}
/*
* Detect if any leaf devices that have seen errors or could not be opened.
*/
static boolean_t
find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t))
{
nvlist_t **child;
vdev_stat_t *vs;
uint_t c, children;
char *type;
/*
* Ignore problems within a 'replacing' vdev, since we're presumably in
* the process of repairing any such errors, and don't want to call them
* out again. We'll pick up the fact that a resilver is happening
* later.
*/
verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0);
if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
return (B_FALSE);
if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,
&children) == 0) {
for (c = 0; c < children; c++)
if (find_vdev_problem(child[c], func))
return (B_TRUE);
} else {
verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS,
(uint64_t **)&vs, &c) == 0);
if (func(vs->vs_state, vs->vs_aux,
vs->vs_read_errors +
vs->vs_write_errors +
vs->vs_checksum_errors))
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Active pool health status.
*
* To determine the status for a pool, we make several passes over the config,
* picking the most egregious error we find. In order of importance, we do the
* following:
*
* - Check for a complete and valid configuration
* - Look for any faulted or missing devices in a non-replicated config
* - Check for any data errors
* - Check for any faulted or missing devices in a replicated config
* - Look for any devices showing errors
* - Check for any resilvering devices
*
* There can obviously be multiple errors within a single pool, so this routine
* only picks the most damaging of all the current errors to report.
*/
static zpool_status_t
check_status(nvlist_t *config, boolean_t isimport)
{
nvlist_t *nvroot;
vdev_stat_t *vs;
pool_scan_stat_t *ps = NULL;
uint_t vsc, psc;
uint64_t nerr;
uint64_t version;
uint64_t stateval;
uint64_t suspended;
uint64_t hostid = 0;
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
&version) == 0);
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
(uint64_t **)&vs, &vsc) == 0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
&stateval) == 0);
/*
* Currently resilvering a vdev
*/
(void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS,
(uint64_t **)&ps, &psc);
if (ps && ps->pss_func == POOL_SCAN_RESILVER &&
ps->pss_state == DSS_SCANNING)
return (ZPOOL_STATUS_RESILVERING);
/*
* Pool last accessed by another system.
*/
(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);
if (hostid != 0 && (unsigned long)hostid != gethostid() &&
stateval == POOL_STATE_ACTIVE)
return (ZPOOL_STATUS_HOSTID_MISMATCH);
/*
* Newer on-disk version.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_VERSION_NEWER)
return (ZPOOL_STATUS_VERSION_NEWER);
/*
* Check that the config is complete.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_BAD_GUID_SUM)
return (ZPOOL_STATUS_BAD_GUID_SUM);
/*
* Check whether the pool has suspended due to failed I/O.
*/
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,
&suspended) == 0) {
if (suspended == ZIO_FAILURE_MODE_CONTINUE)
return (ZPOOL_STATUS_IO_FAILURE_CONTINUE);
return (ZPOOL_STATUS_IO_FAILURE_WAIT);
}
/*
* Could not read a log.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_BAD_LOG) {
return (ZPOOL_STATUS_BAD_LOG);
}
/*
* Bad devices in non-replicated config.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
find_vdev_problem(nvroot, vdev_faulted))
return (ZPOOL_STATUS_FAULTED_DEV_NR);
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
find_vdev_problem(nvroot, vdev_missing))
return (ZPOOL_STATUS_MISSING_DEV_NR);
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
find_vdev_problem(nvroot, vdev_broken))
return (ZPOOL_STATUS_CORRUPT_LABEL_NR);
/*
* Corrupted pool metadata
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
return (ZPOOL_STATUS_CORRUPT_POOL);
/*
* Persistent data errors.
*/
if (!isimport) {
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
&nerr) == 0 && nerr != 0)
return (ZPOOL_STATUS_CORRUPT_DATA);
}
/*
* Missing devices in a replicated config.
*/
if (find_vdev_problem(nvroot, vdev_faulted))
return (ZPOOL_STATUS_FAULTED_DEV_R);
if (find_vdev_problem(nvroot, vdev_missing))
return (ZPOOL_STATUS_MISSING_DEV_R);
if (find_vdev_problem(nvroot, vdev_broken))
return (ZPOOL_STATUS_CORRUPT_LABEL_R);
/*
* Devices with errors
*/
if (!isimport && find_vdev_problem(nvroot, vdev_errors))
return (ZPOOL_STATUS_FAILING_DEV);
/*
* Offlined devices
*/
if (find_vdev_problem(nvroot, vdev_offlined))
return (ZPOOL_STATUS_OFFLINE_DEV);
/*
* Removed device
*/
if (find_vdev_problem(nvroot, vdev_removed))
return (ZPOOL_STATUS_REMOVED_DEV);
/*
* Outdated, but usable, version
*/
if (version < SPA_VERSION)
return (ZPOOL_STATUS_VERSION_OLDER);
return (ZPOOL_STATUS_OK);
}
zpool_status_t
zpool_get_status(zpool_handle_t *zhp, char **msgid)
{
zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE);
if (ret >= NMSGID)
*msgid = NULL;
else
*msgid = zfs_msgid_table[ret];
return (ret);
}
zpool_status_t
zpool_import_status(nvlist_t *config, char **msgid)
{
zpool_status_t ret = check_status(config, B_TRUE);
if (ret >= NMSGID)
*msgid = NULL;
else
*msgid = zfs_msgid_table[ret];
return (ret);
}
static void
dump_ddt_stat(const ddt_stat_t *dds, int h)
{
char refcnt[6];
char blocks[6], lsize[6], psize[6], dsize[6];
char ref_blocks[6], ref_lsize[6], ref_psize[6], ref_dsize[6];
if (dds == NULL || dds->dds_blocks == 0)
return;
if (h == -1)
(void) strcpy(refcnt, "Total");
else
zfs_nicenum(1ULL << h, refcnt, sizeof (refcnt));
zfs_nicenum(dds->dds_blocks, blocks, sizeof (blocks));
zfs_nicenum(dds->dds_lsize, lsize, sizeof (lsize));
zfs_nicenum(dds->dds_psize, psize, sizeof (psize));
zfs_nicenum(dds->dds_dsize, dsize, sizeof (dsize));
zfs_nicenum(dds->dds_ref_blocks, ref_blocks, sizeof (ref_blocks));
zfs_nicenum(dds->dds_ref_lsize, ref_lsize, sizeof (ref_lsize));
zfs_nicenum(dds->dds_ref_psize, ref_psize, sizeof (ref_psize));
zfs_nicenum(dds->dds_ref_dsize, ref_dsize, sizeof (ref_dsize));
(void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n",
refcnt,
blocks, lsize, psize, dsize,
ref_blocks, ref_lsize, ref_psize, ref_dsize);
}
/*
* Print the DDT histogram and the column totals.
*/
void
zpool_dump_ddt(const ddt_stat_t *dds_total, const ddt_histogram_t *ddh)
{
int h;
(void) printf("\n");
(void) printf("bucket "
" allocated "
" referenced \n");
(void) printf("______ "
"______________________________ "
"______________________________\n");
(void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n",
"refcnt",
"blocks", "LSIZE", "PSIZE", "DSIZE",
"blocks", "LSIZE", "PSIZE", "DSIZE");
(void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n",
"------",
"------", "-----", "-----", "-----",
"------", "-----", "-----", "-----");
for (h = 0; h < 64; h++)
dump_ddt_stat(&ddh->ddh_stat[h], h);
dump_ddt_stat(dds_total, -1);
(void) printf("\n");
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,981 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <assert.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <sys/spa.h>
#include <sys/stat.h>
#include <sys/processor.h>
#include <sys/zfs_context.h>
#include <sys/zmod.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
/*
* Emulation of kernel services in userland.
*/
int aok;
uint64_t physmem;
vnode_t *rootdir = (vnode_t *)0xabcd1234;
char hw_serial[HW_HOSTID_LEN];
struct utsname utsname = {
"userland", "libzpool", "1", "1", "na"
};
/* this only exists to have its address taken */
struct proc p0;
/*
* =========================================================================
* threads
* =========================================================================
*/
/*ARGSUSED*/
kthread_t *
zk_thread_create(void (*func)(), void *arg)
{
thread_t tid;
VERIFY(thr_create(0, 0, (void *(*)(void *))func, arg, THR_DETACHED,
&tid) == 0);
return ((void *)(uintptr_t)tid);
}
/*
* =========================================================================
* kstats
* =========================================================================
*/
/*ARGSUSED*/
kstat_t *
kstat_create(char *module, int instance, char *name, char *class,
uchar_t type, ulong_t ndata, uchar_t ks_flag)
{
return (NULL);
}
/*ARGSUSED*/
void
kstat_install(kstat_t *ksp)
{}
/*ARGSUSED*/
void
kstat_delete(kstat_t *ksp)
{}
/*
* =========================================================================
* mutexes
* =========================================================================
*/
void
zmutex_init(kmutex_t *mp)
{
mp->m_owner = NULL;
mp->initialized = B_TRUE;
(void) _mutex_init(&mp->m_lock, USYNC_THREAD, NULL);
}
void
zmutex_destroy(kmutex_t *mp)
{
ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner == NULL);
(void) _mutex_destroy(&(mp)->m_lock);
mp->m_owner = (void *)-1UL;
mp->initialized = B_FALSE;
}
void
mutex_enter(kmutex_t *mp)
{
ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner != (void *)-1UL);
ASSERT(mp->m_owner != curthread);
VERIFY(mutex_lock(&mp->m_lock) == 0);
ASSERT(mp->m_owner == NULL);
mp->m_owner = curthread;
}
int
mutex_tryenter(kmutex_t *mp)
{
ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner != (void *)-1UL);
if (0 == mutex_trylock(&mp->m_lock)) {
ASSERT(mp->m_owner == NULL);
mp->m_owner = curthread;
return (1);
} else {
return (0);
}
}
void
mutex_exit(kmutex_t *mp)
{
ASSERT(mp->initialized == B_TRUE);
ASSERT(mutex_owner(mp) == curthread);
mp->m_owner = NULL;
VERIFY(mutex_unlock(&mp->m_lock) == 0);
}
void *
mutex_owner(kmutex_t *mp)
{
ASSERT(mp->initialized == B_TRUE);
return (mp->m_owner);
}
/*
* =========================================================================
* rwlocks
* =========================================================================
*/
/*ARGSUSED*/
void
rw_init(krwlock_t *rwlp, char *name, int type, void *arg)
{
rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL);
rwlp->rw_owner = NULL;
rwlp->initialized = B_TRUE;
}
void
rw_destroy(krwlock_t *rwlp)
{
rwlock_destroy(&rwlp->rw_lock);
rwlp->rw_owner = (void *)-1UL;
rwlp->initialized = B_FALSE;
}
void
rw_enter(krwlock_t *rwlp, krw_t rw)
{
ASSERT(!RW_LOCK_HELD(rwlp));
ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
ASSERT(rwlp->rw_owner != curthread);
if (rw == RW_READER)
VERIFY(rw_rdlock(&rwlp->rw_lock) == 0);
else
VERIFY(rw_wrlock(&rwlp->rw_lock) == 0);
rwlp->rw_owner = curthread;
}
void
rw_exit(krwlock_t *rwlp)
{
ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
rwlp->rw_owner = NULL;
VERIFY(rw_unlock(&rwlp->rw_lock) == 0);
}
int
rw_tryenter(krwlock_t *rwlp, krw_t rw)
{
int rv;
ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
if (rw == RW_READER)
rv = rw_tryrdlock(&rwlp->rw_lock);
else
rv = rw_trywrlock(&rwlp->rw_lock);
if (rv == 0) {
rwlp->rw_owner = curthread;
return (1);
}
return (0);
}
/*ARGSUSED*/
int
rw_tryupgrade(krwlock_t *rwlp)
{
ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
return (0);
}
/*
* =========================================================================
* condition variables
* =========================================================================
*/
/*ARGSUSED*/
void
cv_init(kcondvar_t *cv, char *name, int type, void *arg)
{
VERIFY(cond_init(cv, type, NULL) == 0);
}
void
cv_destroy(kcondvar_t *cv)
{
VERIFY(cond_destroy(cv) == 0);
}
void
cv_wait(kcondvar_t *cv, kmutex_t *mp)
{
ASSERT(mutex_owner(mp) == curthread);
mp->m_owner = NULL;
int ret = cond_wait(cv, &mp->m_lock);
VERIFY(ret == 0 || ret == EINTR);
mp->m_owner = curthread;
}
clock_t
cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime)
{
int error;
timestruc_t ts;
clock_t delta;
top:
delta = abstime - ddi_get_lbolt();
if (delta <= 0)
return (-1);
ts.tv_sec = delta / hz;
ts.tv_nsec = (delta % hz) * (NANOSEC / hz);
ASSERT(mutex_owner(mp) == curthread);
mp->m_owner = NULL;
error = cond_reltimedwait(cv, &mp->m_lock, &ts);
mp->m_owner = curthread;
if (error == ETIME)
return (-1);
if (error == EINTR)
goto top;
ASSERT(error == 0);
return (1);
}
void
cv_signal(kcondvar_t *cv)
{
VERIFY(cond_signal(cv) == 0);
}
void
cv_broadcast(kcondvar_t *cv)
{
VERIFY(cond_broadcast(cv) == 0);
}
/*
* =========================================================================
* vnode operations
* =========================================================================
*/
/*
* Note: for the xxxat() versions of these functions, we assume that the
* starting vp is always rootdir (which is true for spa_directory.c, the only
* ZFS consumer of these interfaces). We assert this is true, and then emulate
* them by adding '/' in front of the path.
*/
/*ARGSUSED*/
int
vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
{
int fd;
vnode_t *vp;
int old_umask;
char realpath[MAXPATHLEN];
struct stat64 st;
/*
* If we're accessing a real disk from userland, we need to use
* the character interface to avoid caching. This is particularly
* important if we're trying to look at a real in-kernel storage
* pool from userland, e.g. via zdb, because otherwise we won't
* see the changes occurring under the segmap cache.
* On the other hand, the stupid character device returns zero
* for its size. So -- gag -- we open the block device to get
* its size, and remember it for subsequent VOP_GETATTR().
*/
if (strncmp(path, "/dev/", 5) == 0) {
char *dsk;
fd = open64(path, O_RDONLY);
if (fd == -1)
return (errno);
if (fstat64(fd, &st) == -1) {
close(fd);
return (errno);
}
close(fd);
(void) sprintf(realpath, "%s", path);
dsk = strstr(path, "/dsk/");
if (dsk != NULL)
(void) sprintf(realpath + (dsk - path) + 1, "r%s",
dsk + 1);
} else {
(void) sprintf(realpath, "%s", path);
if (!(flags & FCREAT) && stat64(realpath, &st) == -1)
return (errno);
}
if (flags & FCREAT)
old_umask = umask(0);
/*
* The construct 'flags - FREAD' conveniently maps combinations of
* FREAD and FWRITE to the corresponding O_RDONLY, O_WRONLY, and O_RDWR.
*/
fd = open64(realpath, flags - FREAD, mode);
if (flags & FCREAT)
(void) umask(old_umask);
if (fd == -1)
return (errno);
if (fstat64(fd, &st) == -1) {
close(fd);
return (errno);
}
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
*vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
vp->v_fd = fd;
vp->v_size = st.st_size;
vp->v_path = spa_strdup(path);
return (0);
}
/*ARGSUSED*/
int
vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
int x3, vnode_t *startvp, int fd)
{
char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL);
int ret;
ASSERT(startvp == rootdir);
(void) sprintf(realpath, "/%s", path);
/* fd ignored for now, need if want to simulate nbmand support */
ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3);
umem_free(realpath, strlen(path) + 2);
return (ret);
}
/*ARGSUSED*/
int
vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp)
{
ssize_t iolen, split;
if (uio == UIO_READ) {
iolen = pread64(vp->v_fd, addr, len, offset);
} else {
/*
* To simulate partial disk writes, we split writes into two
* system calls so that the process can be killed in between.
*/
split = (len > 0 ? rand() % len : 0);
iolen = pwrite64(vp->v_fd, addr, split, offset);
iolen += pwrite64(vp->v_fd, (char *)addr + split,
len - split, offset + split);
}
if (iolen == -1)
return (errno);
if (residp)
*residp = len - iolen;
else if (iolen != len)
return (EIO);
return (0);
}
void
vn_close(vnode_t *vp)
{
close(vp->v_fd);
spa_strfree(vp->v_path);
umem_free(vp, sizeof (vnode_t));
}
/*
* At a minimum we need to update the size since vdev_reopen()
* will no longer call vn_openat().
*/
int
fop_getattr(vnode_t *vp, vattr_t *vap)
{
struct stat64 st;
if (fstat64(vp->v_fd, &st) == -1) {
close(vp->v_fd);
return (errno);
}
vap->va_size = st.st_size;
return (0);
}
#ifdef ZFS_DEBUG
/*
* =========================================================================
* Figure out which debugging statements to print
* =========================================================================
*/
static char *dprintf_string;
static int dprintf_print_all;
int
dprintf_find_string(const char *string)
{
char *tmp_str = dprintf_string;
int len = strlen(string);
/*
* Find out if this is a string we want to print.
* String format: file1.c,function_name1,file2.c,file3.c
*/
while (tmp_str != NULL) {
if (strncmp(tmp_str, string, len) == 0 &&
(tmp_str[len] == ',' || tmp_str[len] == '\0'))
return (1);
tmp_str = strchr(tmp_str, ',');
if (tmp_str != NULL)
tmp_str++; /* Get rid of , */
}
return (0);
}
void
dprintf_setup(int *argc, char **argv)
{
int i, j;
/*
* Debugging can be specified two ways: by setting the
* environment variable ZFS_DEBUG, or by including a
* "debug=..." argument on the command line. The command
* line setting overrides the environment variable.
*/
for (i = 1; i < *argc; i++) {
int len = strlen("debug=");
/* First look for a command line argument */
if (strncmp("debug=", argv[i], len) == 0) {
dprintf_string = argv[i] + len;
/* Remove from args */
for (j = i; j < *argc; j++)
argv[j] = argv[j+1];
argv[j] = NULL;
(*argc)--;
}
}
if (dprintf_string == NULL) {
/* Look for ZFS_DEBUG environment variable */
dprintf_string = getenv("ZFS_DEBUG");
}
/*
* Are we just turning on all debugging?
*/
if (dprintf_find_string("on"))
dprintf_print_all = 1;
}
/*
* =========================================================================
* debug printfs
* =========================================================================
*/
void
__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
{
const char *newfile;
va_list adx;
/*
* Get rid of annoying "../common/" prefix to filename.
*/
newfile = strrchr(file, '/');
if (newfile != NULL) {
newfile = newfile + 1; /* Get rid of leading / */
} else {
newfile = file;
}
if (dprintf_print_all ||
dprintf_find_string(newfile) ||
dprintf_find_string(func)) {
/* Print out just the function name if requested */
flockfile(stdout);
if (dprintf_find_string("pid"))
(void) printf("%d ", getpid());
if (dprintf_find_string("tid"))
(void) printf("%u ", thr_self());
if (dprintf_find_string("cpu"))
(void) printf("%u ", getcpuid());
if (dprintf_find_string("time"))
(void) printf("%llu ", gethrtime());
if (dprintf_find_string("long"))
(void) printf("%s, line %d: ", newfile, line);
(void) printf("%s: ", func);
va_start(adx, fmt);
(void) vprintf(fmt, adx);
va_end(adx);
funlockfile(stdout);
}
}
#endif /* ZFS_DEBUG */
/*
* =========================================================================
* cmn_err() and panic()
* =========================================================================
*/
static char ce_prefix[CE_IGNORE][10] = { "", "NOTICE: ", "WARNING: ", "" };
static char ce_suffix[CE_IGNORE][2] = { "", "\n", "\n", "" };
void
vpanic(const char *fmt, va_list adx)
{
(void) fprintf(stderr, "error: ");
(void) vfprintf(stderr, fmt, adx);
(void) fprintf(stderr, "\n");
abort(); /* think of it as a "user-level crash dump" */
}
void
panic(const char *fmt, ...)
{
va_list adx;
va_start(adx, fmt);
vpanic(fmt, adx);
va_end(adx);
}
void
vcmn_err(int ce, const char *fmt, va_list adx)
{
if (ce == CE_PANIC)
vpanic(fmt, adx);
if (ce != CE_NOTE) { /* suppress noise in userland stress testing */
(void) fprintf(stderr, "%s", ce_prefix[ce]);
(void) vfprintf(stderr, fmt, adx);
(void) fprintf(stderr, "%s", ce_suffix[ce]);
}
}
/*PRINTFLIKE2*/
void
cmn_err(int ce, const char *fmt, ...)
{
va_list adx;
va_start(adx, fmt);
vcmn_err(ce, fmt, adx);
va_end(adx);
}
/*
* =========================================================================
* kobj interfaces
* =========================================================================
*/
struct _buf *
kobj_open_file(char *name)
{
struct _buf *file;
vnode_t *vp;
/* set vp as the _fd field of the file */
if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir,
-1) != 0)
return ((void *)-1UL);
file = umem_zalloc(sizeof (struct _buf), UMEM_NOFAIL);
file->_fd = (intptr_t)vp;
return (file);
}
int
kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off)
{
ssize_t resid;
vn_rdwr(UIO_READ, (vnode_t *)file->_fd, buf, size, (offset_t)off,
UIO_SYSSPACE, 0, 0, 0, &resid);
return (size - resid);
}
void
kobj_close_file(struct _buf *file)
{
vn_close((vnode_t *)file->_fd);
umem_free(file, sizeof (struct _buf));
}
int
kobj_get_filesize(struct _buf *file, uint64_t *size)
{
struct stat64 st;
vnode_t *vp = (vnode_t *)file->_fd;
if (fstat64(vp->v_fd, &st) == -1) {
vn_close(vp);
return (errno);
}
*size = st.st_size;
return (0);
}
/*
* =========================================================================
* misc routines
* =========================================================================
*/
void
delay(clock_t ticks)
{
poll(0, 0, ticks * (1000 / hz));
}
/*
* Find highest one bit set.
* Returns bit number + 1 of highest bit that is set, otherwise returns 0.
* High order bit is 31 (or 63 in _LP64 kernel).
*/
int
highbit(ulong_t i)
{
register int h = 1;
if (i == 0)
return (0);
#ifdef _LP64
if (i & 0xffffffff00000000ul) {
h += 32; i >>= 32;
}
#endif
if (i & 0xffff0000) {
h += 16; i >>= 16;
}
if (i & 0xff00) {
h += 8; i >>= 8;
}
if (i & 0xf0) {
h += 4; i >>= 4;
}
if (i & 0xc) {
h += 2; i >>= 2;
}
if (i & 0x2) {
h += 1;
}
return (h);
}
static int random_fd = -1, urandom_fd = -1;
static int
random_get_bytes_common(uint8_t *ptr, size_t len, int fd)
{
size_t resid = len;
ssize_t bytes;
ASSERT(fd != -1);
while (resid != 0) {
bytes = read(fd, ptr, resid);
ASSERT3S(bytes, >=, 0);
ptr += bytes;
resid -= bytes;
}
return (0);
}
int
random_get_bytes(uint8_t *ptr, size_t len)
{
return (random_get_bytes_common(ptr, len, random_fd));
}
int
random_get_pseudo_bytes(uint8_t *ptr, size_t len)
{
return (random_get_bytes_common(ptr, len, urandom_fd));
}
int
ddi_strtoul(const char *hw_serial, char **nptr, int base, unsigned long *result)
{
char *end;
*result = strtoul(hw_serial, &end, base);
if (*result == 0)
return (errno);
return (0);
}
int
ddi_strtoull(const char *str, char **nptr, int base, u_longlong_t *result)
{
char *end;
*result = strtoull(str, &end, base);
if (*result == 0)
return (errno);
return (0);
}
/*
* =========================================================================
* kernel emulation setup & teardown
* =========================================================================
*/
static int
umem_out_of_memory(void)
{
char errmsg[] = "out of memory -- generating core dump\n";
write(fileno(stderr), errmsg, sizeof (errmsg));
abort();
return (0);
}
void
kernel_init(int mode)
{
umem_nofail_callback(umem_out_of_memory);
physmem = sysconf(_SC_PHYS_PAGES);
dprintf("physmem = %llu pages (%.2f GB)\n", physmem,
(double)physmem * sysconf(_SC_PAGE_SIZE) / (1ULL << 30));
(void) snprintf(hw_serial, sizeof (hw_serial), "%ld",
(mode & FWRITE) ? gethostid() : 0);
VERIFY((random_fd = open("/dev/random", O_RDONLY)) != -1);
VERIFY((urandom_fd = open("/dev/urandom", O_RDONLY)) != -1);
system_taskq_init();
spa_init(mode);
}
void
kernel_fini(void)
{
spa_fini();
system_taskq_fini();
close(random_fd);
close(urandom_fd);
random_fd = -1;
urandom_fd = -1;
}
int
z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen)
{
int ret;
uLongf len = *dstlen;
if ((ret = uncompress(dst, &len, src, srclen)) == Z_OK)
*dstlen = (size_t)len;
return (ret);
}
int
z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen,
int level)
{
int ret;
uLongf len = *dstlen;
if ((ret = compress2(dst, &len, src, srclen, level)) == Z_OK)
*dstlen = (size_t)len;
return (ret);
}
uid_t
crgetuid(cred_t *cr)
{
return (0);
}
gid_t
crgetgid(cred_t *cr)
{
return (0);
}
int
crgetngroups(cred_t *cr)
{
return (0);
}
gid_t *
crgetgroups(cred_t *cr)
{
return (NULL);
}
int
zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
{
return (0);
}
int
zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
{
return (0);
}
int
zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
{
return (0);
}
ksiddomain_t *
ksid_lookupdomain(const char *dom)
{
ksiddomain_t *kd;
kd = umem_zalloc(sizeof (ksiddomain_t), UMEM_NOFAIL);
kd->kd_name = spa_strdup(dom);
return (kd);
}
void
ksiddomain_rele(ksiddomain_t *ksid)
{
spa_strfree(ksid->kd_name);
umem_free(ksid, sizeof (ksiddomain_t));
}
/*
* Do not change the length of the returned string; it must be freed
* with strfree().
*/
char *
kmem_asprintf(const char *fmt, ...)
{
int size;
va_list adx;
char *buf;
va_start(adx, fmt);
size = vsnprintf(NULL, 0, fmt, adx) + 1;
va_end(adx);
buf = kmem_alloc(size, KM_SLEEP);
va_start(adx, fmt);
size = vsnprintf(buf, size, fmt, adx);
va_end(adx);
return (buf);
}
/* ARGSUSED */
int
zfs_onexit_fd_hold(int fd, minor_t *minorp)
{
*minorp = 0;
return (0);
}
/* ARGSUSED */
void
zfs_onexit_fd_rele(int fd)
{
}
/* ARGSUSED */
int
zfs_onexit_add_cb(minor_t minor, void (*func)(void *), void *data,
uint64_t *action_handle)
{
return (0);
}
/* ARGSUSED */
int
zfs_onexit_del_cb(minor_t minor, uint64_t action_handle, boolean_t fire)
{
return (0);
}
/* ARGSUSED */
int
zfs_onexit_cb_data(minor_t minor, uint64_t action_handle, void **data)
{
return (0);
}

View File

@ -0,0 +1,611 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _SYS_ZFS_CONTEXT_H
#define _SYS_ZFS_CONTEXT_H
#ifdef __cplusplus
extern "C" {
#endif
#define _SYS_MUTEX_H
#define _SYS_RWLOCK_H
#define _SYS_CONDVAR_H
#define _SYS_SYSTM_H
#define _SYS_DEBUG_H
#define _SYS_T_LOCK_H
#define _SYS_VNODE_H
#define _SYS_VFS_H
#define _SYS_SUNDDI_H
#define _SYS_CALLB_H
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <synch.h>
#include <thread.h>
#include <assert.h>
#include <alloca.h>
#include <umem.h>
#include <limits.h>
#include <atomic.h>
#include <dirent.h>
#include <time.h>
#include <libsysevent.h>
#include <sys/note.h>
#include <sys/types.h>
#include <sys/cred.h>
#include <sys/sysmacros.h>
#include <sys/bitmap.h>
#include <sys/resource.h>
#include <sys/byteorder.h>
#include <sys/list.h>
#include <sys/uio.h>
#include <sys/zfs_debug.h>
#include <sys/sdt.h>
#include <sys/kstat.h>
#include <sys/u8_textprep.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/sysevent/dev.h>
#include <sys/sunddi.h>
/*
* Debugging
*/
/*
* Note that we are not using the debugging levels.
*/
#define CE_CONT 0 /* continuation */
#define CE_NOTE 1 /* notice */
#define CE_WARN 2 /* warning */
#define CE_PANIC 3 /* panic */
#define CE_IGNORE 4 /* print nothing */
/*
* ZFS debugging
*/
#ifdef ZFS_DEBUG
extern void dprintf_setup(int *argc, char **argv);
#endif /* ZFS_DEBUG */
extern void cmn_err(int, const char *, ...);
extern void vcmn_err(int, const char *, __va_list);
extern void panic(const char *, ...);
extern void vpanic(const char *, __va_list);
#define fm_panic panic
extern int aok;
/* This definition is copied from assert.h. */
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
#define zverify(EX) (void)((EX) || (aok) || \
(__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
#else
#define zverify(EX) (void)((EX) || (aok) || \
(__assert(#EX, __FILE__, __LINE__), 0))
#endif /* __STDC_VERSION__ - 0 >= 199901L */
#else
#define zverify(EX) (void)((EX) || (aok) || \
(_assert("EX", __FILE__, __LINE__), 0))
#endif /* __STDC__ */
#define VERIFY zverify
#define ASSERT zverify
#undef assert
#define assert zverify
extern void __assert(const char *, const char *, int);
#ifdef lint
#define VERIFY3_IMPL(x, y, z, t) if (x == z) ((void)0)
#else
/* BEGIN CSTYLED */
#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE) do { \
const TYPE __left = (TYPE)(LEFT); \
const TYPE __right = (TYPE)(RIGHT); \
if (!(__left OP __right) && (!aok)) { \
char *__buf = alloca(256); \
(void) snprintf(__buf, 256, "%s %s %s (0x%llx %s 0x%llx)", \
#LEFT, #OP, #RIGHT, \
(u_longlong_t)__left, #OP, (u_longlong_t)__right); \
__assert(__buf, __FILE__, __LINE__); \
} \
_NOTE(CONSTCOND) } while (0)
/* END CSTYLED */
#endif /* lint */
#define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t)
#define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t)
#define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t)
#ifdef NDEBUG
#define ASSERT3S(x, y, z) ((void)0)
#define ASSERT3U(x, y, z) ((void)0)
#define ASSERT3P(x, y, z) ((void)0)
#else
#define ASSERT3S(x, y, z) VERIFY3S(x, y, z)
#define ASSERT3U(x, y, z) VERIFY3U(x, y, z)
#define ASSERT3P(x, y, z) VERIFY3P(x, y, z)
#endif
/*
* DTrace SDT probes have different signatures in userland than they do in
* kernel. If they're being used in kernel code, re-define them out of
* existence for their counterparts in libzpool.
*/
#ifdef DTRACE_PROBE
#undef DTRACE_PROBE
#define DTRACE_PROBE(a) ((void)0)
#endif /* DTRACE_PROBE */
#ifdef DTRACE_PROBE1
#undef DTRACE_PROBE1
#define DTRACE_PROBE1(a, b, c) ((void)0)
#endif /* DTRACE_PROBE1 */
#ifdef DTRACE_PROBE2
#undef DTRACE_PROBE2
#define DTRACE_PROBE2(a, b, c, d, e) ((void)0)
#endif /* DTRACE_PROBE2 */
#ifdef DTRACE_PROBE3
#undef DTRACE_PROBE3
#define DTRACE_PROBE3(a, b, c, d, e, f, g) ((void)0)
#endif /* DTRACE_PROBE3 */
#ifdef DTRACE_PROBE4
#undef DTRACE_PROBE4
#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0)
#endif /* DTRACE_PROBE4 */
/*
* Threads
*/
#define curthread ((void *)(uintptr_t)thr_self())
typedef struct kthread kthread_t;
#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
zk_thread_create(func, arg)
#define thread_exit() thr_exit(NULL)
#define thread_join(t) panic("libzpool cannot join threads")
#define newproc(f, a, cid, pri, ctp, pid) (ENOSYS)
/* in libzpool, p0 exists only to have its address taken */
struct proc {
uintptr_t this_is_never_used_dont_dereference_it;
};
extern struct proc p0;
#define PS_NONE -1
extern kthread_t *zk_thread_create(void (*func)(), void *arg);
#define issig(why) (FALSE)
#define ISSIG(thr, why) (FALSE)
/*
* Mutexes
*/
typedef struct kmutex {
void *m_owner;
boolean_t initialized;
mutex_t m_lock;
} kmutex_t;
#define MUTEX_DEFAULT USYNC_THREAD
#undef MUTEX_HELD
#undef MUTEX_NOT_HELD
#define MUTEX_HELD(m) _mutex_held(&(m)->m_lock)
#define MUTEX_NOT_HELD(m) (!MUTEX_HELD(m))
/*
* Argh -- we have to get cheesy here because the kernel and userland
* have different signatures for the same routine.
*/
extern int _mutex_init(mutex_t *mp, int type, void *arg);
extern int _mutex_destroy(mutex_t *mp);
#define mutex_init(mp, b, c, d) zmutex_init((kmutex_t *)(mp))
#define mutex_destroy(mp) zmutex_destroy((kmutex_t *)(mp))
extern void zmutex_init(kmutex_t *mp);
extern void zmutex_destroy(kmutex_t *mp);
extern void mutex_enter(kmutex_t *mp);
extern void mutex_exit(kmutex_t *mp);
extern int mutex_tryenter(kmutex_t *mp);
extern void *mutex_owner(kmutex_t *mp);
/*
* RW locks
*/
typedef struct krwlock {
void *rw_owner;
boolean_t initialized;
rwlock_t rw_lock;
} krwlock_t;
typedef int krw_t;
#define RW_READER 0
#define RW_WRITER 1
#define RW_DEFAULT USYNC_THREAD
#undef RW_READ_HELD
#define RW_READ_HELD(x) _rw_read_held(&(x)->rw_lock)
#undef RW_WRITE_HELD
#define RW_WRITE_HELD(x) _rw_write_held(&(x)->rw_lock)
extern void rw_init(krwlock_t *rwlp, char *name, int type, void *arg);
extern void rw_destroy(krwlock_t *rwlp);
extern void rw_enter(krwlock_t *rwlp, krw_t rw);
extern int rw_tryenter(krwlock_t *rwlp, krw_t rw);
extern int rw_tryupgrade(krwlock_t *rwlp);
extern void rw_exit(krwlock_t *rwlp);
#define rw_downgrade(rwlp) do { } while (0)
extern uid_t crgetuid(cred_t *cr);
extern gid_t crgetgid(cred_t *cr);
extern int crgetngroups(cred_t *cr);
extern gid_t *crgetgroups(cred_t *cr);
/*
* Condition variables
*/
typedef cond_t kcondvar_t;
#define CV_DEFAULT USYNC_THREAD
extern void cv_init(kcondvar_t *cv, char *name, int type, void *arg);
extern void cv_destroy(kcondvar_t *cv);
extern void cv_wait(kcondvar_t *cv, kmutex_t *mp);
extern clock_t cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime);
extern void cv_signal(kcondvar_t *cv);
extern void cv_broadcast(kcondvar_t *cv);
/*
* kstat creation, installation and deletion
*/
extern kstat_t *kstat_create(char *, int,
char *, char *, uchar_t, ulong_t, uchar_t);
extern void kstat_install(kstat_t *);
extern void kstat_delete(kstat_t *);
/*
* Kernel memory
*/
#define KM_SLEEP UMEM_NOFAIL
#define KM_PUSHPAGE KM_SLEEP
#define KM_NOSLEEP UMEM_DEFAULT
#define KMC_NODEBUG UMC_NODEBUG
#define KMC_NOTOUCH 0 /* not needed for userland caches */
#define kmem_alloc(_s, _f) umem_alloc(_s, _f)
#define kmem_zalloc(_s, _f) umem_zalloc(_s, _f)
#define kmem_free(_b, _s) umem_free(_b, _s)
#define kmem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i) \
umem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i)
#define kmem_cache_destroy(_c) umem_cache_destroy(_c)
#define kmem_cache_alloc(_c, _f) umem_cache_alloc(_c, _f)
#define kmem_cache_free(_c, _b) umem_cache_free(_c, _b)
#define kmem_debugging() 0
#define kmem_cache_reap_now(_c) /* nothing */
#define kmem_cache_set_move(_c, _cb) /* nothing */
#define POINTER_INVALIDATE(_pp) /* nothing */
#define POINTER_IS_VALID(_p) 0
typedef umem_cache_t kmem_cache_t;
typedef enum kmem_cbrc {
KMEM_CBRC_YES,
KMEM_CBRC_NO,
KMEM_CBRC_LATER,
KMEM_CBRC_DONT_NEED,
KMEM_CBRC_DONT_KNOW
} kmem_cbrc_t;
/*
* Task queues
*/
typedef struct taskq taskq_t;
typedef uintptr_t taskqid_t;
typedef void (task_func_t)(void *);
#define TASKQ_PREPOPULATE 0x0001
#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
#define TASKQ_THREADS_CPU_PCT 0x0008 /* Scale # threads by # cpus */
#define TASKQ_DC_BATCH 0x0010 /* Mark threads as batch */
#define TQ_SLEEP KM_SLEEP /* Can block for memory */
#define TQ_NOSLEEP KM_NOSLEEP /* cannot block for memory; may fail */
#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
#define TQ_FRONT 0x08 /* Queue in front */
extern taskq_t *system_taskq;
extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
#define taskq_create_proc(a, b, c, d, e, p, f) \
(taskq_create(a, b, c, d, e, f))
#define taskq_create_sysdc(a, b, d, e, p, dc, f) \
(taskq_create(a, b, maxclsyspri, d, e, f))
extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
extern void taskq_destroy(taskq_t *);
extern void taskq_wait(taskq_t *);
extern int taskq_member(taskq_t *, void *);
extern void system_taskq_init(void);
extern void system_taskq_fini(void);
#define XVA_MAPSIZE 3
#define XVA_MAGIC 0x78766174
/*
* vnodes
*/
typedef struct vnode {
uint64_t v_size;
int v_fd;
char *v_path;
} vnode_t;
#define AV_SCANSTAMP_SZ 32 /* length of anti-virus scanstamp */
typedef struct xoptattr {
timestruc_t xoa_createtime; /* Create time of file */
uint8_t xoa_archive;
uint8_t xoa_system;
uint8_t xoa_readonly;
uint8_t xoa_hidden;
uint8_t xoa_nounlink;
uint8_t xoa_immutable;
uint8_t xoa_appendonly;
uint8_t xoa_nodump;
uint8_t xoa_settable;
uint8_t xoa_opaque;
uint8_t xoa_av_quarantined;
uint8_t xoa_av_modified;
uint8_t xoa_av_scanstamp[AV_SCANSTAMP_SZ];
uint8_t xoa_reparse;
uint8_t xoa_offline;
uint8_t xoa_sparse;
} xoptattr_t;
typedef struct vattr {
uint_t va_mask; /* bit-mask of attributes */
u_offset_t va_size; /* file size in bytes */
} vattr_t;
typedef struct xvattr {
vattr_t xva_vattr; /* Embedded vattr structure */
uint32_t xva_magic; /* Magic Number */
uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */
uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */
uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */
uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */
xoptattr_t xva_xoptattrs; /* Optional attributes */
} xvattr_t;
typedef struct vsecattr {
uint_t vsa_mask; /* See below */
int vsa_aclcnt; /* ACL entry count */
void *vsa_aclentp; /* pointer to ACL entries */
int vsa_dfaclcnt; /* default ACL entry count */
void *vsa_dfaclentp; /* pointer to default ACL entries */
size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */
} vsecattr_t;
#define AT_TYPE 0x00001
#define AT_MODE 0x00002
#define AT_UID 0x00004
#define AT_GID 0x00008
#define AT_FSID 0x00010
#define AT_NODEID 0x00020
#define AT_NLINK 0x00040
#define AT_SIZE 0x00080
#define AT_ATIME 0x00100
#define AT_MTIME 0x00200
#define AT_CTIME 0x00400
#define AT_RDEV 0x00800
#define AT_BLKSIZE 0x01000
#define AT_NBLOCKS 0x02000
#define AT_SEQ 0x08000
#define AT_XVATTR 0x10000
#define CRCREAT 0
extern int fop_getattr(vnode_t *vp, vattr_t *vap);
#define VOP_CLOSE(vp, f, c, o, cr, ct) 0
#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) 0
#define VOP_GETATTR(vp, vap, fl, cr, ct) fop_getattr((vp), (vap));
#define VOP_FSYNC(vp, f, cr, ct) fsync((vp)->v_fd)
#define VN_RELE(vp) vn_close(vp)
extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp,
int x2, int x3);
extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp,
int x2, int x3, vnode_t *vp, int fd);
extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len,
offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp);
extern void vn_close(vnode_t *vp);
#define vn_remove(path, x1, x2) remove(path)
#define vn_rename(from, to, seg) rename((from), (to))
#define vn_is_readonly(vp) B_FALSE
extern vnode_t *rootdir;
#include <sys/file.h> /* for FREAD, FWRITE, etc */
/*
* Random stuff
*/
#define ddi_get_lbolt() (gethrtime() >> 23)
#define ddi_get_lbolt64() (gethrtime() >> 23)
#define hz 119 /* frequency when using gethrtime() >> 23 for lbolt */
extern void delay(clock_t ticks);
#define gethrestime_sec() time(NULL)
#define gethrestime(t) \
do {\
(t)->tv_sec = gethrestime_sec();\
(t)->tv_nsec = 0;\
} while (0);
#define max_ncpus 64
#define minclsyspri 60
#define maxclsyspri 99
#define CPU_SEQID (thr_self() & (max_ncpus - 1))
#define kcred NULL
#define CRED() NULL
#define ptob(x) ((x) * PAGESIZE)
extern uint64_t physmem;
extern int highbit(ulong_t i);
extern int random_get_bytes(uint8_t *ptr, size_t len);
extern int random_get_pseudo_bytes(uint8_t *ptr, size_t len);
extern void kernel_init(int);
extern void kernel_fini(void);
struct spa;
extern void nicenum(uint64_t num, char *buf);
extern void show_pool_stats(struct spa *);
typedef struct callb_cpr {
kmutex_t *cc_lockp;
} callb_cpr_t;
#define CALLB_CPR_INIT(cp, lockp, func, name) { \
(cp)->cc_lockp = lockp; \
}
#define CALLB_CPR_SAFE_BEGIN(cp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
}
#define CALLB_CPR_SAFE_END(cp, lockp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
}
#define CALLB_CPR_EXIT(cp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
mutex_exit((cp)->cc_lockp); \
}
#define zone_dataset_visible(x, y) (1)
#define INGLOBALZONE(z) (1)
extern char *kmem_asprintf(const char *fmt, ...);
#define strfree(str) kmem_free((str), strlen(str)+1)
/*
* Hostname information
*/
extern char hw_serial[]; /* for userland-emulated hostid access */
extern int ddi_strtoul(const char *str, char **nptr, int base,
unsigned long *result);
extern int ddi_strtoull(const char *str, char **nptr, int base,
u_longlong_t *result);
/* ZFS Boot Related stuff. */
struct _buf {
intptr_t _fd;
};
struct bootstat {
uint64_t st_size;
};
typedef struct ace_object {
uid_t a_who;
uint32_t a_access_mask;
uint16_t a_flags;
uint16_t a_type;
uint8_t a_obj_type[16];
uint8_t a_inherit_obj_type[16];
} ace_object_t;
#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
extern struct _buf *kobj_open_file(char *name);
extern int kobj_read_file(struct _buf *file, char *buf, unsigned size,
unsigned off);
extern void kobj_close_file(struct _buf *file);
extern int kobj_get_filesize(struct _buf *file, uint64_t *size);
extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
extern int zfs_secpolicy_rename_perms(const char *from, const char *to,
cred_t *cr);
extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
extern zoneid_t getzoneid(void);
/* SID stuff */
typedef struct ksiddomain {
uint_t kd_ref;
uint_t kd_len;
char *kd_name;
} ksiddomain_t;
ksiddomain_t *ksid_lookupdomain(const char *);
void ksiddomain_rele(ksiddomain_t *);
#define DDI_SLEEP KM_SLEEP
#define ddi_log_sysevent(_a, _b, _c, _d, _e, _f, _g) \
sysevent_post_event(_c, _d, _b, "libzpool", _e, _f)
#ifdef __cplusplus
}
#endif
#endif /* _SYS_ZFS_CONTEXT_H */

303
lib/libzpool/common/taskq.c Normal file
View File

@ -0,0 +1,303 @@
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/zfs_context.h>
int taskq_now;
taskq_t *system_taskq;
typedef struct task {
struct task *task_next;
struct task *task_prev;
task_func_t *task_func;
void *task_arg;
} task_t;
#define TASKQ_ACTIVE 0x00010000
struct taskq {
kmutex_t tq_lock;
krwlock_t tq_threadlock;
kcondvar_t tq_dispatch_cv;
kcondvar_t tq_wait_cv;
thread_t *tq_threadlist;
int tq_flags;
int tq_active;
int tq_nthreads;
int tq_nalloc;
int tq_minalloc;
int tq_maxalloc;
kcondvar_t tq_maxalloc_cv;
int tq_maxalloc_wait;
task_t *tq_freelist;
task_t tq_task;
};
static task_t *
task_alloc(taskq_t *tq, int tqflags)
{
task_t *t;
int rv;
again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
tq->tq_freelist = t->task_next;
} else {
if (tq->tq_nalloc >= tq->tq_maxalloc) {
if (!(tqflags & KM_SLEEP))
return (NULL);
/*
* We don't want to exceed tq_maxalloc, but we can't
* wait for other tasks to complete (and thus free up
* task structures) without risking deadlock with
* the caller. So, we just delay for one second
* to throttle the allocation rate. If we have tasks
* complete before one second timeout expires then
* taskq_ent_free will signal us and we will
* immediately retry the allocation.
*/
tq->tq_maxalloc_wait++;
rv = cv_timedwait(&tq->tq_maxalloc_cv,
&tq->tq_lock, ddi_get_lbolt() + hz);
tq->tq_maxalloc_wait--;
if (rv > 0)
goto again; /* signaled */
}
mutex_exit(&tq->tq_lock);
t = kmem_alloc(sizeof (task_t), tqflags);
mutex_enter(&tq->tq_lock);
if (t != NULL)
tq->tq_nalloc++;
}
return (t);
}
static void
task_free(taskq_t *tq, task_t *t)
{
if (tq->tq_nalloc <= tq->tq_minalloc) {
t->task_next = tq->tq_freelist;
tq->tq_freelist = t;
} else {
tq->tq_nalloc--;
mutex_exit(&tq->tq_lock);
kmem_free(t, sizeof (task_t));
mutex_enter(&tq->tq_lock);
}
if (tq->tq_maxalloc_wait)
cv_signal(&tq->tq_maxalloc_cv);
}
taskqid_t
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
{
task_t *t;
if (taskq_now) {
func(arg);
return (1);
}
mutex_enter(&tq->tq_lock);
ASSERT(tq->tq_flags & TASKQ_ACTIVE);
if ((t = task_alloc(tq, tqflags)) == NULL) {
mutex_exit(&tq->tq_lock);
return (0);
}
if (tqflags & TQ_FRONT) {
t->task_next = tq->tq_task.task_next;
t->task_prev = &tq->tq_task;
} else {
t->task_next = &tq->tq_task;
t->task_prev = tq->tq_task.task_prev;
}
t->task_next->task_prev = t;
t->task_prev->task_next = t;
t->task_func = func;
t->task_arg = arg;
cv_signal(&tq->tq_dispatch_cv);
mutex_exit(&tq->tq_lock);
return (1);
}
void
taskq_wait(taskq_t *tq)
{
mutex_enter(&tq->tq_lock);
while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
mutex_exit(&tq->tq_lock);
}
static void *
taskq_thread(void *arg)
{
taskq_t *tq = arg;
task_t *t;
mutex_enter(&tq->tq_lock);
while (tq->tq_flags & TASKQ_ACTIVE) {
if ((t = tq->tq_task.task_next) == &tq->tq_task) {
if (--tq->tq_active == 0)
cv_broadcast(&tq->tq_wait_cv);
cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
tq->tq_active++;
continue;
}
t->task_prev->task_next = t->task_next;
t->task_next->task_prev = t->task_prev;
mutex_exit(&tq->tq_lock);
rw_enter(&tq->tq_threadlock, RW_READER);
t->task_func(t->task_arg);
rw_exit(&tq->tq_threadlock);
mutex_enter(&tq->tq_lock);
task_free(tq, t);
}
tq->tq_nthreads--;
cv_broadcast(&tq->tq_wait_cv);
mutex_exit(&tq->tq_lock);
return (NULL);
}
/*ARGSUSED*/
taskq_t *
taskq_create(const char *name, int nthreads, pri_t pri,
int minalloc, int maxalloc, uint_t flags)
{
taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
int t;
if (flags & TASKQ_THREADS_CPU_PCT) {
int pct;
ASSERT3S(nthreads, >=, 0);
ASSERT3S(nthreads, <=, 100);
pct = MIN(nthreads, 100);
pct = MAX(pct, 0);
nthreads = (sysconf(_SC_NPROCESSORS_ONLN) * pct) / 100;
nthreads = MAX(nthreads, 1); /* need at least 1 thread */
} else {
ASSERT3S(nthreads, >=, 1);
}
rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL);
cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL);
cv_init(&tq->tq_maxalloc_cv, NULL, CV_DEFAULT, NULL);
tq->tq_flags = flags | TASKQ_ACTIVE;
tq->tq_active = nthreads;
tq->tq_nthreads = nthreads;
tq->tq_minalloc = minalloc;
tq->tq_maxalloc = maxalloc;
tq->tq_task.task_next = &tq->tq_task;
tq->tq_task.task_prev = &tq->tq_task;
tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
if (flags & TASKQ_PREPOPULATE) {
mutex_enter(&tq->tq_lock);
while (minalloc-- > 0)
task_free(tq, task_alloc(tq, KM_SLEEP));
mutex_exit(&tq->tq_lock);
}
for (t = 0; t < nthreads; t++)
(void) thr_create(0, 0, taskq_thread,
tq, THR_BOUND, &tq->tq_threadlist[t]);
return (tq);
}
void
taskq_destroy(taskq_t *tq)
{
int t;
int nthreads = tq->tq_nthreads;
taskq_wait(tq);
mutex_enter(&tq->tq_lock);
tq->tq_flags &= ~TASKQ_ACTIVE;
cv_broadcast(&tq->tq_dispatch_cv);
while (tq->tq_nthreads != 0)
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
tq->tq_minalloc = 0;
while (tq->tq_nalloc != 0) {
ASSERT(tq->tq_freelist != NULL);
task_free(tq, task_alloc(tq, KM_SLEEP));
}
mutex_exit(&tq->tq_lock);
for (t = 0; t < nthreads; t++)
(void) thr_join(tq->tq_threadlist[t], NULL, NULL);
kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
rw_destroy(&tq->tq_threadlock);
mutex_destroy(&tq->tq_lock);
cv_destroy(&tq->tq_dispatch_cv);
cv_destroy(&tq->tq_wait_cv);
cv_destroy(&tq->tq_maxalloc_cv);
kmem_free(tq, sizeof (taskq_t));
}
int
taskq_member(taskq_t *tq, void *t)
{
int i;
if (taskq_now)
return (1);
for (i = 0; i < tq->tq_nthreads; i++)
if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t)
return (1);
return (0);
}
void
system_taskq_init(void)
{
system_taskq = taskq_create("system_taskq", 64, minclsyspri, 4, 512,
TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
}
void
system_taskq_fini(void)
{
taskq_destroy(system_taskq);
system_taskq = NULL; /* defensive */
}

155
lib/libzpool/common/util.c Normal file
View File

@ -0,0 +1,155 @@
/*
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <assert.h>
#include <sys/zfs_context.h>
#include <sys/avl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/spa.h>
#include <sys/fs/zfs.h>
#include <sys/refcount.h>
/*
* Routines needed by more than one client of libzpool.
*/
void
nicenum(uint64_t num, char *buf)
{
uint64_t n = num;
int index = 0;
char u;
while (n >= 1024) {
n = (n + (1024 / 2)) / 1024; /* Round up or down */
index++;
}
u = " KMGTPE"[index];
if (index == 0) {
(void) sprintf(buf, "%llu", (u_longlong_t)n);
} else if (n < 10 && (num & (num - 1)) != 0) {
(void) sprintf(buf, "%.2f%c",
(double)num / (1ULL << 10 * index), u);
} else if (n < 100 && (num & (num - 1)) != 0) {
(void) sprintf(buf, "%.1f%c",
(double)num / (1ULL << 10 * index), u);
} else {
(void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
}
}
static void
show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent)
{
vdev_stat_t *vs;
vdev_stat_t v0 = { 0 };
uint64_t sec;
uint64_t is_log = 0;
nvlist_t **child;
uint_t c, children;
char used[6], avail[6];
char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6];
char *prefix = "";
if (indent == 0 && desc != NULL) {
(void) printf(" "
" capacity operations bandwidth ---- errors ----\n");
(void) printf("description "
"used avail read write read write read write cksum\n");
}
if (desc != NULL) {
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
if (is_log)
prefix = "log ";
if (nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
(uint64_t **)&vs, &c) != 0)
vs = &v0;
sec = MAX(1, vs->vs_timestamp / NANOSEC);
nicenum(vs->vs_alloc, used);
nicenum(vs->vs_space - vs->vs_alloc, avail);
nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops);
nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops);
nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes);
nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes);
nicenum(vs->vs_read_errors, rerr);
nicenum(vs->vs_write_errors, werr);
nicenum(vs->vs_checksum_errors, cerr);
(void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
indent, "",
prefix,
indent + strlen(prefix) - 25 - (vs->vs_space ? 0 : 12),
desc,
vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
rops, wops, rbytes, wbytes, rerr, werr, cerr);
}
if (nvlist_lookup_nvlist_array(nv, ctype, &child, &children) != 0)
return;
for (c = 0; c < children; c++) {
nvlist_t *cnv = child[c];
char *cname, *tname;
uint64_t np;
if (nvlist_lookup_string(cnv, ZPOOL_CONFIG_PATH, &cname) &&
nvlist_lookup_string(cnv, ZPOOL_CONFIG_TYPE, &cname))
cname = "<unknown>";
tname = calloc(1, strlen(cname) + 2);
(void) strcpy(tname, cname);
if (nvlist_lookup_uint64(cnv, ZPOOL_CONFIG_NPARITY, &np) == 0)
tname[strlen(tname)] = '0' + np;
show_vdev_stats(tname, ctype, cnv, indent + 2);
free(tname);
}
}
void
show_pool_stats(spa_t *spa)
{
nvlist_t *config, *nvroot;
char *name;
VERIFY(spa_get_stats(spa_name(spa), &config, NULL, 0) == 0);
VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
show_vdev_stats(name, ZPOOL_CONFIG_CHILDREN, nvroot, 0);
show_vdev_stats(NULL, ZPOOL_CONFIG_L2CACHE, nvroot, 0);
show_vdev_stats(NULL, ZPOOL_CONFIG_SPARES, nvroot, 0);
nvlist_free(config);
}

View File

@ -0,0 +1,27 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
"""
package which provides an administrative interface to ZFS
"""

396
lib/pyzfs/common/allow.py Normal file
View File

@ -0,0 +1,396 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
"""This module implements the "zfs allow" and "zfs unallow" subcommands.
The only public interface is the zfs.allow.do_allow() function."""
import zfs.util
import zfs.dataset
import optparse
import sys
import pwd
import grp
import errno
_ = zfs.util._
class FSPerms(object):
"""This class represents all the permissions that are set on a
particular filesystem (not including those inherited)."""
__slots__ = "create", "sets", "local", "descend", "ld"
__repr__ = zfs.util.default_repr
def __init__(self, raw):
"""Create a FSPerms based on the dict of raw permissions
from zfs.ioctl.get_fsacl()."""
# set of perms
self.create = set()
# below are { "Ntype name": set(perms) }
# where N is a number that we just use for sorting,
# type is "user", "group", "everyone", or "" (for sets)
# name is a user, group, or set name, or "" (for everyone)
self.sets = dict()
self.local = dict()
self.descend = dict()
self.ld = dict()
# see the comment in dsl_deleg.c for the definition of whokey
for whokey in raw.keys():
perms = raw[whokey].keys()
whotypechr = whokey[0].lower()
ws = whokey[3:]
if whotypechr == "c":
self.create.update(perms)
elif whotypechr == "s":
nwho = "1" + ws
self.sets.setdefault(nwho, set()).update(perms)
else:
if whotypechr == "u":
try:
name = pwd.getpwuid(int(ws)).pw_name
except KeyError:
name = ws
nwho = "1user " + name
elif whotypechr == "g":
try:
name = grp.getgrgid(int(ws)).gr_name
except KeyError:
name = ws
nwho = "2group " + name
elif whotypechr == "e":
nwho = "3everyone"
else:
raise ValueError(whotypechr)
if whokey[1] == "l":
d = self.local
elif whokey[1] == "d":
d = self.descend
else:
raise ValueError(whokey[1])
d.setdefault(nwho, set()).update(perms)
# Find perms that are in both local and descend, and
# move them to ld.
for nwho in self.local:
if nwho not in self.descend:
continue
# note: these are set operations
self.ld[nwho] = self.local[nwho] & self.descend[nwho]
self.local[nwho] -= self.ld[nwho]
self.descend[nwho] -= self.ld[nwho]
@staticmethod
def __ldstr(d, header):
s = ""
for (nwho, perms) in sorted(d.items()):
# local and descend may have entries where perms
# is an empty set, due to consolidating all
# permissions into ld
if perms:
s += "\t%s %s\n" % \
(nwho[1:], ",".join(sorted(perms)))
if s:
s = header + s
return s
def __str__(self):
s = self.__ldstr(self.sets, _("Permission sets:\n"))
if self.create:
s += _("Create time permissions:\n")
s += "\t%s\n" % ",".join(sorted(self.create))
s += self.__ldstr(self.local, _("Local permissions:\n"))
s += self.__ldstr(self.descend, _("Descendent permissions:\n"))
s += self.__ldstr(self.ld, _("Local+Descendent permissions:\n"))
return s.rstrip()
def args_to_perms(parser, options, who, perms):
"""Return a dict of raw perms {"whostr" -> {"perm" -> None}}
based on the command-line input."""
# perms is not set if we are doing a "zfs unallow <who> <fs>" to
# remove all of someone's permissions
if perms:
setperms = dict(((p, None) for p in perms if p[0] == "@"))
baseperms = dict(((canonicalized_perm(p), None)
for p in perms if p[0] != "@"))
else:
setperms = None
baseperms = None
d = dict()
def storeperm(typechr, inheritchr, arg):
assert typechr in "ugecs"
assert inheritchr in "ld-"
def mkwhokey(t):
return "%c%c$%s" % (t, inheritchr, arg)
if baseperms or not perms:
d[mkwhokey(typechr)] = baseperms
if setperms or not perms:
d[mkwhokey(typechr.upper())] = setperms
def decodeid(w, toidfunc, fmt):
try:
return int(w)
except ValueError:
try:
return toidfunc(w)[2]
except KeyError:
parser.error(fmt % w)
if options.set:
storeperm("s", "-", who)
elif options.create:
storeperm("c", "-", "")
else:
for w in who:
if options.user:
id = decodeid(w, pwd.getpwnam,
_("invalid user %s"))
typechr = "u"
elif options.group:
id = decodeid(w, grp.getgrnam,
_("invalid group %s"))
typechr = "g"
elif w == "everyone":
id = ""
typechr = "e"
else:
try:
id = pwd.getpwnam(w)[2]
typechr = "u"
except KeyError:
try:
id = grp.getgrnam(w)[2]
typechr = "g"
except KeyError:
parser.error(_("invalid user/group %s") % w)
if options.local:
storeperm(typechr, "l", id)
if options.descend:
storeperm(typechr, "d", id)
return d
perms_subcmd = dict(
create=_("Must also have the 'mount' ability"),
destroy=_("Must also have the 'mount' ability"),
snapshot="",
rollback="",
clone=_("""Must also have the 'create' ability and 'mount'
\t\t\t\tability in the origin file system"""),
promote=_("""Must also have the 'mount'
\t\t\t\tand 'promote' ability in the origin file system"""),
rename=_("""Must also have the 'mount' and 'create'
\t\t\t\tability in the new parent"""),
receive=_("Must also have the 'mount' and 'create' ability"),
allow=_("Must also have the permission that is being\n\t\t\t\tallowed"),
mount=_("Allows mount/umount of ZFS datasets"),
share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"),
send="",
hold=_("Allows adding a user hold to a snapshot"),
release=_("Allows releasing a user hold which\n\t\t\t\tmight destroy the snapshot"),
diff=_("Allows lookup of paths within a dataset,\n\t\t\t\tgiven an object number. Ordinary users need this\n\t\t\t\tin order to use zfs diff"),
)
perms_other = dict(
userprop=_("Allows changing any user property"),
userquota=_("Allows accessing any userquota@... property"),
groupquota=_("Allows accessing any groupquota@... property"),
userused=_("Allows reading any userused@... property"),
groupused=_("Allows reading any groupused@... property"),
)
def hasset(ds, setname):
"""Return True if the given setname (string) is defined for this
ds (Dataset)."""
# It would be nice to cache the result of get_fsacl().
for raw in ds.get_fsacl().values():
for whokey in raw.keys():
if whokey[0].lower() == "s" and whokey[3:] == setname:
return True
return False
def canonicalized_perm(permname):
"""Return the canonical name (string) for this permission (string).
Raises ZFSError if it is not a valid permission."""
if permname in perms_subcmd.keys() or permname in perms_other.keys():
return permname
try:
return zfs.dataset.getpropobj(permname).name
except KeyError:
raise zfs.util.ZFSError(errno.EINVAL, permname,
_("invalid permission"))
def print_perms():
"""Print the set of supported permissions."""
print(_("\nThe following permissions are supported:\n"))
fmt = "%-16s %-14s\t%s"
print(fmt % (_("NAME"), _("TYPE"), _("NOTES")))
for (name, note) in sorted(perms_subcmd.iteritems()):
print(fmt % (name, _("subcommand"), note))
for (name, note) in sorted(perms_other.iteritems()):
print(fmt % (name, _("other"), note))
for (name, prop) in sorted(zfs.dataset.proptable.iteritems()):
if prop.visible and prop.delegatable():
print(fmt % (name, _("property"), ""))
def do_allow():
"""Implements the "zfs allow" and "zfs unallow" subcommands."""
un = (sys.argv[1] == "unallow")
def usage(msg=None):
parser.print_help()
print_perms()
if msg:
print
parser.exit("zfs: error: " + msg)
else:
parser.exit()
if un:
u = _("""unallow [-rldug] <"everyone"|user|group>[,...]
[<perm|@setname>[,...]] <filesystem|volume>
unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume>
unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume>
unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""")
verb = _("remove")
sstr = _("undefine permission set")
else:
u = _("""allow <filesystem|volume>
allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...]
<filesystem|volume>
allow [-ld] -e <perm|@setname>[,...] <filesystem|volume>
allow -c <perm|@setname>[,...] <filesystem|volume>
allow -s @setname <perm|@setname>[,...] <filesystem|volume>""")
verb = _("set")
sstr = _("define permission set")
parser = optparse.OptionParser(usage=u, prog="zfs")
parser.add_option("-l", action="store_true", dest="local",
help=_("%s permission locally") % verb)
parser.add_option("-d", action="store_true", dest="descend",
help=_("%s permission for descendents") % verb)
parser.add_option("-u", action="store_true", dest="user",
help=_("%s permission for user") % verb)
parser.add_option("-g", action="store_true", dest="group",
help=_("%s permission for group") % verb)
parser.add_option("-e", action="store_true", dest="everyone",
help=_("%s permission for everyone") % verb)
parser.add_option("-c", action="store_true", dest="create",
help=_("%s create time permissions") % verb)
parser.add_option("-s", action="store_true", dest="set", help=sstr)
if un:
parser.add_option("-r", action="store_true", dest="recursive",
help=_("remove permissions recursively"))
if len(sys.argv) == 3 and not un:
# just print the permissions on this fs
if sys.argv[2] == "-h":
# hack to make "zfs allow -h" work
usage()
ds = zfs.dataset.Dataset(sys.argv[2], snaps=False)
p = dict()
for (fs, raw) in ds.get_fsacl().items():
p[fs] = FSPerms(raw)
for fs in sorted(p.keys(), reverse=True):
s = _("---- Permissions on %s ") % fs
print(s + "-" * (70-len(s)))
print(p[fs])
return
(options, args) = parser.parse_args(sys.argv[2:])
if sum((bool(options.everyone), bool(options.user),
bool(options.group))) > 1:
parser.error(_("-u, -g, and -e are mutually exclusive"))
def mungeargs(expected_len):
if un and len(args) == expected_len-1:
return (None, args[expected_len-2])
elif len(args) == expected_len:
return (args[expected_len-2].split(","),
args[expected_len-1])
else:
usage(_("wrong number of parameters"))
if options.set:
if options.local or options.descend or options.user or \
options.group or options.everyone or options.create:
parser.error(_("invalid option combined with -s"))
if args[0][0] != "@":
parser.error(_("invalid set name: missing '@' prefix"))
(perms, fsname) = mungeargs(3)
who = args[0]
elif options.create:
if options.local or options.descend or options.user or \
options.group or options.everyone or options.set:
parser.error(_("invalid option combined with -c"))
(perms, fsname) = mungeargs(2)
who = None
elif options.everyone:
if options.user or options.group or \
options.create or options.set:
parser.error(_("invalid option combined with -e"))
(perms, fsname) = mungeargs(2)
who = ["everyone"]
else:
(perms, fsname) = mungeargs(3)
who = args[0].split(",")
if not options.local and not options.descend:
options.local = True
options.descend = True
d = args_to_perms(parser, options, who, perms)
ds = zfs.dataset.Dataset(fsname, snaps=False)
if not un and perms:
for p in perms:
if p[0] == "@" and not hasset(ds, p):
parser.error(_("set %s is not defined") % p)
ds.set_fsacl(un, d)
if un and options.recursive:
for child in ds.descendents():
child.set_fsacl(un, d)

234
lib/pyzfs/common/dataset.py Normal file
View File

@ -0,0 +1,234 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
"""Implements the Dataset class, providing methods for manipulating ZFS
datasets. Also implements the Property class, which describes ZFS
properties."""
import zfs.ioctl
import zfs.util
import errno
_ = zfs.util._
class Property(object):
"""This class represents a ZFS property. It contains
information about the property -- if it's readonly, a number vs
string vs index, etc. Only native properties are represented by
this class -- not user properties (eg "user:prop") or userspace
properties (eg "userquota@joe")."""
__slots__ = "name", "number", "type", "default", "attr", "validtypes", \
"values", "colname", "rightalign", "visible", "indextable"
__repr__ = zfs.util.default_repr
def __init__(self, t):
"""t is the tuple of information about this property
from zfs.ioctl.get_proptable, which should match the
members of zprop_desc_t (see zfs_prop.h)."""
self.name = t[0]
self.number = t[1]
self.type = t[2]
if self.type == "string":
self.default = t[3]
else:
self.default = t[4]
self.attr = t[5]
self.validtypes = t[6]
self.values = t[7]
self.colname = t[8]
self.rightalign = t[9]
self.visible = t[10]
self.indextable = t[11]
def delegatable(self):
"""Return True if this property can be delegated with
"zfs allow"."""
return self.attr != "readonly"
proptable = dict()
for name, t in zfs.ioctl.get_proptable().iteritems():
proptable[name] = Property(t)
del name, t
def getpropobj(name):
"""Return the Property object that is identified by the given
name string. It can be the full name, or the column name."""
try:
return proptable[name]
except KeyError:
for p in proptable.itervalues():
if p.colname and p.colname.lower() == name:
return p
raise
class Dataset(object):
"""Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).
Generally, this class provides interfaces to the C functions in
zfs.ioctl which actually interface with the kernel to manipulate
datasets.
Unless otherwise noted, any method can raise a ZFSError to
indicate failure."""
__slots__ = "name", "__props"
__repr__ = zfs.util.default_repr
def __init__(self, name, props=None,
types=("filesystem", "volume"), snaps=True):
"""Open the named dataset, checking that it exists and
is of the specified type.
name is the string name of this dataset.
props is the property settings dict from zfs.ioctl.next_dataset.
types is an iterable of strings specifying which types
of datasets are permitted. Accepted strings are
"filesystem" and "volume". Defaults to accepting all
types.
snaps is a boolean specifying if snapshots are acceptable.
Raises a ZFSError if the dataset can't be accessed (eg
doesn't exist) or is not of the specified type.
"""
self.name = name
e = zfs.util.ZFSError(errno.EINVAL,
_("cannot open %s") % name,
_("operation not applicable to datasets of this type"))
if "@" in name and not snaps:
raise e
if not props:
props = zfs.ioctl.dataset_props(name)
self.__props = props
if "volume" not in types and self.getprop("type") == 3:
raise e
if "filesystem" not in types and self.getprop("type") == 2:
raise e
def getprop(self, propname):
"""Return the value of the given property for this dataset.
Currently only works for native properties (those with a
Property object.)
Raises KeyError if propname does not specify a native property.
Does not raise ZFSError.
"""
p = getpropobj(propname)
try:
return self.__props[p.name]["value"]
except KeyError:
return p.default
def parent(self):
"""Return a Dataset representing the parent of this one."""
return Dataset(self.name[:self.name.rindex("/")])
def descendents(self):
"""A generator function which iterates over all
descendent Datasets (not including snapshots."""
cookie = 0
while True:
# next_dataset raises StopIteration when done
(name, cookie, props) = \
zfs.ioctl.next_dataset(self.name, False, cookie)
ds = Dataset(name, props)
yield ds
for child in ds.descendents():
yield child
def userspace(self, prop):
"""A generator function which iterates over a
userspace-type property.
prop specifies which property ("userused@",
"userquota@", "groupused@", or "groupquota@").
returns 3-tuple of domain (string), rid (int), and space (int).
"""
d = zfs.ioctl.userspace_many(self.name, prop)
for ((domain, rid), space) in d.iteritems():
yield (domain, rid, space)
def userspace_upgrade(self):
"""Initialize the accounting information for
userused@... and groupused@... properties."""
return zfs.ioctl.userspace_upgrade(self.name)
def set_fsacl(self, un, d):
"""Add to the "zfs allow"-ed permissions on this Dataset.
un is True if the specified permissions should be removed.
d is a dict specifying which permissions to add/remove:
{ "whostr" -> None # remove all perms for this entity
"whostr" -> { "perm" -> None} # add/remove these perms
} """
return zfs.ioctl.set_fsacl(self.name, un, d)
def get_fsacl(self):
"""Get the "zfs allow"-ed permissions on the Dataset.
Return a dict("whostr": { "perm" -> None })."""
return zfs.ioctl.get_fsacl(self.name)
def get_holds(self):
"""Get the user holds on this Dataset.
Return a dict("tag": timestamp)."""
return zfs.ioctl.get_holds(self.name)
def snapshots_fromcmdline(dsnames, recursive):
for dsname in dsnames:
if not "@" in dsname:
raise zfs.util.ZFSError(errno.EINVAL,
_("cannot open %s") % dsname,
_("operation only applies to snapshots"))
try:
ds = Dataset(dsname)
yield ds
except zfs.util.ZFSError, e:
if not recursive or e.errno != errno.ENOENT:
raise
if recursive:
(base, snapname) = dsname.split('@')
parent = Dataset(base)
for child in parent.descendents():
try:
yield Dataset(child.name + "@" +
snapname)
except zfs.util.ZFSError, e:
if e.errno != errno.ENOENT:
raise

View File

@ -0,0 +1,28 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
import zfs.userspace
do_groupspace = zfs.userspace.do_userspace

75
lib/pyzfs/common/holds.py Normal file
View File

@ -0,0 +1,75 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
"""This module implements the "zfs holds" subcommand.
The only public interface is the zfs.holds.do_holds() function."""
import optparse
import sys
import errno
import time
import zfs.util
import zfs.dataset
import zfs.table
_ = zfs.util._
def do_holds():
"""Implements the "zfs holds" subcommand."""
def usage(msg=None):
parser.print_help()
if msg:
print
parser.exit("zfs: error: " + msg)
else:
parser.exit()
u = _("""holds [-r] <snapshot> ...""")
parser = optparse.OptionParser(usage=u, prog="zfs")
parser.add_option("-r", action="store_true", dest="recursive",
help=_("list holds recursively"))
(options, args) = parser.parse_args(sys.argv[2:])
if len(args) < 1:
usage(_("missing snapshot argument"))
fields = ("name", "tag", "timestamp")
rjustfields = ()
printing = False
gotone = False
t = zfs.table.Table(fields, rjustfields)
for ds in zfs.dataset.snapshots_fromcmdline(args, options.recursive):
gotone = True
for tag, tm in ds.get_holds().iteritems():
val = {"name": ds.name, "tag": tag,
"timestamp": time.ctime(tm)}
t.addline(ds.name, val)
printing = True
if printing:
t.printme()
elif not gotone:
raise zfs.util.ZFSError(errno.ENOENT, _("no matching datasets"))

543
lib/pyzfs/common/ioctl.c Normal file
View File

@ -0,0 +1,543 @@
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <Python.h>
#include <sys/zfs_ioctl.h>
#include <sys/fs/zfs.h>
#include <strings.h>
#include <unistd.h>
#include <libnvpair.h>
#include <libintl.h>
#include <libzfs.h>
#include "zfs_prop.h"
static PyObject *ZFSError;
static int zfsdevfd;
#ifdef __lint
#define dgettext(x, y) y
#endif
#define _(s) dgettext(TEXT_DOMAIN, s)
/*PRINTFLIKE1*/
static void
seterr(char *fmt, ...)
{
char errstr[1024];
va_list v;
va_start(v, fmt);
(void) vsnprintf(errstr, sizeof (errstr), fmt, v);
va_end(v);
PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr));
}
static char cmdstr[HIS_MAX_RECORD_LEN];
static int
ioctl_with_cmdstr(int ioc, zfs_cmd_t *zc)
{
int err;
if (cmdstr[0])
zc->zc_history = (uint64_t)(uintptr_t)cmdstr;
err = ioctl(zfsdevfd, ioc, zc);
cmdstr[0] = '\0';
return (err);
}
static PyObject *
nvl2py(nvlist_t *nvl)
{
PyObject *pyo;
nvpair_t *nvp;
pyo = PyDict_New();
for (nvp = nvlist_next_nvpair(nvl, NULL); nvp;
nvp = nvlist_next_nvpair(nvl, nvp)) {
PyObject *pyval;
char *sval;
uint64_t ival;
boolean_t bval;
nvlist_t *nval;
switch (nvpair_type(nvp)) {
case DATA_TYPE_STRING:
(void) nvpair_value_string(nvp, &sval);
pyval = Py_BuildValue("s", sval);
break;
case DATA_TYPE_UINT64:
(void) nvpair_value_uint64(nvp, &ival);
pyval = Py_BuildValue("K", ival);
break;
case DATA_TYPE_NVLIST:
(void) nvpair_value_nvlist(nvp, &nval);
pyval = nvl2py(nval);
break;
case DATA_TYPE_BOOLEAN:
Py_INCREF(Py_None);
pyval = Py_None;
break;
case DATA_TYPE_BOOLEAN_VALUE:
(void) nvpair_value_boolean_value(nvp, &bval);
pyval = Py_BuildValue("i", bval);
break;
default:
PyErr_SetNone(PyExc_ValueError);
Py_DECREF(pyo);
return (NULL);
}
PyDict_SetItemString(pyo, nvpair_name(nvp), pyval);
Py_DECREF(pyval);
}
return (pyo);
}
static nvlist_t *
dict2nvl(PyObject *d)
{
nvlist_t *nvl;
int err;
PyObject *key, *value;
int pos = 0;
if (!PyDict_Check(d)) {
PyErr_SetObject(PyExc_ValueError, d);
return (NULL);
}
err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
assert(err == 0);
while (PyDict_Next(d, &pos, &key, &value)) {
char *keystr = PyString_AsString(key);
if (keystr == NULL) {
PyErr_SetObject(PyExc_KeyError, key);
nvlist_free(nvl);
return (NULL);
}
if (PyDict_Check(value)) {
nvlist_t *valnvl = dict2nvl(value);
err = nvlist_add_nvlist(nvl, keystr, valnvl);
nvlist_free(valnvl);
} else if (value == Py_None) {
err = nvlist_add_boolean(nvl, keystr);
} else if (PyString_Check(value)) {
char *valstr = PyString_AsString(value);
err = nvlist_add_string(nvl, keystr, valstr);
} else if (PyInt_Check(value)) {
uint64_t valint = PyInt_AsUnsignedLongLongMask(value);
err = nvlist_add_uint64(nvl, keystr, valint);
} else if (PyBool_Check(value)) {
boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE;
err = nvlist_add_boolean_value(nvl, keystr, valbool);
} else {
PyErr_SetObject(PyExc_ValueError, value);
nvlist_free(nvl);
return (NULL);
}
assert(err == 0);
}
return (nvl);
}
static PyObject *
fakepropval(uint64_t value)
{
PyObject *d = PyDict_New();
PyDict_SetItemString(d, "value", Py_BuildValue("K", value));
return (d);
}
static void
add_ds_props(zfs_cmd_t *zc, PyObject *nvl)
{
dmu_objset_stats_t *s = &zc->zc_objset_stats;
PyDict_SetItemString(nvl, "numclones",
fakepropval(s->dds_num_clones));
PyDict_SetItemString(nvl, "issnap",
fakepropval(s->dds_is_snapshot));
PyDict_SetItemString(nvl, "inconsistent",
fakepropval(s->dds_inconsistent));
}
/* On error, returns NULL but does not set python exception. */
static PyObject *
ioctl_with_dstnv(int ioc, zfs_cmd_t *zc)
{
int nvsz = 2048;
void *nvbuf;
PyObject *pynv = NULL;
again:
nvbuf = malloc(nvsz);
zc->zc_nvlist_dst_size = nvsz;
zc->zc_nvlist_dst = (uintptr_t)nvbuf;
if (ioctl(zfsdevfd, ioc, zc) == 0) {
nvlist_t *nvl;
errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0);
if (errno == 0) {
pynv = nvl2py(nvl);
nvlist_free(nvl);
}
} else if (errno == ENOMEM) {
free(nvbuf);
nvsz = zc->zc_nvlist_dst_size;
goto again;
}
free(nvbuf);
return (pynv);
}
static PyObject *
py_next_dataset(PyObject *self, PyObject *args)
{
int ioc;
uint64_t cookie;
zfs_cmd_t zc = { 0 };
int snaps;
char *name;
PyObject *nvl;
PyObject *ret = NULL;
if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie))
return (NULL);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
zc.zc_cookie = cookie;
if (snaps)
ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT;
else
ioc = ZFS_IOC_DATASET_LIST_NEXT;
nvl = ioctl_with_dstnv(ioc, &zc);
if (nvl) {
add_ds_props(&zc, nvl);
ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl);
Py_DECREF(nvl);
} else if (errno == ESRCH) {
PyErr_SetNone(PyExc_StopIteration);
} else {
if (snaps)
seterr(_("cannot get snapshots of %s"), name);
else
seterr(_("cannot get child datasets of %s"), name);
}
return (ret);
}
static PyObject *
py_dataset_props(PyObject *self, PyObject *args)
{
zfs_cmd_t zc = { 0 };
int snaps;
char *name;
PyObject *nvl;
if (!PyArg_ParseTuple(args, "s", &name))
return (NULL);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc);
if (nvl) {
add_ds_props(&zc, nvl);
} else {
seterr(_("cannot access dataset %s"), name);
}
return (nvl);
}
static PyObject *
py_get_fsacl(PyObject *self, PyObject *args)
{
zfs_cmd_t zc = { 0 };
char *name;
PyObject *nvl;
if (!PyArg_ParseTuple(args, "s", &name))
return (NULL);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc);
if (nvl == NULL)
seterr(_("cannot get permissions on %s"), name);
return (nvl);
}
static PyObject *
py_set_fsacl(PyObject *self, PyObject *args)
{
int un;
size_t nvsz;
zfs_cmd_t zc = { 0 };
char *name, *nvbuf;
PyObject *dict, *file;
nvlist_t *nvl;
int err;
if (!PyArg_ParseTuple(args, "siO!", &name, &un,
&PyDict_Type, &dict))
return (NULL);
nvl = dict2nvl(dict);
if (nvl == NULL)
return (NULL);
err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
assert(err == 0);
nvbuf = malloc(nvsz);
err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
assert(err == 0);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
zc.zc_nvlist_src_size = nvsz;
zc.zc_nvlist_src = (uintptr_t)nvbuf;
zc.zc_perm_action = un;
err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc);
free(nvbuf);
if (err) {
seterr(_("cannot set permissions on %s"), name);
return (NULL);
}
Py_RETURN_NONE;
}
static PyObject *
py_get_holds(PyObject *self, PyObject *args)
{
zfs_cmd_t zc = { 0 };
char *name;
PyObject *nvl;
if (!PyArg_ParseTuple(args, "s", &name))
return (NULL);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc);
if (nvl == NULL)
seterr(_("cannot get holds for %s"), name);
return (nvl);
}
static PyObject *
py_userspace_many(PyObject *self, PyObject *args)
{
zfs_cmd_t zc = { 0 };
zfs_userquota_prop_t type;
char *name, *propname;
int bufsz = 1<<20;
void *buf;
PyObject *dict, *file;
int error;
if (!PyArg_ParseTuple(args, "ss", &name, &propname))
return (NULL);
for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++)
if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0)
break;
if (type == ZFS_NUM_USERQUOTA_PROPS) {
PyErr_SetString(PyExc_KeyError, propname);
return (NULL);
}
dict = PyDict_New();
buf = malloc(bufsz);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
zc.zc_objset_type = type;
zc.zc_cookie = 0;
while (1) {
zfs_useracct_t *zua = buf;
zc.zc_nvlist_dst = (uintptr_t)buf;
zc.zc_nvlist_dst_size = bufsz;
error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc);
if (error || zc.zc_nvlist_dst_size == 0)
break;
while (zc.zc_nvlist_dst_size > 0) {
PyObject *pykey, *pyval;
pykey = Py_BuildValue("sI",
zua->zu_domain, zua->zu_rid);
pyval = Py_BuildValue("K", zua->zu_space);
PyDict_SetItem(dict, pykey, pyval);
Py_DECREF(pykey);
Py_DECREF(pyval);
zua++;
zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
}
}
free(buf);
if (error != 0) {
Py_DECREF(dict);
seterr(_("cannot get %s property on %s"), propname, name);
return (NULL);
}
return (dict);
}
static PyObject *
py_userspace_upgrade(PyObject *self, PyObject *args)
{
zfs_cmd_t zc = { 0 };
char *name;
int error;
if (!PyArg_ParseTuple(args, "s", &name))
return (NULL);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc);
if (error != 0) {
seterr(_("cannot initialize user accounting information on %s"),
name);
return (NULL);
}
Py_RETURN_NONE;
}
static PyObject *
py_set_cmdstr(PyObject *self, PyObject *args)
{
char *str;
if (!PyArg_ParseTuple(args, "s", &str))
return (NULL);
(void) strlcpy(cmdstr, str, sizeof (cmdstr));
Py_RETURN_NONE;
}
static PyObject *
py_get_proptable(PyObject *self, PyObject *args)
{
zprop_desc_t *t = zfs_prop_get_table();
PyObject *d = PyDict_New();
zfs_prop_t i;
for (i = 0; i < ZFS_NUM_PROPS; i++) {
zprop_desc_t *p = &t[i];
PyObject *tuple;
static const char *typetable[] =
{"number", "string", "index"};
static const char *attrtable[] =
{"default", "readonly", "inherit", "onetime"};
PyObject *indextable;
if (p->pd_proptype == PROP_TYPE_INDEX) {
const zprop_index_t *it = p->pd_table;
indextable = PyDict_New();
int j;
for (j = 0; it[j].pi_name; j++) {
PyDict_SetItemString(indextable,
it[j].pi_name,
Py_BuildValue("K", it[j].pi_value));
}
} else {
Py_INCREF(Py_None);
indextable = Py_None;
}
tuple = Py_BuildValue("sissKsissiiO",
p->pd_name, p->pd_propnum, typetable[p->pd_proptype],
p->pd_strdefault, p->pd_numdefault,
attrtable[p->pd_attr], p->pd_types,
p->pd_values, p->pd_colname,
p->pd_rightalign, p->pd_visible, indextable);
PyDict_SetItemString(d, p->pd_name, tuple);
Py_DECREF(tuple);
}
return (d);
}
static PyMethodDef zfsmethods[] = {
{"next_dataset", py_next_dataset, METH_VARARGS,
"Get next child dataset or snapshot."},
{"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."},
{"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."},
{"userspace_many", py_userspace_many, METH_VARARGS,
"Get user space accounting."},
{"userspace_upgrade", py_userspace_upgrade, METH_VARARGS,
"Upgrade fs to enable user space accounting."},
{"set_cmdstr", py_set_cmdstr, METH_VARARGS,
"Set command string for history logging."},
{"dataset_props", py_dataset_props, METH_VARARGS,
"Get dataset properties."},
{"get_proptable", py_get_proptable, METH_NOARGS,
"Get property table."},
{"get_holds", py_get_holds, METH_VARARGS, "Get user holds."},
{NULL, NULL, 0, NULL}
};
void
initioctl(void)
{
PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods);
PyObject *zfs_util = PyImport_ImportModule("zfs.util");
PyObject *devfile;
if (zfs_util == NULL)
return;
ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError");
devfile = PyObject_GetAttrString(zfs_util, "dev");
zfsdevfd = PyObject_AsFileDescriptor(devfile);
zfs_prop_init();
}

70
lib/pyzfs/common/table.py Normal file
View File

@ -0,0 +1,70 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
import zfs.util
class Table:
__slots__ = "fields", "rjustfields", "maxfieldlen", "lines"
__repr__ = zfs.util.default_repr
def __init__(self, fields, rjustfields=()):
# XXX maybe have a defaults, too?
self.fields = fields
self.rjustfields = rjustfields
self.maxfieldlen = dict.fromkeys(fields, 0)
self.lines = list()
def __updatemax(self, k, v):
self.maxfieldlen[k] = max(self.maxfieldlen.get(k, None), v)
def addline(self, sortkey, values):
"""values is a dict from field name to value"""
va = list()
for f in self.fields:
v = str(values[f])
va.append(v)
self.__updatemax(f, len(v))
self.lines.append((sortkey, va))
def printme(self, headers=True):
if headers:
d = dict([(f, f.upper()) for f in self.fields])
self.addline(None, d)
self.lines.sort()
for (k, va) in self.lines:
line = str()
for i in range(len(self.fields)):
if not headers:
line += va[i]
line += "\t"
else:
if self.fields[i] in self.rjustfields:
fmt = "%*s "
else:
fmt = "%-*s "
mfl = self.maxfieldlen[self.fields[i]]
line += fmt % (mfl, va[i])
print(line)

View File

@ -0,0 +1,27 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
import zfs.allow
do_unallow = zfs.allow.do_allow

View File

@ -0,0 +1,246 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
"""This module implements the "zfs userspace" and "zfs groupspace" subcommands.
The only public interface is the zfs.userspace.do_userspace() function."""
import optparse
import sys
import pwd
import grp
import errno
import solaris.misc
import zfs.util
import zfs.ioctl
import zfs.dataset
import zfs.table
_ = zfs.util._
# map from property name prefix -> (field name, isgroup)
props = {
"userused@": ("used", False),
"userquota@": ("quota", False),
"groupused@": ("used", True),
"groupquota@": ("quota", True),
}
def skiptype(options, prop):
"""Return True if this property (eg "userquota@") should be skipped."""
(field, isgroup) = props[prop]
if field not in options.fields:
return True
if isgroup and "posixgroup" not in options.types and \
"smbgroup" not in options.types:
return True
if not isgroup and "posixuser" not in options.types and \
"smbuser" not in options.types:
return True
return False
def new_entry(options, isgroup, domain, rid):
"""Return a dict("field": value) for this domain (string) + rid (int)"""
if domain:
idstr = "%s-%u" % (domain, rid)
else:
idstr = "%u" % rid
(typename, mapfunc) = {
(1, 1): ("SMB Group", lambda id: solaris.misc.sid_to_name(id, 0)),
(1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name),
(0, 1): ("SMB User", lambda id: solaris.misc.sid_to_name(id, 1)),
(0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name)
}[isgroup, bool(domain)]
if typename.lower().replace(" ", "") not in options.types:
return None
v = dict()
v["type"] = typename
# python's getpwuid/getgrgid is confused by ephemeral uids
if not options.noname and rid < 1<<31:
try:
v["name"] = mapfunc(idstr)
except KeyError:
pass
if "name" not in v:
v["name"] = idstr
if not domain:
# it's just a number, so pad it with spaces so
# that it will sort numerically
v["name.sort"] = "%20d" % rid
# fill in default values
v["used"] = "0"
v["used.sort"] = 0
v["quota"] = "none"
v["quota.sort"] = 0
return v
def process_one_raw(acct, options, prop, elem):
"""Update the acct dict to incorporate the
information from this elem from Dataset.userspace(prop)."""
(domain, rid, value) = elem
(field, isgroup) = props[prop]
if options.translate and domain:
try:
rid = solaris.misc.sid_to_id("%s-%u" % (domain, rid),
not isgroup)
domain = None
except KeyError:
pass;
key = (isgroup, domain, rid)
try:
v = acct[key]
except KeyError:
v = new_entry(options, isgroup, domain, rid)
if not v:
return
acct[key] = v
# Add our value to an existing value, which may be present if
# options.translate is set.
value = v[field + ".sort"] = value + v[field + ".sort"]
if options.parsable:
v[field] = str(value)
else:
v[field] = zfs.util.nicenum(value)
def do_userspace():
"""Implements the "zfs userspace" and "zfs groupspace" subcommands."""
def usage(msg=None):
parser.print_help()
if msg:
print
parser.exit("zfs: error: " + msg)
else:
parser.exit()
if sys.argv[1] == "userspace":
defaulttypes = "posixuser,smbuser"
else:
defaulttypes = "posixgroup,smbgroup"
fields = ("type", "name", "used", "quota")
rjustfields = ("used", "quota")
types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup")
u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1]
u += _(" [-t type[,...]] <filesystem|snapshot>")
parser = optparse.OptionParser(usage=u, prog="zfs")
parser.add_option("-n", action="store_true", dest="noname",
help=_("Print numeric ID instead of user/group name"))
parser.add_option("-i", action="store_true", dest="translate",
help=_("translate SID to posix (possibly ephemeral) ID"))
parser.add_option("-H", action="store_true", dest="noheaders",
help=_("no headers, tab delimited output"))
parser.add_option("-p", action="store_true", dest="parsable",
help=_("exact (parsable) numeric output"))
parser.add_option("-o", dest="fields", metavar="field[,...]",
default="type,name,used,quota",
help=_("print only these fields (eg type,name,used,quota)"))
parser.add_option("-s", dest="sortfields", metavar="field",
type="choice", choices=fields, default=list(),
action="callback", callback=zfs.util.append_with_opt,
help=_("sort field"))
parser.add_option("-S", dest="sortfields", metavar="field",
type="choice", choices=fields, #-s sets the default
action="callback", callback=zfs.util.append_with_opt,
help=_("reverse sort field"))
parser.add_option("-t", dest="types", metavar="type[,...]",
default=defaulttypes,
help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)"))
(options, args) = parser.parse_args(sys.argv[2:])
if len(args) != 1:
usage(_("wrong number of arguments"))
dsname = args[0]
options.fields = options.fields.split(",")
for f in options.fields:
if f not in fields:
usage(_("invalid field %s") % f)
options.types = options.types.split(",")
for t in options.types:
if t not in types:
usage(_("invalid type %s") % t)
if not options.sortfields:
options.sortfields = [("-s", "type"), ("-s", "name")]
if "all" in options.types:
options.types = types[1:]
ds = zfs.dataset.Dataset(dsname, types=("filesystem"))
if ds.getprop("zoned") and solaris.misc.isglobalzone():
options.noname = True
if not ds.getprop("useraccounting"):
print(_("Initializing accounting information on old filesystem, please wait..."))
ds.userspace_upgrade()
# gather and process accounting information
# Due to -i, we need to keep a dict, so we can potentially add
# together the posix ID and SID's usage. Grr.
acct = dict()
for prop in props.keys():
if skiptype(options, prop):
continue;
for elem in ds.userspace(prop):
process_one_raw(acct, options, prop, elem)
def cmpkey(val):
l = list()
for (opt, field) in options.sortfields:
try:
n = val[field + ".sort"]
except KeyError:
n = val[field]
if opt == "-S":
# reverse sorting
try:
n = -n
except TypeError:
# it's a string; decompose it
# into an array of integers,
# each one the negative of that
# character
n = [-ord(c) for c in n]
l.append(n)
return l
t = zfs.table.Table(options.fields, rjustfields)
for val in acct.itervalues():
t.addline(cmpkey(val), val)
t.printme(not options.noheaders)

141
lib/pyzfs/common/util.py Normal file
View File

@ -0,0 +1,141 @@
#! /usr/bin/python2.6
#
# 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#
"""This module provides utility functions for ZFS.
zfs.util.dev -- a file object of /dev/zfs """
import gettext
import errno
import os
import solaris.misc
# Note: this module (zfs.util) should not import zfs.ioctl, because that
# would introduce a circular dependency
errno.ECANCELED = 47
errno.ENOTSUP = 48
dev = open("/dev/zfs", "w")
try:
_ = gettext.translation("SUNW_OST_OSLIB", "/usr/lib/locale",
fallback=True).gettext
except:
_ = solaris.misc.gettext
def default_repr(self):
"""A simple __repr__ function."""
if self.__slots__:
str = "<" + self.__class__.__name__
for v in self.__slots__:
str += " %s: %r" % (v, getattr(self, v))
return str + ">"
else:
return "<%s %s>" % \
(self.__class__.__name__, repr(self.__dict__))
class ZFSError(StandardError):
"""This exception class represents a potentially user-visible
ZFS error. If uncaught, it will be printed and the process will
exit with exit code 1.
errno -- the error number (eg, from ioctl(2))."""
__slots__ = "why", "task", "errno"
__repr__ = default_repr
def __init__(self, eno, task=None, why=None):
"""Create a ZFS exception.
eno -- the error number (errno)
task -- a string describing the task that failed
why -- a string describing why it failed (defaults to
strerror(eno))"""
self.errno = eno
self.task = task
self.why = why
def __str__(self):
s = ""
if self.task:
s += self.task + ": "
if self.why:
s += self.why
else:
s += self.strerror
return s
__strs = {
errno.EPERM: _("permission denied"),
errno.ECANCELED:
_("delegated administration is disabled on pool"),
errno.EINTR: _("signal received"),
errno.EIO: _("I/O error"),
errno.ENOENT: _("dataset does not exist"),
errno.ENOSPC: _("out of space"),
errno.EEXIST: _("dataset already exists"),
errno.EBUSY: _("dataset is busy"),
errno.EROFS:
_("snapshot permissions cannot be modified"),
errno.ENAMETOOLONG: _("dataset name is too long"),
errno.ENOTSUP: _("unsupported version"),
errno.EAGAIN: _("pool I/O is currently suspended"),
}
__strs[errno.EACCES] = __strs[errno.EPERM]
__strs[errno.ENXIO] = __strs[errno.EIO]
__strs[errno.ENODEV] = __strs[errno.EIO]
__strs[errno.EDQUOT] = __strs[errno.ENOSPC]
@property
def strerror(self):
return ZFSError.__strs.get(self.errno, os.strerror(self.errno))
def nicenum(num):
"""Return a nice string (eg "1.23M") for this integer."""
index = 0;
n = num;
while n >= 1024:
n /= 1024
index += 1
u = " KMGTPE"[index]
if index == 0:
return "%u" % n;
elif n >= 100 or num & ((1024*index)-1) == 0:
# it's an exact multiple of its index, or it wouldn't
# fit as floating point, so print as an integer
return "%u%c" % (n, u)
else:
# due to rounding, it's tricky to tell what precision to
# use; try each precision and see which one fits
for i in (2, 1, 0):
s = "%.*f%c" % (i, float(num) / (1<<(10*index)), u)
if len(s) <= 5:
return s
def append_with_opt(option, opt, value, parser):
"""A function for OptionParser which appends a tuple (opt, value)."""
getattr(parser.values, option.dest).append((opt, value))