Merge OpenZFS support in to HEAD.
The primary benefit is maintaining a completely shared code base with the community allowing FreeBSD to receive new features sooner and with less effort. I would advise against doing 'zpool upgrade' or creating indispensable pools using new features until this change has had a month+ to soak. Work on merging FreeBSD support in to what was at the time "ZFS on Linux" began in August 2018. I first publicly proposed transitioning FreeBSD to (new) OpenZFS on December 18th, 2018. FreeBSD support in OpenZFS was finally completed in December 2019. A CFT for downstreaming OpenZFS support in to FreeBSD was first issued on July 8th. All issues that were reported have been addressed or, for a couple of less critical matters there are pull requests in progress with OpenZFS. iXsystems has tested and dogfooded extensively internally. The TrueNAS 12 release is based on OpenZFS with some additional features that have not yet made it upstream. Improvements include: project quotas, encrypted datasets, allocation classes, vectorized raidz, vectorized checksums, various command line improvements, zstd compression. Thanks to those who have helped along the way: Ryan Moeller, Allan Jude, Zack Welch, and many others. Sponsored by: iXsystems, Inc. Differential Revision: https://reviews.freebsd.org/D25872
This commit is contained in:
parent
22df1ffd81
commit
9e5787d228
@ -2442,7 +2442,7 @@ _btxld= usr.sbin/btxld
|
||||
# Rebuild ctfconvert and ctfmerge to avoid difficult-to-diagnose failures
|
||||
# resulting from missing bug fixes or ELF Toolchain updates.
|
||||
.if ${MK_CDDL} != "no"
|
||||
_dtrace_tools= cddl/lib/libctf cddl/usr.bin/ctfconvert \
|
||||
_dtrace_tools= cddl/lib/libctf cddl/lib/libspl cddl/usr.bin/ctfconvert \
|
||||
cddl/usr.bin/ctfmerge
|
||||
.endif
|
||||
|
||||
@ -2756,7 +2756,12 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \
|
||||
${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \
|
||||
${_cddl_lib_libuutil} \
|
||||
${_cddl_lib_libavl} \
|
||||
${_cddl_lib_libicp} \
|
||||
${_cddl_lib_libicp_rescue} \
|
||||
${_cddl_lib_libspl} \
|
||||
${_cddl_lib_libtpool} \
|
||||
${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \
|
||||
${_cddl_lib_libzutil} \
|
||||
${_cddl_lib_libctf} \
|
||||
lib/libufs \
|
||||
lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \
|
||||
@ -2826,21 +2831,34 @@ _cddl_lib_libumem= cddl/lib/libumem
|
||||
_cddl_lib_libnvpair= cddl/lib/libnvpair
|
||||
_cddl_lib_libavl= cddl/lib/libavl
|
||||
_cddl_lib_libuutil= cddl/lib/libuutil
|
||||
_cddl_lib_libspl= cddl/lib/libspl
|
||||
|
||||
cddl/lib/libuutil__L: cddl/lib/libavl__L cddl/lib/libspl__L
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
_cddl_lib_libicp= cddl/lib/libicp
|
||||
_cddl_lib_libicp_rescue= cddl/lib/libicp_rescue
|
||||
_cddl_lib_libtpool= cddl/lib/libtpool
|
||||
_cddl_lib_libzutil= cddl/lib/libzutil
|
||||
_cddl_lib_libzfs_core= cddl/lib/libzfs_core
|
||||
_cddl_lib_libzfs= cddl/lib/libzfs
|
||||
|
||||
cddl/lib/libtpool__L: cddl/lib/libspl__L
|
||||
|
||||
cddl/lib/libzutil__L: cddl/lib/libavl__L cddl/lib/libtpool__L
|
||||
|
||||
cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L
|
||||
|
||||
cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L
|
||||
cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L
|
||||
cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L
|
||||
cddl/lib/libzfs__L: cddl/lib/libnvpair__L cddl/lib/libzutil__L
|
||||
|
||||
lib/libbe__L: cddl/lib/libzfs__L
|
||||
.endif
|
||||
_cddl_lib_libctf= cddl/lib/libctf
|
||||
_cddl_lib= cddl/lib
|
||||
cddl/lib/libctf__L: lib/libz__L
|
||||
cddl/lib/libctf__L: lib/libz__L cddl/lib/libspl__L
|
||||
.endif
|
||||
# cddl/lib/libdtrace requires lib/libproc and lib/librtld_db
|
||||
_prebuild_libs+= lib/libprocstat lib/libproc lib/librtld_db
|
||||
|
@ -32,7 +32,9 @@
|
||||
|
||||
#include_next <fcntl.h>
|
||||
|
||||
#ifndef open64
|
||||
#define open64(...) open(__VA_ARGS__)
|
||||
#endif
|
||||
#define openat64(...) openat(__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
@ -1,35 +0,0 @@
|
||||
/* $FreeBSD$ */
|
||||
|
||||
#ifndef _OPENSOLARIS_MNTTAB_H_
|
||||
#define _OPENSOLARIS_MNTTAB_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <paths.h>
|
||||
|
||||
#define MNTTAB _PATH_DEVZERO
|
||||
#define MNT_LINE_MAX 1024
|
||||
|
||||
#define MS_OVERLAY 0x0
|
||||
#define MS_NOMNTTAB 0x0
|
||||
#define MS_RDONLY 0x1
|
||||
|
||||
#define umount2(p, f) unmount(p, f)
|
||||
|
||||
struct mnttab {
|
||||
char *mnt_special;
|
||||
char *mnt_mountp;
|
||||
char *mnt_fstype;
|
||||
char *mnt_mntopts;
|
||||
};
|
||||
#define extmnttab mnttab
|
||||
|
||||
int getmntany(FILE *fd, struct mnttab *mgetp, struct mnttab *mrefp);
|
||||
int getmntent(FILE *fp, struct mnttab *mp);
|
||||
char *hasmntopt(struct mnttab *mnt, char *opt);
|
||||
|
||||
void statfs2mnttab(struct statfs *sfs, struct mnttab *mp);
|
||||
|
||||
#endif /* !_OPENSOLARIS_MNTTAB_H_ */
|
@ -54,6 +54,7 @@
|
||||
#endif
|
||||
#include <sys/cpuvar.h>
|
||||
|
||||
|
||||
typedef struct syment {
|
||||
uintptr_t addr;
|
||||
char *name;
|
||||
@ -72,6 +73,11 @@ static char maxsymname[64];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define __sElfN(x) typedef __CONCAT(__CONCAT(__CONCAT(Elf,__ELF_WORD_SIZE),_),x) x
|
||||
__sElfN(Sym);
|
||||
__sElfN(Shdr);
|
||||
#define elf_getshdr __elfN(getshdr)
|
||||
|
||||
static void
|
||||
add_symbol(char *name, uintptr_t addr, size_t size)
|
||||
{
|
||||
|
@ -1,414 +0,0 @@
|
||||
.\"
|
||||
.\" This file and its contents are supplied under the terms of the
|
||||
.\" Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
.\" You may only use this file in accordance with the terms of version
|
||||
.\" 1.0 of the CDDL.
|
||||
.\"
|
||||
.\" A full copy of the text of the CDDL should have accompanied this
|
||||
.\" source. A copy of the CDDL is also available via the Internet at
|
||||
.\" http://www.illumos.org/license/CDDL.
|
||||
.\"
|
||||
.\"
|
||||
.\" Copyright 2012, Richard Lowe.
|
||||
.\" Copyright (c) 2012, 2018 by Delphix. All rights reserved.
|
||||
.\" Copyright 2017 Nexenta Systems, Inc.
|
||||
.\"
|
||||
.Dd February 25, 2020
|
||||
.Dt ZDB 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm zdb
|
||||
.Nd display zpool debugging and consistency information
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl AbcdDFGhikLMPsvX
|
||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||
.Op Fl I Ar inflight I/Os
|
||||
.Oo Fl o Ar var Ns = Ns Ar value Oc Ns ...
|
||||
.Op Fl t Ar txg
|
||||
.Op Fl U Ar cache
|
||||
.Op Fl x Ar dumpdir
|
||||
.Op Ar poolname Op Ar object ...
|
||||
.Nm
|
||||
.Op Fl AdiPv
|
||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||
.Op Fl U Ar cache
|
||||
.Ar dataset Op Ar object ...
|
||||
.Nm
|
||||
.Fl C
|
||||
.Op Fl A
|
||||
.Op Fl U Ar cache
|
||||
.Nm
|
||||
.Fl E
|
||||
.Op Fl A
|
||||
.Ar word0 Ns \&: Ns Ar word1 Ns :...: Ns Ar word15
|
||||
.Nm
|
||||
.Fl l
|
||||
.Op Fl Aqu
|
||||
.Ar device
|
||||
.Nm
|
||||
.Fl m
|
||||
.Op Fl AFLPX
|
||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||
.Op Fl t Ar txg
|
||||
.Op Fl U Ar cache
|
||||
.Ar poolname Op Ar vdev Op Ar metaslab ...
|
||||
.Nm
|
||||
.Fl O
|
||||
.Ar dataset path
|
||||
.Nm
|
||||
.Fl R
|
||||
.Op Fl A
|
||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||
.Op Fl U Ar cache
|
||||
.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
|
||||
.Nm
|
||||
.Fl S
|
||||
.Op Fl AP
|
||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||
.Op Fl U Ar cache
|
||||
.Ar poolname
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility displays information about a ZFS pool useful for debugging and performs
|
||||
some amount of consistency checking.
|
||||
It is a not a general purpose tool and options
|
||||
.Pq and facilities
|
||||
may change.
|
||||
This is neither a
|
||||
.Xr fsck 8
|
||||
nor an
|
||||
.Xr fsdb 8
|
||||
utility.
|
||||
.Pp
|
||||
The output of this command in general reflects the on-disk structure of a ZFS
|
||||
pool, and is inherently unstable.
|
||||
The precise output of most invocations is not documented, a knowledge of ZFS
|
||||
internals is assumed.
|
||||
.Pp
|
||||
If the
|
||||
.Ar dataset
|
||||
argument does not contain any
|
||||
.Qq Sy /
|
||||
or
|
||||
.Qq Sy @
|
||||
characters, it is interpreted as a pool name.
|
||||
The root dataset can be specified as
|
||||
.Ar pool Ns /
|
||||
.Pq pool name followed by a slash .
|
||||
.Pp
|
||||
When operating on an imported and active pool it is possible, though unlikely,
|
||||
that zdb may interpret inconsistent pool data and behave erratically.
|
||||
.Sh OPTIONS
|
||||
Display options:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl b
|
||||
Display statistics regarding the number, size
|
||||
.Pq logical, physical and allocated
|
||||
and deduplication of blocks.
|
||||
.It Fl c
|
||||
Verify the checksum of all metadata blocks while printing block statistics
|
||||
.Po see
|
||||
.Fl b
|
||||
.Pc .
|
||||
.Pp
|
||||
If specified multiple times, verify the checksums of all blocks.
|
||||
.It Fl C
|
||||
Display information about the configuration.
|
||||
If specified with no other options, instead display information about the cache
|
||||
file
|
||||
.Pq Pa /boot/zfs/zpool.cache .
|
||||
To specify the cache file to display, see
|
||||
.Fl U .
|
||||
.Pp
|
||||
If specified multiple times, and a pool name is also specified display both the
|
||||
cached configuration and the on-disk configuration.
|
||||
If specified multiple times with
|
||||
.Fl e
|
||||
also display the configuration that would be used were the pool to be imported.
|
||||
.It Fl d
|
||||
Display information about datasets.
|
||||
Specified once, displays basic dataset information: ID, create transaction,
|
||||
size, and object count.
|
||||
.Pp
|
||||
If specified multiple times provides greater and greater verbosity.
|
||||
.Pp
|
||||
If object IDs are specified, display information about those specific objects
|
||||
only.
|
||||
.It Fl D
|
||||
Display deduplication statistics, including the deduplication ratio
|
||||
.Pq Sy dedup ,
|
||||
compression ratio
|
||||
.Pq Sy compress ,
|
||||
inflation due to the zfs copies property
|
||||
.Pq Sy copies ,
|
||||
and an overall effective ratio
|
||||
.Pq Sy dedup No * Sy compress No / Sy copies .
|
||||
.It Fl DD
|
||||
Display a histogram of deduplication statistics, showing the allocated
|
||||
.Pq physically present on disk
|
||||
and referenced
|
||||
.Pq logically referenced in the pool
|
||||
block counts and sizes by reference count.
|
||||
.It Fl DDD
|
||||
Display the statistics independently for each deduplication table.
|
||||
.It Fl DDDD
|
||||
Dump the contents of the deduplication tables describing duplicate blocks.
|
||||
.It Fl DDDDD
|
||||
Also dump the contents of the deduplication tables describing unique blocks.
|
||||
.It Fl E Ar word0 Ns \&: Ns Ar word1 Ns :...: Ns Ar word15
|
||||
Decode and display block from an embedded block pointer specified by the
|
||||
.Ar word
|
||||
arguments.
|
||||
.It Fl h
|
||||
Display pool history similar to
|
||||
.Nm zpool Cm history ,
|
||||
but include internal changes, transaction, and dataset information.
|
||||
.It Fl i
|
||||
Display information about intent log
|
||||
.Pq ZIL
|
||||
entries relating to each dataset.
|
||||
If specified multiple times, display counts of each intent log transaction type.
|
||||
.It Fl k
|
||||
Examine the checkpointed state of the pool.
|
||||
Note, the on disk format of the pool is not reverted to the checkpointed state.
|
||||
.It Fl l Ar device
|
||||
Read the vdev labels from the specified device.
|
||||
.Nm Fl l
|
||||
will return 0 if valid label was found, 1 if error occurred, and 2 if no valid
|
||||
labels were found.
|
||||
.Pp
|
||||
If the
|
||||
.Fl q
|
||||
option is also specified, don't print the labels.
|
||||
.Pp
|
||||
If the
|
||||
.Fl u
|
||||
option is also specified, also display the uberblocks on this device.
|
||||
.It Fl L
|
||||
Disable leak detection and the loading of space maps.
|
||||
By default,
|
||||
.Nm
|
||||
verifies that all non-free blocks are referenced, which can be very expensive.
|
||||
.It Fl m
|
||||
Display the offset, spacemap, and free space of each metaslab.
|
||||
.It Fl mm
|
||||
Also display information about the on-disk free space histogram associated with
|
||||
each metaslab.
|
||||
.It Fl mmm
|
||||
Display the maximum contiguous free space, the in-core free space histogram, and
|
||||
the percentage of free space in each space map.
|
||||
.It Fl mmmm
|
||||
Display every spacemap record.
|
||||
.It Fl M
|
||||
Display the offset, spacemap, and free space of each metaslab.
|
||||
.It Fl MM
|
||||
Also display information about the maximum contiguous free space and the
|
||||
percentage of free space in each space map.
|
||||
.It Fl MMM
|
||||
Display every spacemap record.
|
||||
.It Fl O Ar dataset path
|
||||
Look up the specified
|
||||
.Ar path
|
||||
inside of the
|
||||
.Ar dataset
|
||||
and display its metadata and indirect blocks.
|
||||
Specified
|
||||
.Ar path
|
||||
must be relative to the root of
|
||||
.Ar dataset .
|
||||
This option can be combined with
|
||||
.Fl v
|
||||
for increasing verbosity.
|
||||
.It Xo
|
||||
.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
|
||||
.Xc
|
||||
Read and display a block from the specified device.
|
||||
By default the block is displayed as a hex dump, but see the description of the
|
||||
.Sy r
|
||||
flag, below.
|
||||
.Pp
|
||||
The block is specified in terms of a colon-separated tuple
|
||||
.Ar vdev
|
||||
.Pq an integer vdev identifier
|
||||
.Ar offset
|
||||
.Pq the offset within the vdev
|
||||
.Ar size
|
||||
.Pq the size of the block to read
|
||||
and, optionally,
|
||||
.Ar flags
|
||||
.Pq a set of flags, described below .
|
||||
.Pp
|
||||
.Bl -tag -compact -width "b offset"
|
||||
.It Sy b Ar offset
|
||||
Print block pointer
|
||||
.It Sy d
|
||||
Decompress the block
|
||||
.It Sy e
|
||||
Byte swap the block
|
||||
.It Sy g
|
||||
Dump gang block header
|
||||
.It Sy i
|
||||
Dump indirect block
|
||||
.It Sy r
|
||||
Dump raw uninterpreted block data
|
||||
.El
|
||||
.It Fl s
|
||||
Report statistics on
|
||||
.Nm zdb
|
||||
I/O.
|
||||
Display operation counts, bandwidth, and error counts of I/O to the pool from
|
||||
.Nm .
|
||||
.It Fl S
|
||||
Simulate the effects of deduplication, constructing a DDT and then display
|
||||
that DDT as with
|
||||
.Fl DD .
|
||||
.It Fl u
|
||||
Display the current uberblock.
|
||||
.El
|
||||
.Pp
|
||||
Other options:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl A
|
||||
Do not abort should any assertion fail.
|
||||
.It Fl AA
|
||||
Enable panic recovery, certain errors which would otherwise be fatal are
|
||||
demoted to warnings.
|
||||
.It Fl AAA
|
||||
Do not abort if asserts fail and also enable panic recovery.
|
||||
.It Fl e Op Fl p Ar path ...
|
||||
Operate on an exported pool, not present in
|
||||
.Pa /boot/zfs/zpool.cache .
|
||||
The
|
||||
.Fl p
|
||||
flag specifies the path under which devices are to be searched.
|
||||
.It Fl x Ar dumpdir
|
||||
All blocks accessed will be copied to files in the specified directory.
|
||||
The blocks will be placed in sparse files whose name is the same as
|
||||
that of the file or device read.
|
||||
.Nm
|
||||
can be then run on the generated files.
|
||||
Note that the
|
||||
.Fl bbc
|
||||
flags are sufficient to access
|
||||
.Pq and thus copy
|
||||
all metadata on the pool.
|
||||
.It Fl F
|
||||
Attempt to make an unreadable pool readable by trying progressively older
|
||||
transactions.
|
||||
.It Fl G
|
||||
Dump the contents of the zfs_dbgmsg buffer before exiting
|
||||
.Nm .
|
||||
zfs_dbgmsg is a buffer used by ZFS to dump advanced debug information.
|
||||
.It Fl I Ar inflight I/Os
|
||||
Limit the number of outstanding checksum I/Os to the specified value.
|
||||
The default value is 200.
|
||||
This option affects the performance of the
|
||||
.Fl c
|
||||
option.
|
||||
.It Fl o Ar var Ns = Ns Ar value ...
|
||||
Set the given global libzpool variable to the provided value.
|
||||
The value must be an unsigned 32-bit integer.
|
||||
Currently only little-endian systems are supported to avoid accidentally setting
|
||||
the high 32 bits of 64-bit variables.
|
||||
.It Fl P
|
||||
Print numbers in an unscaled form more amenable to parsing, eg. 1000000 rather
|
||||
than 1M.
|
||||
.It Fl t Ar transaction
|
||||
Specify the highest transaction to use when searching for uberblocks.
|
||||
See also the
|
||||
.Fl u
|
||||
and
|
||||
.Fl l
|
||||
options for a means to see the available uberblocks and their associated
|
||||
transaction numbers.
|
||||
.It Fl U Ar cachefile
|
||||
Use a cache file other than
|
||||
.Pa /boot/zfs/zpool.cache .
|
||||
.It Fl v
|
||||
Enable verbosity.
|
||||
Specify multiple times for increased verbosity.
|
||||
.It Fl V
|
||||
Attempt verbatim import.
|
||||
This mimics the behavior of the kernel when loading a pool from a cachefile.
|
||||
Only usable with
|
||||
.Fl e .
|
||||
.It Fl X
|
||||
Attempt
|
||||
.Qq extreme
|
||||
transaction rewind, that is attempt the same recovery as
|
||||
.Fl F
|
||||
but read transactions otherwise deemed too old.
|
||||
.El
|
||||
.Pp
|
||||
Specifying a display option more than once enables verbosity for only that
|
||||
option, with more occurrences enabling more verbosity.
|
||||
.Pp
|
||||
If no options are specified, all information about the named pool will be
|
||||
displayed at default verbosity.
|
||||
.Sh EXAMPLES
|
||||
.Bl -tag -width Ds
|
||||
.It Xo
|
||||
.Sy Example 1
|
||||
Display the configuration of imported pool
|
||||
.Pa rpool
|
||||
.Xc
|
||||
.Bd -literal
|
||||
# zdb -C rpool
|
||||
|
||||
MOS Configuration:
|
||||
version: 28
|
||||
name: 'rpool'
|
||||
...
|
||||
.Ed
|
||||
.It Xo
|
||||
.Sy Example 2
|
||||
Display basic dataset information about
|
||||
.Pa rpool
|
||||
.Xc
|
||||
.Bd -literal
|
||||
# zdb -d rpool
|
||||
Dataset mos [META], ID 0, cr_txg 4, 26.9M, 1051 objects
|
||||
Dataset rpool/swap [ZVOL], ID 59, cr_txg 356, 486M, 2 objects
|
||||
...
|
||||
.Ed
|
||||
.It Xo
|
||||
.Sy Example 3
|
||||
Display basic information about object 0 in
|
||||
.Pa rpool/export/home
|
||||
.Xc
|
||||
.Bd -literal
|
||||
# zdb -d rpool/export/home 0
|
||||
Dataset rpool/export/home [ZPL], ID 137, cr_txg 1546, 32K, 8 objects
|
||||
|
||||
Object lvl iblk dblk dsize lsize %full type
|
||||
0 7 16K 16K 15.0K 16K 25.00 DMU dnode
|
||||
.Ed
|
||||
.It Xo
|
||||
.Sy Example 4
|
||||
Display the predicted effect of enabling deduplication on
|
||||
.Pa rpool
|
||||
.Xc
|
||||
.Bd -literal
|
||||
# zdb -S rpool
|
||||
Simulated DDT histogram:
|
||||
|
||||
bucket allocated referenced
|
||||
______ ______________________________ ______________________________
|
||||
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
|
||||
------ ------ ----- ----- ----- ------ ----- ----- -----
|
||||
1 694K 27.1G 15.0G 15.0G 694K 27.1G 15.0G 15.0G
|
||||
2 35.0K 1.33G 699M 699M 74.7K 2.79G 1.45G 1.45G
|
||||
...
|
||||
dedup = 1.11, compress = 1.80, copies = 1.00, dedup * compress / copies = 2.00
|
||||
.Ed
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr zfs 8 ,
|
||||
.Xr zpool 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
utility first appeared in
|
||||
.Fx 7.0 .
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 2017 Spectra Logic Corp Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ZDB_H
|
||||
#define _ZDB_H
|
||||
|
||||
void dump_intent_log(zilog_t *);
|
||||
extern uint8_t dump_opt[256];
|
||||
|
||||
#endif /* _ZDB_H */
|
@ -1,424 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, 2017 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/abd.h>
|
||||
|
||||
#include "zdb.h"
|
||||
|
||||
extern uint8_t dump_opt[256];
|
||||
|
||||
static char tab_prefix[4] = "\t\t\t";
|
||||
|
||||
static void
|
||||
print_log_bp(const blkptr_t *bp, const char *prefix)
|
||||
{
|
||||
char blkbuf[BP_SPRINTF_LEN];
|
||||
|
||||
snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
|
||||
(void) printf("%s%s\n", prefix, blkbuf);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_create(zilog_t *zilog, int txtype, void *arg)
|
||||
{
|
||||
lr_create_t *lr = arg;
|
||||
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", tab_prefix, name, link);
|
||||
} else if (txtype != TX_MKXATTR) {
|
||||
(void) printf("%s%s\n", tab_prefix, name);
|
||||
}
|
||||
|
||||
(void) printf("%s%s", tab_prefix, ctime(&crtime));
|
||||
(void) printf("%sdoid %" PRIu64 ", foid %" PRIu64 ", slots %" PRIu64
|
||||
", mode %" PRIo64 "\n",
|
||||
tab_prefix, lr->lr_doid,
|
||||
(uint64_t)LR_FOID_GET_OBJ(lr->lr_foid),
|
||||
(uint64_t)LR_FOID_GET_SLOTS(lr->lr_foid),
|
||||
lr->lr_mode);
|
||||
(void) printf("%suid %" PRIu64 ", gid %" PRIu64 ", gen %" PRIu64
|
||||
", rdev %#" PRIx64 "\n",
|
||||
tab_prefix, lr->lr_uid, lr->lr_gid, lr->lr_gen, lr->lr_rdev);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_remove(zilog_t *zilog, int txtype, void *arg)
|
||||
{
|
||||
lr_remove_t *lr = arg;
|
||||
|
||||
(void) printf("%sdoid %llu, name %s\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_doid, (char *)(lr + 1));
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_link(zilog_t *zilog, int txtype, void *arg)
|
||||
{
|
||||
lr_link_t *lr = arg;
|
||||
|
||||
(void) printf("%sdoid %llu, link_obj %llu, name %s\n", tab_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, void *arg)
|
||||
{
|
||||
lr_rename_t *lr = arg;
|
||||
char *snm = (char *)(lr + 1);
|
||||
char *tnm = snm + strlen(snm) + 1;
|
||||
|
||||
(void) printf("%ssdoid %llu, tdoid %llu\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid);
|
||||
(void) printf("%ssrc %s tgt %s\n", tab_prefix, snm, tnm);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zil_prt_rec_write_cb(void *data, size_t len, void *unused)
|
||||
{
|
||||
char *cdata = data;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (isprint(*cdata))
|
||||
(void) printf("%c ", *cdata);
|
||||
else
|
||||
(void) printf("%2X", *cdata);
|
||||
cdata++;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_write(zilog_t *zilog, int txtype, void *arg)
|
||||
{
|
||||
lr_write_t *lr = arg;
|
||||
abd_t *data;
|
||||
blkptr_t *bp = &lr->lr_blkptr;
|
||||
zbookmark_phys_t zb;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
int error;
|
||||
|
||||
(void) printf("%sfoid %llu, offset %llx, length %llx\n", tab_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", tab_prefix,
|
||||
!BP_IS_HOLE(bp) &&
|
||||
bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa) ?
|
||||
"will claim" : "won't claim");
|
||||
print_log_bp(bp, tab_prefix);
|
||||
|
||||
if (BP_IS_HOLE(bp)) {
|
||||
(void) printf("\t\t\tLSIZE 0x%llx\n",
|
||||
(u_longlong_t)BP_GET_LSIZE(bp));
|
||||
(void) printf("%s<hole>\n", tab_prefix);
|
||||
return;
|
||||
}
|
||||
if (bp->blk_birth < zilog->zl_header->zh_claim_txg) {
|
||||
(void) printf("%s<block already committed>\n",
|
||||
tab_prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
SET_BOOKMARK(&zb, dmu_objset_id(zilog->zl_os),
|
||||
lr->lr_foid, ZB_ZIL_LEVEL,
|
||||
lr->lr_offset / BP_GET_LSIZE(bp));
|
||||
|
||||
data = abd_alloc(BP_GET_LSIZE(bp), B_FALSE);
|
||||
error = zio_wait(zio_read(NULL, zilog->zl_spa,
|
||||
bp, data, BP_GET_LSIZE(bp), NULL, NULL,
|
||||
ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL, &zb));
|
||||
if (error)
|
||||
goto out;
|
||||
} else {
|
||||
/* data is stored after the end of the lr_write record */
|
||||
data = abd_alloc(lr->lr_length, B_FALSE);
|
||||
abd_copy_from_buf(data, lr + 1, lr->lr_length);
|
||||
}
|
||||
|
||||
(void) printf("%s", tab_prefix);
|
||||
(void) abd_iterate_func(data,
|
||||
0, MIN(lr->lr_length, (verbose < 6 ? 20 : SPA_MAXBLOCKSIZE)),
|
||||
zil_prt_rec_write_cb, NULL);
|
||||
(void) printf("\n");
|
||||
|
||||
out:
|
||||
abd_free(data);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_truncate(zilog_t *zilog, int txtype, void *arg)
|
||||
{
|
||||
lr_truncate_t *lr = arg;
|
||||
|
||||
(void) printf("%sfoid %llu, offset 0x%llx, length 0x%llx\n", tab_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, void *arg)
|
||||
{
|
||||
lr_setattr_t *lr = arg;
|
||||
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", tab_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", tab_prefix,
|
||||
(longlong_t)lr->lr_mode);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_UID) {
|
||||
(void) printf("%sAT_UID %llu\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_uid);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_GID) {
|
||||
(void) printf("%sAT_GID %llu\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_gid);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_SIZE) {
|
||||
(void) printf("%sAT_SIZE %llu\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_size);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_ATIME) {
|
||||
(void) printf("%sAT_ATIME %llu.%09llu %s", tab_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", tab_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, void *arg)
|
||||
{
|
||||
lr_acl_t *lr = arg;
|
||||
|
||||
(void) printf("%sfoid %llu, aclcnt %llu\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt);
|
||||
}
|
||||
|
||||
typedef void (*zil_prt_rec_func_t)(zilog_t *, int, void *);
|
||||
typedef struct zil_rec_info {
|
||||
zil_prt_rec_func_t zri_print;
|
||||
const char *zri_name;
|
||||
uint64_t zri_count;
|
||||
} zil_rec_info_t;
|
||||
|
||||
static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
|
||||
{.zri_print = NULL, .zri_name = "Total "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKXATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_SYMLINK "},
|
||||
{.zri_print = zil_prt_rec_remove, .zri_name = "TX_REMOVE "},
|
||||
{.zri_print = zil_prt_rec_remove, .zri_name = "TX_RMDIR "},
|
||||
{.zri_print = zil_prt_rec_link, .zri_name = "TX_LINK "},
|
||||
{.zri_print = zil_prt_rec_rename, .zri_name = "TX_RENAME "},
|
||||
{.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE "},
|
||||
{.zri_print = zil_prt_rec_truncate, .zri_name = "TX_TRUNCATE "},
|
||||
{.zri_print = zil_prt_rec_setattr, .zri_name = "TX_SETATTR "},
|
||||
{.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_V0 "},
|
||||
{.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_ACL "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL_ATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL_ATTR "},
|
||||
{.zri_print = zil_prt_rec_write, .zri_name = "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']);
|
||||
const char *claim;
|
||||
|
||||
if (verbose <= 3)
|
||||
return (0);
|
||||
|
||||
if (verbose >= 5) {
|
||||
(void) strcpy(blkbuf, ", ");
|
||||
snprintf_blkptr(blkbuf + strlen(blkbuf),
|
||||
sizeof (blkbuf) - strlen(blkbuf), bp);
|
||||
} else {
|
||||
blkbuf[0] = '\0';
|
||||
}
|
||||
|
||||
if (claim_txg != 0)
|
||||
claim = "already claimed";
|
||||
else if (bp->blk_birth >= spa_min_claim_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)
|
||||
{
|
||||
unsigned 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 (BP_IS_HOLE(&zh->zh_log) || 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;
|
||||
|
||||
/* see comment in zil_claim() or zil_check_log_chain() */
|
||||
if (zilog->zl_spa->spa_uberblock.ub_checkpoint_txg != 0 &&
|
||||
zh->zh_claim_txg == 0)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,551 +0,0 @@
|
||||
.\" This file and its contents are supplied under the terms of the
|
||||
.\" Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
.\" You may only use this file in accordance with the terms of version
|
||||
.\" 1.0 of the CDDL.
|
||||
.\"
|
||||
.\" A full copy of the text of the CDDL should have accompanied this
|
||||
.\" source. A copy of the CDDL is also available via the Internet at
|
||||
.\" http://www.illumos.org/license/CDDL.
|
||||
.\"
|
||||
.\"
|
||||
.\" Copyright (c) 2016, 2017 by Delphix. All rights reserved.
|
||||
.\" Copyright (c) 2018 Datto Inc.
|
||||
.\"
|
||||
.Dd April 18, 2020
|
||||
.Dt ZFS-PROGRAM 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm zfs program
|
||||
.Nd executes ZFS channel programs
|
||||
.Sh SYNOPSIS
|
||||
.Cm zfs program
|
||||
.Op Fl jn
|
||||
.Op Fl t Ar instruction-limit
|
||||
.Op Fl m Ar memory-limit
|
||||
.Ar pool
|
||||
.Ar script
|
||||
.\".Op Ar optional arguments to channel program
|
||||
.Sh DESCRIPTION
|
||||
The ZFS channel program interface allows ZFS administrative operations to be
|
||||
run programmatically as a Lua script.
|
||||
The entire script is executed atomically, with no other administrative
|
||||
operations taking effect concurrently.
|
||||
A library of ZFS calls is made available to channel program scripts.
|
||||
Channel programs may only be run with root privileges.
|
||||
.Pp
|
||||
A modified version of the Lua 5.2 interpreter is used to run channel program
|
||||
scripts.
|
||||
The Lua 5.2 manual can be found at:
|
||||
.Bd -centered -offset indent
|
||||
.Lk http://www.lua.org/manual/5.2/
|
||||
.Ed
|
||||
.Pp
|
||||
The channel program given by
|
||||
.Ar script
|
||||
will be run on
|
||||
.Ar pool ,
|
||||
and any attempts to access or modify other pools will cause an error.
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width "-t"
|
||||
.It Fl j
|
||||
Display channel program output in JSON format.
|
||||
When this flag is specified and standard output is empty -
|
||||
channel program encountered an error.
|
||||
The details of such an error will be printed to standard error in plain text.
|
||||
.It Fl n
|
||||
Executes a read-only channel program, which runs faster.
|
||||
The program cannot change on-disk state by calling functions from the
|
||||
zfs.sync submodule.
|
||||
The program can be used to gather information such as properties and
|
||||
determining if changes would succeed (zfs.check.*).
|
||||
Without this flag, all pending changes must be synced to disk before a
|
||||
channel program can complete.
|
||||
.It Fl t Ar instruction-limit
|
||||
Execution time limit, in number of Lua instructions to execute.
|
||||
If a channel program executes more than the specified number of instructions,
|
||||
it will be stopped and an error will be returned.
|
||||
The default limit is 10 million instructions, and it can be set to a maximum of
|
||||
100 million instructions.
|
||||
.It Fl m Ar memory-limit
|
||||
Memory limit, in bytes.
|
||||
If a channel program attempts to allocate more memory than the given limit, it
|
||||
will be stopped and an error returned.
|
||||
The default memory limit is 10 MB, and can be set to a maximum of 100 MB.
|
||||
.El
|
||||
.Pp
|
||||
All remaining argument strings will be passed directly to the Lua script as
|
||||
described in the
|
||||
.Sx LUA INTERFACE
|
||||
section below.
|
||||
.Sh LUA INTERFACE
|
||||
A channel program can be invoked either from the command line, or via a library
|
||||
call to
|
||||
.Fn lzc_channel_program .
|
||||
.Ss Arguments
|
||||
Arguments passed to the channel program are converted to a Lua table.
|
||||
If invoked from the command line, extra arguments to the Lua script will be
|
||||
accessible as an array stored in the argument table with the key 'argv':
|
||||
.Bd -literal -offset indent
|
||||
args = ...
|
||||
argv = args["argv"]
|
||||
-- argv == {1="arg1", 2="arg2", ...}
|
||||
.Ed
|
||||
.Pp
|
||||
If invoked from the libZFS interface, an arbitrary argument list can be
|
||||
passed to the channel program, which is accessible via the same
|
||||
"..." syntax in Lua:
|
||||
.Bd -literal -offset indent
|
||||
args = ...
|
||||
-- args == {"foo"="bar", "baz"={...}, ...}
|
||||
.Ed
|
||||
.Pp
|
||||
Note that because Lua arrays are 1-indexed, arrays passed to Lua from the
|
||||
libZFS interface will have their indices incremented by 1.
|
||||
That is, the element
|
||||
in
|
||||
.Va arr[0]
|
||||
in a C array passed to a channel program will be stored in
|
||||
.Va arr[1]
|
||||
when accessed from Lua.
|
||||
.Ss Return Values
|
||||
Lua return statements take the form:
|
||||
.Bd -literal -offset indent
|
||||
return ret0, ret1, ret2, ...
|
||||
.Ed
|
||||
.Pp
|
||||
Return statements returning multiple values are permitted internally in a
|
||||
channel program script, but attempting to return more than one value from the
|
||||
top level of the channel program is not permitted and will throw an error.
|
||||
However, tables containing multiple values can still be returned.
|
||||
If invoked from the command line, a return statement:
|
||||
.Bd -literal -offset indent
|
||||
a = {foo="bar", baz=2}
|
||||
return a
|
||||
.Ed
|
||||
.Pp
|
||||
Will be output formatted as:
|
||||
.Bd -literal -offset indent
|
||||
Channel program fully executed with return value:
|
||||
return:
|
||||
baz: 2
|
||||
foo: 'bar'
|
||||
.Ed
|
||||
.Ss Fatal Errors
|
||||
If the channel program encounters a fatal error while running, a non-zero exit
|
||||
status will be returned.
|
||||
If more information about the error is available, a singleton list will be
|
||||
returned detailing the error:
|
||||
.Bd -literal -offset indent
|
||||
error: "error string, including Lua stack trace"
|
||||
.Ed
|
||||
.Pp
|
||||
If a fatal error is returned, the channel program may have not executed at all,
|
||||
may have partially executed, or may have fully executed but failed to pass a
|
||||
return value back to userland.
|
||||
.Pp
|
||||
If the channel program exhausts an instruction or memory limit, a fatal error
|
||||
will be generated and the program will be stopped, leaving the program partially
|
||||
executed.
|
||||
No attempt is made to reverse or undo any operations already performed.
|
||||
Note that because both the instruction count and amount of memory used by a
|
||||
channel program are deterministic when run against the same inputs and
|
||||
filesystem state, as long as a channel program has run successfully once, you
|
||||
can guarantee that it will finish successfully against a similar size system.
|
||||
.Pp
|
||||
If a channel program attempts to return too large a value, the program will
|
||||
fully execute but exit with a nonzero status code and no return value.
|
||||
.Pp
|
||||
.Em Note:
|
||||
ZFS API functions do not generate Fatal Errors when correctly invoked, they
|
||||
return an error code and the channel program continues executing.
|
||||
See the
|
||||
.Sx ZFS API
|
||||
section below for function-specific details on error return codes.
|
||||
.Ss Lua to C Value Conversion
|
||||
When invoking a channel program via the libZFS interface, it is necessary to
|
||||
translate arguments and return values from Lua values to their C equivalents,
|
||||
and vice-versa.
|
||||
.Pp
|
||||
There is a correspondence between nvlist values in C and Lua tables.
|
||||
A Lua table which is returned from the channel program will be recursively
|
||||
converted to an nvlist, with table values converted to their natural
|
||||
equivalents:
|
||||
.Bd -literal -offset indent
|
||||
string -> string
|
||||
number -> int64
|
||||
boolean -> boolean_value
|
||||
nil -> boolean (no value)
|
||||
table -> nvlist
|
||||
.Ed
|
||||
.Pp
|
||||
Likewise, table keys are replaced by string equivalents as follows:
|
||||
.Bd -literal -offset indent
|
||||
string -> no change
|
||||
number -> signed decimal string ("%lld")
|
||||
boolean -> "true" | "false"
|
||||
.Ed
|
||||
.Pp
|
||||
Any collision of table key strings (for example, the string "true" and a
|
||||
true boolean value) will cause a fatal error.
|
||||
.Pp
|
||||
Lua numbers are represented internally as signed 64-bit integers.
|
||||
.Sh LUA STANDARD LIBRARY
|
||||
The following Lua built-in base library functions are available:
|
||||
.Bd -literal -offset indent
|
||||
assert rawlen
|
||||
collectgarbage rawget
|
||||
error rawset
|
||||
getmetatable select
|
||||
ipairs setmetatable
|
||||
next tonumber
|
||||
pairs tostring
|
||||
rawequal type
|
||||
.Ed
|
||||
.Pp
|
||||
All functions in the
|
||||
.Em coroutine ,
|
||||
.Em string ,
|
||||
and
|
||||
.Em table
|
||||
built-in submodules are also available.
|
||||
A complete list and documentation of these modules is available in the Lua
|
||||
manual.
|
||||
.Pp
|
||||
The following functions base library functions have been disabled and are
|
||||
not available for use in channel programs:
|
||||
.Bd -literal -offset indent
|
||||
dofile
|
||||
loadfile
|
||||
load
|
||||
pcall
|
||||
print
|
||||
xpcall
|
||||
.Ed
|
||||
.Sh ZFS API
|
||||
.Ss Function Arguments
|
||||
Each API function takes a fixed set of required positional arguments and
|
||||
optional keyword arguments.
|
||||
For example, the destroy function takes a single positional string argument
|
||||
(the name of the dataset to destroy) and an optional "defer" keyword boolean
|
||||
argument.
|
||||
When using parentheses to specify the arguments to a Lua function, only
|
||||
positional arguments can be used:
|
||||
.Bd -literal -offset indent
|
||||
zfs.sync.destroy("rpool@snap")
|
||||
.Ed
|
||||
.Pp
|
||||
To use keyword arguments, functions must be called with a single argument that
|
||||
is a Lua table containing entries mapping integers to positional arguments and
|
||||
strings to keyword arguments:
|
||||
.Bd -literal -offset indent
|
||||
zfs.sync.destroy({1="rpool@snap", defer=true})
|
||||
.Ed
|
||||
.Pp
|
||||
The Lua language allows curly braces to be used in place of parenthesis as
|
||||
syntactic sugar for this calling convention:
|
||||
.Bd -literal -offset indent
|
||||
zfs.sync.snapshot{"rpool@snap", defer=true}
|
||||
.Ed
|
||||
.Ss Function Return Values
|
||||
If an API function succeeds, it returns 0.
|
||||
If it fails, it returns an error code and the channel program continues
|
||||
executing.
|
||||
API functions do not generate Fatal Errors except in the case of an
|
||||
unrecoverable internal file system error.
|
||||
.Pp
|
||||
In addition to returning an error code, some functions also return extra
|
||||
details describing what caused the error.
|
||||
This extra description is given as a second return value, and will always be a
|
||||
Lua table, or Nil if no error details were returned.
|
||||
Different keys will exist in the error details table depending on the function
|
||||
and error case.
|
||||
Any such function may be called expecting a single return value:
|
||||
.Bd -literal -offset indent
|
||||
errno = zfs.sync.promote(dataset)
|
||||
.Ed
|
||||
.Pp
|
||||
Or, the error details can be retrieved:
|
||||
.Bd -literal -offset indent
|
||||
errno, details = zfs.sync.promote(dataset)
|
||||
if (errno == EEXIST) then
|
||||
assert(details ~= Nil)
|
||||
list_of_conflicting_snapshots = details
|
||||
end
|
||||
.Ed
|
||||
.Pp
|
||||
The following global aliases for API function error return codes are defined
|
||||
for use in channel programs:
|
||||
.Bd -literal -offset indent
|
||||
EPERM ECHILD ENODEV ENOSPC
|
||||
ENOENT EAGAIN ENOTDIR ESPIPE
|
||||
ESRCH ENOMEM EISDIR EROFS
|
||||
EINTR EACCES EINVAL EMLINK
|
||||
EIO EFAULT ENFILE EPIPE
|
||||
ENXIO ENOTBLK EMFILE EDOM
|
||||
E2BIG EBUSY ENOTTY ERANGE
|
||||
ENOEXEC EEXIST ETXTBSY EDQUOT
|
||||
EBADF EXDEV EFBIG
|
||||
.Ed
|
||||
.Ss API Functions
|
||||
For detailed descriptions of the exact behavior of any zfs administrative
|
||||
operations, see the main
|
||||
.Xr zfs 8
|
||||
manual page.
|
||||
.Bl -tag -width "xx"
|
||||
.It Em zfs.debug(msg)
|
||||
Record a debug message in the zfs_dbgmsg log.
|
||||
A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or
|
||||
can be monitored live by running:
|
||||
.Bd -literal -offset indent
|
||||
dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}'
|
||||
.Ed
|
||||
.Pp
|
||||
msg (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Debug message to be printed.
|
||||
.Ed
|
||||
.It Em zfs.exists(dataset)
|
||||
Returns true if the given dataset exists, or false if it doesn't.
|
||||
A fatal error will be thrown if the dataset is not in the target pool.
|
||||
That is, in a channel program running on rpool,
|
||||
zfs.exists("rpool/nonexistent_fs") returns false, but
|
||||
zfs.exists("somepool/fs_that_may_exist") will error.
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Dataset to check for existence.
|
||||
Must be in the target pool.
|
||||
.Ed
|
||||
.It Em zfs.get_prop(dataset, property)
|
||||
Returns two values.
|
||||
First, a string, number or table containing the property value for the given
|
||||
dataset.
|
||||
Second, a string containing the source of the property (i.e. the name of the
|
||||
dataset in which it was set or nil if it is readonly).
|
||||
Throws a Lua error if the dataset is invalid or the property doesn't exist.
|
||||
Note that Lua only supports int64 number types whereas ZFS number properties
|
||||
are uint64.
|
||||
This means very large values (like guid) may wrap around and appear negative.
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Filesystem or snapshot path to retrieve properties from.
|
||||
.Ed
|
||||
.Pp
|
||||
property (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Name of property to retrieve.
|
||||
All filesystem, snapshot and volume properties are supported except
|
||||
for 'mounted' and 'iscsioptions.'
|
||||
Also supports the 'written@snap' and 'written#bookmark' properties and
|
||||
the '<user|group><quota|used>@id' properties, though the id must be in numeric
|
||||
form.
|
||||
.Ed
|
||||
.El
|
||||
.Bl -tag -width "xx"
|
||||
.It Sy zfs.sync submodule
|
||||
The sync submodule contains functions that modify the on-disk state.
|
||||
They are executed in "syncing context".
|
||||
.Pp
|
||||
The available sync submodule functions are as follows:
|
||||
.Bl -tag -width "xx"
|
||||
.It Em zfs.sync.destroy(dataset, [defer=true|false])
|
||||
Destroy the given dataset.
|
||||
Returns 0 on successful destroy, or a nonzero error code if the dataset could
|
||||
not be destroyed (for example, if the dataset has any active children or
|
||||
clones).
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Filesystem or snapshot to be destroyed.
|
||||
.Ed
|
||||
.Pp
|
||||
[optional] defer (boolean)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Valid only for destroying snapshots.
|
||||
If set to true, and the snapshot has holds or clones, allows the snapshot to be
|
||||
marked for deferred deletion rather than failing.
|
||||
.Ed
|
||||
.It Em zfs.sync.promote(dataset)
|
||||
Promote the given clone to a filesystem.
|
||||
Returns 0 on successful promotion, or a nonzero error code otherwise.
|
||||
If EEXIST is returned, the second return value will be an array of the clone's
|
||||
snapshots whose names collide with snapshots of the parent filesystem.
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Clone to be promoted.
|
||||
.Ed
|
||||
.It Em zfs.sync.rollback(filesystem)
|
||||
Rollback to the previous snapshot for a dataset.
|
||||
Returns 0 on successful rollback, or a nonzero error code otherwise.
|
||||
Rollbacks can be performed on filesystems or zvols, but not on snapshots
|
||||
or mounted datasets.
|
||||
EBUSY is returned in the case where the filesystem is mounted.
|
||||
.Pp
|
||||
filesystem (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Filesystem to rollback.
|
||||
.Ed
|
||||
.It Em zfs.sync.snapshot(dataset)
|
||||
Create a snapshot of a filesystem.
|
||||
Returns 0 if the snapshot was successfully created,
|
||||
and a nonzero error code otherwise.
|
||||
.Pp
|
||||
Note: Taking a snapshot will fail on any pool older than legacy version 27.
|
||||
To enable taking snapshots from ZCP scripts, the pool must be upgraded.
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Name of snapshot to create.
|
||||
.Ed
|
||||
.El
|
||||
.It Sy zfs.check submodule
|
||||
For each function in the zfs.sync submodule, there is a corresponding zfs.check
|
||||
function which performs a "dry run" of the same operation.
|
||||
Each takes the same arguments as its zfs.sync counterpart and returns 0 if the
|
||||
operation would succeed, or a non-zero error code if it would fail, along with
|
||||
any other error details.
|
||||
That is, each has the same behavior as the corresponding sync function except
|
||||
for actually executing the requested change.
|
||||
For example,
|
||||
.Em zfs.check.destroy("fs")
|
||||
returns 0 if
|
||||
.Em zfs.sync.destroy("fs")
|
||||
would successfully destroy the dataset.
|
||||
.Pp
|
||||
The available zfs.check functions are:
|
||||
.Bl -tag -width "xx"
|
||||
.It Em zfs.check.destroy(dataset, [defer=true|false])
|
||||
.It Em zfs.check.promote(dataset)
|
||||
.It Em zfs.check.rollback(filesystem)
|
||||
.It Em zfs.check.snapshot(dataset)
|
||||
.El
|
||||
.It Sy zfs.list submodule
|
||||
The zfs.list submodule provides functions for iterating over datasets and
|
||||
properties.
|
||||
Rather than returning tables, these functions act as Lua iterators, and are
|
||||
generally used as follows:
|
||||
.Bd -literal -offset indent
|
||||
for child in zfs.list.children("rpool") do
|
||||
...
|
||||
end
|
||||
.Ed
|
||||
.Pp
|
||||
The available zfs.list functions are:
|
||||
.Bl -tag -width "xx"
|
||||
.It Em zfs.list.clones(snapshot)
|
||||
Iterate through all clones of the given snapshot.
|
||||
.Pp
|
||||
snapshot (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Must be a valid snapshot path in the current pool.
|
||||
.Ed
|
||||
.It Em zfs.list.snapshots(dataset)
|
||||
Iterate through all snapshots of the given dataset.
|
||||
Each snapshot is returned as a string containing the full dataset name, e.g.
|
||||
"pool/fs@snap".
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Must be a valid filesystem or volume.
|
||||
.Ed
|
||||
.It Em zfs.list.children(dataset)
|
||||
Iterate through all direct children of the given dataset.
|
||||
Each child is returned as a string containing the full dataset name, e.g.
|
||||
"pool/fs/child".
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Must be a valid filesystem or volume.
|
||||
.Ed
|
||||
.It Em zfs.list.properties(dataset)
|
||||
Iterate through all user properties for the given dataset.
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Must be a valid filesystem, snapshot, or volume.
|
||||
.Ed
|
||||
.It Em zfs.list.system_properties(dataset)
|
||||
Returns an array of strings, the names of the valid system (non-user defined)
|
||||
properties for the given dataset.
|
||||
Throws a Lua error if the dataset is invalid.
|
||||
.Pp
|
||||
dataset (string)
|
||||
.Bd -ragged -compact -offset "xxxx"
|
||||
Must be a valid filesystem, snapshot or volume.
|
||||
.Ed
|
||||
.El
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
.Ss Example 1
|
||||
The following channel program recursively destroys a filesystem and all its
|
||||
snapshots and children in a naive manner.
|
||||
Note that this does not involve any error handling or reporting.
|
||||
.Bd -literal -offset indent
|
||||
function destroy_recursive(root)
|
||||
for child in zfs.list.children(root) do
|
||||
destroy_recursive(child)
|
||||
end
|
||||
for snap in zfs.list.snapshots(root) do
|
||||
zfs.sync.destroy(snap)
|
||||
end
|
||||
zfs.sync.destroy(root)
|
||||
end
|
||||
destroy_recursive("pool/somefs")
|
||||
.Ed
|
||||
.Ss Example 2
|
||||
A more verbose and robust version of the same channel program, which
|
||||
properly detects and reports errors, and also takes the dataset to destroy
|
||||
as a command line argument, would be as follows:
|
||||
.Bd -literal -offset indent
|
||||
succeeded = {}
|
||||
failed = {}
|
||||
|
||||
function destroy_recursive(root)
|
||||
for child in zfs.list.children(root) do
|
||||
destroy_recursive(child)
|
||||
end
|
||||
for snap in zfs.list.snapshots(root) do
|
||||
err = zfs.sync.destroy(snap)
|
||||
if (err ~= 0) then
|
||||
failed[snap] = err
|
||||
else
|
||||
succeeded[snap] = err
|
||||
end
|
||||
end
|
||||
err = zfs.sync.destroy(root)
|
||||
if (err ~= 0) then
|
||||
failed[root] = err
|
||||
else
|
||||
succeeded[root] = err
|
||||
end
|
||||
end
|
||||
|
||||
args = ...
|
||||
argv = args["argv"]
|
||||
|
||||
destroy_recursive(argv[1])
|
||||
|
||||
results = {}
|
||||
results["succeeded"] = succeeded
|
||||
results["failed"] = failed
|
||||
return results
|
||||
.Ed
|
||||
.Ss Example 3
|
||||
The following function performs a forced promote operation by attempting to
|
||||
promote the given clone and destroying any conflicting snapshots.
|
||||
.Bd -literal -offset indent
|
||||
function force_promote(ds)
|
||||
errno, details = zfs.check.promote(ds)
|
||||
if (errno == EEXIST) then
|
||||
assert(details ~= Nil)
|
||||
for i, snap in ipairs(details) do
|
||||
zfs.sync.destroy(ds .. "@" .. snap)
|
||||
end
|
||||
elseif (errno ~= 0) then
|
||||
return errno
|
||||
end
|
||||
return zfs.sync.promote(ds)
|
||||
end
|
||||
.Ed
|
File diff suppressed because it is too large
Load Diff
@ -1,497 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
|
||||
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. 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 boolean_t
|
||||
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;
|
||||
boolean_t should_close = B_TRUE;
|
||||
boolean_t include_snaps = zfs_include_snapshots(zhp, cb);
|
||||
boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);
|
||||
|
||||
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),
|
||||
(cb->cb_flags & ZFS_ITER_LITERAL_PROPS))
|
||||
!= 0) {
|
||||
free(node);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
uu_avl_insert(cb->cb_avl, node, idx);
|
||||
should_close = B_FALSE;
|
||||
} 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 |
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_snaps)
|
||||
(void) zfs_iter_snapshots(zhp,
|
||||
(cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
|
||||
data, 0, 0);
|
||||
if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks)
|
||||
(void) zfs_iter_bookmarks(zhp, zfs_callback, data);
|
||||
cb->cb_depth--;
|
||||
}
|
||||
|
||||
if (should_close)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
boolean_t
|
||||
zfs_sort_only_by_name(const zfs_sort_column_t *sc)
|
||||
{
|
||||
|
||||
return (sc != NULL && sc->sc_next == NULL &&
|
||||
sc->sc_prop == ZFS_PROP_NAME);
|
||||
}
|
||||
|
||||
/* 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 && (lat != NULL || rat != NULL)) {
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* Both lcreate and rcreate being 0 means we don't have
|
||||
* properties and we should compare full name.
|
||||
*/
|
||||
if (lcreate == 0 && rcreate == 0)
|
||||
ret = strcmp(lat + 1, rat + 1);
|
||||
else 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 (psc->sc_prop == ZFS_PROP_NAME) {
|
||||
lvalid = rvalid = B_TRUE;
|
||||
|
||||
(void) strlcpy(lbuf, zfs_get_name(l), sizeof (lbuf));
|
||||
(void) strlcpy(rbuf, zfs_get_name(r), sizeof (rbuf));
|
||||
|
||||
lstr = lbuf;
|
||||
rstr = rbuf;
|
||||
} 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 or
|
||||
* bookmarks, then we can take volumes as well.
|
||||
*/
|
||||
argtype = types;
|
||||
if (flags & ZFS_ITER_RECURSE) {
|
||||
argtype |= ZFS_TYPE_FILESYSTEM;
|
||||
if (types & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK))
|
||||
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);
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
|
||||
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#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)
|
||||
#define ZFS_ITER_SIMPLE (1 << 5)
|
||||
#define ZFS_ITER_LITERAL_PROPS (1 << 6)
|
||||
|
||||
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 *);
|
||||
boolean_t zfs_sort_only_by_name(const zfs_sort_column_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZFS_ITER_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
extern libzfs_handle_t *g_zfs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZFS_UTIL_H */
|
@ -1,535 +0,0 @@
|
||||
/*
|
||||
* 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) 2011, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* zhack is a debugging tool that can write changes to ZFS pool using libzpool
|
||||
* for testing purposes. Altering pools with zhack is unsupported and may
|
||||
* result in corrupted pools.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/zap.h>
|
||||
#include <sys/zfs_znode.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/vdev.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dsl_pool.h>
|
||||
#include <sys/zio_checksum.h>
|
||||
#include <sys/zio_compress.h>
|
||||
#include <sys/zfeature.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#undef verify
|
||||
#include <libzfs.h>
|
||||
|
||||
extern boolean_t zfeature_checks_disable;
|
||||
|
||||
const char cmdname[] = "zhack";
|
||||
libzfs_handle_t *g_zfs;
|
||||
static importargs_t g_importargs;
|
||||
static char *g_pool;
|
||||
static boolean_t g_readonly;
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
(void) fprintf(stderr,
|
||||
"Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
|
||||
"where <subcommand> <args> is one of the following:\n"
|
||||
"\n", cmdname);
|
||||
|
||||
(void) fprintf(stderr,
|
||||
" feature stat <pool>\n"
|
||||
" print information about enabled features\n"
|
||||
" feature enable [-d desc] <pool> <feature>\n"
|
||||
" add a new enabled feature to the pool\n"
|
||||
" -d <desc> sets the feature's description\n"
|
||||
" feature ref [-md] <pool> <feature>\n"
|
||||
" change the refcount on the given feature\n"
|
||||
" -d decrease instead of increase the refcount\n"
|
||||
" -m add the feature to the label if increasing refcount\n"
|
||||
"\n"
|
||||
" <feature> : should be a feature guid\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fatal(spa_t *spa, void *tag, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (spa != NULL) {
|
||||
spa_close(spa, tag);
|
||||
(void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
(void) fprintf(stderr, "%s: ", cmdname);
|
||||
(void) vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
(void) fprintf(stderr, "\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
space_delta_cb(dmu_object_type_t bonustype, void *data,
|
||||
uint64_t *userp, uint64_t *groupp)
|
||||
{
|
||||
/*
|
||||
* Is it a valid type of object to track?
|
||||
*/
|
||||
if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
|
||||
return (ENOENT);
|
||||
(void) fprintf(stderr, "modifying object that needs user accounting");
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* Target is the dataset whose pool we want to open.
|
||||
*/
|
||||
static void
|
||||
zhack_import(char *target, boolean_t readonly)
|
||||
{
|
||||
nvlist_t *config;
|
||||
nvlist_t *props;
|
||||
int error;
|
||||
|
||||
kernel_init(readonly ? FREAD : (FREAD | FWRITE));
|
||||
g_zfs = libzfs_init();
|
||||
ASSERT(g_zfs != NULL);
|
||||
|
||||
dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
|
||||
|
||||
g_readonly = readonly;
|
||||
g_importargs.unique = B_TRUE;
|
||||
g_importargs.can_be_active = readonly;
|
||||
g_pool = strdup(target);
|
||||
|
||||
error = zpool_tryimport(g_zfs, target, &config, &g_importargs);
|
||||
if (error)
|
||||
fatal(NULL, FTAG, "cannot import '%s': %s", target,
|
||||
libzfs_error_description(g_zfs));
|
||||
|
||||
props = NULL;
|
||||
if (readonly) {
|
||||
VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
|
||||
VERIFY(nvlist_add_uint64(props,
|
||||
zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
|
||||
}
|
||||
|
||||
zfeature_checks_disable = B_TRUE;
|
||||
error = spa_import(target, config, props,
|
||||
(readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
|
||||
zfeature_checks_disable = B_FALSE;
|
||||
if (error == EEXIST)
|
||||
error = 0;
|
||||
|
||||
if (error)
|
||||
fatal(NULL, FTAG, "can't import '%s': %s", target,
|
||||
strerror(error));
|
||||
}
|
||||
|
||||
static void
|
||||
zhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa)
|
||||
{
|
||||
int err;
|
||||
|
||||
zhack_import(target, readonly);
|
||||
|
||||
zfeature_checks_disable = B_TRUE;
|
||||
err = spa_open(target, spa, tag);
|
||||
zfeature_checks_disable = B_FALSE;
|
||||
|
||||
if (err != 0)
|
||||
fatal(*spa, FTAG, "cannot open '%s': %s", target,
|
||||
strerror(err));
|
||||
if (spa_version(*spa) < SPA_VERSION_FEATURES) {
|
||||
fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
|
||||
target, (int)spa_version(*spa));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dump_obj(objset_t *os, uint64_t obj, const char *name)
|
||||
{
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
|
||||
(void) printf("%s_obj:\n", name);
|
||||
|
||||
for (zap_cursor_init(&zc, os, obj);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
if (za.za_integer_length == 8) {
|
||||
ASSERT(za.za_num_integers == 1);
|
||||
(void) printf("\t%s = %llu\n",
|
||||
za.za_name, (u_longlong_t)za.za_first_integer);
|
||||
} else {
|
||||
ASSERT(za.za_integer_length == 1);
|
||||
char val[1024];
|
||||
VERIFY(zap_lookup(os, obj, za.za_name,
|
||||
1, sizeof (val), val) == 0);
|
||||
(void) printf("\t%s = %s\n", za.za_name, val);
|
||||
}
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_mos(spa_t *spa)
|
||||
{
|
||||
nvlist_t *nv = spa->spa_label_features;
|
||||
|
||||
(void) printf("label config:\n");
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
|
||||
pair != NULL;
|
||||
pair = nvlist_next_nvpair(nv, pair)) {
|
||||
(void) printf("\t%s\n", nvpair_name(pair));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
zhack_do_feature_stat(int argc, char **argv)
|
||||
{
|
||||
spa_t *spa;
|
||||
objset_t *os;
|
||||
char *target;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (argc < 1) {
|
||||
(void) fprintf(stderr, "error: missing pool name\n");
|
||||
usage();
|
||||
}
|
||||
target = argv[0];
|
||||
|
||||
zhack_spa_open(target, B_TRUE, FTAG, &spa);
|
||||
os = spa->spa_meta_objset;
|
||||
|
||||
dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
|
||||
dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
|
||||
dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
|
||||
if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
|
||||
dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
|
||||
}
|
||||
dump_mos(spa);
|
||||
|
||||
spa_close(spa, FTAG);
|
||||
}
|
||||
|
||||
static void
|
||||
zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
|
||||
zfeature_info_t *feature = arg;
|
||||
|
||||
feature_enable_sync(spa, feature, tx);
|
||||
|
||||
spa_history_log_internal(spa, "zhack enable feature", tx,
|
||||
"guid=%s flags=%x",
|
||||
feature->fi_guid, feature->fi_flags);
|
||||
}
|
||||
|
||||
static void
|
||||
zhack_do_feature_enable(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
char *desc, *target;
|
||||
spa_t *spa;
|
||||
objset_t *mos;
|
||||
zfeature_info_t feature;
|
||||
spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
|
||||
|
||||
/*
|
||||
* Features are not added to the pool's label until their refcounts
|
||||
* are incremented, so fi_mos can just be left as false for now.
|
||||
*/
|
||||
desc = NULL;
|
||||
feature.fi_uname = "zhack";
|
||||
feature.fi_flags = 0;
|
||||
feature.fi_depends = nodeps;
|
||||
feature.fi_feature = SPA_FEATURE_NONE;
|
||||
|
||||
optind = 1;
|
||||
while ((c = getopt(argc, argv, "rmd:")) != -1) {
|
||||
switch (c) {
|
||||
case 'r':
|
||||
feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
|
||||
break;
|
||||
case 'd':
|
||||
desc = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (desc == NULL)
|
||||
desc = strdup("zhack injected");
|
||||
feature.fi_desc = desc;
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 2) {
|
||||
(void) fprintf(stderr, "error: missing feature or pool name\n");
|
||||
usage();
|
||||
}
|
||||
target = argv[0];
|
||||
feature.fi_guid = argv[1];
|
||||
|
||||
if (!zfeature_is_valid_guid(feature.fi_guid))
|
||||
fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
|
||||
|
||||
zhack_spa_open(target, B_FALSE, FTAG, &spa);
|
||||
mos = spa->spa_meta_objset;
|
||||
|
||||
if (zfeature_is_supported(feature.fi_guid))
|
||||
fatal(spa, FTAG, "'%s' is a real feature, will not enable");
|
||||
if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
|
||||
fatal(spa, FTAG, "feature already enabled: %s",
|
||||
feature.fi_guid);
|
||||
|
||||
VERIFY0(dsl_sync_task(spa_name(spa), NULL,
|
||||
zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
|
||||
|
||||
spa_close(spa, FTAG);
|
||||
|
||||
free(desc);
|
||||
}
|
||||
|
||||
static void
|
||||
feature_incr_sync(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
|
||||
zfeature_info_t *feature = arg;
|
||||
uint64_t refcount;
|
||||
|
||||
VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
|
||||
feature_sync(spa, feature, refcount + 1, tx);
|
||||
spa_history_log_internal(spa, "zhack feature incr", tx,
|
||||
"name=%s", feature->fi_guid);
|
||||
}
|
||||
|
||||
static void
|
||||
feature_decr_sync(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
|
||||
zfeature_info_t *feature = arg;
|
||||
uint64_t refcount;
|
||||
|
||||
VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
|
||||
feature_sync(spa, feature, refcount - 1, tx);
|
||||
spa_history_log_internal(spa, "zhack feature decr", tx,
|
||||
"name=%s", feature->fi_guid);
|
||||
}
|
||||
|
||||
static void
|
||||
zhack_do_feature_ref(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
char *target;
|
||||
boolean_t decr = B_FALSE;
|
||||
spa_t *spa;
|
||||
objset_t *mos;
|
||||
zfeature_info_t feature;
|
||||
spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
|
||||
|
||||
/*
|
||||
* fi_desc does not matter here because it was written to disk
|
||||
* when the feature was enabled, but we need to properly set the
|
||||
* feature for read or write based on the information we read off
|
||||
* disk later.
|
||||
*/
|
||||
feature.fi_uname = "zhack";
|
||||
feature.fi_flags = 0;
|
||||
feature.fi_desc = NULL;
|
||||
feature.fi_depends = nodeps;
|
||||
feature.fi_feature = SPA_FEATURE_NONE;
|
||||
|
||||
optind = 1;
|
||||
while ((c = getopt(argc, argv, "md")) != -1) {
|
||||
switch (c) {
|
||||
case 'm':
|
||||
feature.fi_flags |= ZFEATURE_FLAG_MOS;
|
||||
break;
|
||||
case 'd':
|
||||
decr = B_TRUE;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 2) {
|
||||
(void) fprintf(stderr, "error: missing feature or pool name\n");
|
||||
usage();
|
||||
}
|
||||
target = argv[0];
|
||||
feature.fi_guid = argv[1];
|
||||
|
||||
if (!zfeature_is_valid_guid(feature.fi_guid))
|
||||
fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
|
||||
|
||||
zhack_spa_open(target, B_FALSE, FTAG, &spa);
|
||||
mos = spa->spa_meta_objset;
|
||||
|
||||
if (zfeature_is_supported(feature.fi_guid)) {
|
||||
fatal(spa, FTAG,
|
||||
"'%s' is a real feature, will not change refcount");
|
||||
}
|
||||
|
||||
if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
|
||||
feature.fi_guid)) {
|
||||
feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
|
||||
} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
|
||||
feature.fi_guid)) {
|
||||
feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
|
||||
} else {
|
||||
fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
|
||||
}
|
||||
|
||||
if (decr) {
|
||||
uint64_t count;
|
||||
if (feature_get_refcount_from_disk(spa, &feature,
|
||||
&count) == 0 && count != 0) {
|
||||
fatal(spa, FTAG, "feature refcount already 0: %s",
|
||||
feature.fi_guid);
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY0(dsl_sync_task(spa_name(spa), NULL,
|
||||
decr ? feature_decr_sync : feature_incr_sync, &feature,
|
||||
5, ZFS_SPACE_CHECK_NORMAL));
|
||||
|
||||
spa_close(spa, FTAG);
|
||||
}
|
||||
|
||||
static int
|
||||
zhack_do_feature(int argc, char **argv)
|
||||
{
|
||||
char *subcommand;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
if (argc == 0) {
|
||||
(void) fprintf(stderr,
|
||||
"error: no feature operation specified\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
subcommand = argv[0];
|
||||
if (strcmp(subcommand, "stat") == 0) {
|
||||
zhack_do_feature_stat(argc, argv);
|
||||
} else if (strcmp(subcommand, "enable") == 0) {
|
||||
zhack_do_feature_enable(argc, argv);
|
||||
} else if (strcmp(subcommand, "ref") == 0) {
|
||||
zhack_do_feature_ref(argc, argv);
|
||||
} else {
|
||||
(void) fprintf(stderr, "error: unknown subcommand: %s\n",
|
||||
subcommand);
|
||||
usage();
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#define MAX_NUM_PATHS 1024
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
extern void zfs_prop_init(void);
|
||||
|
||||
char *path[MAX_NUM_PATHS];
|
||||
const char *subcommand;
|
||||
int rv = 0;
|
||||
char c;
|
||||
|
||||
g_importargs.path = path;
|
||||
|
||||
dprintf_setup(&argc, argv);
|
||||
zfs_prop_init();
|
||||
|
||||
while ((c = getopt(argc, argv, "c:d:")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
g_importargs.cachefile = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
assert(g_importargs.paths < MAX_NUM_PATHS);
|
||||
g_importargs.path[g_importargs.paths++] = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
optind = 1;
|
||||
|
||||
if (argc == 0) {
|
||||
(void) fprintf(stderr, "error: no command specified\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
subcommand = argv[0];
|
||||
|
||||
if (strcmp(subcommand, "feature") == 0) {
|
||||
rv = zhack_do_feature(argc, argv);
|
||||
} else {
|
||||
(void) fprintf(stderr, "error: unknown subcommand: %s\n",
|
||||
subcommand);
|
||||
usage();
|
||||
}
|
||||
|
||||
if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
|
||||
fatal(NULL, FTAG, "pool export failed; "
|
||||
"changes may not be committed to disk\n");
|
||||
}
|
||||
|
||||
libzfs_fini(g_zfs);
|
||||
kernel_fini();
|
||||
|
||||
return (rv);
|
||||
}
|
@ -1,492 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2012, 2020 by Delphix. 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 "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 statfs sfs;
|
||||
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 (statfs(fullpath, &sfs) == -1) {
|
||||
(void) fprintf(stderr, "cannot find mountpoint for '%s': %s\n",
|
||||
fullpath, strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (strcmp(sfs.f_fstypename, MNTTYPE_ZFS) != 0) {
|
||||
(void) fprintf(stderr, "invalid path '%s': not a ZFS "
|
||||
"filesystem\n", fullpath);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (strncmp(fullpath, sfs.f_mntonname, strlen(sfs.f_mntonname)) != 0) {
|
||||
(void) fprintf(stderr, "invalid path '%s': mountpoint "
|
||||
"doesn't match path\n", fullpath);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
(void) strcpy(dataset, sfs.f_mntfromname);
|
||||
|
||||
rel = fullpath + strlen(sfs.f_mntonname);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Device faults can take on three different forms:
|
||||
* 1). delayed or hanging I/O
|
||||
* 2). zfs label faults
|
||||
* 3). generic disk faults
|
||||
*/
|
||||
if (record->zi_timer != 0) {
|
||||
record->zi_cmd = ZINJECT_DELAY_IO;
|
||||
} else if (label_type != TYPE_INVAL) {
|
||||
record->zi_cmd = ZINJECT_LABEL_FAULT;
|
||||
} else {
|
||||
record->zi_cmd = ZINJECT_DEVICE_FAULT;
|
||||
}
|
||||
|
||||
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_be);
|
||||
record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,411 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,674 +0,0 @@
|
||||
'\" te
|
||||
.\" Copyright (c) 2012, Martin Matuska <mm@FreeBSD.org>.
|
||||
.\" All Rights Reserved.
|
||||
.\"
|
||||
.\" 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]
|
||||
.\"
|
||||
.\" Copyright (c) 2012, 2017 by Delphix. All rights reserved.
|
||||
.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||
.\" Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 16, 2019
|
||||
.Dt ZPOOL-FEATURES 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm zpool-features
|
||||
.Nd ZFS pool feature descriptions
|
||||
.Sh DESCRIPTION
|
||||
ZFS pool on\-disk format versions are specified via "features" which replace
|
||||
the old on\-disk format numbers (the last supported on\-disk format number is
|
||||
28).
|
||||
To enable a feature on a pool use the
|
||||
.Cm upgrade
|
||||
subcommand of the
|
||||
.Xr zpool 8
|
||||
command, or set the
|
||||
.Sy feature@feature_name
|
||||
property to
|
||||
.Ar enabled .
|
||||
.Pp
|
||||
The pool format does not affect file system version compatibility or the ability
|
||||
to send file systems between pools.
|
||||
.Pp
|
||||
Since most features can be enabled independently of each other the on\-disk
|
||||
format of the pool is specified by the set of all features marked as
|
||||
.Sy active
|
||||
on the pool.
|
||||
If the pool was created by another software version this set may
|
||||
include unsupported features.
|
||||
.Ss Identifying features
|
||||
Every feature has a guid of the form
|
||||
.Sy com.example:feature_name .
|
||||
The reverse DNS name ensures that the feature's guid is unique across all ZFS
|
||||
implementations.
|
||||
When unsupported features are encountered on a pool they will
|
||||
be identified by their guids.
|
||||
Refer to the documentation for the ZFS implementation that created the pool
|
||||
for information about those features.
|
||||
.Pp
|
||||
Each supported feature also has a short name.
|
||||
By convention a feature's short name is the portion of its guid which follows
|
||||
the ':' (e.g.
|
||||
.Sy com.example:feature_name
|
||||
would have the short name
|
||||
.Sy feature_name ),
|
||||
however a feature's short name may differ across ZFS implementations if
|
||||
following the convention would result in name conflicts.
|
||||
.Ss Feature states
|
||||
Features can be in one of three states:
|
||||
.Bl -tag -width "XXXXXXXX"
|
||||
.It Sy active
|
||||
This feature's on\-disk format changes are in effect on the pool.
|
||||
Support for this feature is required to import the pool in read\-write mode.
|
||||
If this feature is not read-only compatible, support is also required to
|
||||
import the pool in read\-only mode (see "Read\-only compatibility").
|
||||
.It Sy enabled
|
||||
An administrator has marked this feature as enabled on the pool, but the
|
||||
feature's on\-disk format changes have not been made yet.
|
||||
The pool can still be imported by software that does not support this feature,
|
||||
but changes may be made to the on\-disk format at any time which will move
|
||||
the feature to the
|
||||
.Sy active
|
||||
state.
|
||||
Some features may support returning to the
|
||||
.Sy enabled
|
||||
state after becoming
|
||||
.Sy active .
|
||||
See feature\-specific documentation for details.
|
||||
.It Sy disabled
|
||||
This feature's on\-disk format changes have not been made and will not be made
|
||||
unless an administrator moves the feature to the
|
||||
.Sy enabled
|
||||
state.
|
||||
Features cannot be disabled once they have been enabled.
|
||||
.El
|
||||
.Pp
|
||||
The state of supported features is exposed through pool properties of the form
|
||||
.Sy feature@short_name .
|
||||
.Ss Read\-only compatibility
|
||||
Some features may make on\-disk format changes that do not interfere with other
|
||||
software's ability to read from the pool.
|
||||
These features are referred to as "read\-only compatible".
|
||||
If all unsupported features on a pool are read\-only compatible, the pool can
|
||||
be imported in read\-only mode by setting the
|
||||
.Sy readonly
|
||||
property during import (see
|
||||
.Xr zpool 8
|
||||
for details on importing pools).
|
||||
.Ss Unsupported features
|
||||
For each unsupported feature enabled on an imported pool a pool property
|
||||
named
|
||||
.Sy unsupported@feature_guid
|
||||
will indicate why the import was allowed despite the unsupported feature.
|
||||
Possible values for this property are:
|
||||
.Bl -tag -width "XXXXXXXX"
|
||||
.It Sy inactive
|
||||
The feature is in the
|
||||
.Sy enabled
|
||||
state and therefore the pool's on\-disk format is still compatible with
|
||||
software that does not support this feature.
|
||||
.It Sy readonly
|
||||
The feature is read\-only compatible and the pool has been imported in
|
||||
read\-only mode.
|
||||
.El
|
||||
.Ss Feature dependencies
|
||||
Some features depend on other features being enabled in order to function
|
||||
properly.
|
||||
Enabling a feature will automatically enable any features it depends on.
|
||||
.Sh FEATURES
|
||||
The following features are supported on this system:
|
||||
.Bl -tag -width "XXXXXXXX"
|
||||
.It Sy async_destroy
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:async_destroy"
|
||||
.It GUID Ta com.delphix:async_destroy
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
Destroying a file system requires traversing all of its data in order to
|
||||
return its used space to the pool.
|
||||
Without
|
||||
.Sy async_destroy
|
||||
the file system is not fully removed until all space has been reclaimed.
|
||||
If the destroy operation is interrupted by a reboot or power outage the next
|
||||
attempt to open the pool will need to complete the destroy operation
|
||||
synchronously.
|
||||
.Pp
|
||||
When
|
||||
.Sy async_destroy
|
||||
is enabled the file system's data will be reclaimed by a background process,
|
||||
allowing the destroy operation to complete without traversing the entire file
|
||||
system.
|
||||
The background process is able to resume interrupted destroys after the pool
|
||||
has been opened, eliminating the need to finish interrupted destroys as part
|
||||
of the open operation.
|
||||
The amount of space remaining to be reclaimed by the background process is
|
||||
available through the
|
||||
.Sy freeing
|
||||
property.
|
||||
.Pp
|
||||
This feature is only
|
||||
.Sy active
|
||||
while
|
||||
.Sy freeing
|
||||
is non\-zero.
|
||||
.It Sy empty_bpobj
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:empty_bpobj"
|
||||
.It GUID Ta com.delphix:empty_bpobj
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature increases the performance of creating and using a large number
|
||||
of snapshots of a single filesystem or volume, and also reduces the disk
|
||||
space required.
|
||||
.Pp
|
||||
When there are many snapshots, each snapshot uses many Block Pointer Objects
|
||||
.Pq bpobj's
|
||||
to track blocks associated with that snapshot.
|
||||
However, in common use cases, most of these bpobj's are empty.
|
||||
This feature allows us to create each bpobj on-demand, thus eliminating the
|
||||
empty bpobjs.
|
||||
.Pp
|
||||
This feature is
|
||||
.Sy active
|
||||
while there are any filesystems, volumes, or snapshots which were created
|
||||
after enabling this feature.
|
||||
.It Sy filesystem_limits
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.joyent:filesystem_limits"
|
||||
.It GUID Ta com.joyent:filesystem_limits
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
This feature enables filesystem and snapshot limits.
|
||||
These limits can be used
|
||||
to control how many filesystems and/or snapshots can be created at the point in
|
||||
the tree on which the limits are set.
|
||||
.Pp
|
||||
This feature is
|
||||
.Sy active
|
||||
once either of the limit properties has been
|
||||
set on a dataset.
|
||||
Once activated the feature is never deactivated.
|
||||
.It Sy lz4_compress
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "org.illumos:lz4_compress"
|
||||
.It GUID Ta org.illumos:lz4_compress
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
.Sy lz4
|
||||
is a high-performance real-time compression algorithm that
|
||||
features significantly faster compression and decompression as well as a
|
||||
higher compression ratio than the older
|
||||
.Sy lzjb
|
||||
compression.
|
||||
Typically,
|
||||
.Sy lz4
|
||||
compression is approximately 50% faster on
|
||||
compressible data and 200% faster on incompressible data than
|
||||
.Sy lzjb .
|
||||
It is also approximately 80% faster on decompression, while
|
||||
giving approximately 10% better compression ratio.
|
||||
.Pp
|
||||
When the
|
||||
.Sy lz4_compress
|
||||
feature is set to
|
||||
.Sy enabled ,
|
||||
the
|
||||
administrator can turn on
|
||||
.Sy lz4
|
||||
compression on any dataset on the
|
||||
pool using the
|
||||
.Xr zfs 8
|
||||
command.
|
||||
Also, all newly written metadata
|
||||
will be compressed with
|
||||
.Sy lz4
|
||||
algorithm.
|
||||
Since this feature is not read-only compatible, this
|
||||
operation will render the pool unimportable on systems without support
|
||||
for the
|
||||
.Sy lz4_compress
|
||||
feature.
|
||||
Booting off of
|
||||
.Sy lz4
|
||||
-compressed root pools is supported.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
as soon as it is enabled and will
|
||||
never return to being
|
||||
.Sy enabled .
|
||||
.It Sy multi_vdev_crash_dump
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.joyent:multi_vdev_crash_dump"
|
||||
.It GUID Ta com.joyent:multi_vdev_crash_dump
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature allows a dump device to be configured with a pool comprised
|
||||
of multiple vdevs.
|
||||
Those vdevs may be arranged in any mirrored or raidz
|
||||
configuration.
|
||||
.\" TODO: this is not yet supported on FreeBSD.
|
||||
.\" .Pp
|
||||
.\" When the
|
||||
.\" .Sy multi_vdev_crash_dump
|
||||
.\" feature is set to
|
||||
.\" .Sy enabled ,
|
||||
.\" the administrator can use the
|
||||
.\" .Xr dumpon 8
|
||||
.\" command to configure a
|
||||
.\" dump device on a pool comprised of multiple vdevs.
|
||||
.It Sy spacemap_histogram
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:spacemap_histogram"
|
||||
.It GUID Ta com.delphix:spacemap_histogram
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature allows ZFS to maintain more information about how free space
|
||||
is organized within the pool.
|
||||
If this feature is
|
||||
.Sy enabled ,
|
||||
ZFS will
|
||||
set this feature to
|
||||
.Sy active
|
||||
when a new space map object is created or
|
||||
an existing space map is upgraded to the new format.
|
||||
Once the feature is
|
||||
.Sy active ,
|
||||
it will remain in that state until the pool is destroyed.
|
||||
.It Sy extensible_dataset
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:extensible_dataset"
|
||||
.It GUID Ta com.delphix:extensible_dataset
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature allows more flexible use of internal ZFS data structures,
|
||||
and exists for other features to depend on.
|
||||
.Pp
|
||||
This feature will be
|
||||
.Sy active
|
||||
when the first dependent feature uses it,
|
||||
and will be returned to the
|
||||
.Sy enabled
|
||||
state when all datasets that use
|
||||
this feature are destroyed.
|
||||
.It Sy bookmarks
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:bookmarks"
|
||||
.It GUID Ta com.delphix:bookmarks
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
This feature enables use of the
|
||||
.Nm zfs
|
||||
.Cm bookmark
|
||||
subcommand.
|
||||
.Pp
|
||||
This feature is
|
||||
.Sy active
|
||||
while any bookmarks exist in the pool.
|
||||
All bookmarks in the pool can be listed by running
|
||||
.Nm zfs
|
||||
.Cm list
|
||||
.Fl t No bookmark Fl r Ar poolname .
|
||||
.It Sy enabled_txg
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:enabled_txg"
|
||||
.It GUID Ta com.delphix:enabled_txg
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
Once this feature is enabled ZFS records the transaction group number
|
||||
in which new features are enabled.
|
||||
This has no user-visible impact,
|
||||
but other features may depend on this feature.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
as soon as it is enabled and will
|
||||
never return to being
|
||||
.Sy enabled .
|
||||
.It Sy hole_birth
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:hole_birth"
|
||||
.It GUID Ta com.delphix:hole_birth
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta enabled_txg
|
||||
.El
|
||||
.Pp
|
||||
This feature improves performance of incremental sends
|
||||
.Pq Dq zfs send -i
|
||||
and receives for objects with many holes.
|
||||
The most common case of
|
||||
hole-filled objects is zvols.
|
||||
.Pp
|
||||
An incremental send stream from snapshot
|
||||
.Sy A
|
||||
to snapshot
|
||||
.Sy B
|
||||
contains information about every block that changed between
|
||||
.Sy A
|
||||
and
|
||||
.Sy B .
|
||||
Blocks which did not change between those snapshots can be
|
||||
identified and omitted from the stream using a piece of metadata called
|
||||
the 'block birth time', but birth times are not recorded for holes
|
||||
.Pq blocks filled only with zeroes .
|
||||
Since holes created after
|
||||
.Sy A
|
||||
cannot be
|
||||
distinguished from holes created before
|
||||
.Sy A ,
|
||||
information about every
|
||||
hole in the entire filesystem or zvol is included in the send stream.
|
||||
.Pp
|
||||
For workloads where holes are rare this is not a problem.
|
||||
However, when
|
||||
incrementally replicating filesystems or zvols with many holes
|
||||
.Pq for example a zvol formatted with another filesystem
|
||||
a lot of time will
|
||||
be spent sending and receiving unnecessary information about holes that
|
||||
already exist on the receiving side.
|
||||
.Pp
|
||||
Once the
|
||||
.Sy hole_birth
|
||||
feature has been enabled the block birth times
|
||||
of all new holes will be recorded.
|
||||
Incremental sends between snapshots
|
||||
created after this feature is enabled will use this new metadata to avoid
|
||||
sending information about holes that already exist on the receiving side.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
as soon as it is enabled and will
|
||||
never return to being
|
||||
.Sy enabled .
|
||||
.It Sy embedded_data
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:embedded_data"
|
||||
.It GUID Ta com.delphix:embedded_data
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature improves the performance and compression ratio of
|
||||
highly-compressible blocks.
|
||||
Blocks whose contents can compress to 112 bytes
|
||||
or smaller can take advantage of this feature.
|
||||
.Pp
|
||||
When this feature is enabled, the contents of highly-compressible blocks are
|
||||
stored in the block "pointer" itself
|
||||
.Po a misnomer in this case, as it contains
|
||||
the compressed data, rather than a pointer to its location on disk
|
||||
.Pc .
|
||||
Thus
|
||||
the space of the block
|
||||
.Pq one sector, typically 512 bytes or 4KB
|
||||
is saved,
|
||||
and no additional i/o is needed to read and write the data block.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
as soon as it is enabled and will
|
||||
never return to being
|
||||
.Sy enabled .
|
||||
.It Sy zpool_checkpoint
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:zpool_checkpoint"
|
||||
.It GUID Ta com.delphix:zpool_checkpoint
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature enables the "zpool checkpoint" subcommand that can
|
||||
checkpoint the state of the pool at the time it was issued and later
|
||||
rewind back to it or discard it.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
when the "zpool checkpoint" command is used to checkpoint the pool.
|
||||
The feature will only return back to being
|
||||
.Sy enabled
|
||||
when the pool is rewound or the checkpoint has been discarded.
|
||||
.It Sy device_removal
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:device_removal"
|
||||
.It GUID Ta com.delphix:device_removal
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature enables the "zpool remove" subcommand to remove top-level
|
||||
vdevs, evacuating them to reduce the total size of the pool.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
when the "zpool remove" command is used
|
||||
on a top-level vdev, and will never return to being
|
||||
.Sy enabled .
|
||||
.It Sy obsolete_counts
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:obsolete_counts"
|
||||
.It GUID Ta com.delphix:obsolete_counts
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta device_removal
|
||||
.El
|
||||
.Pp
|
||||
This feature is an enhancement of device_removal, which will over time
|
||||
reduce the memory used to track removed devices. When indirect blocks
|
||||
are freed or remapped, we note that their part of the indirect mapping
|
||||
is "obsolete", i.e. no longer needed. See also the "zfs remap"
|
||||
subcommand in
|
||||
.Xr zfs 8 .
|
||||
|
||||
This feature becomes
|
||||
.Sy active
|
||||
when the "zpool remove" command is
|
||||
used on a top-level vdev, and will never return to being
|
||||
.Sy enabled .
|
||||
.It Sy spacemap_v2
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:spacemap_v2"
|
||||
.It GUID Ta com.delphix:spacemap_v2
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature enables the use of the new space map encoding which
|
||||
consists of two words (instead of one) whenever it is advantageous.
|
||||
The new encoding allows space maps to represent large regions of
|
||||
space more efficiently on-disk while also increasing their maximum
|
||||
addressable offset.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
as soon as it is enabled and will
|
||||
never return to being
|
||||
.Sy enabled .
|
||||
.It Sy large_blocks
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "org.open-zfs:large_block"
|
||||
.It GUID Ta org.open-zfs:large_block
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Sy large_block
|
||||
feature allows the record size on a dataset to be
|
||||
set larger than 128KB.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
once a
|
||||
.Sy recordsize
|
||||
property has been set larger than 128KB, and will return to being
|
||||
.Sy enabled
|
||||
once all filesystems that have ever had their recordsize larger than 128KB
|
||||
are destroyed.
|
||||
.Pp
|
||||
Booting from datasets that use the
|
||||
.Sy large_block
|
||||
feature is supported by the
|
||||
.Fx
|
||||
boot loader.
|
||||
.It Sy large_dnode
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "org.zfsonlinux:large_dnode"
|
||||
.It GUID Ta org.zfsonlinux:large_dnode
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Sy large_dnode
|
||||
feature allows the size of dnodes in a dataset to be set larger than 512B.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
once a dataset contains an object with a dnode larger than 512B,
|
||||
which occurs as a result of setting the
|
||||
.Sy dnodesize
|
||||
dataset property to a value other than
|
||||
.Sy legacy .
|
||||
The feature will return to being
|
||||
.Sy enabled
|
||||
once all filesystems that have ever contained a dnode larger than 512B are
|
||||
destroyed.
|
||||
Large dnodes allow more data to be stored in the bonus buffer, thus potentially
|
||||
improving performance by avoiding the use of spill blocks.
|
||||
.It Sy sha512
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "org.illumos:sha512"
|
||||
.It GUID Ta org.illumos:sha512
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Sy sha512
|
||||
feature enables the use of the SHA-512/256 truncated hash algorithm
|
||||
.Pq FIPS 180-4
|
||||
for checksum and dedup.
|
||||
The native 64-bit arithmetic of SHA-512 provides an approximate 50%
|
||||
performance boost over SHA-256 on 64-bit hardware and is thus a good
|
||||
minimum-change replacement candidate for systems where hash performance is
|
||||
important, but these systems cannot for whatever reason utilize the faster
|
||||
.Sy skein
|
||||
algorithms.
|
||||
.Pp
|
||||
When the
|
||||
.Sy sha512
|
||||
feature is set to
|
||||
.Sy enabled ,
|
||||
the administrator can turn on the
|
||||
.Sy sha512
|
||||
checksum on any dataset using the
|
||||
.Dl # zfs set checksum=sha512 Ar dataset
|
||||
command.
|
||||
This feature becomes
|
||||
.Sy active
|
||||
once a
|
||||
.Sy checksum
|
||||
property has been set to
|
||||
.Sy sha512 ,
|
||||
and will return to being
|
||||
.Sy enabled
|
||||
once all filesystems that have ever had their checksum set to
|
||||
.Sy sha512
|
||||
are destroyed.
|
||||
.It Sy skein
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "org.illumos:skein"
|
||||
.It GUID Ta org.illumos:skein
|
||||
.It READ\-ONLY COMPATIBLE Ta no
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Sy skein
|
||||
feature enables the use of the Skein hash algorithm for checksum and dedup.
|
||||
Skein is a high-performance secure hash algorithm that was a finalist in the
|
||||
NIST SHA-3 competition.
|
||||
It provides a very high security margin and high performance on 64-bit hardware
|
||||
.Pq 80% faster than SHA-256 .
|
||||
This implementation also utilizes the new salted checksumming functionality in
|
||||
ZFS, which means that the checksum is pre-seeded with a secret 256-bit random
|
||||
key
|
||||
.Pq stored on the pool
|
||||
before being fed the data block to be checksummed.
|
||||
Thus the produced checksums are unique to a given pool, preventing hash
|
||||
collision attacks on systems with dedup.
|
||||
.Pp
|
||||
When the
|
||||
.Sy skein
|
||||
feature is set to
|
||||
.Sy enabled ,
|
||||
the administrator can turn on the
|
||||
.Sy skein
|
||||
checksum on any dataset using the
|
||||
.Dl # zfs set checksum=skein Ar dataset
|
||||
command.
|
||||
This feature becomes
|
||||
.Sy active
|
||||
once a
|
||||
.Sy checksum
|
||||
property has been set to
|
||||
.Sy skein ,
|
||||
and will return to being
|
||||
.Sy enabled
|
||||
once all filesystems that have ever had their checksum set to
|
||||
.Sy skein
|
||||
are destroyed.
|
||||
Booting off of pools using
|
||||
.Sy skein
|
||||
is supported.
|
||||
.It Sy allocation_classes
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.intel:allocation_classes"
|
||||
.It GUID Ta com.intel:allocation_classes
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta none
|
||||
.El
|
||||
.Pp
|
||||
This feature enables support for separate allocation classes.
|
||||
.Pp
|
||||
This feature becomes
|
||||
.Sy active
|
||||
when a dedicated allocation class vdev
|
||||
(dedup or special) is created with
|
||||
.Dq zpool create
|
||||
or
|
||||
.Dq zpool add .
|
||||
With device removal, it can be returned to the
|
||||
.Sy enabled
|
||||
state if all the top-level vdevs from an allocation class are removed.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr zpool 8
|
||||
.Sh AUTHORS
|
||||
This manual page is a
|
||||
.Xr mdoc 7
|
||||
reimplementation of the
|
||||
.Tn illumos
|
||||
manual page
|
||||
.Em zpool-features(5) ,
|
||||
modified and customized for
|
||||
.Fx
|
||||
and licensed under the Common Development and Distribution License
|
||||
.Pq Tn CDDL .
|
||||
.Pp
|
||||
The
|
||||
.Xr mdoc 7
|
||||
implementation of this manual page was initially written by
|
||||
.An Martin Matuska Aq mm@FreeBSD.org .
|
File diff suppressed because it is too large
Load Diff
@ -1,255 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
|
||||
*/
|
||||
|
||||
#include <solaris.h>
|
||||
#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])) !=
|
||||
NULL) {
|
||||
if (add_pool(zhp, zlp) != 0)
|
||||
*err = B_TRUE;
|
||||
} else {
|
||||
*err = B_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (zlp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for any new pools, adding them to the list. We only add pools when no
|
||||
* options were given on the command line. Otherwise, we keep the list fixed as
|
||||
* those that were explicitly specified.
|
||||
*/
|
||||
void
|
||||
pool_list_update(zpool_list_t *zlp)
|
||||
{
|
||||
if (zlp->zl_findall)
|
||||
(void) zpool_iter(g_zfs, add_pool, zlp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all pools in the list, executing the callback for each
|
||||
*/
|
||||
int
|
||||
pool_list_iter(zpool_list_t *zlp, int unavail, zpool_iter_f func,
|
||||
void *data)
|
||||
{
|
||||
zpool_node_t *node, *next_node;
|
||||
int ret = 0;
|
||||
|
||||
for (node = uu_avl_first(zlp->zl_avl); node != NULL; node = next_node) {
|
||||
next_node = uu_avl_next(zlp->zl_avl, node);
|
||||
if (zpool_get_state(node->zn_handle) != POOL_STATE_UNAVAIL ||
|
||||
unavail)
|
||||
ret |= func(node->zn_handle, data);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the given pool from the list. When running iostat, we want to remove
|
||||
* those pools that no longer exist.
|
||||
*/
|
||||
void
|
||||
pool_list_remove(zpool_list_t *zlp, zpool_handle_t *zhp)
|
||||
{
|
||||
zpool_node_t search, *node;
|
||||
|
||||
search.zn_handle = zhp;
|
||||
if ((node = uu_avl_find(zlp->zl_avl, &search, NULL, NULL)) != NULL) {
|
||||
uu_avl_remove(zlp->zl_avl, node);
|
||||
zpool_close(node->zn_handle);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all the handles associated with this list.
|
||||
*/
|
||||
void
|
||||
pool_list_free(zpool_list_t *zlp)
|
||||
{
|
||||
uu_avl_walk_t *walk;
|
||||
zpool_node_t *node;
|
||||
|
||||
if ((walk = uu_avl_walk_start(zlp->zl_avl, UU_WALK_ROBUST)) == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("internal error: out of memory"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((node = uu_avl_walk_next(walk)) != NULL) {
|
||||
uu_avl_remove(zlp->zl_avl, node);
|
||||
zpool_close(node->zn_handle);
|
||||
free(node);
|
||||
}
|
||||
|
||||
uu_avl_walk_end(walk);
|
||||
uu_avl_destroy(zlp->zl_avl);
|
||||
uu_avl_pool_destroy(zlp->zl_pool);
|
||||
|
||||
free(zlp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of elements in the pool list.
|
||||
*/
|
||||
int
|
||||
pool_list_count(zpool_list_t *zlp)
|
||||
{
|
||||
return (uu_avl_numnodes(zlp->zl_avl));
|
||||
}
|
||||
|
||||
/*
|
||||
* High level function which iterates over all pools given on the command line,
|
||||
* using the pool_list_* interfaces.
|
||||
*/
|
||||
int
|
||||
for_each_pool(int argc, char **argv, boolean_t unavail,
|
||||
zprop_list_t **proplist, zpool_iter_f func, void *data)
|
||||
{
|
||||
zpool_list_t *list;
|
||||
int ret = 0;
|
||||
|
||||
if ((list = pool_list_get(argc, argv, proplist, &ret)) == NULL)
|
||||
return (1);
|
||||
|
||||
if (pool_list_iter(list, unavail, func, data) != 0)
|
||||
ret = 1;
|
||||
|
||||
pool_list_free(list);
|
||||
|
||||
return (ret);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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, zpool_boot_label_t boot_type,
|
||||
uint64_t boot_size, 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 *);
|
||||
|
||||
extern libzfs_handle_t *g_zfs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZPOOL_UTIL_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,76 +0,0 @@
|
||||
'\" te
|
||||
.\" Copyright (c) 2011, Martin Matuska <mm@FreeBSD.org>.
|
||||
.\" All Rights Reserved.
|
||||
.\"
|
||||
.\" 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]
|
||||
.\"
|
||||
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved.
|
||||
.\" Copyright (c) 2013, Delphix. All Rights Reserved.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 25, 2020
|
||||
.Dt ZSTREAMDUMP 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm zstreamdump
|
||||
.Nd filter data in zfs send stream
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl C
|
||||
.Op Fl d
|
||||
.Op Fl v
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility reads from the output of the
|
||||
.Qq Nm zfs Cm send
|
||||
command, then displays headers and some statistics from that output. See
|
||||
.Xr zfs 8 .
|
||||
.Pp
|
||||
The following options are supported:
|
||||
.Bl -tag -width indent
|
||||
.It Fl C
|
||||
Suppress the validation of checksums.
|
||||
.It Fl d
|
||||
Dump contents of blocks modified, implies verbose.
|
||||
.It Fl v
|
||||
Verbose. Dump all headers, not only begin and end headers.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr zfs 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
utility first appeared in
|
||||
.Fx 7.0 .
|
||||
.Sh AUTHORS
|
||||
This manual page is a
|
||||
.Xr mdoc 7
|
||||
reimplementation of the
|
||||
.Tn OpenSolaris
|
||||
manual page
|
||||
.Em zstreamdump(1M) ,
|
||||
modified and customized for
|
||||
.Fx
|
||||
and licensed under the
|
||||
.Tn Common Development and Distribution License
|
||||
.Pq Tn CDDL .
|
||||
.Pp
|
||||
The
|
||||
.Xr mdoc 7
|
||||
implementation of this manual page was initially written by
|
||||
.An Martin Matuska Aq mm@FreeBSD.org .
|
@ -1,644 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014 Integros [integros.com]
|
||||
* Copyright (c) 2013, 2015 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <libnvpair.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <sys/zio.h>
|
||||
#include <zfs_fletcher.h>
|
||||
|
||||
/*
|
||||
* If dump mode is enabled, the number of bytes to print per line
|
||||
*/
|
||||
#define BYTES_PER_LINE 16
|
||||
/*
|
||||
* If dump mode is enabled, the number of bytes to group together, separated
|
||||
* by newlines or spaces
|
||||
*/
|
||||
#define DUMP_GROUPING 4
|
||||
|
||||
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;
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
(void) fprintf(stderr, "usage: zstreamdump [-v] [-C] [-d] < file\n");
|
||||
(void) fprintf(stderr, "\t -v -- verbose\n");
|
||||
(void) fprintf(stderr, "\t -C -- suppress checksum verification\n");
|
||||
(void) fprintf(stderr, "\t -d -- dump contents of blocks modified, "
|
||||
"implies verbose\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void *
|
||||
safe_malloc(size_t size)
|
||||
{
|
||||
void *rv = malloc(size);
|
||||
if (rv == NULL) {
|
||||
(void) fprintf(stderr, "ERROR; failed to allocate %zu bytes\n",
|
||||
size);
|
||||
abort();
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
if (do_byteswap)
|
||||
fletcher_4_incremental_byteswap(buf, len, cksum);
|
||||
else
|
||||
fletcher_4_incremental_native(buf, len, cksum);
|
||||
}
|
||||
total_stream_len += len;
|
||||
return (outlen);
|
||||
}
|
||||
|
||||
static size_t
|
||||
read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum)
|
||||
{
|
||||
ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
|
||||
==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
|
||||
size_t r = ssread(drr, sizeof (*drr) - sizeof (zio_cksum_t), cksum);
|
||||
if (r == 0)
|
||||
return (0);
|
||||
zio_cksum_t saved_cksum = *cksum;
|
||||
r = ssread(&drr->drr_u.drr_checksum.drr_checksum,
|
||||
sizeof (zio_cksum_t), cksum);
|
||||
if (r == 0)
|
||||
return (0);
|
||||
if (!ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.drr_checksum.drr_checksum) &&
|
||||
!ZIO_CHECKSUM_EQUAL(saved_cksum,
|
||||
drr->drr_u.drr_checksum.drr_checksum)) {
|
||||
fprintf(stderr, "invalid checksum\n");
|
||||
(void) printf("Incorrect checksum in record header.\n");
|
||||
(void) printf("Expected checksum = %llx/%llx/%llx/%llx\n",
|
||||
saved_cksum.zc_word[0],
|
||||
saved_cksum.zc_word[1],
|
||||
saved_cksum.zc_word[2],
|
||||
saved_cksum.zc_word[3]);
|
||||
return (0);
|
||||
}
|
||||
return (sizeof (*drr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print part of a block in ASCII characters
|
||||
*/
|
||||
static void
|
||||
print_ascii_block(char *subbuf, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
char char_print = isprint(subbuf[i]) ? subbuf[i] : '.';
|
||||
if (i != 0 && i % DUMP_GROUPING == 0) {
|
||||
(void) printf(" ");
|
||||
}
|
||||
(void) printf("%c", char_print);
|
||||
}
|
||||
(void) printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* print_block - Dump the contents of a modified block to STDOUT
|
||||
*
|
||||
* Assume that buf has capacity evenly divisible by BYTES_PER_LINE
|
||||
*/
|
||||
static void
|
||||
print_block(char *buf, int length)
|
||||
{
|
||||
int i;
|
||||
/*
|
||||
* Start printing ASCII characters at a constant offset, after
|
||||
* the hex prints. Leave 3 characters per byte on a line (2 digit
|
||||
* hex number plus 1 space) plus spaces between characters and
|
||||
* groupings.
|
||||
*/
|
||||
int ascii_start = BYTES_PER_LINE * 3 +
|
||||
BYTES_PER_LINE / DUMP_GROUPING + 2;
|
||||
|
||||
for (i = 0; i < length; i += BYTES_PER_LINE) {
|
||||
int j;
|
||||
int this_line_length = MIN(BYTES_PER_LINE, length - i);
|
||||
int print_offset = 0;
|
||||
|
||||
for (j = 0; j < this_line_length; j++) {
|
||||
int buf_offset = i + j;
|
||||
|
||||
/*
|
||||
* Separate every DUMP_GROUPING bytes by a space.
|
||||
*/
|
||||
if (buf_offset % DUMP_GROUPING == 0) {
|
||||
print_offset += printf(" ");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the two-digit hex value for this byte.
|
||||
*/
|
||||
unsigned char hex_print = buf[buf_offset];
|
||||
print_offset += printf("%02x ", hex_print);
|
||||
}
|
||||
|
||||
(void) printf("%*s", ascii_start - print_offset, " ");
|
||||
|
||||
print_ascii_block(buf + i, this_line_length);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *buf = safe_malloc(SPA_MAXBLOCKSIZE);
|
||||
uint64_t drr_record_count[DRR_NUMTYPES] = { 0 };
|
||||
uint64_t total_records = 0;
|
||||
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;
|
||||
struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded;
|
||||
struct drr_checksum *drrc = &thedrr.drr_u.drr_checksum;
|
||||
char c;
|
||||
boolean_t verbose = B_FALSE;
|
||||
boolean_t very_verbose = B_FALSE;
|
||||
boolean_t first = B_TRUE;
|
||||
/*
|
||||
* dump flag controls whether the contents of any modified data blocks
|
||||
* are printed to the console during processing of the stream. Warning:
|
||||
* for large streams, this can obviously lead to massive prints.
|
||||
*/
|
||||
boolean_t dump = B_FALSE;
|
||||
int err;
|
||||
zio_cksum_t zc = { 0 };
|
||||
zio_cksum_t pcksum = { 0 };
|
||||
|
||||
while ((c = getopt(argc, argv, ":vCd")) != -1) {
|
||||
switch (c) {
|
||||
case 'C':
|
||||
do_cksum = B_FALSE;
|
||||
break;
|
||||
case 'v':
|
||||
if (verbose)
|
||||
very_verbose = B_TRUE;
|
||||
verbose = B_TRUE;
|
||||
break;
|
||||
case 'd':
|
||||
dump = B_TRUE;
|
||||
verbose = B_TRUE;
|
||||
very_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();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 (read_hdr(drr, &zc)) {
|
||||
|
||||
/*
|
||||
* If this is the first DMU record being processed, check for
|
||||
* the magic bytes and figure out the endian-ness based on them.
|
||||
*/
|
||||
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]++;
|
||||
total_records++;
|
||||
|
||||
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 (drr->drr_payloadlen != 0) {
|
||||
nvlist_t *nv;
|
||||
int sz = drr->drr_payloadlen;
|
||||
|
||||
if (sz > SPA_MAXBLOCKSIZE) {
|
||||
free(buf);
|
||||
buf = safe_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 = %" PRIu64
|
||||
" type = %u bonustype = %u blksz = %u"
|
||||
" bonuslen = %u dn_slots = %u\n",
|
||||
drro->drr_object,
|
||||
drro->drr_type,
|
||||
drro->drr_bonustype,
|
||||
drro->drr_blksz,
|
||||
drro->drr_bonuslen,
|
||||
drro->drr_dn_slots);
|
||||
}
|
||||
if (drro->drr_bonuslen > 0) {
|
||||
(void) ssread(buf,
|
||||
P2ROUNDUP(drro->drr_bonuslen, 8), &zc);
|
||||
if (dump) {
|
||||
print_block(buf,
|
||||
P2ROUNDUP(drro->drr_bonuslen, 8));
|
||||
}
|
||||
}
|
||||
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_logical_size =
|
||||
BSWAP_64(drrw->drr_logical_size);
|
||||
drrw->drr_toguid = BSWAP_64(drrw->drr_toguid);
|
||||
drrw->drr_key.ddk_prop =
|
||||
BSWAP_64(drrw->drr_key.ddk_prop);
|
||||
drrw->drr_compressed_size =
|
||||
BSWAP_64(drrw->drr_compressed_size);
|
||||
}
|
||||
|
||||
uint64_t payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
|
||||
|
||||
/*
|
||||
* If this is verbose and/or dump output,
|
||||
* print info on the modified block
|
||||
*/
|
||||
if (verbose) {
|
||||
(void) printf("WRITE object = %llu type = %u "
|
||||
"checksum type = %u compression type = %u\n"
|
||||
" offset = %llu logical_size = %llu "
|
||||
"compressed_size = %llu "
|
||||
"payload_size = %llu "
|
||||
"props = %llx\n",
|
||||
(u_longlong_t)drrw->drr_object,
|
||||
drrw->drr_type,
|
||||
drrw->drr_checksumtype,
|
||||
drrw->drr_compressiontype,
|
||||
(u_longlong_t)drrw->drr_offset,
|
||||
(u_longlong_t)drrw->drr_logical_size,
|
||||
(u_longlong_t)drrw->drr_compressed_size,
|
||||
(u_longlong_t)payload_size,
|
||||
(u_longlong_t)drrw->drr_key.ddk_prop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the contents of the block in from STDIN to buf
|
||||
*/
|
||||
(void) ssread(buf, payload_size, &zc);
|
||||
/*
|
||||
* If in dump mode
|
||||
*/
|
||||
if (dump) {
|
||||
print_block(buf, payload_size);
|
||||
}
|
||||
total_write_size += payload_size;
|
||||
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);
|
||||
if (dump) {
|
||||
print_block(buf, drrs->drr_length);
|
||||
}
|
||||
break;
|
||||
case DRR_WRITE_EMBEDDED:
|
||||
if (do_byteswap) {
|
||||
drrwe->drr_object =
|
||||
BSWAP_64(drrwe->drr_object);
|
||||
drrwe->drr_offset =
|
||||
BSWAP_64(drrwe->drr_offset);
|
||||
drrwe->drr_length =
|
||||
BSWAP_64(drrwe->drr_length);
|
||||
drrwe->drr_toguid =
|
||||
BSWAP_64(drrwe->drr_toguid);
|
||||
drrwe->drr_lsize =
|
||||
BSWAP_32(drrwe->drr_lsize);
|
||||
drrwe->drr_psize =
|
||||
BSWAP_32(drrwe->drr_psize);
|
||||
}
|
||||
if (verbose) {
|
||||
(void) printf("WRITE_EMBEDDED object = %llu "
|
||||
"offset = %llu length = %llu\n"
|
||||
" toguid = %llx comp = %u etype = %u "
|
||||
"lsize = %u psize = %u\n",
|
||||
(u_longlong_t)drrwe->drr_object,
|
||||
(u_longlong_t)drrwe->drr_offset,
|
||||
(u_longlong_t)drrwe->drr_length,
|
||||
(u_longlong_t)drrwe->drr_toguid,
|
||||
drrwe->drr_compression,
|
||||
drrwe->drr_etype,
|
||||
drrwe->drr_lsize,
|
||||
drrwe->drr_psize);
|
||||
}
|
||||
(void) ssread(buf,
|
||||
P2ROUNDUP(drrwe->drr_psize, 8), &zc);
|
||||
break;
|
||||
}
|
||||
if (drr->drr_type != DRR_BEGIN && very_verbose) {
|
||||
(void) printf(" checksum = %llx/%llx/%llx/%llx\n",
|
||||
(longlong_t)drrc->drr_checksum.zc_word[0],
|
||||
(longlong_t)drrc->drr_checksum.zc_word[1],
|
||||
(longlong_t)drrc->drr_checksum.zc_word[2],
|
||||
(longlong_t)drrc->drr_checksum.zc_word[3]);
|
||||
}
|
||||
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_WRITE_BYREF records = %lld\n",
|
||||
(u_longlong_t)drr_record_count[DRR_WRITE_BYREF]);
|
||||
(void) printf("\tTotal DRR_WRITE_EMBEDDED records = %lld\n",
|
||||
(u_longlong_t)drr_record_count[DRR_WRITE_EMBEDDED]);
|
||||
(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)total_records);
|
||||
(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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <limits.h>
|
||||
|
@ -77,7 +77,6 @@
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
|
||||
#include <dt_module.h>
|
||||
#include <dt_printf.h>
|
||||
|
@ -44,12 +44,19 @@
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
|
||||
#include <sys/byteorder.h>
|
||||
#include <dt_printf.h>
|
||||
#include <dt_string.h>
|
||||
#include <dt_impl.h>
|
||||
|
||||
#ifndef NS_IN6ADDRSZ
|
||||
#define NS_IN6ADDRSZ 16
|
||||
#endif
|
||||
|
||||
#ifndef NS_INADDRSZ
|
||||
#define NS_INADDRSZ 4
|
||||
#endif
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
pfcheck_addr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2013, Joyent, Inc. 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 int nvlist_print_json(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 */
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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;
|
@ -1,406 +0,0 @@
|
||||
/*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2014, Joyent, Inc.
|
||||
* Copyright (c) 2017 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <wchar.h>
|
||||
#include <sys/debug.h>
|
||||
|
||||
#include "libnvpair.h"
|
||||
|
||||
#define FPRINTF(fp, ...) \
|
||||
do { \
|
||||
if (fprintf(fp, __VA_ARGS__) < 0) \
|
||||
return (-1); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* When formatting a string for JSON output we must escape certain characters,
|
||||
* as described in RFC4627. This applies to both member names and
|
||||
* DATA_TYPE_STRING values.
|
||||
*
|
||||
* This function will only operate correctly if the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. The input String is encoded in the current locale.
|
||||
*
|
||||
* 2. The current locale includes the Basic Multilingual Plane (plane 0)
|
||||
* as defined in the Unicode standard.
|
||||
*
|
||||
* The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all
|
||||
* representable Unicode characters included in their escaped numeric form.
|
||||
*/
|
||||
static int
|
||||
nvlist_print_json_string(FILE *fp, const char *input)
|
||||
{
|
||||
mbstate_t mbr;
|
||||
wchar_t c;
|
||||
size_t sz;
|
||||
|
||||
bzero(&mbr, sizeof (mbr));
|
||||
|
||||
FPRINTF(fp, "\"");
|
||||
while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
|
||||
switch (c) {
|
||||
case '"':
|
||||
FPRINTF(fp, "\\\"");
|
||||
break;
|
||||
case '\n':
|
||||
FPRINTF(fp, "\\n");
|
||||
break;
|
||||
case '\r':
|
||||
FPRINTF(fp, "\\r");
|
||||
break;
|
||||
case '\\':
|
||||
FPRINTF(fp, "\\\\");
|
||||
break;
|
||||
case '\f':
|
||||
FPRINTF(fp, "\\f");
|
||||
break;
|
||||
case '\t':
|
||||
FPRINTF(fp, "\\t");
|
||||
break;
|
||||
case '\b':
|
||||
FPRINTF(fp, "\\b");
|
||||
break;
|
||||
default:
|
||||
if ((c >= 0x00 && c <= 0x1f) ||
|
||||
(c > 0x7f && c <= 0xffff)) {
|
||||
/*
|
||||
* Render both Control Characters and Unicode
|
||||
* characters in the Basic Multilingual Plane
|
||||
* as JSON-escaped multibyte characters.
|
||||
*/
|
||||
FPRINTF(fp, "\\u%04x", (int)(0xffff & c));
|
||||
} else if (c >= 0x20 && c <= 0x7f) {
|
||||
/*
|
||||
* Render other 7-bit ASCII characters directly
|
||||
* and drop other, unrepresentable characters.
|
||||
*/
|
||||
FPRINTF(fp, "%c", (int)(0xff & c));
|
||||
}
|
||||
break;
|
||||
}
|
||||
input += sz;
|
||||
}
|
||||
|
||||
if (sz == (size_t)-1 || sz == (size_t)-2) {
|
||||
/*
|
||||
* We last read an invalid multibyte character sequence,
|
||||
* so return an error.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
|
||||
FPRINTF(fp, "\"");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump a JSON-formatted representation of an nvlist to the provided FILE *.
|
||||
* This routine does not output any new-lines or additional whitespace other
|
||||
* than that contained in strings, nor does it call fflush(3C).
|
||||
*/
|
||||
int
|
||||
nvlist_print_json(FILE *fp, nvlist_t *nvl)
|
||||
{
|
||||
nvpair_t *curr;
|
||||
boolean_t first = B_TRUE;
|
||||
|
||||
FPRINTF(fp, "{");
|
||||
|
||||
for (curr = nvlist_next_nvpair(nvl, NULL); curr;
|
||||
curr = nvlist_next_nvpair(nvl, curr)) {
|
||||
data_type_t type = nvpair_type(curr);
|
||||
|
||||
if (!first)
|
||||
FPRINTF(fp, ",");
|
||||
else
|
||||
first = B_FALSE;
|
||||
|
||||
if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1)
|
||||
return (-1);
|
||||
FPRINTF(fp, ":");
|
||||
|
||||
switch (type) {
|
||||
case DATA_TYPE_STRING: {
|
||||
char *string = fnvpair_value_string(curr);
|
||||
if (nvlist_print_json_string(fp, string) == -1)
|
||||
return (-1);
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BOOLEAN: {
|
||||
FPRINTF(fp, "true");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BOOLEAN_VALUE: {
|
||||
FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) ==
|
||||
B_TRUE ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BYTE: {
|
||||
FPRINTF(fp, "%hhu", fnvpair_value_byte(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT8: {
|
||||
FPRINTF(fp, "%hhd", fnvpair_value_int8(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT8: {
|
||||
FPRINTF(fp, "%hhu", fnvpair_value_uint8_t(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT16: {
|
||||
FPRINTF(fp, "%hd", fnvpair_value_int16(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT16: {
|
||||
FPRINTF(fp, "%hu", fnvpair_value_uint16(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT32: {
|
||||
FPRINTF(fp, "%d", fnvpair_value_int32(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT32: {
|
||||
FPRINTF(fp, "%u", fnvpair_value_uint32(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT64: {
|
||||
FPRINTF(fp, "%lld",
|
||||
(long long)fnvpair_value_int64(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT64: {
|
||||
FPRINTF(fp, "%llu",
|
||||
(unsigned long long)fnvpair_value_uint64(curr));
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_HRTIME: {
|
||||
hrtime_t val;
|
||||
VERIFY0(nvpair_value_hrtime(curr, &val));
|
||||
FPRINTF(fp, "%llu", (unsigned long long)val);
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_DOUBLE: {
|
||||
double val;
|
||||
VERIFY0(nvpair_value_double(curr, &val));
|
||||
FPRINTF(fp, "%f", val);
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_NVLIST: {
|
||||
if (nvlist_print_json(fp,
|
||||
fnvpair_value_nvlist(curr)) == -1)
|
||||
return (-1);
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_STRING_ARRAY: {
|
||||
char **val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
if (nvlist_print_json_string(fp, val[i]) == -1)
|
||||
return (-1);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_NVLIST_ARRAY: {
|
||||
nvlist_t **val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
if (nvlist_print_json(fp, val[i]) == -1)
|
||||
return (-1);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BOOLEAN_ARRAY: {
|
||||
boolean_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, val[i] == B_TRUE ?
|
||||
"true" : "false");
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_BYTE_ARRAY: {
|
||||
uchar_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%hhu", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT8_ARRAY: {
|
||||
uint8_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%hhu", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT8_ARRAY: {
|
||||
int8_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%hhd", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT16_ARRAY: {
|
||||
uint16_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%hu", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT16_ARRAY: {
|
||||
int16_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%hd", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT32_ARRAY: {
|
||||
uint32_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%u", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT32_ARRAY: {
|
||||
int32_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%d", val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UINT64_ARRAY: {
|
||||
uint64_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%llu",
|
||||
(unsigned long long)val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_INT64_ARRAY: {
|
||||
int64_t *val;
|
||||
uint_t valsz, i;
|
||||
VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
|
||||
FPRINTF(fp, "[");
|
||||
for (i = 0; i < valsz; i++) {
|
||||
if (i > 0)
|
||||
FPRINTF(fp, ",");
|
||||
FPRINTF(fp, "%lld", (long long)val[i]);
|
||||
}
|
||||
FPRINTF(fp, "]");
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_TYPE_UNKNOWN:
|
||||
case DATA_TYPE_DONTCARE:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FPRINTF(fp, "}");
|
||||
return (0);
|
||||
}
|
@ -1,391 +0,0 @@
|
||||
/*
|
||||
* 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 <solaris.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 */
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,570 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
(void) pthread_mutex_destroy(&pp->uap_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);
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
* 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 <string.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);
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,718 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define HAVE_ASSFAIL 1
|
||||
|
||||
#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 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 (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");
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
/*
|
||||
* 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 = "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);
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,300 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,894 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved.
|
||||
* Copyright (c) 2011, 2020 by Delphix. All rights reserved.
|
||||
* Copyright 2019 Joyent, Inc.
|
||||
* Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||
* Copyright (c) 2014 Integros [integros.com]
|
||||
* Copyright 2016 Nexenta Systems, Inc.
|
||||
* Copyright (c) 2019 Datto Inc.
|
||||
*/
|
||||
|
||||
#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 <sys/zfs_ioctl.h>
|
||||
#include <libzfs_core.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Miscellaneous ZFS constants
|
||||
*/
|
||||
#define ZFS_MAXPROPLEN MAXPATHLEN
|
||||
#define ZPOOL_MAXPROPLEN MAXPATHLEN
|
||||
|
||||
/*
|
||||
* libzfs errors
|
||||
*/
|
||||
typedef enum zfs_error {
|
||||
EZFS_SUCCESS = 0, /* no error -- success */
|
||||
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_SCRUB_PAUSED, /* scrub currently paused */
|
||||
EZFS_ACTIVE_POOL, /* pool is imported on a different system */
|
||||
EZFS_NO_PENDING, /* cannot cancel, no operation is pending */
|
||||
EZFS_CHECKPOINT_EXISTS, /* checkpoint exists */
|
||||
EZFS_DISCARDING_CHECKPOINT, /* currently discarding a checkpoint */
|
||||
EZFS_NO_CHECKPOINT, /* pool has no checkpoint */
|
||||
EZFS_DEVRM_IN_PROGRESS, /* a device is currently being removed */
|
||||
EZFS_VDEV_TOO_BIG, /* a device is too big to be used */
|
||||
EZFS_TOOMANY, /* argument list too long */
|
||||
EZFS_INITIALIZING, /* currently initializing */
|
||||
EZFS_NO_INITIALIZE, /* no active initialize */
|
||||
EZFS_WRONG_PARENT, /* invalid parent dataset (e.g ZVOL) */
|
||||
EZFS_IOC_NOTSUPPORTED, /* operation not supported by zfs module */
|
||||
EZFS_UNKNOWN
|
||||
} zfs_error_t;
|
||||
|
||||
/*
|
||||
* UEFI boot support parameters. When creating whole disk boot pool,
|
||||
* zpool create should allow to create EFI System partition for UEFI boot
|
||||
* program. In case of BIOS, the EFI System partition is not used
|
||||
* even if it does exist.
|
||||
*/
|
||||
typedef enum zpool_boot_label {
|
||||
ZPOOL_NO_BOOT_LABEL = 0,
|
||||
ZPOOL_CREATE_BOOT_LABEL,
|
||||
ZPOOL_COPY_BOOT_LABEL
|
||||
} zpool_boot_label_t;
|
||||
|
||||
/*
|
||||
* 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 void zfs_save_arguments(int argc, char **, char *, int);
|
||||
extern int zpool_log_history(libzfs_handle_t *, const char *);
|
||||
|
||||
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 int zfs_standard_error(libzfs_handle_t *, int, const char *);
|
||||
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 const char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
|
||||
extern const char *zpool_pool_state_to_name(pool_state_t);
|
||||
extern void zpool_free_handles(libzfs_handle_t *);
|
||||
extern int zpool_nextboot(libzfs_handle_t *, uint64_t, uint64_t, const char *);
|
||||
|
||||
/*
|
||||
* 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 *);
|
||||
extern boolean_t zpool_skip_pool(const char *);
|
||||
|
||||
/*
|
||||
* 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 *, const char *);
|
||||
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;
|
||||
int name_flags;
|
||||
} splitflags_t;
|
||||
|
||||
/*
|
||||
* Functions to manipulate pool and vdev state
|
||||
*/
|
||||
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t, pool_scrub_cmd_t);
|
||||
extern int zpool_initialize(zpool_handle_t *, pool_initialize_func_t,
|
||||
nvlist_t *);
|
||||
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
|
||||
extern int zpool_reguid(zpool_handle_t *);
|
||||
extern int zpool_reopen(zpool_handle_t *);
|
||||
|
||||
extern int zpool_sync_one(zpool_handle_t *, void *);
|
||||
|
||||
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_remove_cancel(zpool_handle_t *);
|
||||
extern int zpool_vdev_indirect_size(zpool_handle_t *, const char *, uint64_t *);
|
||||
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 *, const char *,
|
||||
zpool_boot_label_t, uint64_t, int *);
|
||||
|
||||
/*
|
||||
* 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 *, boolean_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.
|
||||
* This must be kept in sync with the zfs_msgid_table in
|
||||
* lib/libzfs/libzfs_status.c.
|
||||
*/
|
||||
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_HOSTID_ACTIVE, /* currently active on another system */
|
||||
ZPOOL_STATUS_HOSTID_REQUIRED, /* multihost=on and hostid=0 */
|
||||
ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */
|
||||
ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
|
||||
ZPOOL_STATUS_IO_FAILURE_MMP, /* failed MMP, failmode not 'panic' */
|
||||
ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
|
||||
|
||||
/*
|
||||
* If the pool has unsupported features but can still be opened in
|
||||
* read-only mode, its status is ZPOOL_STATUS_UNSUP_FEAT_WRITE. If the
|
||||
* pool has unsupported features but cannot be opened at all, its
|
||||
* status is ZPOOL_STATUS_UNSUP_FEAT_READ.
|
||||
*/
|
||||
ZPOOL_STATUS_UNSUP_FEAT_READ, /* unsupported features for read */
|
||||
ZPOOL_STATUS_UNSUP_FEAT_WRITE, /* unsupported features for write */
|
||||
|
||||
/*
|
||||
* 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 legacy on-disk version */
|
||||
ZPOOL_STATUS_FEAT_DISABLED, /* supported features are disabled */
|
||||
ZPOOL_STATUS_RESILVERING, /* device being resilvered */
|
||||
ZPOOL_STATUS_OFFLINE_DEV, /* device offline */
|
||||
ZPOOL_STATUS_REMOVED_DEV, /* removed device */
|
||||
ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */
|
||||
|
||||
/*
|
||||
* 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 nvlist_t *zpool_get_features(zpool_handle_t *);
|
||||
extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
|
||||
extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
|
||||
extern boolean_t zpool_is_bootable(zpool_handle_t *);
|
||||
|
||||
/*
|
||||
* Import and export functions
|
||||
*/
|
||||
extern int zpool_export(zpool_handle_t *, boolean_t, const char *);
|
||||
extern int zpool_export_force(zpool_handle_t *, const char *);
|
||||
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);
|
||||
extern void zpool_print_unsup_feat(nvlist_t *config);
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
nvlist_t *policy; /* load policy (max txg, rewind, etc.) */
|
||||
} importargs_t;
|
||||
|
||||
extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *);
|
||||
extern int zpool_tryimport(libzfs_handle_t *hdl, char *target,
|
||||
nvlist_t **configp, importargs_t *args);
|
||||
|
||||
/* 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[];
|
||||
|
||||
typedef enum {
|
||||
VDEV_NAME_PATH = 1 << 0,
|
||||
VDEV_NAME_GUID = 1 << 1,
|
||||
VDEV_NAME_FOLLOW_LINKS = 1 << 2,
|
||||
VDEV_NAME_TYPE_ID = 1 << 3,
|
||||
} vdev_name_t;
|
||||
|
||||
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
|
||||
int name_flags);
|
||||
extern int zpool_upgrade(zpool_handle_t *, uint64_t);
|
||||
extern int zpool_get_history(zpool_handle_t *, nvlist_t **, uint64_t *,
|
||||
boolean_t *);
|
||||
extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
|
||||
nvlist_t ***, uint_t *);
|
||||
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 request, 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 *);
|
||||
extern int zpool_checkpoint(zpool_handle_t *);
|
||||
extern int zpool_discard_checkpoint(zpool_handle_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 zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
|
||||
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 *);
|
||||
extern const char *zfs_get_pool_name(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 *, zpool_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_set_list(zfs_handle_t *, nvlist_t *);
|
||||
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 int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
|
||||
uint64_t *propvalue);
|
||||
extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
|
||||
char *propbuf, int proplen, boolean_t literal);
|
||||
extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname,
|
||||
char *buf, size_t len);
|
||||
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 *);
|
||||
extern nvlist_t *zfs_get_clones_nvl(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,
|
||||
boolean_t);
|
||||
extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *);
|
||||
|
||||
#define ZFS_MOUNTPOINT_NONE "none"
|
||||
#define ZFS_MOUNTPOINT_LEGACY "legacy"
|
||||
|
||||
#define ZFS_FEATURE_DISABLED "disabled"
|
||||
#define ZFS_FEATURE_ENABLED "enabled"
|
||||
#define ZFS_FEATURE_ACTIVE "active"
|
||||
|
||||
#define ZFS_UNSUPPORTED_INACTIVE "inactive"
|
||||
#define ZFS_UNSUPPORTED_READONLY "readonly"
|
||||
|
||||
/*
|
||||
* zpool property management
|
||||
*/
|
||||
extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
|
||||
extern int zpool_prop_get_feature(zpool_handle_t *, const char *, char *,
|
||||
size_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 *, boolean_t, zfs_iter_f, void *,
|
||||
uint64_t, uint64_t);
|
||||
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *,
|
||||
uint64_t, uint64_t);
|
||||
extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_bookmarks(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;
|
||||
} get_all_cb_t;
|
||||
|
||||
void zfs_foreach_mountpoint(libzfs_handle_t *, zfs_handle_t **, size_t,
|
||||
zfs_iter_f, void*, boolean_t);
|
||||
|
||||
void libzfs_add_handle(get_all_cb_t *, zfs_handle_t *);
|
||||
|
||||
/*
|
||||
* 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_destroy_snaps_nvl(libzfs_handle_t *, nvlist_t *, 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_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
|
||||
nvlist_t *props);
|
||||
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
|
||||
|
||||
typedef struct renameflags {
|
||||
/* recursive rename */
|
||||
int recurse : 1;
|
||||
|
||||
/* don't unmount file systems */
|
||||
int nounmount : 1;
|
||||
|
||||
/* force unmount file systems */
|
||||
int forceunmount : 1;
|
||||
} renameflags_t;
|
||||
|
||||
extern int zfs_rename(zfs_handle_t *, const char *, const char *,
|
||||
renameflags_t flags);
|
||||
|
||||
typedef struct sendflags {
|
||||
/* print informational messages (ie, -v was specified) */
|
||||
boolean_t verbose;
|
||||
|
||||
/* recursive send (ie, -R) */
|
||||
boolean_t replicate;
|
||||
|
||||
/* for incrementals, do all intermediate snapshots */
|
||||
boolean_t doall;
|
||||
|
||||
/* if dataset is a clone, do incremental from its origin */
|
||||
boolean_t fromorigin;
|
||||
|
||||
/* do deduplication */
|
||||
boolean_t dedup;
|
||||
|
||||
/* send properties (ie, -p) */
|
||||
boolean_t props;
|
||||
|
||||
/* do not send (no-op, ie. -n) */
|
||||
boolean_t dryrun;
|
||||
|
||||
/* parsable verbose output (ie. -P) */
|
||||
boolean_t parsable;
|
||||
|
||||
/* show progress (ie. -v) */
|
||||
boolean_t progress;
|
||||
|
||||
/* large blocks (>128K) are permitted */
|
||||
boolean_t largeblock;
|
||||
|
||||
/* WRITE_EMBEDDED records of type DATA are permitted */
|
||||
boolean_t embed_data;
|
||||
|
||||
/* compressed WRITE records are permitted */
|
||||
boolean_t compress;
|
||||
|
||||
/* show progress as process title(ie. -V) */
|
||||
boolean_t progressastitle;
|
||||
} sendflags_t;
|
||||
|
||||
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
||||
|
||||
extern int zfs_send(zfs_handle_t *, const char *, const char *,
|
||||
sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
|
||||
extern int zfs_send_one(zfs_handle_t *, const char *, int, sendflags_t flags);
|
||||
extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd,
|
||||
const char *);
|
||||
extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl,
|
||||
const char *token);
|
||||
|
||||
extern int zfs_promote(zfs_handle_t *);
|
||||
extern int zfs_hold(zfs_handle_t *, const char *, const char *,
|
||||
boolean_t, int);
|
||||
extern int zfs_hold_nvl(zfs_handle_t *, int, nvlist_t *);
|
||||
extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
|
||||
extern int zfs_get_holds(zfs_handle_t *, nvlist_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 *, zfs_userquota_prop_t,
|
||||
zfs_userspace_cb_t, void *);
|
||||
|
||||
extern int zfs_get_fsacl(zfs_handle_t *, nvlist_t **);
|
||||
extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
|
||||
|
||||
typedef struct recvflags {
|
||||
/* print informational messages (ie, -v was specified) */
|
||||
boolean_t verbose;
|
||||
|
||||
/* the destination is a prefix, not the exact fs (ie, -d) */
|
||||
boolean_t isprefix;
|
||||
|
||||
/*
|
||||
* Only the tail of the sent snapshot path is appended to the
|
||||
* destination to determine the received snapshot name (ie, -e).
|
||||
*/
|
||||
boolean_t istail;
|
||||
|
||||
/* do not actually do the recv, just check if it would work (ie, -n) */
|
||||
boolean_t dryrun;
|
||||
|
||||
/* rollback/destroy filesystems as necessary (eg, -F) */
|
||||
boolean_t force;
|
||||
|
||||
/* set "canmount=off" on all modified filesystems */
|
||||
boolean_t canmountoff;
|
||||
|
||||
/*
|
||||
* Mark the file systems as "resumable" and do not destroy them if the
|
||||
* receive is interrupted
|
||||
*/
|
||||
boolean_t resumable;
|
||||
|
||||
/* byteswap flag is used internally; callers need not specify */
|
||||
boolean_t byteswap;
|
||||
|
||||
/* do not mount file systems as they are extracted (private) */
|
||||
boolean_t nomount;
|
||||
|
||||
/* force unmount while recv snapshot (private) */
|
||||
boolean_t forceunmount;
|
||||
} recvflags_t;
|
||||
|
||||
extern int zfs_receive(libzfs_handle_t *, const char *, nvlist_t *,
|
||||
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 *);
|
||||
extern boolean_t zfs_bookmark_exists(const char *path);
|
||||
extern ulong_t get_system_hostid(void);
|
||||
|
||||
/*
|
||||
* Mount support functions.
|
||||
*/
|
||||
extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
|
||||
extern boolean_t zfs_is_mounted(zfs_handle_t *, char **);
|
||||
extern int zfs_mount(zfs_handle_t *, const char *, int);
|
||||
extern int zfs_mount_at(zfs_handle_t *, const char *, int, const char *);
|
||||
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);
|
||||
|
||||
/*
|
||||
* FreeBSD-specific jail support function.
|
||||
*/
|
||||
extern int zfs_jail(zfs_handle_t *, int, int);
|
||||
|
||||
/*
|
||||
* When dealing with nvlists, verify() is extremely useful
|
||||
*/
|
||||
#ifndef verify
|
||||
#ifdef NDEBUG
|
||||
#define verify(EX) ((void)(EX))
|
||||
#else
|
||||
#define verify(EX) assert(EX)
|
||||
#endif
|
||||
#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_read_all_labels(int, nvlist_t **);
|
||||
extern int zpool_clear_label(int);
|
||||
extern int zpool_set_bootenv(zpool_handle_t *, const char *);
|
||||
extern int zpool_get_bootenv(zpool_handle_t *, char *, size_t, off_t);
|
||||
|
||||
/* 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 *);
|
||||
|
||||
#ifndef illumos
|
||||
extern int zmount(const char *, const char *, int, char *, char *, int, char *,
|
||||
int);
|
||||
#endif
|
||||
extern int zfs_remap_indirects(libzfs_handle_t *hdl, const char *);
|
||||
|
||||
/* Allow consumers to initialize libshare externally for optimal performance */
|
||||
extern int zfs_init_libshare_arg(libzfs_handle_t *, int, void *);
|
||||
/*
|
||||
* For most consumers, zfs_init_libshare_arg is sufficient on its own, and
|
||||
* zfs_uninit_libshare is unnecessary. zfs_uninit_libshare should only be called
|
||||
* if the caller has already initialized libshare for one set of zfs handles,
|
||||
* and wishes to share or unshare filesystems outside of that set. In that case,
|
||||
* the caller should uninitialize libshare, and then re-initialize it with the
|
||||
* new handles being shared or unshared.
|
||||
*/
|
||||
extern void zfs_uninit_libshare(libzfs_handle_t *);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBZFS_H */
|
@ -1,736 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
*/
|
||||
|
||||
#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 (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
|
||||
break;
|
||||
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;
|
||||
|
||||
default:
|
||||
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;
|
||||
#ifdef illumos
|
||||
size_t num_datasets = 0, i;
|
||||
zfs_handle_t **zhandle_arr;
|
||||
sa_init_selective_arg_t sharearg;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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 &&
|
||||
!(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) {
|
||||
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);
|
||||
|
||||
#ifdef illumos
|
||||
/*
|
||||
* For efficiencies sake, we initialize libshare for only a few
|
||||
* shares (the ones affected here). Future initializations in
|
||||
* this process should just use the cached initialization.
|
||||
*/
|
||||
for (cn = uu_list_last(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_prev(clp->cl_list, cn)) {
|
||||
num_datasets++;
|
||||
}
|
||||
|
||||
zhandle_arr = zfs_alloc(hdl,
|
||||
num_datasets * sizeof (zfs_handle_t *));
|
||||
for (i = 0, cn = uu_list_last(clp->cl_list); cn != NULL;
|
||||
cn = uu_list_prev(clp->cl_list, cn)) {
|
||||
zhandle_arr[i++] = cn->cn_handle;
|
||||
zfs_refresh_properties(cn->cn_handle);
|
||||
}
|
||||
assert(i == num_datasets);
|
||||
sharearg.zhandle_arr = zhandle_arr;
|
||||
sharearg.zhandle_len = num_datasets;
|
||||
errors = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
|
||||
&sharearg);
|
||||
free(zhandle_arr);
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
#ifndef illumos
|
||||
zfs_refresh_properties(cn->cn_handle);
|
||||
#endif
|
||||
|
||||
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 = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
|
||||
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_MAX_DATASET_NAME_LEN];
|
||||
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* 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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "libzfs_compat.h"
|
||||
|
||||
int zfs_ioctl_version = ZFS_IOCVER_UNDEF;
|
||||
static int zfs_spa_version = -1;
|
||||
|
||||
/*
|
||||
* Get zfs_ioctl_version
|
||||
*/
|
||||
int
|
||||
get_zfs_ioctl_version(void)
|
||||
{
|
||||
size_t ver_size;
|
||||
int ver = ZFS_IOCVER_NONE;
|
||||
|
||||
ver_size = sizeof(ver);
|
||||
sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0);
|
||||
|
||||
return (ver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the SPA version
|
||||
*/
|
||||
static int
|
||||
get_zfs_spa_version(void)
|
||||
{
|
||||
size_t ver_size;
|
||||
int ver = 0;
|
||||
|
||||
ver_size = sizeof(ver);
|
||||
sysctlbyname("vfs.zfs.version.spa", &ver, &ver_size, NULL, 0);
|
||||
|
||||
return (ver);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is FreeBSD version of ioctl, because Solaris' ioctl() updates
|
||||
* zc_nvlist_dst_size even if an error is returned, on FreeBSD if an
|
||||
* error is returned zc_nvlist_dst_size won't be updated.
|
||||
*/
|
||||
int
|
||||
zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
|
||||
{
|
||||
size_t oldsize;
|
||||
int ret, cflag = ZFS_CMD_COMPAT_NONE;
|
||||
|
||||
if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
|
||||
zfs_ioctl_version = get_zfs_ioctl_version();
|
||||
|
||||
if (zfs_ioctl_version >= ZFS_IOCVER_DEADMAN) {
|
||||
switch (zfs_ioctl_version) {
|
||||
case ZFS_IOCVER_INLANES:
|
||||
cflag = ZFS_CMD_COMPAT_INLANES;
|
||||
break;
|
||||
case ZFS_IOCVER_RESUME:
|
||||
cflag = ZFS_CMD_COMPAT_RESUME;
|
||||
break;
|
||||
case ZFS_IOCVER_EDBP:
|
||||
cflag = ZFS_CMD_COMPAT_EDBP;
|
||||
break;
|
||||
case ZFS_IOCVER_ZCMD:
|
||||
cflag = ZFS_CMD_COMPAT_ZCMD;
|
||||
break;
|
||||
case ZFS_IOCVER_LZC:
|
||||
cflag = ZFS_CMD_COMPAT_LZC;
|
||||
break;
|
||||
case ZFS_IOCVER_DEADMAN:
|
||||
cflag = ZFS_CMD_COMPAT_DEADMAN;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If vfs.zfs.version.ioctl is not defined, assume we have v28
|
||||
* compatible binaries and use vfs.zfs.version.spa to test for v15
|
||||
*/
|
||||
cflag = ZFS_CMD_COMPAT_V28;
|
||||
|
||||
if (zfs_spa_version < 0)
|
||||
zfs_spa_version = get_zfs_spa_version();
|
||||
|
||||
if (zfs_spa_version == SPA_VERSION_15 ||
|
||||
zfs_spa_version == SPA_VERSION_14 ||
|
||||
zfs_spa_version == SPA_VERSION_13)
|
||||
cflag = ZFS_CMD_COMPAT_V15;
|
||||
}
|
||||
|
||||
oldsize = zc->zc_nvlist_dst_size;
|
||||
ret = zcmd_ioctl_compat(fd, request, zc, cflag);
|
||||
|
||||
if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) {
|
||||
ret = -1;
|
||||
errno = ENOMEM;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZFS_COMPAT_H
|
||||
#define _LIBZFS_COMPAT_H
|
||||
|
||||
#include <zfs_ioctl_compat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int get_zfs_ioctl_version(void);
|
||||
int zcmd_ioctl(int fd, int request, zfs_cmd_t *zc);
|
||||
|
||||
#define ioctl(fd, ioc, zc) zcmd_ioctl((fd), (ioc), (zc))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBZFS_COMPAT_H */
|
@ -1,469 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2015 by Syneto S.R.L. All rights reserved.
|
||||
* Copyright 2016 Nexenta Systems, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves a list of enabled features and their refcounts and caches it in
|
||||
* the pool handle.
|
||||
*/
|
||||
nvlist_t *
|
||||
zpool_get_features(zpool_handle_t *zhp)
|
||||
{
|
||||
nvlist_t *config, *features;
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
|
||||
if (config == NULL || !nvlist_exists(config,
|
||||
ZPOOL_CONFIG_FEATURE_STATS)) {
|
||||
int error;
|
||||
boolean_t missing = B_FALSE;
|
||||
|
||||
error = zpool_refresh_stats(zhp, &missing);
|
||||
|
||||
if (error != 0 || missing)
|
||||
return (NULL);
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
|
||||
&features) != 0)
|
||||
return (NULL);
|
||||
|
||||
return (features);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following environment variables are undocumented
|
||||
* and should be used for testing purposes only:
|
||||
*
|
||||
* __ZFS_POOL_EXCLUDE - don't iterate over the pools it lists
|
||||
* __ZFS_POOL_RESTRICT - iterate only over the pools it lists
|
||||
*
|
||||
* This function returns B_TRUE if the pool should be skipped
|
||||
* during iteration.
|
||||
*/
|
||||
boolean_t
|
||||
zpool_skip_pool(const char *poolname)
|
||||
{
|
||||
static boolean_t initialized = B_FALSE;
|
||||
static const char *exclude = NULL;
|
||||
static const char *restricted = NULL;
|
||||
|
||||
const char *cur, *end;
|
||||
int len;
|
||||
int namelen = strlen(poolname);
|
||||
|
||||
if (!initialized) {
|
||||
initialized = B_TRUE;
|
||||
exclude = getenv("__ZFS_POOL_EXCLUDE");
|
||||
restricted = getenv("__ZFS_POOL_RESTRICT");
|
||||
}
|
||||
|
||||
if (exclude != NULL) {
|
||||
cur = exclude;
|
||||
do {
|
||||
end = strchr(cur, ' ');
|
||||
len = (NULL == end) ? strlen(cur) : (end - cur);
|
||||
if (len == namelen && 0 == strncmp(cur, poolname, len))
|
||||
return (B_TRUE);
|
||||
cur += (len + 1);
|
||||
} while (NULL != end);
|
||||
}
|
||||
|
||||
if (NULL == restricted)
|
||||
return (B_FALSE);
|
||||
|
||||
cur = restricted;
|
||||
do {
|
||||
end = strchr(cur, ' ');
|
||||
len = (NULL == end) ? strlen(cur) : (end - cur);
|
||||
|
||||
if (len == namelen && 0 == strncmp(cur, poolname, len)) {
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
cur += (len + 1);
|
||||
} while (NULL != end);
|
||||
|
||||
return (B_TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_skip_pool(cn->cn_name))
|
||||
continue;
|
||||
|
||||
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 (zpool_skip_pool(cn->cn_name))
|
||||
continue;
|
||||
|
||||
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
@ -1,834 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2015, 2018 by Delphix. All rights reserved.
|
||||
* Copyright 2016 Joyent, Inc.
|
||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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'
|
||||
|
||||
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 == ESTALE) {
|
||||
(void) snprintf(pn, maxlen, "(on_delete_queue)");
|
||||
return (0);
|
||||
} else 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 %jd in %s"), (uintmax_t)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)
|
||||
{
|
||||
char c;
|
||||
|
||||
while ((c = *string++) != '\0') {
|
||||
if (c > ' ' && c != '\\' && c < '\177') {
|
||||
(void) fprintf(fp, "%c", c);
|
||||
} else {
|
||||
(void) fprintf(fp, "\\%03o", (uint8_t)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
#ifdef S_IFDOOR
|
||||
case S_IFDOOR:
|
||||
symbol = '>';
|
||||
break;
|
||||
#endif
|
||||
case S_IFIFO:
|
||||
symbol = '|';
|
||||
break;
|
||||
case S_IFLNK:
|
||||
symbol = '@';
|
||||
break;
|
||||
#ifdef S_IFPORT
|
||||
case S_IFPORT:
|
||||
symbol = 'P';
|
||||
break;
|
||||
#endif
|
||||
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;
|
||||
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 */
|
||||
|
||||
/* 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 (strcmp(fobjname, tobjname) == 0) {
|
||||
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)) != 0)
|
||||
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) {
|
||||
return (-1);
|
||||
}
|
||||
/* Don't print if in the delete queue on from side */
|
||||
if (di->zerr == ESTALE) {
|
||||
di->zerr = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
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 (> %jd) find failure"),
|
||||
(uintmax_t)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 == EPIPE);
|
||||
(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) {
|
||||
#ifdef illumos
|
||||
(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));
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
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_MAX_DATASET_NAME_LEN];
|
||||
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) {
|
||||
if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
|
||||
sizeof (origin), &src, NULL, 0, B_FALSE) != 0) {
|
||||
(void) zfs_close(zhp);
|
||||
zhp = NULL;
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
@ -1,452 +0,0 @@
|
||||
/*
|
||||
* 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 *))) == 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 *));
|
||||
}
|
||||
}
|
@ -1,228 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved.
|
||||
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZFS_IMPL_H
|
||||
#define _LIBZFS_IMPL_H
|
||||
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/nvpair.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
|
||||
#include <libshare.h>
|
||||
#include <libuutil.h>
|
||||
#include <libzfs.h>
|
||||
#include <libzfs_core.h>
|
||||
#include <libzfs_compat.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];
|
||||
int libzfs_printerr;
|
||||
int libzfs_storeerr; /* stuff error messages into buffer */
|
||||
void *libzfs_sharehdl; /* libshare handle */
|
||||
boolean_t libzfs_mnttab_enable;
|
||||
/*
|
||||
* We need a lock to handle the case where parallel mount
|
||||
* threads are populating the mnttab cache simultaneously. The
|
||||
* lock only protects the integrity of the avl tree, and does
|
||||
* not protect the contents of the mnttab entries themselves.
|
||||
*/
|
||||
pthread_mutex_t libzfs_mnttab_cache_lock;
|
||||
avl_tree_t libzfs_mnttab_cache;
|
||||
int libzfs_pool_iter;
|
||||
libzfs_fru_t **libzfs_fru_hash;
|
||||
libzfs_fru_t *libzfs_fru_list;
|
||||
char libzfs_chassis_id[256];
|
||||
boolean_t libzfs_prop_debug;
|
||||
};
|
||||
|
||||
struct zfs_handle {
|
||||
libzfs_handle_t *zfs_hdl;
|
||||
zpool_handle_t *zpool_hdl;
|
||||
char zfs_name[ZFS_MAX_DATASET_NAME_LEN];
|
||||
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[ZFS_MAX_DATASET_NAME_LEN];
|
||||
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;
|
||||
|
||||
#define CONFIG_BUF_MINSIZE 262144
|
||||
|
||||
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 *);
|
||||
zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
|
||||
zfs_handle_t *make_dataset_simple_handle_zc(zfs_handle_t *, zfs_cmd_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 0x01
|
||||
/*
|
||||
* Use this changelist_gather() flag to prevent unmounting of file systems.
|
||||
*/
|
||||
#define CL_GATHER_DONT_UNMOUNT 0x02
|
||||
|
||||
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 *);
|
||||
zfs_handle_t *make_bookmark_handle(zfs_handle_t *, const char *,
|
||||
nvlist_t *props);
|
||||
|
||||
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 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 /* _LIBZFS_IMPL_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,546 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2013, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
|
||||
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2019 Datto Inc.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <libintl.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
int
|
||||
zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
nvlist_t *nvl = zfs_get_clones_nvl(zhp);
|
||||
nvpair_t *pair;
|
||||
|
||||
if (nvl == NULL)
|
||||
return (0);
|
||||
|
||||
for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(nvl, pair)) {
|
||||
zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
||||
if (clone != NULL) {
|
||||
int err = func(clone, data);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
|
||||
{
|
||||
int rc;
|
||||
uint64_t orig_cookie;
|
||||
|
||||
orig_cookie = zc->zc_cookie;
|
||||
top:
|
||||
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
|
||||
rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
|
||||
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
/* expand nvlist memory and try again */
|
||||
if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
|
||||
zcmd_free_nvlists(zc);
|
||||
return (-1);
|
||||
}
|
||||
zc->zc_cookie = orig_cookie;
|
||||
goto top;
|
||||
/*
|
||||
* An errno value of ESRCH indicates normal completion.
|
||||
* If ENOENT is returned, then the underlying dataset
|
||||
* has been removed since we obtained the handle.
|
||||
*/
|
||||
case ESRCH:
|
||||
case ENOENT:
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
rc = zfs_standard_error(zhp->zfs_hdl, errno,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot iterate filesystems"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all child filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
/*
|
||||
* Silently ignore errors, as the only plausible explanation is
|
||||
* that the pool has since been removed.
|
||||
*/
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all snapshots
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
|
||||
void *data, uint64_t min_txg, uint64_t max_txg)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
nvlist_t *range_nvl = NULL;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
|
||||
zhp->zfs_type == ZFS_TYPE_BOOKMARK)
|
||||
return (0);
|
||||
|
||||
zc.zc_simple = simple;
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
if (min_txg != 0) {
|
||||
range_nvl = fnvlist_alloc();
|
||||
fnvlist_add_uint64(range_nvl, SNAP_ITER_MIN_TXG, min_txg);
|
||||
}
|
||||
if (max_txg != 0) {
|
||||
if (range_nvl == NULL)
|
||||
range_nvl = fnvlist_alloc();
|
||||
fnvlist_add_uint64(range_nvl, SNAP_ITER_MAX_TXG, max_txg);
|
||||
}
|
||||
|
||||
if (range_nvl != NULL &&
|
||||
zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, range_nvl) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
fnvlist_free(range_nvl);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
|
||||
if (simple)
|
||||
nzhp = make_dataset_simple_handle_zc(zhp, &zc);
|
||||
else
|
||||
nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
|
||||
if (nzhp == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
fnvlist_free(range_nvl);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
fnvlist_free(range_nvl);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all bookmarks
|
||||
*/
|
||||
int
|
||||
zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_handle_t *nzhp;
|
||||
nvlist_t *props = NULL;
|
||||
nvlist_t *bmarks = NULL;
|
||||
int err;
|
||||
|
||||
if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0)
|
||||
return (0);
|
||||
|
||||
/* Setup the requested properties nvlist. */
|
||||
props = fnvlist_alloc();
|
||||
fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_GUID));
|
||||
fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||
fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATION));
|
||||
|
||||
if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0)
|
||||
goto out;
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) {
|
||||
char name[ZFS_MAX_DATASET_NAME_LEN];
|
||||
char *bmark_name;
|
||||
nvlist_t *bmark_props;
|
||||
|
||||
bmark_name = nvpair_name(pair);
|
||||
bmark_props = fnvpair_value_nvlist(pair);
|
||||
|
||||
(void) snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name,
|
||||
bmark_name);
|
||||
|
||||
nzhp = make_bookmark_handle(zhp, name, bmark_props);
|
||||
if (nzhp == NULL)
|
||||
continue;
|
||||
|
||||
if ((err = func(nzhp, data)) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
fnvlist_free(props);
|
||||
fnvlist_free(bmarks);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for dealing with the sorted snapshot functionality
|
||||
*/
|
||||
typedef struct zfs_node {
|
||||
zfs_handle_t *zn_handle;
|
||||
avl_node_t zn_avlnode;
|
||||
} zfs_node_t;
|
||||
|
||||
static int
|
||||
zfs_sort_snaps(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
avl_tree_t *avl = data;
|
||||
zfs_node_t *node;
|
||||
zfs_node_t search;
|
||||
|
||||
search.zn_handle = zhp;
|
||||
node = avl_find(avl, &search, NULL);
|
||||
if (node) {
|
||||
/*
|
||||
* If this snapshot was renamed while we were creating the
|
||||
* AVL tree, it's possible that we already inserted it under
|
||||
* its old name. Remove the old handle before adding the new
|
||||
* one.
|
||||
*/
|
||||
zfs_close(node->zn_handle);
|
||||
avl_remove(avl, node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
|
||||
node->zn_handle = zhp;
|
||||
avl_add(avl, node);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_snapshot_compare(const void *larg, const void *rarg)
|
||||
{
|
||||
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
|
||||
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
|
||||
uint64_t lcreate, rcreate;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
return (AVL_CMP(lcreate, rcreate));
|
||||
}
|
||||
|
||||
int
|
||||
zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data,
|
||||
uint64_t min_txg, uint64_t max_txg)
|
||||
{
|
||||
int ret = 0;
|
||||
zfs_node_t *node;
|
||||
avl_tree_t avl;
|
||||
void *cookie = NULL;
|
||||
|
||||
avl_create(&avl, zfs_snapshot_compare,
|
||||
sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
|
||||
|
||||
ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl, min_txg,
|
||||
max_txg);
|
||||
|
||||
for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
|
||||
ret |= callback(node->zn_handle, data);
|
||||
|
||||
while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
|
||||
free(node);
|
||||
|
||||
avl_destroy(&avl);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *ssa_first;
|
||||
char *ssa_last;
|
||||
boolean_t ssa_seenfirst;
|
||||
boolean_t ssa_seenlast;
|
||||
zfs_iter_f ssa_func;
|
||||
void *ssa_arg;
|
||||
} snapspec_arg_t;
|
||||
|
||||
static int
|
||||
snapspec_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
snapspec_arg_t *ssa = arg;
|
||||
const char *shortsnapname;
|
||||
int err = 0;
|
||||
|
||||
if (ssa->ssa_seenlast)
|
||||
return (0);
|
||||
|
||||
shortsnapname = strchr(zfs_get_name(zhp), '@') + 1;
|
||||
if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
|
||||
ssa->ssa_seenfirst = B_TRUE;
|
||||
if (strcmp(shortsnapname, ssa->ssa_last) == 0)
|
||||
ssa->ssa_seenlast = B_TRUE;
|
||||
|
||||
if (ssa->ssa_seenfirst) {
|
||||
err = ssa->ssa_func(zhp, ssa->ssa_arg);
|
||||
} else {
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* spec is a string like "A,B%C,D"
|
||||
*
|
||||
* <snaps>, where <snaps> can be:
|
||||
* <snap> (single snapshot)
|
||||
* <snap>%<snap> (range of snapshots, inclusive)
|
||||
* %<snap> (range of snapshots, starting with earliest)
|
||||
* <snap>% (range of snapshots, ending with last)
|
||||
* % (all snapshots)
|
||||
* <snaps>[,...] (comma separated list of the above)
|
||||
*
|
||||
* If a snapshot can not be opened, continue trying to open the others, but
|
||||
* return ENOENT at the end.
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
|
||||
zfs_iter_f func, void *arg)
|
||||
{
|
||||
char *buf, *comma_separated, *cp;
|
||||
int err = 0;
|
||||
int ret = 0;
|
||||
|
||||
buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
|
||||
cp = buf;
|
||||
|
||||
while ((comma_separated = strsep(&cp, ",")) != NULL) {
|
||||
char *pct = strchr(comma_separated, '%');
|
||||
if (pct != NULL) {
|
||||
snapspec_arg_t ssa = { 0 };
|
||||
ssa.ssa_func = func;
|
||||
ssa.ssa_arg = arg;
|
||||
|
||||
if (pct == comma_separated)
|
||||
ssa.ssa_seenfirst = B_TRUE;
|
||||
else
|
||||
ssa.ssa_first = comma_separated;
|
||||
*pct = '\0';
|
||||
ssa.ssa_last = pct + 1;
|
||||
|
||||
/*
|
||||
* If there is a lastname specified, make sure it
|
||||
* exists.
|
||||
*/
|
||||
if (ssa.ssa_last[0] != '\0') {
|
||||
char snapname[ZFS_MAX_DATASET_NAME_LEN];
|
||||
(void) snprintf(snapname, sizeof (snapname),
|
||||
"%s@%s", zfs_get_name(fs_zhp),
|
||||
ssa.ssa_last);
|
||||
if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
|
||||
snapname, ZFS_TYPE_SNAPSHOT)) {
|
||||
ret = ENOENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
err = zfs_iter_snapshots_sorted(fs_zhp,
|
||||
snapspec_cb, &ssa, 0, 0);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
if (ret == 0 && (!ssa.ssa_seenfirst ||
|
||||
(ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
|
||||
ret = ENOENT;
|
||||
}
|
||||
} else {
|
||||
char snapname[ZFS_MAX_DATASET_NAME_LEN];
|
||||
zfs_handle_t *snap_zhp;
|
||||
(void) snprintf(snapname, sizeof (snapname), "%s@%s",
|
||||
zfs_get_name(fs_zhp), comma_separated);
|
||||
snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
|
||||
snapname);
|
||||
if (snap_zhp == NULL) {
|
||||
ret = ENOENT;
|
||||
continue;
|
||||
}
|
||||
err = func(snap_zhp, arg);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children, snapshots and filesystems
|
||||
* Process snapshots before filesystems because they are nearer the input
|
||||
* handle: this is extremely important when used with zfs_iter_f functions
|
||||
* looking for data, following the logic that we would like to find it as soon
|
||||
* and as close as possible.
|
||||
*/
|
||||
int
|
||||
zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = zfs_iter_snapshots(zhp, B_FALSE, func, data, 0, 0)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (zfs_iter_filesystems(zhp, func, data));
|
||||
}
|
||||
|
||||
|
||||
typedef struct iter_stack_frame {
|
||||
struct iter_stack_frame *next;
|
||||
zfs_handle_t *zhp;
|
||||
} iter_stack_frame_t;
|
||||
|
||||
typedef struct iter_dependents_arg {
|
||||
boolean_t first;
|
||||
boolean_t allowrecursion;
|
||||
iter_stack_frame_t *stack;
|
||||
zfs_iter_f func;
|
||||
void *data;
|
||||
} iter_dependents_arg_t;
|
||||
|
||||
static int
|
||||
iter_dependents_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
iter_dependents_arg_t *ida = arg;
|
||||
int err = 0;
|
||||
boolean_t first = ida->first;
|
||||
ida->first = B_FALSE;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
|
||||
err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
|
||||
} else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
|
||||
iter_stack_frame_t isf;
|
||||
iter_stack_frame_t *f;
|
||||
|
||||
/*
|
||||
* check if there is a cycle by seeing if this fs is already
|
||||
* on the stack.
|
||||
*/
|
||||
for (f = ida->stack; f != NULL; f = f->next) {
|
||||
if (f->zhp->zfs_dmustats.dds_guid ==
|
||||
zhp->zfs_dmustats.dds_guid) {
|
||||
if (ida->allowrecursion) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
} else {
|
||||
zfs_error_aux(zhp->zfs_hdl,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"recursive dependency at '%s'"),
|
||||
zfs_get_name(zhp));
|
||||
err = zfs_error(zhp->zfs_hdl,
|
||||
EZFS_RECURSIVE,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot determine dependent "
|
||||
"datasets"));
|
||||
zfs_close(zhp);
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isf.zhp = zhp;
|
||||
isf.next = ida->stack;
|
||||
ida->stack = &isf;
|
||||
err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
|
||||
if (err == 0) {
|
||||
err = zfs_iter_snapshots(zhp, B_FALSE,
|
||||
iter_dependents_cb, ida, 0, 0);
|
||||
}
|
||||
ida->stack = isf.next;
|
||||
}
|
||||
|
||||
if (!first && err == 0)
|
||||
err = ida->func(zhp, ida->data);
|
||||
else
|
||||
zfs_close(zhp);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
|
||||
zfs_iter_f func, void *data)
|
||||
{
|
||||
iter_dependents_arg_t ida;
|
||||
ida.allowrecursion = allowrecursion;
|
||||
ida.stack = NULL;
|
||||
ida.func = func;
|
||||
ida.data = data;
|
||||
ida.first = B_TRUE;
|
||||
return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
|
||||
}
|
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
@ -1,511 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 Steven Hartland. 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"
|
||||
#include "zfeature_common.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", /* ZPOOL_STATUS_CORRUPT_CACHE */
|
||||
"ZFS-8000-2Q", /* ZPOOL_STATUS_MISSING_DEV_R */
|
||||
"ZFS-8000-3C", /* ZPOOL_STATUS_MISSING_DEV_NR */
|
||||
"ZFS-8000-4J", /* ZPOOL_STATUS_CORRUPT_LABEL_R */
|
||||
"ZFS-8000-5E", /* ZPOOL_STATUS_CORRUPT_LABEL_NR */
|
||||
"ZFS-8000-6X", /* ZPOOL_STATUS_BAD_GUID_SUM */
|
||||
"ZFS-8000-72", /* ZPOOL_STATUS_CORRUPT_POOL */
|
||||
"ZFS-8000-8A", /* ZPOOL_STATUS_CORRUPT_DATA */
|
||||
"ZFS-8000-9P", /* ZPOOL_STATUS_FAILING_DEV */
|
||||
"ZFS-8000-A5", /* ZPOOL_STATUS_VERSION_NEWER */
|
||||
"ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_MISMATCH */
|
||||
"ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_ACTIVE */
|
||||
"ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_REQUIRED */
|
||||
"ZFS-8000-HC", /* ZPOOL_STATUS_IO_FAILURE_WAIT */
|
||||
"ZFS-8000-JQ", /* ZPOOL_STATUS_IO_FAILURE_CONTINUE */
|
||||
"ZFS-8000-MM", /* ZPOOL_STATUS_IO_FAILURE_MMP */
|
||||
"ZFS-8000-K4", /* ZPOOL_STATUS_BAD_LOG */
|
||||
/*
|
||||
* The following results have no message ID.
|
||||
* ZPOOL_STATUS_UNSUP_FEAT_READ
|
||||
* ZPOOL_STATUS_UNSUP_FEAT_WRITE
|
||||
* ZPOOL_STATUS_FAULTED_DEV_R
|
||||
* ZPOOL_STATUS_FAULTED_DEV_NR
|
||||
* ZPOOL_STATUS_VERSION_OLDER
|
||||
* ZPOOL_STATUS_FEAT_DISABLED
|
||||
* ZPOOL_STATUS_RESILVERING
|
||||
* ZPOOL_STATUS_OFFLINE_DEV
|
||||
* ZPOOL_STATUS_REMOVED_DEV
|
||||
* ZPOOL_STATUS_OK
|
||||
*/
|
||||
};
|
||||
|
||||
#define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_missing(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_OPEN_FAILED);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_faulted(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (vs->vs_state == VDEV_STATE_FAULTED);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_errors(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (vs->vs_state == VDEV_STATE_DEGRADED ||
|
||||
vs->vs_read_errors != 0 || vs->vs_write_errors != 0 ||
|
||||
vs->vs_checksum_errors != 0);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_broken(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (vs->vs_state == VDEV_STATE_CANT_OPEN);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_offlined(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (vs->vs_state == VDEV_STATE_OFFLINE);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
vdev_removed(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (vs->vs_state == VDEV_STATE_REMOVED);
|
||||
}
|
||||
|
||||
static int
|
||||
vdev_non_native_ashift(vdev_stat_t *vs, uint_t vsc)
|
||||
{
|
||||
return (VDEV_STAT_VALID(vs_physical_ashift, vsc) &&
|
||||
vs->vs_configured_ashift < vs->vs_physical_ashift);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)(vdev_stat_t *, uint_t),
|
||||
boolean_t ignore_replacing)
|
||||
{
|
||||
nvlist_t **child;
|
||||
vdev_stat_t *vs;
|
||||
uint_t c, vsc, children;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (ignore_replacing == B_TRUE) {
|
||||
char *type;
|
||||
|
||||
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, ignore_replacing))
|
||||
return (B_TRUE);
|
||||
} else {
|
||||
verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS,
|
||||
(uint64_t **)&vs, &vsc) == 0);
|
||||
|
||||
if (func(vs, vsc) != 0)
|
||||
return (B_TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check any L2 cache devs
|
||||
*/
|
||||
if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_L2CACHE, &child,
|
||||
&children) == 0) {
|
||||
for (c = 0; c < children; c++)
|
||||
if (find_vdev_problem(child[c], func, ignore_replacing))
|
||||
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;
|
||||
unsigned long system_hostid = get_system_hostid();
|
||||
|
||||
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 != NULL && ps->pss_func == POOL_SCAN_RESILVER &&
|
||||
ps->pss_state == DSS_SCANNING)
|
||||
return (ZPOOL_STATUS_RESILVERING);
|
||||
|
||||
/*
|
||||
* The multihost property is set and the pool may be active.
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_ACTIVE) {
|
||||
mmp_state_t mmp_state;
|
||||
nvlist_t *nvinfo;
|
||||
|
||||
nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO);
|
||||
mmp_state = fnvlist_lookup_uint64(nvinfo,
|
||||
ZPOOL_CONFIG_MMP_STATE);
|
||||
|
||||
if (mmp_state == MMP_STATE_ACTIVE)
|
||||
return (ZPOOL_STATUS_HOSTID_ACTIVE);
|
||||
else if (mmp_state == MMP_STATE_NO_HOSTID)
|
||||
return (ZPOOL_STATUS_HOSTID_REQUIRED);
|
||||
else
|
||||
return (ZPOOL_STATUS_HOSTID_MISMATCH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pool last accessed by another system.
|
||||
*/
|
||||
(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);
|
||||
if (hostid != 0 && (unsigned long)hostid != system_hostid &&
|
||||
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);
|
||||
|
||||
/*
|
||||
* Unsupported feature(s).
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_UNSUP_FEAT) {
|
||||
nvlist_t *nvinfo;
|
||||
|
||||
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO,
|
||||
&nvinfo) == 0);
|
||||
if (nvlist_exists(nvinfo, ZPOOL_CONFIG_CAN_RDONLY))
|
||||
return (ZPOOL_STATUS_UNSUP_FEAT_WRITE);
|
||||
return (ZPOOL_STATUS_UNSUP_FEAT_READ);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,
|
||||
&suspended) == 0) {
|
||||
uint64_t reason;
|
||||
|
||||
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED_REASON,
|
||||
&reason) == 0 && reason == ZIO_SUSPEND_MMP)
|
||||
return (ZPOOL_STATUS_IO_FAILURE_MMP);
|
||||
|
||||
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, B_TRUE))
|
||||
return (ZPOOL_STATUS_FAULTED_DEV_NR);
|
||||
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
find_vdev_problem(nvroot, vdev_missing, B_TRUE))
|
||||
return (ZPOOL_STATUS_MISSING_DEV_NR);
|
||||
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
find_vdev_problem(nvroot, vdev_broken, B_TRUE))
|
||||
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, B_TRUE))
|
||||
return (ZPOOL_STATUS_FAULTED_DEV_R);
|
||||
if (find_vdev_problem(nvroot, vdev_missing, B_TRUE))
|
||||
return (ZPOOL_STATUS_MISSING_DEV_R);
|
||||
if (find_vdev_problem(nvroot, vdev_broken, B_TRUE))
|
||||
return (ZPOOL_STATUS_CORRUPT_LABEL_R);
|
||||
|
||||
/*
|
||||
* Devices with errors
|
||||
*/
|
||||
if (!isimport && find_vdev_problem(nvroot, vdev_errors, B_TRUE))
|
||||
return (ZPOOL_STATUS_FAILING_DEV);
|
||||
|
||||
/*
|
||||
* Offlined devices
|
||||
*/
|
||||
if (find_vdev_problem(nvroot, vdev_offlined, B_TRUE))
|
||||
return (ZPOOL_STATUS_OFFLINE_DEV);
|
||||
|
||||
/*
|
||||
* Removed device
|
||||
*/
|
||||
if (find_vdev_problem(nvroot, vdev_removed, B_TRUE))
|
||||
return (ZPOOL_STATUS_REMOVED_DEV);
|
||||
|
||||
/*
|
||||
* Suboptimal, but usable, ashift configuration.
|
||||
*/
|
||||
if (find_vdev_problem(nvroot, vdev_non_native_ashift, B_FALSE))
|
||||
return (ZPOOL_STATUS_NON_NATIVE_ASHIFT);
|
||||
|
||||
/*
|
||||
* Outdated, but usable, version
|
||||
*/
|
||||
if (SPA_VERSION_IS_SUPPORTED(version) && version != SPA_VERSION)
|
||||
return (ZPOOL_STATUS_VERSION_OLDER);
|
||||
|
||||
/*
|
||||
* Usable pool with disabled features
|
||||
*/
|
||||
if (version >= SPA_VERSION_FEATURES) {
|
||||
int i;
|
||||
nvlist_t *feat;
|
||||
|
||||
if (isimport) {
|
||||
feat = fnvlist_lookup_nvlist(config,
|
||||
ZPOOL_CONFIG_LOAD_INFO);
|
||||
if (nvlist_exists(feat, ZPOOL_CONFIG_ENABLED_FEAT))
|
||||
feat = fnvlist_lookup_nvlist(feat,
|
||||
ZPOOL_CONFIG_ENABLED_FEAT);
|
||||
} else {
|
||||
feat = fnvlist_lookup_nvlist(config,
|
||||
ZPOOL_CONFIG_FEATURE_STATS);
|
||||
}
|
||||
|
||||
for (i = 0; i < SPA_FEATURES; i++) {
|
||||
zfeature_info_t *fi = &spa_feature_table[i];
|
||||
if (!nvlist_exists(feat, fi->fi_guid))
|
||||
return (ZPOOL_STATUS_FEAT_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
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
File diff suppressed because it is too large
Load Diff
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* 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) 2012, 2020 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
* Copyright 2017 RackTop Systems.
|
||||
* Copyright (c) 2017 Datto Inc.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZFS_CORE_H
|
||||
#define _LIBZFS_CORE_H
|
||||
|
||||
#include <libnvpair.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int libzfs_core_init(void);
|
||||
void libzfs_core_fini(void);
|
||||
|
||||
/*
|
||||
* NB: this type should be kept binary compatible with dmu_objset_type_t.
|
||||
*/
|
||||
enum lzc_dataset_type {
|
||||
LZC_DATSET_TYPE_ZFS = 2,
|
||||
LZC_DATSET_TYPE_ZVOL
|
||||
};
|
||||
|
||||
int lzc_remap(const char *fsname);
|
||||
int lzc_snapshot(nvlist_t *, nvlist_t *, nvlist_t **);
|
||||
int lzc_create(const char *, enum lzc_dataset_type, nvlist_t *);
|
||||
int lzc_clone(const char *, const char *, nvlist_t *);
|
||||
int lzc_promote(const char *, char *, int);
|
||||
int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **);
|
||||
int lzc_bookmark(nvlist_t *, nvlist_t **);
|
||||
int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **);
|
||||
int lzc_destroy_bookmarks(nvlist_t *, nvlist_t **);
|
||||
int lzc_initialize(const char *, pool_initialize_func_t, nvlist_t *,
|
||||
nvlist_t **);
|
||||
|
||||
int lzc_snaprange_space(const char *, const char *, uint64_t *);
|
||||
|
||||
int lzc_hold(nvlist_t *, int, nvlist_t **);
|
||||
int lzc_release(nvlist_t *, nvlist_t **);
|
||||
int lzc_get_holds(const char *, nvlist_t **);
|
||||
|
||||
enum lzc_send_flags {
|
||||
LZC_SEND_FLAG_EMBED_DATA = 1 << 0,
|
||||
LZC_SEND_FLAG_LARGE_BLOCK = 1 << 1,
|
||||
LZC_SEND_FLAG_COMPRESS = 1 << 2
|
||||
};
|
||||
|
||||
int lzc_send(const char *, const char *, int, enum lzc_send_flags);
|
||||
int lzc_send_resume(const char *, const char *, int,
|
||||
enum lzc_send_flags, uint64_t, uint64_t);
|
||||
int lzc_send_space(const char *, const char *, enum lzc_send_flags, uint64_t *);
|
||||
|
||||
struct dmu_replay_record;
|
||||
|
||||
int lzc_receive(const char *, nvlist_t *, const char *, boolean_t, int);
|
||||
int lzc_receive_resumable(const char *, nvlist_t *, const char *,
|
||||
boolean_t, int);
|
||||
int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
|
||||
boolean_t, int, const struct dmu_replay_record *);
|
||||
|
||||
boolean_t lzc_exists(const char *);
|
||||
|
||||
int lzc_rollback(const char *, char *, int);
|
||||
int lzc_rollback_to(const char *, const char *);
|
||||
|
||||
int lzc_sync(const char *, nvlist_t *, nvlist_t **);
|
||||
|
||||
int lzc_rename(const char *, const char *);
|
||||
int lzc_destroy(const char *);
|
||||
|
||||
int lzc_channel_program(const char *, const char *, uint64_t,
|
||||
uint64_t, nvlist_t *, nvlist_t **);
|
||||
int lzc_channel_program_nosync(const char *, const char *, uint64_t,
|
||||
uint64_t, nvlist_t *, nvlist_t **);
|
||||
|
||||
int lzc_pool_checkpoint(const char *);
|
||||
int lzc_pool_checkpoint_discard(const char *);
|
||||
|
||||
int lzc_set_bootenv(const char *, const char *);
|
||||
int lzc_get_bootenv(const char *, nvlist_t **);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBZFS_CORE_H */
|
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* 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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_ioctl.h>
|
||||
#include <zfs_ioctl_compat.h>
|
||||
#include "libzfs_core_compat.h"
|
||||
|
||||
extern int zfs_ioctl_version;
|
||||
|
||||
int
|
||||
lzc_compat_pre(zfs_cmd_t *zc, zfs_ioc_t *ioc, nvlist_t **source)
|
||||
{
|
||||
nvlist_t *nvl = NULL;
|
||||
nvpair_t *pair, *hpair;
|
||||
char *buf, *val;
|
||||
zfs_ioc_t vecnum;
|
||||
uint32_t type32;
|
||||
int32_t cleanup_fd;
|
||||
int error = 0;
|
||||
int pos;
|
||||
|
||||
if (zfs_ioctl_version >= ZFS_IOCVER_LZC)
|
||||
return (0);
|
||||
|
||||
vecnum = *ioc;
|
||||
|
||||
switch (vecnum) {
|
||||
case ZFS_IOC_CREATE:
|
||||
type32 = fnvlist_lookup_int32(*source, "type");
|
||||
zc->zc_objset_type = (uint64_t)type32;
|
||||
nvlist_lookup_nvlist(*source, "props", &nvl);
|
||||
*source = nvl;
|
||||
break;
|
||||
case ZFS_IOC_CLONE:
|
||||
buf = fnvlist_lookup_string(*source, "origin");
|
||||
strlcpy(zc->zc_value, buf, MAXPATHLEN);
|
||||
nvlist_lookup_nvlist(*source, "props", &nvl);
|
||||
*ioc = ZFS_IOC_CREATE;
|
||||
*source = nvl;
|
||||
break;
|
||||
case ZFS_IOC_SNAPSHOT:
|
||||
nvl = fnvlist_lookup_nvlist(*source, "snaps");
|
||||
pair = nvlist_next_nvpair(nvl, NULL);
|
||||
if (pair != NULL) {
|
||||
buf = nvpair_name(pair);
|
||||
pos = strcspn(buf, "@");
|
||||
strlcpy(zc->zc_name, buf, pos + 1);
|
||||
strlcpy(zc->zc_value, buf + pos + 1, MAXPATHLEN);
|
||||
} else
|
||||
error = EINVAL;
|
||||
/* old kernel cannot create multiple snapshots */
|
||||
if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
|
||||
error = EOPNOTSUPP;
|
||||
nvlist_free(nvl);
|
||||
nvl = NULL;
|
||||
nvlist_lookup_nvlist(*source, "props", &nvl);
|
||||
*source = nvl;
|
||||
break;
|
||||
case ZFS_IOC_SPACE_SNAPS:
|
||||
buf = fnvlist_lookup_string(*source, "firstsnap");
|
||||
strlcpy(zc->zc_value, buf, MAXPATHLEN);
|
||||
break;
|
||||
case ZFS_IOC_DESTROY_SNAPS:
|
||||
nvl = fnvlist_lookup_nvlist(*source, "snaps");
|
||||
pair = nvlist_next_nvpair(nvl, NULL);
|
||||
if (pair != NULL) {
|
||||
buf = nvpair_name(pair);
|
||||
pos = strcspn(buf, "@");
|
||||
strlcpy(zc->zc_name, buf, pos + 1);
|
||||
} else
|
||||
error = EINVAL;
|
||||
/* old kernel cannot atomically destroy multiple snaps */
|
||||
if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
|
||||
error = EOPNOTSUPP;
|
||||
*source = nvl;
|
||||
break;
|
||||
case ZFS_IOC_HOLD:
|
||||
nvl = fnvlist_lookup_nvlist(*source, "holds");
|
||||
pair = nvlist_next_nvpair(nvl, NULL);
|
||||
if (pair != NULL) {
|
||||
buf = nvpair_name(pair);
|
||||
pos = strcspn(buf, "@");
|
||||
strlcpy(zc->zc_name, buf, pos + 1);
|
||||
strlcpy(zc->zc_value, buf + pos + 1, MAXPATHLEN);
|
||||
if (nvpair_value_string(pair, &val) == 0)
|
||||
strlcpy(zc->zc_string, val, MAXNAMELEN);
|
||||
else
|
||||
error = EINVAL;
|
||||
} else
|
||||
error = EINVAL;
|
||||
/* old kernel cannot atomically create multiple holds */
|
||||
if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
|
||||
error = EOPNOTSUPP;
|
||||
nvlist_free(nvl);
|
||||
if (nvlist_lookup_int32(*source, "cleanup_fd",
|
||||
&cleanup_fd) == 0)
|
||||
zc->zc_cleanup_fd = cleanup_fd;
|
||||
else
|
||||
zc->zc_cleanup_fd = -1;
|
||||
break;
|
||||
case ZFS_IOC_RELEASE:
|
||||
pair = nvlist_next_nvpair(*source, NULL);
|
||||
if (pair != NULL) {
|
||||
buf = nvpair_name(pair);
|
||||
pos = strcspn(buf, "@");
|
||||
strlcpy(zc->zc_name, buf, pos + 1);
|
||||
strlcpy(zc->zc_value, buf + pos + 1, MAXPATHLEN);
|
||||
if (nvpair_value_nvlist(pair, &nvl) == 0) {
|
||||
hpair = nvlist_next_nvpair(nvl, NULL);
|
||||
if (hpair != NULL)
|
||||
strlcpy(zc->zc_string,
|
||||
nvpair_name(hpair), MAXNAMELEN);
|
||||
else
|
||||
error = EINVAL;
|
||||
if (!error && nvlist_next_nvpair(nvl,
|
||||
hpair) != NULL)
|
||||
error = EOPNOTSUPP;
|
||||
} else
|
||||
error = EINVAL;
|
||||
} else
|
||||
error = EINVAL;
|
||||
/* old kernel cannot atomically release multiple holds */
|
||||
if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
|
||||
error = EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
lzc_compat_post(zfs_cmd_t *zc, const zfs_ioc_t ioc)
|
||||
{
|
||||
if (zfs_ioctl_version >= ZFS_IOCVER_LZC)
|
||||
return;
|
||||
|
||||
switch (ioc) {
|
||||
case ZFS_IOC_CREATE:
|
||||
case ZFS_IOC_CLONE:
|
||||
case ZFS_IOC_SNAPSHOT:
|
||||
case ZFS_IOC_SPACE_SNAPS:
|
||||
case ZFS_IOC_DESTROY_SNAPS:
|
||||
zc->zc_nvlist_dst_filled = B_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
lzc_compat_outnvl(zfs_cmd_t *zc, const zfs_ioc_t ioc, nvlist_t **outnvl)
|
||||
{
|
||||
nvlist_t *nvl;
|
||||
|
||||
if (zfs_ioctl_version >= ZFS_IOCVER_LZC)
|
||||
return (0);
|
||||
|
||||
switch (ioc) {
|
||||
case ZFS_IOC_SPACE_SNAPS:
|
||||
nvl = fnvlist_alloc();
|
||||
fnvlist_add_uint64(nvl, "used", zc->zc_cookie);
|
||||
fnvlist_add_uint64(nvl, "compressed", zc->zc_objset_type);
|
||||
fnvlist_add_uint64(nvl, "uncompressed", zc->zc_perm_action);
|
||||
*outnvl = nvl;
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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) 2013 by Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZFS_CORE_COMPAT_H
|
||||
#define _LIBZFS_CORE_COMPAT_H
|
||||
|
||||
#include <libnvpair.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/zfs_ioctl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int lzc_compat_pre(zfs_cmd_t *, zfs_ioc_t *, nvlist_t **);
|
||||
void lzc_compat_post(zfs_cmd_t *, const zfs_ioc_t);
|
||||
int lzc_compat_outnvl(zfs_cmd_t *, const zfs_ioc_t, nvlist_t **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBZFS_CORE_COMPAT_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,838 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2011 Nexenta Systems, Inc. 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_T_LOCK_H
|
||||
#define _SYS_VNODE_H
|
||||
#define _SYS_VFS_H
|
||||
#define _SYS_SUNDDI_H
|
||||
#define _SYS_CALLB_H
|
||||
#define _SYS_SCHED_H_
|
||||
|
||||
#include <solaris.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 <thread.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <umem.h>
|
||||
#include <inttypes.h>
|
||||
#include <fsshare.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <setjmp.h>
|
||||
#include <sys/debug.h>
|
||||
#include <sys/note.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/cred.h>
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/bitmap.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <sys/list.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/mntent.h>
|
||||
#include <sys/mnttab.h>
|
||||
#include <sys/zfs_debug.h>
|
||||
#include <sys/sdt.h>
|
||||
#include <sys/kstat.h>
|
||||
#include <sys/u8_textprep.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/disk.h>
|
||||
#include <sys/sysevent.h>
|
||||
#include <sys/sysevent/eventdefs.h>
|
||||
#include <sys/sysevent/dev.h>
|
||||
#include <machine/atomic.h>
|
||||
#include <sys/debug.h>
|
||||
#ifdef illumos
|
||||
#include "zfs.h"
|
||||
#endif
|
||||
|
||||
#define ZFS_EXPORTS_PATH "/etc/zfs/exports"
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define ZFS_LOG(...) do { } while (0)
|
||||
|
||||
typedef u_longlong_t rlim64_t;
|
||||
#define RLIM64_INFINITY ((rlim64_t)-3)
|
||||
|
||||
#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 *, ...) __NORETURN;
|
||||
extern void vpanic(const char *, __va_list) __NORETURN;
|
||||
|
||||
#define fm_panic panic
|
||||
|
||||
extern int aok;
|
||||
|
||||
/*
|
||||
* DTrace SDT probes have different signatures in userland than they do in
|
||||
* the kernel. If they're being used in kernel code, re-define them out of
|
||||
* existence for their counterparts in libzpool.
|
||||
*
|
||||
* Here's an example of how to use the set-error probes in userland:
|
||||
* zfs$target:::set-error /arg0 == EBUSY/ {stack();}
|
||||
*
|
||||
* Here's an example of how to use DTRACE_PROBE probes in userland:
|
||||
* If there is a probe declared as follows:
|
||||
* DTRACE_PROBE2(zfs__probe_name, uint64_t, blkid, dnode_t *, dn);
|
||||
* Then you can use it as follows:
|
||||
* zfs$target:::probe2 /copyinstr(arg0) == "zfs__probe_name"/
|
||||
* {printf("%u %p\n", arg1, arg2);}
|
||||
*/
|
||||
|
||||
#ifdef DTRACE_PROBE
|
||||
#undef DTRACE_PROBE
|
||||
#endif /* DTRACE_PROBE */
|
||||
#ifdef illumos
|
||||
#define DTRACE_PROBE(a) \
|
||||
ZFS_PROBE0(#a)
|
||||
#endif
|
||||
|
||||
#ifdef DTRACE_PROBE1
|
||||
#undef DTRACE_PROBE1
|
||||
#endif /* DTRACE_PROBE1 */
|
||||
#ifdef illumos
|
||||
#define DTRACE_PROBE1(a, b, c) \
|
||||
ZFS_PROBE1(#a, (unsigned long)c)
|
||||
#endif
|
||||
|
||||
#ifdef DTRACE_PROBE2
|
||||
#undef DTRACE_PROBE2
|
||||
#endif /* DTRACE_PROBE2 */
|
||||
#ifdef illumos
|
||||
#define DTRACE_PROBE2(a, b, c, d, e) \
|
||||
ZFS_PROBE2(#a, (unsigned long)c, (unsigned long)e)
|
||||
#endif
|
||||
|
||||
#ifdef DTRACE_PROBE3
|
||||
#undef DTRACE_PROBE3
|
||||
#endif /* DTRACE_PROBE3 */
|
||||
#ifdef illumos
|
||||
#define DTRACE_PROBE3(a, b, c, d, e, f, g) \
|
||||
ZFS_PROBE3(#a, (unsigned long)c, (unsigned long)e, (unsigned long)g)
|
||||
#endif
|
||||
|
||||
#ifdef DTRACE_PROBE4
|
||||
#undef DTRACE_PROBE4
|
||||
#endif /* DTRACE_PROBE4 */
|
||||
#ifdef illumos
|
||||
#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) \
|
||||
ZFS_PROBE4(#a, (unsigned long)c, (unsigned long)e, (unsigned long)g, \
|
||||
(unsigned long)i)
|
||||
#endif
|
||||
|
||||
#ifdef illumos
|
||||
/*
|
||||
* We use the comma operator so that this macro can be used without much
|
||||
* additional code. For example, "return (EINVAL);" becomes
|
||||
* "return (SET_ERROR(EINVAL));". Note that the argument will be evaluated
|
||||
* twice, so it should not have side effects (e.g. something like:
|
||||
* "return (SET_ERROR(log_error(EINVAL, info)));" would log the error twice).
|
||||
*/
|
||||
#define SET_ERROR(err) (ZFS_SET_ERROR(err), err)
|
||||
#else /* !illumos */
|
||||
|
||||
#define DTRACE_PROBE(a) ((void)0)
|
||||
#define DTRACE_PROBE1(a, b, c) ((void)0)
|
||||
#define DTRACE_PROBE2(a, b, c, d, e) ((void)0)
|
||||
#define DTRACE_PROBE3(a, b, c, d, e, f, g) ((void)0)
|
||||
#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0)
|
||||
|
||||
#define SET_ERROR(err) (err)
|
||||
#endif /* !illumos */
|
||||
|
||||
/*
|
||||
* Threads
|
||||
*/
|
||||
#define curthread ((void *)(uintptr_t)thr_self())
|
||||
|
||||
#define kpreempt(x) sched_yield()
|
||||
|
||||
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 curproc (&p0)
|
||||
|
||||
#define PS_NONE -1
|
||||
|
||||
extern kthread_t *zk_thread_create(void (*func)(void*), 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) ((m)->m_owner == curthread)
|
||||
#define MUTEX_NOT_HELD(m) (!MUTEX_HELD(m))
|
||||
#define _mutex_held(m) pthread_mutex_isowned_np(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);
|
||||
//extern int _mutex_owned(mutex_t *mp);
|
||||
|
||||
#define mutex_init(mp, b, c, d) zmutex_init((kmutex_t *)(mp))
|
||||
#define mutex_destroy(mp) zmutex_destroy((kmutex_t *)(mp))
|
||||
#define mutex_owned(mp) zmutex_owned((kmutex_t *)(mp))
|
||||
|
||||
extern void zmutex_init(kmutex_t *mp);
|
||||
extern void zmutex_destroy(kmutex_t *mp);
|
||||
extern int zmutex_owned(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 {
|
||||
int rw_count;
|
||||
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) ((x)->rw_owner == NULL && (x)->rw_count > 0)
|
||||
|
||||
#undef RW_WRITE_HELD
|
||||
#define RW_WRITE_HELD(x) ((x)->rw_owner == curthread)
|
||||
#define RW_LOCK_HELD(x) rw_lock_held(x)
|
||||
|
||||
#undef RW_LOCK_HELD
|
||||
#define RW_LOCK_HELD(x) (RW_READ_HELD(x) || RW_WRITE_HELD(x))
|
||||
|
||||
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);
|
||||
extern int rw_lock_held(krwlock_t *rwlp);
|
||||
#define rw_downgrade(rwlp) do { } while (0)
|
||||
|
||||
extern uid_t crgetuid(cred_t *cr);
|
||||
extern uid_t crgetruid(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
|
||||
#define CALLOUT_FLAG_ABSOLUTE 0x2
|
||||
|
||||
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 int cv_wait_sig(kcondvar_t *cv, kmutex_t *mp);
|
||||
extern clock_t cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime);
|
||||
#define cv_timedwait_sig(cvp, mp, t) cv_timedwait(cvp, mp, t)
|
||||
extern clock_t cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp, hrtime_t tim,
|
||||
hrtime_t res, int flag);
|
||||
#define cv_timedwait_sig_hires(cvp, mp, t, r, f) \
|
||||
cv_timedwait_hires(cvp, mp, t, r, f)
|
||||
extern void cv_signal(kcondvar_t *cv);
|
||||
extern void cv_broadcast(kcondvar_t *cv);
|
||||
|
||||
/*
|
||||
* Thread-specific data
|
||||
*/
|
||||
#define tsd_get(k) pthread_getspecific(k)
|
||||
#define tsd_set(k, v) pthread_setspecific(k, v)
|
||||
#define tsd_create(kp, d) pthread_key_create(kp, d)
|
||||
#define tsd_destroy(kp) /* nothing */
|
||||
|
||||
/*
|
||||
* Kernel memory
|
||||
*/
|
||||
#define KM_SLEEP UMEM_NOFAIL
|
||||
#define KM_PUSHPAGE KM_SLEEP
|
||||
#define KM_NOSLEEP UMEM_DEFAULT
|
||||
#define KM_NORMALPRI 0 /* not needed with UMEM_DEFAULT */
|
||||
#define KMC_NODEBUG UMC_NODEBUG
|
||||
#define KMC_NOTOUCH 0 /* not needed for userland caches */
|
||||
#define KM_NODEBUG 0
|
||||
#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_size() (physmem * PAGESIZE)
|
||||
#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_active() (B_FALSE)
|
||||
#define kmem_cache_reap_soon(_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 *);
|
||||
|
||||
typedef struct taskq_ent {
|
||||
struct taskq_ent *tqent_next;
|
||||
struct taskq_ent *tqent_prev;
|
||||
task_func_t *tqent_func;
|
||||
void *tqent_arg;
|
||||
uintptr_t tqent_flags;
|
||||
} taskq_ent_t;
|
||||
|
||||
#define TQENT_FLAG_PREALLOC 0x1 /* taskq_dispatch_ent used */
|
||||
|
||||
#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 */
|
||||
|
||||
#define TASKQID_INVALID ((taskqid_t)0)
|
||||
|
||||
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_dispatch_ent(taskq_t *, task_func_t, void *, uint_t,
|
||||
taskq_ent_t *);
|
||||
extern void taskq_destroy(taskq_t *);
|
||||
extern void taskq_wait(taskq_t *);
|
||||
extern void taskq_wait_id(taskq_t *, taskqid_t);
|
||||
extern int taskq_member(taskq_t *, void *);
|
||||
extern void system_taskq_init(void);
|
||||
extern void system_taskq_fini(void);
|
||||
|
||||
#define taskq_dispatch_safe(tq, func, arg, flags, task) \
|
||||
taskq_dispatch((tq), (func), (arg), (flags))
|
||||
|
||||
#define XVA_MAPSIZE 3
|
||||
#define XVA_MAGIC 0x78766174
|
||||
|
||||
/*
|
||||
* vnodes
|
||||
*/
|
||||
typedef struct vnode {
|
||||
uint64_t v_size;
|
||||
int v_fd;
|
||||
char *v_path;
|
||||
int v_dump_fd;
|
||||
} vnode_t;
|
||||
|
||||
extern char *vn_dumpdir;
|
||||
#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, cr) fop_getattr((vp), (vap));
|
||||
|
||||
#define VOP_FSYNC(vp, f, cr, ct) fsync((vp)->v_fd)
|
||||
|
||||
#define VN_RELE(vp) vn_close(vp, 0, NULL, NULL)
|
||||
#define VN_RELE_ASYNC(vp, taskq) vn_close(vp, 0, NULL, NULL)
|
||||
|
||||
#define vn_lock(vp, type)
|
||||
#define VOP_UNLOCK(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, int openflag, cred_t *cr, kthread_t *td);
|
||||
|
||||
#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 */
|
||||
#define FTRUNC O_TRUNC
|
||||
|
||||
/*
|
||||
* 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 SEC_TO_TICK(sec) ((sec) * hz)
|
||||
#define NSEC_TO_TICK(nsec) ((nsec) / (NANOSEC / hz))
|
||||
|
||||
#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 boot_ncpus (sysconf(_SC_NPROCESSORS_ONLN))
|
||||
|
||||
#define minclsyspri 60
|
||||
#define maxclsyspri 99
|
||||
|
||||
#define CPU_SEQID (thr_self() & (max_ncpus - 1))
|
||||
|
||||
#define kcred NULL
|
||||
#define CRED() NULL
|
||||
|
||||
#ifndef ptob
|
||||
#define ptob(x) ((x) * PAGESIZE)
|
||||
#endif
|
||||
|
||||
extern uint64_t physmem;
|
||||
|
||||
extern int highbit64(uint64_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, size_t);
|
||||
extern void show_pool_stats(struct spa *);
|
||||
extern int set_global_var(char *arg);
|
||||
|
||||
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 uint32_t zone_get_hostid(void *zonep);
|
||||
|
||||
extern char *kmem_asprintf(const char *fmt, ...);
|
||||
#define strfree(str) kmem_free((str), strlen(str) + 1)
|
||||
|
||||
/*
|
||||
* Hostname information
|
||||
*/
|
||||
extern struct utsname utsname;
|
||||
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);
|
||||
/* Random compatibility stuff. */
|
||||
#define pwrite64(d, p, n, o) pwrite(d, p, n, o)
|
||||
#define readdir64(d) readdir(d)
|
||||
#define SIGPENDING(td) (0)
|
||||
#define root_mount_wait() do { } while (0)
|
||||
#define root_mounted() (1)
|
||||
|
||||
#define noinline __attribute__((noinline))
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
|
||||
struct file {
|
||||
void *dummy;
|
||||
};
|
||||
|
||||
#define FCREAT O_CREAT
|
||||
#define FOFFMAX 0x0
|
||||
|
||||
/* 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 *);
|
||||
|
||||
typedef uint32_t idmap_rid_t;
|
||||
|
||||
#define DDI_SLEEP KM_SLEEP
|
||||
#define ddi_log_sysevent(_a, _b, _c, _d, _e, _f, _g) (0)
|
||||
|
||||
#define SX_SYSINIT(name, lock, desc)
|
||||
|
||||
#define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \
|
||||
intptr_t arg2, struct sysctl_req *req
|
||||
|
||||
/*
|
||||
* This describes the access space for a sysctl request. This is needed
|
||||
* so that we can use the interface from the kernel or from user-space.
|
||||
*/
|
||||
struct sysctl_req {
|
||||
struct thread *td; /* used for access checking */
|
||||
int lock; /* wiring state */
|
||||
void *oldptr;
|
||||
size_t oldlen;
|
||||
size_t oldidx;
|
||||
int (*oldfunc)(struct sysctl_req *, const void *, size_t);
|
||||
void *newptr;
|
||||
size_t newlen;
|
||||
size_t newidx;
|
||||
int (*newfunc)(struct sysctl_req *, void *, size_t);
|
||||
size_t validlen;
|
||||
int flags;
|
||||
};
|
||||
|
||||
SLIST_HEAD(sysctl_oid_list, sysctl_oid);
|
||||
|
||||
/*
|
||||
* This describes one "oid" in the MIB tree. Potentially more nodes can
|
||||
* be hidden behind it, expanded by the handler.
|
||||
*/
|
||||
struct sysctl_oid {
|
||||
struct sysctl_oid_list *oid_parent;
|
||||
SLIST_ENTRY(sysctl_oid) oid_link;
|
||||
int oid_number;
|
||||
u_int oid_kind;
|
||||
void *oid_arg1;
|
||||
intptr_t oid_arg2;
|
||||
const char *oid_name;
|
||||
int (*oid_handler)(SYSCTL_HANDLER_ARGS);
|
||||
const char *oid_fmt;
|
||||
int oid_refcnt;
|
||||
u_int oid_running;
|
||||
const char *oid_descr;
|
||||
};
|
||||
|
||||
#define SYSCTL_DECL(...)
|
||||
#define SYSCTL_NODE(...)
|
||||
#define SYSCTL_INT(...)
|
||||
#define SYSCTL_UINT(...)
|
||||
#define SYSCTL_ULONG(...)
|
||||
#define SYSCTL_PROC(...)
|
||||
#define SYSCTL_QUAD(...)
|
||||
#define SYSCTL_UQUAD(...)
|
||||
#ifdef TUNABLE_INT
|
||||
#undef TUNABLE_INT
|
||||
#undef TUNABLE_ULONG
|
||||
#undef TUNABLE_QUAD
|
||||
#endif
|
||||
#define TUNABLE_INT(...)
|
||||
#define TUNABLE_ULONG(...)
|
||||
#define TUNABLE_QUAD(...)
|
||||
|
||||
int sysctl_handle_64(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
/* Errors */
|
||||
|
||||
#ifndef ERESTART
|
||||
#define ERESTART (-1)
|
||||
#endif
|
||||
|
||||
#ifdef illumos
|
||||
/*
|
||||
* Cyclic information
|
||||
*/
|
||||
extern kmutex_t cpu_lock;
|
||||
|
||||
typedef uintptr_t cyclic_id_t;
|
||||
typedef uint16_t cyc_level_t;
|
||||
typedef void (*cyc_func_t)(void *);
|
||||
|
||||
#define CY_LOW_LEVEL 0
|
||||
#define CY_INFINITY INT64_MAX
|
||||
#define CYCLIC_NONE ((cyclic_id_t)0)
|
||||
|
||||
typedef struct cyc_time {
|
||||
hrtime_t cyt_when;
|
||||
hrtime_t cyt_interval;
|
||||
} cyc_time_t;
|
||||
|
||||
typedef struct cyc_handler {
|
||||
cyc_func_t cyh_func;
|
||||
void *cyh_arg;
|
||||
cyc_level_t cyh_level;
|
||||
} cyc_handler_t;
|
||||
|
||||
extern cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *);
|
||||
extern void cyclic_remove(cyclic_id_t);
|
||||
extern int cyclic_reprogram(cyclic_id_t, hrtime_t);
|
||||
#endif /* illumos */
|
||||
|
||||
#ifdef illumos
|
||||
/*
|
||||
* Buf structure
|
||||
*/
|
||||
#define B_BUSY 0x0001
|
||||
#define B_DONE 0x0002
|
||||
#define B_ERROR 0x0004
|
||||
#define B_READ 0x0040 /* read when I/O occurs */
|
||||
#define B_WRITE 0x0100 /* non-read pseudo-flag */
|
||||
|
||||
typedef struct buf {
|
||||
int b_flags;
|
||||
size_t b_bcount;
|
||||
union {
|
||||
caddr_t b_addr;
|
||||
} b_un;
|
||||
|
||||
lldaddr_t _b_blkno;
|
||||
#define b_lblkno _b_blkno._f
|
||||
size_t b_resid;
|
||||
size_t b_bufsize;
|
||||
int (*b_iodone)(struct buf *);
|
||||
int b_error;
|
||||
void *b_private;
|
||||
} buf_t;
|
||||
|
||||
extern void bioinit(buf_t *);
|
||||
extern void biodone(buf_t *);
|
||||
extern void bioerror(buf_t *, int);
|
||||
extern int geterror(buf_t *);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_ZFS_CONTEXT_H */
|
@ -1,353 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
|
||||
* Copyright (c) 2014 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
int taskq_now;
|
||||
taskq_t *system_taskq;
|
||||
|
||||
#define TASKQ_ACTIVE 0x00010000
|
||||
#define TASKQ_NAMELEN 31
|
||||
|
||||
struct taskq {
|
||||
char tq_name[TASKQ_NAMELEN + 1];
|
||||
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;
|
||||
taskq_ent_t *tq_freelist;
|
||||
taskq_ent_t tq_task;
|
||||
};
|
||||
|
||||
static taskq_ent_t *
|
||||
task_alloc(taskq_t *tq, int tqflags)
|
||||
{
|
||||
taskq_ent_t *t;
|
||||
int rv;
|
||||
|
||||
again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
|
||||
tq->tq_freelist = t->tqent_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++;
|
||||
#ifdef __FreeBSD__
|
||||
rv = cv_timedwait(&tq->tq_maxalloc_cv,
|
||||
&tq->tq_lock, hz);
|
||||
#else
|
||||
rv = cv_timedwait(&tq->tq_maxalloc_cv,
|
||||
&tq->tq_lock, ddi_get_lbolt() + hz);
|
||||
#endif
|
||||
tq->tq_maxalloc_wait--;
|
||||
if (rv > 0)
|
||||
goto again; /* signaled */
|
||||
}
|
||||
mutex_exit(&tq->tq_lock);
|
||||
|
||||
t = kmem_alloc(sizeof (taskq_ent_t), tqflags & KM_SLEEP);
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
if (t != NULL)
|
||||
tq->tq_nalloc++;
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
static void
|
||||
task_free(taskq_t *tq, taskq_ent_t *t)
|
||||
{
|
||||
if (tq->tq_nalloc <= tq->tq_minalloc) {
|
||||
t->tqent_next = tq->tq_freelist;
|
||||
tq->tq_freelist = t;
|
||||
} else {
|
||||
tq->tq_nalloc--;
|
||||
mutex_exit(&tq->tq_lock);
|
||||
kmem_free(t, sizeof (taskq_ent_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)
|
||||
{
|
||||
taskq_ent_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->tqent_next = tq->tq_task.tqent_next;
|
||||
t->tqent_prev = &tq->tq_task;
|
||||
} else {
|
||||
t->tqent_next = &tq->tq_task;
|
||||
t->tqent_prev = tq->tq_task.tqent_prev;
|
||||
}
|
||||
t->tqent_next->tqent_prev = t;
|
||||
t->tqent_prev->tqent_next = t;
|
||||
t->tqent_func = func;
|
||||
t->tqent_arg = arg;
|
||||
t->tqent_flags = 0;
|
||||
cv_signal(&tq->tq_dispatch_cv);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
|
||||
taskq_ent_t *t)
|
||||
{
|
||||
ASSERT(func != NULL);
|
||||
ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
|
||||
|
||||
/*
|
||||
* Mark it as a prealloc'd task. This is important
|
||||
* to ensure that we don't free it later.
|
||||
*/
|
||||
t->tqent_flags |= TQENT_FLAG_PREALLOC;
|
||||
/*
|
||||
* Enqueue the task to the underlying queue.
|
||||
*/
|
||||
mutex_enter(&tq->tq_lock);
|
||||
|
||||
if (flags & TQ_FRONT) {
|
||||
t->tqent_next = tq->tq_task.tqent_next;
|
||||
t->tqent_prev = &tq->tq_task;
|
||||
} else {
|
||||
t->tqent_next = &tq->tq_task;
|
||||
t->tqent_prev = tq->tq_task.tqent_prev;
|
||||
}
|
||||
t->tqent_next->tqent_prev = t;
|
||||
t->tqent_prev->tqent_next = t;
|
||||
t->tqent_func = func;
|
||||
t->tqent_arg = arg;
|
||||
cv_signal(&tq->tq_dispatch_cv);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_wait(taskq_t *tq)
|
||||
{
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0)
|
||||
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_wait_id(taskq_t *tq, taskqid_t id)
|
||||
{
|
||||
taskq_wait(tq);
|
||||
}
|
||||
|
||||
static void *
|
||||
taskq_thread(void *arg)
|
||||
{
|
||||
taskq_t *tq = arg;
|
||||
taskq_ent_t *t;
|
||||
boolean_t prealloc;
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (tq->tq_flags & TASKQ_ACTIVE) {
|
||||
if ((t = tq->tq_task.tqent_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->tqent_prev->tqent_next = t->tqent_next;
|
||||
t->tqent_next->tqent_prev = t->tqent_prev;
|
||||
t->tqent_next = NULL;
|
||||
t->tqent_prev = NULL;
|
||||
prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC;
|
||||
mutex_exit(&tq->tq_lock);
|
||||
|
||||
rw_enter(&tq->tq_threadlock, RW_READER);
|
||||
t->tqent_func(t->tqent_arg);
|
||||
rw_exit(&tq->tq_threadlock);
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
if (!prealloc)
|
||||
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);
|
||||
(void) strncpy(tq->tq_name, name, TASKQ_NAMELEN + 1);
|
||||
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.tqent_next = &tq->tq_task;
|
||||
tq->tq_task.tqent_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 */
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright (c) 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <dlfcn.h>
|
||||
|
||||
/*
|
||||
* Routines needed by more than one client of libzpool.
|
||||
*/
|
||||
|
||||
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];
|
||||
|
||||
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) {
|
||||
char *suffix = "", *bias = NULL;
|
||||
char bias_suffix[32];
|
||||
|
||||
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
|
||||
(void) nvlist_lookup_string(nv, ZPOOL_CONFIG_ALLOCATION_BIAS,
|
||||
&bias);
|
||||
if (nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
|
||||
(uint64_t **)&vs, &c) != 0)
|
||||
vs = &v0;
|
||||
|
||||
if (bias != NULL) {
|
||||
(void) snprintf(bias_suffix, sizeof (bias_suffix),
|
||||
" (%s)", bias);
|
||||
suffix = bias_suffix;
|
||||
} else if (is_log) {
|
||||
suffix = " (log)";
|
||||
}
|
||||
|
||||
sec = MAX(1, vs->vs_timestamp / NANOSEC);
|
||||
|
||||
nicenum(vs->vs_alloc, used, sizeof (used));
|
||||
nicenum(vs->vs_space - vs->vs_alloc, avail, sizeof (avail));
|
||||
nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops, sizeof (rops));
|
||||
nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops, sizeof (wops));
|
||||
nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes,
|
||||
sizeof (rbytes));
|
||||
nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes,
|
||||
sizeof (wbytes));
|
||||
nicenum(vs->vs_read_errors, rerr, sizeof (rerr));
|
||||
nicenum(vs->vs_write_errors, werr, sizeof (werr));
|
||||
nicenum(vs->vs_checksum_errors, cerr, sizeof (cerr));
|
||||
|
||||
(void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
|
||||
indent, "",
|
||||
desc,
|
||||
(int)(indent+strlen(desc)-25-(vs->vs_space ? 0 : 12)),
|
||||
suffix,
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets given global variable in libzpool to given unsigned 32-bit value.
|
||||
* arg: "<variable>=<value>"
|
||||
*/
|
||||
int
|
||||
set_global_var(char *arg)
|
||||
{
|
||||
void *zpoolhdl;
|
||||
char *varname = arg, *varval;
|
||||
u_longlong_t val;
|
||||
|
||||
#ifndef _LITTLE_ENDIAN
|
||||
/*
|
||||
* On big endian systems changing a 64-bit variable would set the high
|
||||
* 32 bits instead of the low 32 bits, which could cause unexpected
|
||||
* results.
|
||||
*/
|
||||
fprintf(stderr, "Setting global variables is only supported on "
|
||||
"little-endian systems\n", varname);
|
||||
return (ENOTSUP);
|
||||
#endif
|
||||
if ((varval = strchr(arg, '=')) != NULL) {
|
||||
*varval = '\0';
|
||||
varval++;
|
||||
val = strtoull(varval, NULL, 0);
|
||||
if (val > UINT32_MAX) {
|
||||
fprintf(stderr, "Value for global variable '%s' must "
|
||||
"be a 32-bit unsigned integer\n", varname);
|
||||
return (EOVERFLOW);
|
||||
}
|
||||
} else {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
zpoolhdl = dlopen("libzpool.so", RTLD_LAZY);
|
||||
if (zpoolhdl != NULL) {
|
||||
uint32_t *var;
|
||||
var = dlsym(zpoolhdl, varname);
|
||||
if (var == NULL) {
|
||||
fprintf(stderr, "Global variable '%s' does not exist "
|
||||
"in libzpool.so\n", varname);
|
||||
return (EINVAL);
|
||||
}
|
||||
*var = (uint32_t)val;
|
||||
|
||||
dlclose(zpoolhdl);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to open libzpool.so to set global "
|
||||
"variable\n");
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
provider zfs {
|
||||
probe probe0(char *probename);
|
||||
probe probe1(char *probename, unsigned long arg1);
|
||||
probe probe2(char *probename, unsigned long arg1, unsigned long arg2);
|
||||
probe probe3(char *probename, unsigned long arg1, unsigned long arg2,
|
||||
unsigned long arg3);
|
||||
probe probe4(char *probename, unsigned long arg1, unsigned long arg2,
|
||||
unsigned long arg3, unsigned long arg4);
|
||||
|
||||
probe set__error(int err);
|
||||
};
|
||||
|
||||
#pragma D attributes Evolving/Evolving/ISA provider zfs provider
|
||||
#pragma D attributes Private/Private/Unknown provider zfs module
|
||||
#pragma D attributes Private/Private/Unknown provider zfs function
|
||||
#pragma D attributes Evolving/Evolving/ISA provider zfs name
|
||||
#pragma D attributes Evolving/Evolving/ISA provider zfs args
|
@ -29,6 +29,7 @@
|
||||
* Utility functions
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -6,27 +6,40 @@ SUBDIR= drti \
|
||||
libavl \
|
||||
libctf \
|
||||
libdtrace \
|
||||
${_libicp} \
|
||||
${_libicp_rescue} \
|
||||
libnvpair \
|
||||
libspl \
|
||||
${_libtpool} \
|
||||
libumem \
|
||||
libuutil \
|
||||
${_libzfs_core} \
|
||||
${_libzfs} \
|
||||
${_libzpool} \
|
||||
${_libzutil}
|
||||
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
_libzfs_core= libzfs_core
|
||||
_libicp= libicp
|
||||
_libicp_rescue= libicp_rescue
|
||||
_libzfs= libzfs
|
||||
_libzutil= libzutil
|
||||
.if ${MK_LIBTHR} != "no"
|
||||
_libzpool= libzpool
|
||||
_libtpool= libtpool
|
||||
.endif
|
||||
.endif
|
||||
|
||||
SUBDIR_DEPEND_libctf= libspl
|
||||
SUBDIR_DEPEND_libdtrace= libctf
|
||||
SUBDIR_DEPEND_libtpool= libspl
|
||||
SUBDIR_DEPEND_libuutil= libavl libspl
|
||||
SUBDIR_DEPEND_libzfs_core= libnvpair
|
||||
SUBDIR_DEPEND_libzfs= libavl libnvpair libumem libuutil libzfs_core
|
||||
SUBDIR_DEPEND_libzpool= libavl libnvpair libumem
|
||||
SUBDIR_DEPEND_libzfs= libavl libnvpair libumem libuutil libzfs_core libzutil
|
||||
SUBDIR_DEPEND_libzpool= libavl libnvpair libumem libicp
|
||||
SUBDIR_DEPEND_libzutil= libavl libtpool
|
||||
|
||||
SUBDIR_PARALLEL=
|
||||
|
||||
|
@ -11,7 +11,14 @@ FILESDIR= ${LIBDIR}/dtrace
|
||||
CLEANFILES= ${FILES}
|
||||
# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY.
|
||||
.undef LIBRARIES_ONLY
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/head \
|
||||
|
@ -1,12 +1,15 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/avl
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/avl
|
||||
|
||||
PACKAGE= runtime
|
||||
LIB= avl
|
||||
SRCS= avl.c
|
||||
WARNS?= 3
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -21,6 +21,14 @@ MAN= ctf.5
|
||||
WARNS?= 2
|
||||
CFLAGS+= -DCTF_OLD_VERSIONS
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/head \
|
||||
@ -28,6 +36,6 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/lib/libctf/common \
|
||||
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
|
||||
|
||||
LIBADD+= z
|
||||
LIBADD+= spl z
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -66,6 +66,16 @@ FILESMODE= ${NOBINMODE}
|
||||
|
||||
WARNS?= 1
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
|
||||
|
||||
CFLAGS+= -I${.OBJDIR} -I${.CURDIR} \
|
||||
-I${SRCTOP}/sys/cddl/dev/dtrace/${MACHINE_ARCH} \
|
||||
-I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
|
101
cddl/lib/libicp/Makefile
Normal file
101
cddl/lib/libicp/Makefile
Normal file
@ -0,0 +1,101 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/icp
|
||||
|
||||
PACKAGE= runtime
|
||||
LIB= icp
|
||||
LIBADD=
|
||||
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
ASM_SOURCES_C = asm-x86_64/aes/aeskey.c
|
||||
ASM_SOURCES_AS = \
|
||||
asm-x86_64/aes/aes_amd64.S \
|
||||
asm-x86_64/aes/aes_aesni.S \
|
||||
asm-x86_64/modes/gcm_pclmulqdq.S \
|
||||
asm-x86_64/modes/aesni-gcm-x86_64.S \
|
||||
asm-x86_64/modes/ghash-x86_64.S \
|
||||
asm-x86_64/sha1/sha1-x86_64.S \
|
||||
asm-x86_64/sha2/sha256_impl.S \
|
||||
asm-x86_64/sha2/sha512_impl.S
|
||||
|
||||
CFLAGS+= -D__amd64 -D_SYS_STACK_H -UHAVE_AES
|
||||
.else
|
||||
ASM_SOURCES_C =
|
||||
ASM_SOURCES_AS =
|
||||
.endif
|
||||
|
||||
|
||||
KERNEL_C = \
|
||||
spi/kcf_spi.c \
|
||||
api/kcf_ctxops.c \
|
||||
api/kcf_digest.c \
|
||||
api/kcf_cipher.c \
|
||||
api/kcf_miscapi.c \
|
||||
api/kcf_mac.c \
|
||||
algs/aes/aes_impl_aesni.c \
|
||||
algs/aes/aes_impl_generic.c \
|
||||
algs/aes/aes_impl_x86-64.c \
|
||||
algs/aes/aes_impl.c \
|
||||
algs/aes/aes_modes.c \
|
||||
algs/edonr/edonr.c \
|
||||
algs/modes/modes.c \
|
||||
algs/modes/cbc.c \
|
||||
algs/modes/gcm_generic.c \
|
||||
algs/modes/gcm_pclmulqdq.c \
|
||||
algs/modes/gcm.c \
|
||||
algs/modes/ctr.c \
|
||||
algs/modes/ccm.c \
|
||||
algs/modes/ecb.c \
|
||||
algs/sha1/sha1.c \
|
||||
algs/sha2/sha2.c \
|
||||
algs/skein/skein.c \
|
||||
algs/skein/skein_block.c \
|
||||
algs/skein/skein_iv.c \
|
||||
illumos-crypto.c \
|
||||
io/aes.c \
|
||||
io/edonr_mod.c \
|
||||
io/sha1_mod.c \
|
||||
io/sha2_mod.c \
|
||||
io/skein_mod.c \
|
||||
os/modhash.c \
|
||||
os/modconf.c \
|
||||
core/kcf_sched.c \
|
||||
core/kcf_prov_lib.c \
|
||||
core/kcf_callprov.c \
|
||||
core/kcf_mech_tabs.c \
|
||||
core/kcf_prov_tabs.c \
|
||||
$(ASM_SOURCES_C)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SRCS= $(ASM_SOURCES_AS) $(KERNEL_C)
|
||||
|
||||
WARNS?= 2
|
||||
SHLIB_MAJOR= 3
|
||||
CSTD= c99
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
|
||||
|
||||
CFLAGS.aes_amd64.S+= -DLOCORE
|
||||
CFLAGS.aes_aesni.S+= -DLOCORE
|
||||
CFLAGS.gcm_pclmulqdq.S+= -DLOCORE
|
||||
CFLAGS.aesni-gcm-x86_64.S+= -DLOCORE
|
||||
CFLAGS.ghash-x86_64.S+= -DLOCORE
|
||||
CFLAGS.sha1-x86_64.S+= -DLOCORE
|
||||
CFLAGS.sha256_impl.S+= -DLOCORE
|
||||
CFLAGS.sha512_impl.S+= -DLOCORE
|
||||
|
||||
.include <bsd.lib.mk>
|
99
cddl/lib/libicp_rescue/Makefile
Normal file
99
cddl/lib/libicp_rescue/Makefile
Normal file
@ -0,0 +1,99 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/icp
|
||||
|
||||
PACKAGE= runtime
|
||||
LIB= icp_rescue
|
||||
LIBADD=
|
||||
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
ASM_SOURCES_C = asm-x86_64/aes/aeskey.c
|
||||
ASM_SOURCES_AS = \
|
||||
asm-x86_64/aes/aes_amd64.S \
|
||||
asm-x86_64/aes/aes_aesni.S \
|
||||
asm-x86_64/modes/gcm_pclmulqdq.S \
|
||||
asm-x86_64/modes/aesni-gcm-x86_64.S \
|
||||
asm-x86_64/sha1/sha1-x86_64.S \
|
||||
asm-x86_64/sha2/sha256_impl.S \
|
||||
asm-x86_64/sha2/sha512_impl.S
|
||||
|
||||
CFLAGS+= -D__amd64 -D_SYS_STACK_H
|
||||
.else
|
||||
ASM_SOURCES_C =
|
||||
ASM_SOURCES_AS =
|
||||
.endif
|
||||
|
||||
|
||||
KERNEL_C = \
|
||||
spi/kcf_spi.c \
|
||||
api/kcf_ctxops.c \
|
||||
api/kcf_digest.c \
|
||||
api/kcf_cipher.c \
|
||||
api/kcf_miscapi.c \
|
||||
api/kcf_mac.c \
|
||||
algs/aes/aes_impl_aesni.c \
|
||||
algs/aes/aes_impl_generic.c \
|
||||
algs/aes/aes_impl_x86-64.c \
|
||||
algs/aes/aes_impl.c \
|
||||
algs/aes/aes_modes.c \
|
||||
algs/edonr/edonr.c \
|
||||
algs/modes/modes.c \
|
||||
algs/modes/cbc.c \
|
||||
algs/modes/gcm_generic.c \
|
||||
algs/modes/gcm_pclmulqdq.c \
|
||||
algs/modes/gcm.c \
|
||||
algs/modes/ctr.c \
|
||||
algs/modes/ccm.c \
|
||||
algs/modes/ecb.c \
|
||||
algs/sha1/sha1.c \
|
||||
algs/sha2/sha2.c \
|
||||
algs/skein/skein_block.c \
|
||||
illumos-crypto.c \
|
||||
io/aes.c \
|
||||
io/edonr_mod.c \
|
||||
io/sha1_mod.c \
|
||||
io/sha2_mod.c \
|
||||
io/skein_mod.c \
|
||||
os/modhash.c \
|
||||
os/modconf.c \
|
||||
core/kcf_sched.c \
|
||||
core/kcf_prov_lib.c \
|
||||
core/kcf_callprov.c \
|
||||
core/kcf_mech_tabs.c \
|
||||
core/kcf_prov_tabs.c \
|
||||
$(ASM_SOURCES_C)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SRCS= $(ASM_SOURCES_AS) $(KERNEL_C)
|
||||
|
||||
WARNS?= 2
|
||||
SHLIB_MAJOR= 3
|
||||
CSTD= c99
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID -UHAVE_AVX -DRESCUE
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
|
||||
|
||||
CFLAGS.aes_amd64.S+= -DLOCORE
|
||||
CFLAGS.aes_aesni.S+= -DLOCORE
|
||||
CFLAGS.gcm_pclmulqdq.S+= -DLOCORE
|
||||
CFLAGS.aesni-gcm-x86_64.S+= -DLOCORE
|
||||
CFLAGS.ghash-x86_64.S+= -DLOCORE
|
||||
CFLAGS.sha1-x86_64.S+= -DLOCORE
|
||||
CFLAGS.sha256_impl.S+= -DLOCORE
|
||||
CFLAGS.sha512_impl.S+= -DLOCORE
|
||||
CFLAGS.gcm.c+= -UCAN_USE_GCM_ASM
|
||||
|
||||
.include <bsd.lib.mk>
|
@ -1,36 +1,30 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/nvpair
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/nvpair
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libnvpair
|
||||
|
||||
LIB= nvpair
|
||||
|
||||
PACKAGE= runtime
|
||||
INCS= libnvpair.h
|
||||
# user
|
||||
SRCS= libnvpair.c \
|
||||
nvpair_alloc_system.c \
|
||||
nvpair_json.c \
|
||||
opensolaris_fnvpair.c \
|
||||
opensolaris_nvpair.c \
|
||||
opensolaris_nvpair_alloc_fixed.c
|
||||
libnvpair_json.c \
|
||||
nvpair_alloc_system.c
|
||||
# kernel
|
||||
SRCS+= nvpair_alloc_fixed.c \
|
||||
nvpair.c \
|
||||
fnvpair.c
|
||||
|
||||
WARNS?= 1
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
WARNS?= 2
|
||||
CFLAGS+= -DIN_BASE -DHAVE_RPC_TYPES
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID -DHAVE_CONFIG_H -DHAVE_XDR_BYTESREC
|
||||
|
||||
# This library uses macros to define fprintf behavior for several object types
|
||||
# The compiler will see the non-string literal arguments to the fprintf calls and
|
||||
# omit warnings for them. Quiesce these warnings in contrib code:
|
||||
#
|
||||
# cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c:743:12: warning: format
|
||||
# string is not a string literal (potentially insecure) [-Wformat-security]
|
||||
# ARENDER(pctl, nvlist_array, nvl, name, val, nelem);
|
||||
#
|
||||
CFLAGS+= -Wno-format-security
|
||||
|
||||
CFLAGS.nvpair.c+= -UHAVE_RPC_TYPES
|
||||
.include <bsd.lib.mk>
|
||||
|
56
cddl/lib/libspl/Makefile
Normal file
56
cddl/lib/libspl/Makefile
Normal file
@ -0,0 +1,56 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/os/freebsd
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/include
|
||||
|
||||
|
||||
LIB= spl
|
||||
LIBADD=
|
||||
PACKAGE= runtime
|
||||
|
||||
SRCS = \
|
||||
assert.c \
|
||||
list.c \
|
||||
mkdirp.c \
|
||||
page.c \
|
||||
strlcat.c \
|
||||
strlcpy.c \
|
||||
timestamp.c \
|
||||
zone.c \
|
||||
include/sys/list.h \
|
||||
include/sys/list_impl.h
|
||||
|
||||
SRCS += \
|
||||
getexecname.c \
|
||||
gethostid.c \
|
||||
getmntany.c \
|
||||
mnttab.c
|
||||
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/asm-x86_64
|
||||
SRCS += atomic.S
|
||||
.elif ${MACHINE_ARCH} == "i386"
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/asm-i386
|
||||
SRCS += atomic.S
|
||||
.else
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/asm-generic
|
||||
SRCS += atomic.c
|
||||
.endif
|
||||
|
||||
|
||||
WARNS?= 2
|
||||
CSTD= c99
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
CFLAGS.atomic.S+= -DLOCORE
|
||||
|
||||
.include <bsd.lib.mk>
|
27
cddl/lib/libtpool/Makefile
Normal file
27
cddl/lib/libtpool/Makefile
Normal file
@ -0,0 +1,27 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libtpool
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/include
|
||||
|
||||
|
||||
LIB= tpool
|
||||
LIBADD= spl
|
||||
PACKAGE= runtime
|
||||
|
||||
INCS= thread_pool_impl.h
|
||||
SRCS= thread_pool.c
|
||||
|
||||
WARNS?= 2
|
||||
CSTD= c99
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
|
||||
.include <bsd.lib.mk>
|
@ -1,11 +1,10 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/avl
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libuutil
|
||||
|
||||
PACKAGE= runtime
|
||||
LIB= uutil
|
||||
SRCS= avl.c \
|
||||
SRCS=\
|
||||
uu_alloc.c \
|
||||
uu_avl.c \
|
||||
uu_dprintf.c \
|
||||
@ -14,14 +13,17 @@ SRCS= avl.c \
|
||||
uu_misc.c \
|
||||
uu_open.c \
|
||||
uu_pname.c \
|
||||
uu_strtoint.c
|
||||
uu_string.c
|
||||
|
||||
WARNS?= 1
|
||||
CFLAGS+= -DNATIVE_BUILD
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
WARNS?= 2
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
|
||||
LIBADD= avl spl
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -1,27 +1,24 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/compat/opensolaris/misc
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils/common
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/icp
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zcommon
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfs
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfs/os/freebsd
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libshare
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libshare/os/freebsd
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/include
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zstd
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zstd/lib
|
||||
|
||||
PACKAGE= runtime
|
||||
LIB= zfs
|
||||
LIBADD= md pthread umem util uutil m avl bsdxml geom nvpair z zfs_core
|
||||
SRCS= deviceid.c \
|
||||
fsshare.c \
|
||||
mkdirp.c \
|
||||
mnttab.c \
|
||||
thread_pool.c \
|
||||
zmount.c \
|
||||
zone.c
|
||||
LIBADD= md pthread umem util uutil m avl bsdxml geom nvpair z zfs_core zutil
|
||||
|
||||
SRCS+= nicenum.c
|
||||
|
||||
SRCS+= libzfs_changelist.c \
|
||||
libzfs_compat.c \
|
||||
INCS= libzfs.h
|
||||
USER_C = \
|
||||
libzfs_changelist.c \
|
||||
libzfs_config.c \
|
||||
libzfs_crypto.c \
|
||||
libzfs_dataset.c \
|
||||
libzfs_diff.c \
|
||||
libzfs_import.c \
|
||||
@ -30,33 +27,76 @@ SRCS+= libzfs_changelist.c \
|
||||
libzfs_pool.c \
|
||||
libzfs_sendrecv.c \
|
||||
libzfs_status.c \
|
||||
libzfs_util.c \
|
||||
libzfs_util.c
|
||||
|
||||
# FreeBSD
|
||||
USER_C += \
|
||||
libzfs_compat.c \
|
||||
libzfs_ioctl_compat.c \
|
||||
libzfs_zmount.c
|
||||
|
||||
# libshare
|
||||
USER_C += \
|
||||
libshare.c \
|
||||
nfs.c \
|
||||
smb.c
|
||||
|
||||
|
||||
KERNEL_C = \
|
||||
algs/sha2/sha2.c \
|
||||
cityhash.c \
|
||||
zfeature_common.c \
|
||||
zfs_comutil.c \
|
||||
zfs_deleg.c \
|
||||
zfs_fletcher.c \
|
||||
zfs_fletcher_superscalar.c \
|
||||
zfs_fletcher_superscalar4.c \
|
||||
zfs_namecheck.c \
|
||||
zfs_prop.c \
|
||||
zfs_uio.c \
|
||||
zpool_prop.c \
|
||||
zprop_common.c \
|
||||
zprop_common.c
|
||||
|
||||
WARNS?= 0
|
||||
SHLIB_MAJOR= 3
|
||||
|
||||
KERNEL_C+= zstd.c \
|
||||
zfs_zstd.c
|
||||
|
||||
|
||||
ARCH_C =
|
||||
.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
|
||||
ARCH_C += zfs_fletcher_intel.c \
|
||||
zfs_fletcher_sse.c
|
||||
CFLAGS += -DHAVE_SSE2
|
||||
.endif
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
ARCH_C += zfs_fletcher_avx512.c
|
||||
CFLAGS+= -DHAVE_AVX2 -DHAVE_AVX -D__x86_64 -DHAVE_AVX512F
|
||||
.endif
|
||||
.if ${MACHINE_ARCH} == "aarch64"
|
||||
ARCH_C += zfs_fletcher_aarch64_neon.c
|
||||
.endif
|
||||
|
||||
SRCS= $(USER_C) $(KERNEL_C) $(ARCH_C)
|
||||
|
||||
WARNS?= 2
|
||||
SHLIB_MAJOR= 4
|
||||
CSTD= c99
|
||||
CFLAGS+= -DZFS_NO_ACL
|
||||
CFLAGS+= -I${SRCTOP}/sbin/mount
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libshare
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/ck/include
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -I${SRCDIR}/sys/contrib/openzfs/module/zstd/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
CFLAGS.zfs_zstd.c= -Wno-cast-qual -Wno-pointer-arith
|
||||
CFLAGS.zstd.c= -fno-tree-vectorize
|
||||
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -1,37 +1,28 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/compat/opensolaris/misc
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfs_core
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/include
|
||||
|
||||
|
||||
LIB= zfs_core
|
||||
LIBADD= nvpair
|
||||
PACKAGE= runtime
|
||||
|
||||
INCS= libzfs_core.h
|
||||
SRCS= libzfs_core.c \
|
||||
libzfs_core_compat.c \
|
||||
zfs_ioctl_compat.c
|
||||
SRCS= libzfs_core.c
|
||||
|
||||
SRCS+= libzfs_compat.c
|
||||
|
||||
WARNS?= 0
|
||||
WARNS?= 2
|
||||
CSTD= c99
|
||||
CFLAGS+= -DZFS_NO_ACL
|
||||
CFLAGS+= -I${SRCTOP}/sbin/mount
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libzfs_core/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -1,20 +1,17 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include "${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/Makefile.files"
|
||||
|
||||
# ZFS_COMMON_SRCS
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zfs
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zcommon
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/unicode
|
||||
# LUA_SRCS
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua
|
||||
# ZFS_SHARED_SRCS
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
# LZ4_COMMON_SRCS
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
|
||||
# KERNEL_SRCS
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
# LIST_SRCS
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/os
|
||||
# ATOMIC_SRCS
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/lua
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/os/linux/zfs
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzpool
|
||||
|
||||
.if exists(${SRCTOP}/sys/cddl/contrib/opensolaris/common/atomic/${MACHINE_ARCH}/opensolaris_atomic.S)
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/atomic/${MACHINE_ARCH}
|
||||
ATOMIC_SRCS= opensolaris_atomic.S
|
||||
@ -23,40 +20,218 @@ ACFLAGS+= -Wa,--noexecstack
|
||||
.PATH: ${SRCTOP}/sys/cddl/compat/opensolaris/kern
|
||||
ATOMIC_SRCS= opensolaris_atomic.c
|
||||
.endif
|
||||
# UNICODE_SRCS
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/unicode
|
||||
# LIBCMDUTILS_SRCS
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils/common
|
||||
|
||||
.if ${MACHINE_ARCH} == "powerpc"
|
||||
# Don't waste GOT entries on small data.
|
||||
PICFLAG= -fPIC
|
||||
.endif
|
||||
|
||||
LIB= zpool
|
||||
|
||||
ZFS_COMMON_SRCS= ${ZFS_COMMON_OBJS:C/.o$/.c/} trim_map.c
|
||||
ZFS_SHARED_SRCS= ${ZFS_SHARED_OBJS:C/.o$/.c/}
|
||||
LZ4_COMMON_SRCS= lz4.c
|
||||
LUA_SRCS= ${LUA_OBJS:C/.o$/.c/}
|
||||
KERNEL_SRCS= kernel.c taskq.c util.c
|
||||
LIST_SRCS= list.c
|
||||
UNICODE_SRCS= u8_textprep.c
|
||||
LIBCMDUTILS_SRCS=nicenum.c
|
||||
|
||||
SRCS= ${ZFS_COMMON_SRCS} ${ZFS_SHARED_SRCS} ${LUA_SRCS} \
|
||||
${LZ4_COMMON_SRCS} ${KERNEL_SRCS} ${LIST_SRCS} ${ATOMIC_SRCS} \
|
||||
${UNICODE_SRCS} ${LIBCMDUTILS_SRCS}
|
||||
|
||||
WARNS?= 0
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
USER_C = \
|
||||
kernel.c \
|
||||
taskq.c \
|
||||
util.c
|
||||
|
||||
KERNEL_C = \
|
||||
zfeature_common.c \
|
||||
zfs_comutil.c \
|
||||
zfs_deleg.c \
|
||||
zfs_fletcher.c \
|
||||
zfs_fletcher_superscalar.c \
|
||||
zfs_fletcher_superscalar4.c \
|
||||
zfs_namecheck.c \
|
||||
zfs_prop.c \
|
||||
zfs_uio.c \
|
||||
zpool_prop.c \
|
||||
zprop_common.c \
|
||||
abd.c \
|
||||
abd_os.c \
|
||||
aggsum.c \
|
||||
arc.c \
|
||||
arc_os.c \
|
||||
blkptr.c \
|
||||
bplist.c \
|
||||
bpobj.c \
|
||||
bptree.c \
|
||||
btree.c \
|
||||
bqueue.c \
|
||||
cityhash.c \
|
||||
dbuf.c \
|
||||
dbuf_stats.c \
|
||||
ddt.c \
|
||||
ddt_zap.c \
|
||||
dmu.c \
|
||||
dmu_diff.c \
|
||||
dmu_object.c \
|
||||
dmu_objset.c \
|
||||
dmu_recv.c \
|
||||
dmu_redact.c \
|
||||
dmu_send.c \
|
||||
dmu_traverse.c \
|
||||
dmu_tx.c \
|
||||
dmu_zfetch.c \
|
||||
dnode.c \
|
||||
dnode_sync.c \
|
||||
dsl_bookmark.c \
|
||||
dsl_dataset.c \
|
||||
dsl_deadlist.c \
|
||||
dsl_deleg.c \
|
||||
dsl_dir.c \
|
||||
dsl_crypt.c \
|
||||
dsl_pool.c \
|
||||
dsl_prop.c \
|
||||
dsl_scan.c \
|
||||
dsl_synctask.c \
|
||||
dsl_destroy.c \
|
||||
dsl_userhold.c \
|
||||
edonr_zfs.c \
|
||||
hkdf.c \
|
||||
fm.c \
|
||||
gzip.c \
|
||||
lzjb.c \
|
||||
lz4.c \
|
||||
metaslab.c \
|
||||
mmp.c \
|
||||
multilist.c \
|
||||
objlist.c \
|
||||
pathname.c \
|
||||
range_tree.c \
|
||||
refcount.c \
|
||||
rrwlock.c \
|
||||
sa.c \
|
||||
sha256.c \
|
||||
skein_zfs.c \
|
||||
spa.c \
|
||||
spa_boot.c \
|
||||
spa_checkpoint.c \
|
||||
spa_config.c \
|
||||
spa_errlog.c \
|
||||
spa_history.c \
|
||||
spa_log_spacemap.c \
|
||||
spa_misc.c \
|
||||
spa_stats.c \
|
||||
space_map.c \
|
||||
space_reftree.c \
|
||||
txg.c \
|
||||
trace.c \
|
||||
uberblock.c \
|
||||
unique.c \
|
||||
vdev.c \
|
||||
vdev_cache.c \
|
||||
vdev_file.c \
|
||||
vdev_indirect_births.c \
|
||||
vdev_indirect.c \
|
||||
vdev_indirect_mapping.c \
|
||||
vdev_initialize.c \
|
||||
vdev_label.c \
|
||||
vdev_mirror.c \
|
||||
vdev_missing.c \
|
||||
vdev_queue.c \
|
||||
vdev_raidz.c \
|
||||
vdev_raidz_math_aarch64_neon.c \
|
||||
vdev_raidz_math_aarch64_neonx2.c \
|
||||
vdev_raidz_math_avx2.c \
|
||||
vdev_raidz_math_avx512bw.c \
|
||||
vdev_raidz_math_avx512f.c \
|
||||
vdev_raidz_math.c \
|
||||
vdev_raidz_math_scalar.c \
|
||||
vdev_rebuild.c \
|
||||
vdev_removal.c \
|
||||
vdev_root.c \
|
||||
vdev_trim.c \
|
||||
zap.c \
|
||||
zap_leaf.c \
|
||||
zap_micro.c \
|
||||
zcp.c \
|
||||
zcp_get.c \
|
||||
zcp_global.c \
|
||||
zcp_iter.c \
|
||||
zcp_set.c \
|
||||
zcp_synctask.c \
|
||||
zfeature.c \
|
||||
zfs_byteswap.c \
|
||||
zfs_debug.c \
|
||||
zfs_fm.c \
|
||||
zfs_fuid.c \
|
||||
zfs_sa.c \
|
||||
zfs_znode.c \
|
||||
zfs_ratelimit.c \
|
||||
zfs_rlock.c \
|
||||
zil.c \
|
||||
zio.c \
|
||||
zio_checksum.c \
|
||||
zio_compress.c \
|
||||
zio_crypt.c \
|
||||
zio_inject.c \
|
||||
zle.c \
|
||||
zrlock.c \
|
||||
zthr.c
|
||||
|
||||
ARCH_C =
|
||||
.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
|
||||
ARCH_C += vdev_raidz_math_sse2.c \
|
||||
vdev_raidz_math_ssse3.c \
|
||||
zfs_fletcher_intel.c \
|
||||
zfs_fletcher_sse.c
|
||||
CFLAGS += -DHAVE_SSE2 -DHAVE_SSE3
|
||||
.endif
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
ARCH_C += zfs_fletcher_avx512.c
|
||||
CFLAGS+= -DHAVE_AVX2 -DHAVE_AVX -D__x86_64 -DHAVE_AVX512F \
|
||||
-DHAVE_AVX512BW
|
||||
.endif
|
||||
.if ${MACHINE_ARCH} == "aarch64"
|
||||
ARCH_C += zfs_fletcher_aarch64_neon.c
|
||||
.endif
|
||||
|
||||
LUA_C = \
|
||||
lapi.c \
|
||||
lauxlib.c \
|
||||
lbaselib.c \
|
||||
lcode.c \
|
||||
lcompat.c \
|
||||
lcorolib.c \
|
||||
lctype.c \
|
||||
ldebug.c \
|
||||
ldo.c \
|
||||
lfunc.c \
|
||||
lgc.c \
|
||||
llex.c \
|
||||
lmem.c \
|
||||
lobject.c \
|
||||
lopcodes.c \
|
||||
lparser.c \
|
||||
lstate.c \
|
||||
lstring.c \
|
||||
lstrlib.c \
|
||||
ltable.c \
|
||||
ltablib.c \
|
||||
ltm.c \
|
||||
lvm.c \
|
||||
lzio.c
|
||||
|
||||
UNICODE_C = u8_textprep.c uconv.c
|
||||
|
||||
SRCS= ${USER_C} ${KERNEL_C} ${LUA_C} ${UNICODE_C} ${ARCH_C}
|
||||
|
||||
WARNS?= 2
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
CFLAGS+= -I${SRCTOP}/sys/modules/zfs
|
||||
CFLAGS+= -DLIB_ZPOOL_BUILD -DZFS_DEBUG
|
||||
|
||||
|
||||
# XXX: pthread doesn't have mutex_owned() equivalent, so we need to look
|
||||
# into libthr private structures. That's sooo evil, but it's only for
|
||||
# ZFS debugging tools needs.
|
||||
@ -64,10 +239,9 @@ CFLAGS+= -DWANTS_MUTEX_OWNED
|
||||
CFLAGS+= -I${SRCTOP}/lib/libpthread/thread
|
||||
CFLAGS+= -I${SRCTOP}/lib/libpthread/sys
|
||||
CFLAGS+= -I${SRCTOP}/lib/libthr/arch/${MACHINE_CPUARCH}/include
|
||||
CFLAGS.lz4.c+= -D_FAKE_KERNEL
|
||||
CFLAGS.gcc+= -fms-extensions
|
||||
|
||||
LIBADD= md pthread z nvpair avl umem
|
||||
LIBADD= md pthread z spl icp nvpair avl umem
|
||||
|
||||
# atomic.S doesn't like profiling.
|
||||
MK_PROFILE= no
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
DIRDEPS = \
|
||||
cddl/lib/libavl \
|
||||
cddl/lib/libicp \
|
||||
cddl/lib/libnvpair \
|
||||
cddl/lib/libumem \
|
||||
gnu/lib/csu \
|
||||
|
42
cddl/lib/libzutil/Makefile
Normal file
42
cddl/lib/libzutil/Makefile
Normal file
@ -0,0 +1,42 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzutil
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzutil/os/freebsd
|
||||
.PATH: ${SRCTOP}/sys/contrib/openzfs/module/os/freebsd/zfs
|
||||
|
||||
LIB= zutil
|
||||
LIBADD= avl tpool
|
||||
PACKAGE= runtime
|
||||
|
||||
INCS = zutil_import.h
|
||||
|
||||
SRCS = \
|
||||
zutil_device_path.c \
|
||||
zutil_import.c \
|
||||
zutil_import.h \
|
||||
zutil_nicenum.c \
|
||||
zutil_pool.c
|
||||
|
||||
SRCS += \
|
||||
zutil_device_path_os.c \
|
||||
zutil_import_os.c \
|
||||
zutil_compat.c
|
||||
|
||||
SRCS += zfs_ioctl_compat.c
|
||||
|
||||
|
||||
WARNS?= 2
|
||||
CSTD= c99
|
||||
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libzutil
|
||||
CFLAGS+= -DHAVE_ISSETUGID -DIN_BASE
|
||||
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
|
||||
.include <bsd.lib.mk>
|
@ -1,27 +1,77 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zfs
|
||||
ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
|
||||
|
||||
.PATH: ${ZFSTOP}/cmd/zfs
|
||||
.PATH: ${ZFSTOP}/man/man8
|
||||
.PATH: ${ZFSTOP}/module/os/freebsd/spl
|
||||
|
||||
PACKAGE= runtime
|
||||
PROG= zfs
|
||||
MAN= zfs.8 zfs-program.8
|
||||
SRCS= zfs_main.c zfs_iter.c
|
||||
MAN= \
|
||||
zfs.8 \
|
||||
zfs-allow.8 \
|
||||
zfs-bookmark.8 \
|
||||
zfs-change-key.8 \
|
||||
zfs-clone.8 \
|
||||
zfs-create.8 \
|
||||
zfs-destroy.8 \
|
||||
zfs-diff.8 \
|
||||
zfs-get.8 \
|
||||
zfs-groupspace.8 \
|
||||
zfs-hold.8 \
|
||||
zfs-inherit.8 \
|
||||
zfs-jail.8 \
|
||||
zfs-list.8 \
|
||||
zfs-load-key.8 \
|
||||
zfs-mount.8 \
|
||||
zfs-program.8 \
|
||||
zfs-project.8 \
|
||||
zfs-projectspace.8 \
|
||||
zfs-promote.8 \
|
||||
zfs-receive.8 \
|
||||
zfs-recv.8 \
|
||||
zfs-redact.8 \
|
||||
zfs-release.8 \
|
||||
zfs-rename.8 \
|
||||
zfs-rollback.8 \
|
||||
zfs-send.8 \
|
||||
zfs-set.8 \
|
||||
zfs-share.8 \
|
||||
zfs-snapshot.8 \
|
||||
zfs-unallow.8 \
|
||||
zfs-unjail.8 \
|
||||
zfs-unload-key.8 \
|
||||
zfs-unmount.8 \
|
||||
zfs-upgrade.8 \
|
||||
zfs-userspace.8 \
|
||||
zfs-wait.8 \
|
||||
zfsconcepts.8 \
|
||||
zfsprops.8
|
||||
SRCS= \
|
||||
zfs_iter.c \
|
||||
zfs_iter.h \
|
||||
zfs_main.c \
|
||||
zfs_util.h \
|
||||
zfs_project.c \
|
||||
zfs_projectutil.h
|
||||
|
||||
WARNS?= 0
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libumem/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
WARNS?= 2
|
||||
|
||||
LIBADD= jail nvpair uutil zfs_core zfs
|
||||
CFLAGS+= \
|
||||
-DIN_BASE \
|
||||
-I${ZFSTOP}/include \
|
||||
-I${ZFSTOP}/include/os/freebsd \
|
||||
-I${ZFSTOP}/lib/libspl/include \
|
||||
-I${ZFSTOP}/lib/libspl/include/os/freebsd \
|
||||
-I${SRCTOP}/sys \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${ZFSTOP}/module/icp/include \
|
||||
-include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
|
||||
-DHAVE_ISSETUGID \
|
||||
-include ${SRCTOP}/sys/modules/zfs/zfs_config.h \
|
||||
-I${SRCTOP}/sys/modules/zfs
|
||||
|
||||
LIBADD= jail avl nvpair geom uutil zfs_core spl tpool zutil zfs m crypto
|
||||
LDADD+= -pthread
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -1,32 +1,76 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zpool
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/stat/common
|
||||
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
|
||||
|
||||
.PATH: ${ZFSTOP}/man/man5
|
||||
.PATH: ${ZFSTOP}/man/man8
|
||||
.PATH: ${ZFSTOP}/cmd/zpool
|
||||
.PATH: ${ZFSTOP}/cmd/zpool/os/freebsd
|
||||
|
||||
|
||||
PACKAGE= runtime
|
||||
PROG= zpool
|
||||
MAN= zpool.8 zpool-features.7
|
||||
SRCS= zpool_main.c zpool_vdev.c zpool_iter.c zpool_util.c zfs_comutil.c
|
||||
SRCS+= timestamp.c
|
||||
MAN= \
|
||||
spl-module-parameters.5 \
|
||||
zfs-module-parameters.5 \
|
||||
zpool.8 \
|
||||
zpool-add.8 \
|
||||
zpool-attach.8 \
|
||||
zpool-checkpoint.8 \
|
||||
zpool-clear.8 \
|
||||
zpool-create.8 \
|
||||
zpool-destroy.8 \
|
||||
zpool-detach.8 \
|
||||
zpool-events.8 \
|
||||
zpool-export.8 \
|
||||
zpool-features.5 \
|
||||
zpool-get.8 \
|
||||
zpool-history.8 \
|
||||
zpool-import.8 \
|
||||
zpool-initialize.8 \
|
||||
zpool-iostat.8 \
|
||||
zpool-labelclear.8 \
|
||||
zpool-list.8 \
|
||||
zpool-offline.8 \
|
||||
zpool-online.8 \
|
||||
zpool-reguid.8 \
|
||||
zpool-remove.8 \
|
||||
zpool-reopen.8 \
|
||||
zpool-replace.8 \
|
||||
zpool-resilver.8 \
|
||||
zpool-scrub.8 \
|
||||
zpool-set.8 \
|
||||
zpool-split.8 \
|
||||
zpool-status.8 \
|
||||
zpool-sync.8 \
|
||||
zpool-trim.8 \
|
||||
zpool-upgrade.8 \
|
||||
zpool-wait.8 \
|
||||
zpoolconcepts.8 \
|
||||
zpoolprops.8
|
||||
SRCS= \
|
||||
zpool_iter.c \
|
||||
zpool_main.c \
|
||||
zpool_util.c \
|
||||
zpool_util.h \
|
||||
zpool_vdev.c \
|
||||
zpool_vdev_os.c
|
||||
|
||||
WARNS?= 0
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libumem/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/cmd/stat/common
|
||||
WARNS?= 2
|
||||
|
||||
LIBADD= geom nvpair uutil zfs
|
||||
CFLAGS+= \
|
||||
-DIN_BASE \
|
||||
-I${ZFSTOP}/include \
|
||||
-I${ZFSTOP}/lib/libspl/include \
|
||||
-I${ZFSTOP}/lib/libspl/include/os/freebsd \
|
||||
-I${SRCTOP}/sys \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${ZFSTOP}/cmd/zpool \
|
||||
-include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
|
||||
-DHAVE_ISSETUGID \
|
||||
-include ${SRCTOP}/sys/modules/zfs/zfs_config.h \
|
||||
-DSYSCONFDIR=\"/etc\"
|
||||
|
||||
LIBADD= geom nvpair uutil zfs zutil avl spl tpool zfs_core m
|
||||
LDADD+= -pthread
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -7,7 +7,7 @@ SUBDIR= \
|
||||
ctfdump \
|
||||
ctfmerge \
|
||||
${_zinject} \
|
||||
${_zlook} \
|
||||
${_zstream} \
|
||||
${_zstreamdump} \
|
||||
${_ztest}
|
||||
|
||||
@ -15,10 +15,9 @@ SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
_zinject= zinject
|
||||
#_zlook= zlook
|
||||
.if ${MK_LIBTHR} != "no"
|
||||
_ztest= ztest
|
||||
_zstreamdump = zstreamdump
|
||||
_zstream = zstream
|
||||
.endif
|
||||
.endif
|
||||
|
||||
|
@ -27,6 +27,12 @@ SRCS= alist.c \
|
||||
traverse.c \
|
||||
util.c
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${OPENSOLARIS_USR_DISTDIR} \
|
||||
@ -35,8 +41,9 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/common \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/cvt \
|
||||
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
|
||||
LIBADD= dwarf elf z pthread
|
||||
LIBADD= spl dwarf elf z pthread
|
||||
|
||||
HAS_TESTS=
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
@ -5,6 +5,7 @@ DIRDEPS = \
|
||||
gnu/lib/csu \
|
||||
include \
|
||||
include/xlocale \
|
||||
cddl/lib/libspl \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
|
@ -8,6 +8,13 @@ SRCS= dump.c \
|
||||
symbol.c \
|
||||
utils.c
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
|
||||
CFLAGS+= -I${SRCTOP}/sys
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${OPENSOLARIS_USR_DISTDIR} \
|
||||
-I${OPENSOLARIS_SYS_DISTDIR} \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/head \
|
||||
@ -16,6 +23,7 @@ CFLAGS+= -I${OPENSOLARIS_USR_DISTDIR} \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/common \
|
||||
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
|
||||
LIBADD= elf z
|
||||
|
||||
|
@ -24,6 +24,13 @@ SRCS= alist.c \
|
||||
|
||||
WARNS?= 1
|
||||
|
||||
|
||||
CFLAGS+= -DIN_BASE
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
|
||||
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${OPENSOLARIS_USR_DISTDIR} \
|
||||
@ -32,7 +39,8 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/common \
|
||||
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/cvt \
|
||||
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
|
||||
CFLAGS+= -DHAVE_ISSETUGID
|
||||
|
||||
LIBADD= elf z pthread
|
||||
LIBADD= spl elf z pthread
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -1,24 +1,28 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zinject
|
||||
ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
|
||||
|
||||
.PATH: ${ZFSTOP}/cmd/zinject
|
||||
.PATH: ${ZFSTOP}/man/man8
|
||||
|
||||
PROG= zinject
|
||||
INCS= zinject.h
|
||||
SRCS= zinject.c translate.c
|
||||
MAN=
|
||||
MAN= zinject.8
|
||||
|
||||
WARNS?= 0
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs/
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
WARNS?= 2
|
||||
CFLAGS+= \
|
||||
-DIN_BASE \
|
||||
-I${ZFSTOP}/include \
|
||||
-I${ZFSTOP}/lib/libspl/include \
|
||||
-I${ZFSTOP}/lib/libspl/include/os/freebsd \
|
||||
-I${SRCTOP}/sys \
|
||||
-I${SRCTOP}/cddl/compat/opensolaris/include \
|
||||
-I${ZFSTOP}/module/icp/include \
|
||||
-include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
|
||||
-DHAVE_ISSETUGID \
|
||||
-include ${SRCTOP}/sys/modules/zfs/zfs_config.h
|
||||
|
||||
LIBADD= geom m nvpair umem uutil zfs_core zfs zpool
|
||||
LIBADD= geom m nvpair umem uutil avl spl zfs_core zfs zutil zpool
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user