Please welcome ZFS - The last word in file systems.

ZFS file system was ported from OpenSolaris operating system. The code in under
CDDL license.

I'd like to thank all SUN developers that created this great piece of software.

Supported by:	Wheel LTD (http://www.wheel.pl/)
Supported by:	The FreeBSD Foundation (http://www.freebsdfoundation.org/)
Supported by:	Sentex (http://www.sentex.net/)
This commit is contained in:
Pawel Jakub Dawidek 2007-04-06 01:09:06 +00:00
parent c8c0ba192e
commit f0a75d274a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=168404
616 changed files with 256448 additions and 0 deletions

8
cddl/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $FreeBSD$
SUBDIR= lib
SUBDIR+=sbin
SUBDIR+=usr.bin
SUBDIR+=usr.sbin
.include <bsd.subdir.mk>

3
cddl/Makefile.inc Normal file
View File

@ -0,0 +1,3 @@
# $FreeBSD$
CFLAGS+=-D_SOLARIS_C_SOURCE

View File

@ -0,0 +1,49 @@
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _OPENSOLARIS_DEVID_H_
#define _OPENSOLARIS_DEVID_H_
#include <sys/types.h>
#include <stdlib.h>
typedef int ddi_devid_t;
typedef struct devid_nmlist {
char *devname;
dev_t dev;
} devid_nmlist_t;
static inline int devid_str_decode(char *devidstr, ddi_devid_t *retdevid, char **retminor_name) { abort(); }
static inline int devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name, devid_nmlist_t **retlist) { abort(); }
static inline void devid_str_free(char *str) { abort(); }
static inline void devid_free(ddi_devid_t devid) { abort(); }
static inline void devid_free_nmlist(devid_nmlist_t *list) { abort(); }
static inline int devid_get(int fd, ddi_devid_t *retdevid) { return -1; }
static inline int devid_get_minor_name(int fd, char **retminor_name) { abort(); }
static inline char *devid_str_encode(ddi_devid_t devid, char *minor_name) { abort(); }
#endif /* !_OPENSOLARIS_DEVID_H_ */

View File

@ -0,0 +1,35 @@
/*-
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _OPENSOLARIS_FSSHARE_H_
#define _OPENSOLARIS_FSSHARE_H_
int fsshare(const char *, const char *, const char *);
int fsunshare(const char *, const char *);
#endif /* !_OPENSOLARIS_FSSHARE_H_ */

View File

@ -0,0 +1,11 @@
#ifndef _LIBINTL_H_
#define _LIBINTL_H_
#include <sys/cdefs.h>
#include <stdio.h>
#define textdomain(domain) 0
#define gettext(...) (__VA_ARGS__)
#define dgettext(domain, ...) (__VA_ARGS__)
#endif /* !_SOLARIS_H_ */

View File

@ -0,0 +1,19 @@
#ifndef _OPENSOLARIS_MNTTAB_H_
#define _OPENSOLARIS_MNTTAB_H_
#include <stdio.h>
#include <paths.h>
#define MNTTAB _PATH_DEVNULL
#define MNT_LINE_MAX 1024
struct mnttab {
char *mnt_special;
char *mnt_mountp;
char *mnt_fstype;
char *mnt_mntopts;
};
int getmntany(FILE *fd, struct mnttab *mgetp, struct mnttab *mrefp);
#endif /* !_OPENSOLARIS_MNTTAB_H_ */

View File

@ -0,0 +1,18 @@
#ifndef _OPENSOLARIS_PRIV_H_
#define _OPENSOLARIS_PRIV_H_
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#define PRIV_SYS_CONFIG 0
static __inline int
priv_ineffect(priv)
{
assert(priv == PRIV_SYS_CONFIG);
return (geteuid() == 0);
}
#endif /* !_OPENSOLARIS_PRIV_H_ */

View File

@ -0,0 +1,8 @@
#ifndef _SOLARIS_H_
#define _SOLARIS_H_
#include <sys/ccompile.h>
#define dirent64 dirent
#endif /* !_SOLARIS_H_ */

View File

@ -0,0 +1,6 @@
#ifndef _ZONE_H_
#define _ZONE_H_
#include <sys/zone.h>
#endif /* !_ZONE_H_ */

View File

@ -0,0 +1,167 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Ricardo Correia. All rights reserved.
* Use is subject to license terms.
*/
#include <umem.h>
#include <stdlib.h>
#include <assert.h>
static umem_nofail_callback_t *nofail_cb = NULL;
struct umem_cache {
umem_constructor_t *constructor;
umem_destructor_t *destructor;
void *callback_data;
size_t bufsize;
};
/*
* Simple stub for umem_alloc(). The callback isn't expected to return.
*/
void *umem_alloc(size_t size, int flags)
{
assert(flags == UMEM_DEFAULT || flags == UMEM_NOFAIL);
if(size == 0)
return NULL;
void *ret = malloc(size);
if(ret == NULL) {
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
return ret;
}
/*
* Simple stub for umem_zalloc().
*/
void *umem_zalloc(size_t size, int flags)
{
assert(flags == UMEM_DEFAULT || flags == UMEM_NOFAIL);
if(size == 0)
return NULL;
void *ret = calloc(1, size);
if(ret == NULL) {
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
return ret;
}
/*
* Simple stub for umem_free().
*/
void umem_free(void *buf, size_t size)
{
free(buf);
}
/*
* Simple stub for umem_nofail_callback().
*/
void umem_nofail_callback(umem_nofail_callback_t *callback)
{
nofail_cb = callback;
}
/*
* Simple stub for umem_cache_create().
*/
umem_cache_t *umem_cache_create(char *debug_name, size_t bufsize, size_t align, umem_constructor_t *constructor, umem_destructor_t *destructor, umem_reclaim_t *reclaim, void *callback_data, void *source, int cflags)
{
assert(source == NULL);
umem_cache_t *cache = malloc(sizeof(umem_cache_t));
if(cache == NULL)
return NULL;
cache->constructor = constructor;
cache->destructor = destructor;
cache->callback_data = callback_data;
cache->bufsize = bufsize;
return cache;
}
/*
* Simple stub for umem_cache_alloc(). The nofail callback isn't expected to return.
*/
void *umem_cache_alloc(umem_cache_t *cache, int flags)
{
void *buf = malloc(cache->bufsize);
if(buf == NULL) {
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
if(cache->constructor != NULL) {
if(cache->constructor(buf, cache->callback_data, flags) != 0) {
free(buf);
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
}
return buf;
}
/*
* Simple stub for umem_cache_free().
*/
void umem_cache_free(umem_cache_t *cache, void *buffer)
{
if(cache->destructor != NULL)
cache->destructor(buffer, cache->callback_data);
free(buffer);
}
/*
* Simple stub for umem_cache_destroy().
*/
void umem_cache_destroy(umem_cache_t *cache)
{
free(cache);
}

View File

@ -0,0 +1,85 @@
/*
* 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.
*/
#ifndef _UMEM_H
#define _UMEM_H
#include <sys/types.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define UMEM_DEFAULT 0x0000 /* normal -- may fail */
#define UMEM_NOFAIL 0x0100 /* Never fails -- may call exit(2) */
#define UMEM_FLAGS 0xffff /* all settable umem flags */
extern void *umem_alloc(size_t, int);
extern void *umem_alloc_align(size_t, size_t, int);
extern void *umem_zalloc(size_t, int);
extern void umem_free(void *, size_t);
extern void umem_free_align(void *, size_t);
/*
* Flags for umem_cache_create()
*/
#define UMC_NOTOUCH 0x00010000
#define UMC_NODEBUG 0x00020000
#define UMC_NOMAGAZINE 0x00040000
#define UMC_NOHASH 0x00080000
struct umem_cache; /* cache structure is opaque to umem clients */
typedef struct umem_cache umem_cache_t;
typedef int umem_constructor_t(void *, void *, int);
typedef void umem_destructor_t(void *, void *);
typedef void umem_reclaim_t(void *);
typedef int umem_nofail_callback_t(void);
#define UMEM_CALLBACK_RETRY 0
#define UMEM_CALLBACK_EXIT(status) (0x100 | ((status) & 0xFF))
extern void umem_nofail_callback(umem_nofail_callback_t *);
extern umem_cache_t *umem_cache_create(char *, size_t,
size_t, umem_constructor_t *, umem_destructor_t *, umem_reclaim_t *,
void *, void *, int);
extern void umem_cache_destroy(umem_cache_t *);
extern void *umem_cache_alloc(umem_cache_t *, int);
extern void umem_cache_free(umem_cache_t *, void *);
extern void umem_reap(void);
#ifdef __cplusplus
}
#endif
#endif /* _UMEM_H */

View File

@ -0,0 +1,242 @@
/*-
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <libutil.h>
#include <assert.h>
#include <pathnames.h> /* _PATH_MOUNTDPID */
#include <fsshare.h>
#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n"
#define OPTSSIZE 1024
#define MAXLINESIZE (PATH_MAX + OPTSSIZE)
static void
restart_mountd(void)
{
struct pidfh *pfh;
pid_t mountdpid;
pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid);
if (pfh != NULL) {
/* Mountd is not running. */
pidfile_remove(pfh);
return;
}
if (errno != EEXIST) {
/* Cannot open pidfile for some reason. */
return;
}
/* We have mountd(8) PID in mountdpid varible. */
kill(mountdpid, SIGHUP);
}
/*
* Read one line from a file. Skip comments, empty lines and a line with a
* mountpoint specified in the 'skip' argument.
*/
static char *
getline(FILE *fd, const char *skip)
{
static char line[MAXLINESIZE];
size_t len, skiplen;
char *s, last;
if (skip != NULL)
skiplen = strlen(skip);
for (;;) {
s = fgets(line, sizeof(line), fd);
if (s == NULL)
return (NULL);
/* Skip empty lines and comments. */
if (line[0] == '\n' || line[0] == '#')
continue;
len = strlen(line);
if (line[len - 1] == '\n')
line[len - 1] = '\0';
last = line[skiplen];
/* Skip the given mountpoint. */
if (skip != NULL && strncmp(skip, line, skiplen) == 0 &&
(last == '\t' || last == ' ' || last == '\0')) {
continue;
}
break;
}
return (line);
}
/*
* Function translate options to a format acceptable by exports(5), eg.
*
* -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0
*
* Accepted input formats:
*
* ro,network=192.168.0.0,mask=255.255.255.0,maproot=0
* ro network=192.168.0.0 mask=255.255.255.0 maproot=0
* -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0
* -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0
*/
static char *
translate_opts(const char *shareopts)
{
static char newopts[OPTSSIZE];
char oldopts[OPTSSIZE], opt[64];
char *o, *s = NULL;
strlcpy(oldopts, shareopts, sizeof(oldopts));
newopts[0] = '\0';
s = oldopts;
while ((o = strsep(&s, "-, ")) != NULL) {
if (o[0] == '\0')
continue;
snprintf(opt, sizeof(opt), "-%s ", o);
strlcat(newopts, opt, sizeof(newopts));
}
return (newopts);
}
static int
fsshare_main(const char *file, const char *mountpoint, const char *shareopts,
int share)
{
char tmpfile[PATH_MAX];
char *line;
FILE *newfd, *oldfd;
int fd, error;
newfd = oldfd = NULL;
error = 0;
/*
* Create temporary file in the same directory, so we can atomically
* rename it.
*/
if (strlcpy(tmpfile, file, sizeof(tmpfile)) >= sizeof(tmpfile))
return (ENAMETOOLONG);
if (strlcat(tmpfile, ".XXXXXXXX", sizeof(tmpfile)) >= sizeof(tmpfile))
return (ENAMETOOLONG);
fd = mkstemp(tmpfile);
if (fd == -1)
return (errno);
/*
* File name is random, so we don't really need file lock now, but it
* will be needed after rename(2).
*/
error = flock(fd, LOCK_EX);
assert(error == 0 || (error == -1 && errno == EOPNOTSUPP));
newfd = fdopen(fd, "r+");
assert(newfd != NULL);
/* Open old exports file. */
oldfd = fopen(file, "r");
if (oldfd == NULL) {
if (share) {
if (errno != ENOENT) {
error = errno;
goto out;
}
} else {
/* If there is no exports file, ignore the error. */
if (errno == ENOENT)
errno = 0;
error = errno;
goto out;
}
} else {
error = flock(fileno(oldfd), LOCK_EX);
assert(error == 0 || (error == -1 && errno == EOPNOTSUPP));
error = 0;
}
/* Place big, fat warning at the begining of the file. */
fprintf(newfd, "%s", FILE_HEADER);
while (oldfd != NULL && (line = getline(oldfd, mountpoint)) != NULL)
fprintf(newfd, "%s\n", line);
if (oldfd != NULL && ferror(oldfd) != 0) {
error = ferror(oldfd);
goto out;
}
if (ferror(newfd) != 0) {
error = ferror(newfd);
goto out;
}
if (share) {
fprintf(newfd, "%s\t%s\n", mountpoint,
translate_opts(shareopts));
}
out:
if (error != 0)
unlink(tmpfile);
else {
if (rename(tmpfile, file) == -1) {
error = errno;
unlink(tmpfile);
} else {
/*
* Send SIGHUP to mountd, but unlock exports file later.
*/
restart_mountd();
}
}
if (oldfd != NULL) {
flock(fileno(oldfd), LOCK_UN);
fclose(oldfd);
}
if (newfd != NULL) {
flock(fileno(newfd), LOCK_UN);
fclose(newfd);
}
return (error);
}
/*
* Add the given mountpoint to the given exports file.
*/
int
fsshare(const char *file, const char *mountpoint, const char *shareopts)
{
return (fsshare_main(file, mountpoint, shareopts, 1));
}
/*
* Remove the given mountpoint from the given exports file.
*/
int
fsunshare(const char *file, const char *mountpoint)
{
return (fsshare_main(file, mountpoint, NULL, 0));
}

View File

@ -0,0 +1,213 @@
/*
* 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 (c) 1988 AT&T */
/* All Rights Reserved */
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)mkdirp.c 1.15 06/01/04 SMI"
/*
* Creates directory and it's parents if the parents do not
* exist yet.
*
* Returns -1 if fails for reasons other than non-existing
* parents.
* Does NOT simplify pathnames with . or .. in them.
*/
#include <sys/types.h>
#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
static char *simplify(const char *str);
int
mkdirp(const char *d, mode_t mode)
{
char *endptr, *ptr, *slash, *str;
str = simplify(d);
/* If space couldn't be allocated for the simplified names, return. */
if (str == NULL)
return (-1);
/* Try to make the directory */
if (mkdir(str, mode) == 0) {
free(str);
return (0);
}
if (errno != ENOENT) {
free(str);
return (-1);
}
endptr = strrchr(str, '\0');
slash = strrchr(str, '/');
/* Search upward for the non-existing parent */
while (slash != NULL) {
ptr = slash;
*ptr = '\0';
/* If reached an existing parent, break */
if (access(str, F_OK) == 0)
break;
/* If non-existing parent */
else {
slash = strrchr(str, '/');
/* If under / or current directory, make it. */
if (slash == NULL || slash == str) {
if (mkdir(str, mode) != 0 && errno != EEXIST) {
free(str);
return (-1);
}
break;
}
}
}
/* Create directories starting from upmost non-existing parent */
while ((ptr = strchr(str, '\0')) != endptr) {
*ptr = '/';
if (mkdir(str, mode) != 0 && errno != EEXIST) {
/*
* If the mkdir fails because str already
* exists (EEXIST), then str has the form
* "existing-dir/..", and this is really
* ok. (Remember, this loop is creating the
* portion of the path that didn't exist)
*/
free(str);
return (-1);
}
}
free(str);
return (0);
}
/*
* simplify - given a pathname, simplify that path by removing
* duplicate contiguous slashes.
*
* A simplified copy of the argument is returned to the
* caller, or NULL is returned on error.
*
* The caller should handle error reporting based upon the
* returned vlaue, and should free the returned value,
* when appropriate.
*/
static char *
simplify(const char *str)
{
int i;
size_t mbPathlen; /* length of multi-byte path */
size_t wcPathlen; /* length of wide-character path */
wchar_t *wptr; /* scratch pointer */
wchar_t *wcPath; /* wide-character version of the path */
char *mbPath; /* The copy fo the path to be returned */
/*
* bail out if there is nothing there.
*/
if (!str)
return (NULL);
/*
* Get a copy of the argument.
*/
if ((mbPath = strdup(str)) == NULL) {
return (NULL);
}
/*
* convert the multi-byte version of the path to a
* wide-character rendering, for doing our figuring.
*/
mbPathlen = strlen(mbPath);
if ((wcPath = calloc(sizeof (wchar_t), mbPathlen+1)) == NULL) {
free(mbPath);
return (NULL);
}
if ((wcPathlen = mbstowcs(wcPath, mbPath, mbPathlen)) == (size_t)-1) {
free(mbPath);
free(wcPath);
return (NULL);
}
/*
* remove duplicate slashes first ("//../" -> "/")
*/
for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
*wptr++ = wcPath[i];
if (wcPath[i] == '/') {
i++;
while (wcPath[i] == '/') {
i++;
}
i--;
}
}
*wptr = '\0';
/*
* now convert back to the multi-byte format.
*/
if (wcstombs(mbPath, wcPath, mbPathlen) == (size_t)-1) {
free(mbPath);
free(wcPath);
return (NULL);
}
free(wcPath);
return (mbPath);
}

View File

@ -0,0 +1,160 @@
/*-
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This file implements Solaris compatible getmntany() and hasmntopt()
* functions.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <stdio.h>
static char *
mntopt(char **p)
{
char *cp = *p;
char *retstr;
while (*cp && isspace(*cp))
cp++;
retstr = cp;
while (*cp && *cp != ',')
cp++;
if (*cp) {
*cp = '\0';
cp++;
}
*p = cp;
return (retstr);
}
char *
hasmntopt(struct mnttab *mnt, char *opt)
{
char tmpopts[MNT_LINE_MAX];
char *f, *opts = tmpopts;
if (mnt->mnt_mntopts == NULL)
return (NULL);
(void) strcpy(opts, mnt->mnt_mntopts);
f = mntopt(&opts);
for (; *f; f = mntopt(&opts)) {
if (strncmp(opt, f, strlen(opt)) == 0)
return (f - tmpopts + mnt->mnt_mntopts);
}
return (NULL);
}
static void
optadd(char *mntopts, size_t size, const char *opt)
{
if (mntopts[0] != '\0')
strlcat(mntopts, ",", size);
strlcat(mntopts, opt, size);
}
int
getmntany(FILE *fd __unused, struct mnttab *mgetp, struct mnttab *mrefp)
{
static struct statfs *sfs = NULL;
static char mntopts[MNTMAXSTR];
struct opt *o;
long i, n, flags;
if (sfs != NULL) {
free(sfs);
sfs = NULL;
}
mntopts[0] = '\0';
n = getfsstat(NULL, 0, MNT_NOWAIT);
if (n == -1)
return (-1);
n = sizeof(*sfs) * (n + 8);
sfs = malloc(n);
if (sfs == NULL)
return (-1);
n = getfsstat(sfs, n, MNT_WAIT);
if (n == -1) {
free(sfs);
sfs = NULL;
return (-1);
}
for (i = 0; i < n; i++) {
if (mrefp->mnt_special != NULL &&
strcmp(mrefp->mnt_special, sfs[i].f_mntfromname) != 0) {
continue;
}
if (mrefp->mnt_mountp != NULL &&
strcmp(mrefp->mnt_mountp, sfs[i].f_mntonname) != 0) {
continue;
}
if (mrefp->mnt_fstype != NULL &&
strcmp(mrefp->mnt_fstype, sfs[i].f_fstypename) != 0) {
continue;
}
flags = sfs[i].f_flags;
#define OPTADD(opt) optadd(mntopts, sizeof(mntopts), (opt))
if (flags & MNT_RDONLY)
OPTADD(MNTOPT_RO);
else
OPTADD(MNTOPT_RW);
if (flags & MNT_NOSUID)
OPTADD(MNTOPT_NOSUID);
else
OPTADD(MNTOPT_SETUID);
if (flags & MNT_UPDATE)
OPTADD(MNTOPT_REMOUNT);
if (flags & MNT_NOATIME)
OPTADD(MNTOPT_NOATIME);
else
OPTADD(MNTOPT_ATIME);
OPTADD(MNTOPT_NOXATTR);
if (flags & MNT_NOEXEC)
OPTADD(MNTOPT_NOEXEC);
else
OPTADD(MNTOPT_EXEC);
#undef OPTADD
mgetp->mnt_special = sfs[i].f_mntfromname;
mgetp->mnt_mountp = sfs[i].f_mntonname;
mgetp->mnt_fstype = sfs[i].f_fstypename;
mgetp->mnt_mntopts = mntopts;
return (0);
}
free(sfs);
sfs = NULL;
return (-1);
}

View File

@ -0,0 +1,102 @@
/*-
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This file implements Solaris compatible zmount() function.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/uio.h>
#include <sys/mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
static void
build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
size_t len)
{
int i;
if (*iovlen < 0)
return;
i = *iovlen;
*iov = realloc(*iov, sizeof(**iov) * (i + 2));
if (*iov == NULL) {
*iovlen = -1;
return;
}
(*iov)[i].iov_base = strdup(name);
(*iov)[i].iov_len = strlen(name) + 1;
i++;
(*iov)[i].iov_base = val;
if (len == (size_t)-1) {
if (val != NULL)
len = strlen(val) + 1;
else
len = 0;
}
(*iov)[i].iov_len = (int)len;
*iovlen = ++i;
}
int
zmount(const char *spec, const char *dir, int mflag, char *fstype,
char *dataptr, int datalen, char *optptr, int optlen)
{
struct iovec *iov;
char *optstr, *os, *p;
int iovlen, rv;
assert(spec != NULL);
assert(dir != NULL);
assert(mflag == 0);
assert(fstype != NULL);
assert(strcmp(fstype, MNTTYPE_ZFS) == 0);
assert(dataptr == NULL);
assert(datalen == 0);
assert(optptr != NULL);
assert(optlen > 0);
optstr = strdup(optptr);
assert(optptr != NULL);
iov = NULL;
iovlen = 0;
build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, dir),
(size_t)-1);
build_iovec(&iov, &iovlen, "from", __DECONST(char *, spec), (size_t)-1);
for (p = optstr; p != NULL; strsep(&p, ",/ "))
build_iovec(&iov, &iovlen, p, NULL, (size_t)-1);
rv = nmount(iov, iovlen, 0);
free(optstr);
return (rv);
}

View File

@ -0,0 +1,46 @@
/*-
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/zone.h>
int
getzoneid(void)
{
size_t size;
int jailid;
/* Information that we are in jail or not is enough for our needs. */
size = sizeof(jailid);
if (sysctlbyname("security.jail.jailed", &jailid, &size, NULL, 0) == -1)
assert(!"No security.jail.jailed sysctl!");
return (jailid);
}

View File

@ -0,0 +1,97 @@
'\" te
.\" 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, Sun Microsystems, Inc. All Rights Reserved.
.TH zdb 1M "31 Oct 2005" "SunOS 5.11" "System Administration Commands"
.SH NAME
zdb \- ZFS debugger
.SH SYNOPSIS
.LP
.nf
\fBzdb\fR \fIpool\fR
.fi
.SH DESCRIPTION
.LP
The \fBzdb\fR command is used by support engineers to diagnose failures and gather statistics. Since the \fBZFS\fR file system is always consistent on disk and is self-repairing, \fBzdb\fR should only be run under the direction by a support engineer.
.LP
If no arguments are specified, \fBzdb\fR, performs basic consistency checks on the pool and associated datasets, and report any problems detected.
.LP
Any options supported by this command are internal to Sun and subject to change at any time.
.SH EXIT STATUS
.LP
The following exit values are returned:
.sp
.ne 2
.mk
.na
\fB\fB0\fR\fR
.ad
.RS 5n
.rt
The pool is consistent.
.RE
.sp
.ne 2
.mk
.na
\fB\fB1\fR\fR
.ad
.RS 5n
.rt
An error was detected.
.RE
.sp
.ne 2
.mk
.na
\fB\fB2\fR\fR
.ad
.RS 5n
.rt
Invalid command line options were specified.
.RE
.SH ATTRIBUTES
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
.sp
.TS
tab() box;
cw(2.75i) |cw(2.75i)
lw(2.75i) |lw(2.75i)
.
ATTRIBUTE TYPEATTRIBUTE VALUE
_
AvailabilitySUNWzfsu
_
Interface StabilityUnstable
.TE
.SH SEE ALSO
.LP
\fBzfs\fR(1M), \fBzpool\fR(1M), \fBattributes\fR(5)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,354 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Print intent log header and statistics.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/dmu.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/zil.h>
#include <sys/zil_impl.h>
extern uint8_t dump_opt[256];
static void
print_log_bp(const blkptr_t *bp, const char *prefix)
{
char blkbuf[BP_SPRINTF_LEN];
sprintf_blkptr(blkbuf, BP_SPRINTF_LEN, bp);
(void) printf("%s%s\n", prefix, blkbuf);
}
/* ARGSUSED */
static void
zil_prt_rec_create(zilog_t *zilog, int txtype, lr_create_t *lr)
{
time_t crtime = lr->lr_crtime[0];
char *name = (char *)(lr + 1);
char *link = name + strlen(name) + 1;
if (txtype == TX_SYMLINK)
(void) printf("\t\t\t%s -> %s\n", name, link);
else
(void) printf("\t\t\t%s\n", name);
(void) printf("\t\t\t%s", ctime(&crtime));
(void) printf("\t\t\tdoid %llu, foid %llu, mode %llo\n",
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_foid,
(longlong_t)lr->lr_mode);
(void) printf("\t\t\tuid %llu, gid %llu, gen %llu, rdev 0x%llx\n",
(u_longlong_t)lr->lr_uid, (u_longlong_t)lr->lr_gid,
(u_longlong_t)lr->lr_gen, (u_longlong_t)lr->lr_rdev);
}
/* ARGSUSED */
static void
zil_prt_rec_remove(zilog_t *zilog, int txtype, lr_remove_t *lr)
{
(void) printf("\t\t\tdoid %llu, name %s\n",
(u_longlong_t)lr->lr_doid, (char *)(lr + 1));
}
/* ARGSUSED */
static void
zil_prt_rec_link(zilog_t *zilog, int txtype, lr_link_t *lr)
{
(void) printf("\t\t\tdoid %llu, link_obj %llu, name %s\n",
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj,
(char *)(lr + 1));
}
/* ARGSUSED */
static void
zil_prt_rec_rename(zilog_t *zilog, int txtype, lr_rename_t *lr)
{
char *snm = (char *)(lr + 1);
char *tnm = snm + strlen(snm) + 1;
(void) printf("\t\t\tsdoid %llu, tdoid %llu\n",
(u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid);
(void) printf("\t\t\tsrc %s tgt %s\n", snm, tnm);
}
/* ARGSUSED */
static void
zil_prt_rec_write(zilog_t *zilog, int txtype, lr_write_t *lr)
{
char *data, *dlimit;
blkptr_t *bp = &lr->lr_blkptr;
char buf[SPA_MAXBLOCKSIZE];
int verbose = MAX(dump_opt['d'], dump_opt['i']);
int error;
(void) printf("\t\t\tfoid %llu, offset 0x%llx,"
" length 0x%llx, blkoff 0x%llx\n",
(u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
(u_longlong_t)lr->lr_length, (u_longlong_t)lr->lr_blkoff);
if (verbose < 5)
return;
if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
(void) printf("\t\t\thas blkptr, %s\n",
bp->blk_birth >= spa_first_txg(zilog->zl_spa) ?
"will claim" : "won't claim");
print_log_bp(bp, "\t\t\t");
if (bp->blk_birth == 0) {
bzero(buf, sizeof (buf));
} else {
zbookmark_t zb;
ASSERT3U(bp->blk_cksum.zc_word[ZIL_ZC_OBJSET], ==,
dmu_objset_id(zilog->zl_os));
zb.zb_objset = bp->blk_cksum.zc_word[ZIL_ZC_OBJSET];
zb.zb_object = 0;
zb.zb_level = -1;
zb.zb_blkid = bp->blk_cksum.zc_word[ZIL_ZC_SEQ];
error = zio_wait(zio_read(NULL, zilog->zl_spa,
bp, buf, BP_GET_LSIZE(bp), NULL, NULL,
ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL, &zb));
if (error)
return;
}
data = buf + lr->lr_blkoff;
} else {
data = (char *)(lr + 1);
}
dlimit = data + MIN(lr->lr_length,
(verbose < 6 ? 20 : SPA_MAXBLOCKSIZE));
(void) printf("\t\t\t");
while (data < dlimit) {
if (isprint(*data))
(void) printf("%c ", *data);
else
(void) printf("%2X", *data);
data++;
}
(void) printf("\n");
}
/* ARGSUSED */
static void
zil_prt_rec_truncate(zilog_t *zilog, int txtype, lr_truncate_t *lr)
{
(void) printf("\t\t\tfoid %llu, offset 0x%llx, length 0x%llx\n",
(u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
(u_longlong_t)lr->lr_length);
}
/* ARGSUSED */
static void
zil_prt_rec_setattr(zilog_t *zilog, int txtype, lr_setattr_t *lr)
{
time_t atime = (time_t)lr->lr_atime[0];
time_t mtime = (time_t)lr->lr_mtime[0];
(void) printf("\t\t\tfoid %llu, mask 0x%llx\n",
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_mask);
if (lr->lr_mask & AT_MODE) {
(void) printf("\t\t\tAT_MODE %llo\n",
(longlong_t)lr->lr_mode);
}
if (lr->lr_mask & AT_UID) {
(void) printf("\t\t\tAT_UID %llu\n",
(u_longlong_t)lr->lr_uid);
}
if (lr->lr_mask & AT_GID) {
(void) printf("\t\t\tAT_GID %llu\n",
(u_longlong_t)lr->lr_gid);
}
if (lr->lr_mask & AT_SIZE) {
(void) printf("\t\t\tAT_SIZE %llu\n",
(u_longlong_t)lr->lr_size);
}
if (lr->lr_mask & AT_ATIME) {
(void) printf("\t\t\tAT_ATIME %llu.%09llu %s",
(u_longlong_t)lr->lr_atime[0],
(u_longlong_t)lr->lr_atime[1],
ctime(&atime));
}
if (lr->lr_mask & AT_MTIME) {
(void) printf("\t\t\tAT_MTIME %llu.%09llu %s",
(u_longlong_t)lr->lr_mtime[0],
(u_longlong_t)lr->lr_mtime[1],
ctime(&mtime));
}
}
/* ARGSUSED */
static void
zil_prt_rec_acl(zilog_t *zilog, int txtype, lr_acl_t *lr)
{
(void) printf("\t\t\tfoid %llu, aclcnt %llu\n",
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt);
}
typedef void (*zil_prt_rec_func_t)();
typedef struct zil_rec_info {
zil_prt_rec_func_t zri_print;
char *zri_name;
uint64_t zri_count;
} zil_rec_info_t;
static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
{ NULL, "Total " },
{ zil_prt_rec_create, "TX_CREATE " },
{ zil_prt_rec_create, "TX_MKDIR " },
{ zil_prt_rec_create, "TX_MKXATTR " },
{ zil_prt_rec_create, "TX_SYMLINK " },
{ zil_prt_rec_remove, "TX_REMOVE " },
{ zil_prt_rec_remove, "TX_RMDIR " },
{ zil_prt_rec_link, "TX_LINK " },
{ zil_prt_rec_rename, "TX_RENAME " },
{ zil_prt_rec_write, "TX_WRITE " },
{ zil_prt_rec_truncate, "TX_TRUNCATE" },
{ zil_prt_rec_setattr, "TX_SETATTR " },
{ zil_prt_rec_acl, "TX_ACL " },
};
/* ARGSUSED */
static void
print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg)
{
int txtype;
int verbose = MAX(dump_opt['d'], dump_opt['i']);
txtype = lr->lrc_txtype;
ASSERT(txtype != 0 && (uint_t)txtype < TX_MAX_TYPE);
ASSERT(lr->lrc_txg);
(void) printf("\t\t%s len %6llu, txg %llu, seq %llu\n",
zil_rec_info[txtype].zri_name,
(u_longlong_t)lr->lrc_reclen,
(u_longlong_t)lr->lrc_txg,
(u_longlong_t)lr->lrc_seq);
if (txtype && verbose >= 3)
zil_rec_info[txtype].zri_print(zilog, txtype, lr);
zil_rec_info[txtype].zri_count++;
zil_rec_info[0].zri_count++;
}
/* ARGSUSED */
static void
print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
{
char blkbuf[BP_SPRINTF_LEN];
int verbose = MAX(dump_opt['d'], dump_opt['i']);
char *claim;
if (verbose <= 3)
return;
if (verbose >= 5) {
(void) strcpy(blkbuf, ", ");
sprintf_blkptr(blkbuf + strlen(blkbuf),
BP_SPRINTF_LEN - strlen(blkbuf), bp);
} else {
blkbuf[0] = '\0';
}
if (claim_txg != 0)
claim = "already claimed";
else if (bp->blk_birth >= spa_first_txg(zilog->zl_spa))
claim = "will claim";
else
claim = "won't claim";
(void) printf("\tBlock seqno %llu, %s%s\n",
(u_longlong_t)bp->blk_cksum.zc_word[ZIL_ZC_SEQ], claim, blkbuf);
}
static void
print_log_stats(int verbose)
{
int i, w, p10;
if (verbose > 3)
(void) printf("\n");
if (zil_rec_info[0].zri_count == 0)
return;
for (w = 1, p10 = 10; zil_rec_info[0].zri_count >= p10; p10 *= 10)
w++;
for (i = 0; i < TX_MAX_TYPE; i++)
if (zil_rec_info[i].zri_count || verbose >= 3)
(void) printf("\t\t%s %*llu\n",
zil_rec_info[i].zri_name, w,
(u_longlong_t)zil_rec_info[i].zri_count);
(void) printf("\n");
}
/* ARGSUSED */
void
dump_intent_log(zilog_t *zilog)
{
const zil_header_t *zh = zilog->zl_header;
int verbose = MAX(dump_opt['d'], dump_opt['i']);
int i;
if (zh->zh_log.blk_birth == 0 || verbose < 2)
return;
(void) printf("\n ZIL header: claim_txg %llu, seq %llu\n",
(u_longlong_t)zh->zh_claim_txg, (u_longlong_t)zh->zh_replay_seq);
if (verbose >= 4)
print_log_bp(&zh->zh_log, "\n\tfirst block: ");
for (i = 0; i < TX_MAX_TYPE; i++)
zil_rec_info[i].zri_count = 0;
if (verbose >= 2) {
(void) printf("\n");
(void) zil_parse(zilog, print_log_block, print_log_record, NULL,
zh->zh_claim_txg);
print_log_stats(verbose);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,405 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libintl.h>
#include <libuutil.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <libzfs.h>
#include "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_recurse;
zfs_type_t cb_types;
zfs_sort_column_t *cb_sortcol;
zfs_proplist_t **cb_proplist;
} callback_data_t;
uu_avl_pool_t *avl_pool;
/*
* Called for each dataset. If the object the object is of an appropriate type,
* add it to the avl tree and recurse over any children as necessary.
*/
int
zfs_callback(zfs_handle_t *zhp, void *data)
{
callback_data_t *cb = data;
int dontclose = 0;
/*
* If this object is of the appropriate type, add it to the AVL tree.
*/
if (zfs_get_type(zhp) & cb->cb_types) {
uu_avl_index_t idx;
zfs_node_t *node = safe_malloc(sizeof (zfs_node_t));
node->zn_handle = zhp;
uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
&idx) == NULL) {
if (cb->cb_proplist &&
zfs_expand_proplist(zhp, cb->cb_proplist) != 0) {
free(node);
return (-1);
}
uu_avl_insert(cb->cb_avl, node, idx);
dontclose = 1;
} else {
free(node);
}
}
/*
* Recurse if necessary.
*/
if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM ||
(zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types &
ZFS_TYPE_SNAPSHOT))))
(void) zfs_iter_children(zhp, zfs_callback, data);
if (!dontclose)
zfs_close(zhp);
return (0);
}
int
zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,
boolean_t reverse)
{
zfs_sort_column_t *col;
zfs_prop_t prop;
if ((prop = zfs_name_to_prop(name)) == ZFS_PROP_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 == ZFS_PROP_INVAL) {
col->sc_user_prop = safe_malloc(strlen(name) + 1);
(void) strcpy(col->sc_user_prop, name);
}
if (*sc == NULL) {
col->sc_last = col;
*sc = col;
} else {
(*sc)->sc_last->sc_next = col;
(*sc)->sc_last = col;
}
return (0);
}
void
zfs_free_sort_columns(zfs_sort_column_t *sc)
{
zfs_sort_column_t *col;
while (sc != NULL) {
col = sc->sc_next;
free(sc->sc_user_prop);
free(sc);
sc = col;
}
}
/* ARGSUSED */
static int
zfs_compare(const void *larg, const void *rarg, void *unused)
{
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
const char *lname = zfs_get_name(l);
const char *rname = zfs_get_name(r);
char *lat, *rat;
uint64_t lcreate, rcreate;
int ret;
lat = (char *)strchr(lname, '@');
rat = (char *)strchr(rname, '@');
if (lat != NULL)
*lat = '\0';
if (rat != NULL)
*rat = '\0';
ret = strcmp(lname, rname);
if (ret == 0) {
/*
* If we're comparing a dataset to one of its snapshots, we
* always make the full dataset first.
*/
if (lat == NULL) {
ret = -1;
} else if (rat == NULL) {
ret = 1;
} else {
/*
* If we have two snapshots from the same dataset, then
* we want to sort them according to creation time. We
* use the hidden CREATETXG property to get an absolute
* ordering of snapshots.
*/
lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
if (lcreate < rcreate)
ret = -1;
else if (lcreate > rcreate)
ret = 1;
}
}
if (lat != NULL)
*lat = '@';
if (rat != NULL)
*rat = '@';
return (ret);
}
/*
* Sort datasets by specified columns.
*
* o Numeric types sort in ascending order.
* o String types sort in alphabetical order.
* o Types inappropriate for a row sort that row to the literal
* bottom, regardless of the specified ordering.
*
* If no sort columns are specified, or two datasets compare equally
* across all specified columns, they are sorted alphabetically by name
* with snapshots grouped under their parents.
*/
static int
zfs_sort(const void *larg, const void *rarg, void *data)
{
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
zfs_sort_column_t *sc = (zfs_sort_column_t *)data;
zfs_sort_column_t *psc;
for (psc = sc; psc != NULL; psc = psc->sc_next) {
char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN];
char *lstr, *rstr;
uint64_t lnum, rnum;
boolean_t lvalid, rvalid;
int ret = 0;
/*
* We group the checks below the generic code. If 'lstr' and
* 'rstr' are non-NULL, then we do a string based comparison.
* Otherwise, we compare 'lnum' and 'rnum'.
*/
lstr = rstr = NULL;
if (psc->sc_prop == ZFS_PROP_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,
ZFS_PROP_VALUE, &lstr) == 0);
if (rvalid)
verify(nvlist_lookup_string(rval,
ZFS_PROP_VALUE, &rstr) == 0);
} else if (zfs_prop_is_string(psc->sc_prop)) {
lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);
rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf,
sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0);
lstr = lbuf;
rstr = rbuf;
} else {
lvalid = zfs_prop_valid_for_type(psc->sc_prop,
zfs_get_type(l));
rvalid = zfs_prop_valid_for_type(psc->sc_prop,
zfs_get_type(r));
if (lvalid)
(void) zfs_prop_get_numeric(l, psc->sc_prop,
&lnum, NULL, NULL, 0);
if (rvalid)
(void) zfs_prop_get_numeric(r, psc->sc_prop,
&rnum, NULL, NULL, 0);
}
if (!lvalid && !rvalid)
continue;
else if (!lvalid)
return (1);
else if (!rvalid)
return (-1);
if (lstr)
ret = strcmp(lstr, rstr);
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, boolean_t recurse, zfs_type_t types,
zfs_sort_column_t *sortcol, zfs_proplist_t **proplist, zfs_iter_f callback,
void *data, boolean_t args_can_be_paths)
{
callback_data_t cb;
int ret = 0;
zfs_node_t *node;
uu_avl_walk_t *walk;
avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t),
offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT);
if (avl_pool == NULL) {
(void) fprintf(stderr,
gettext("internal error: out of memory\n"));
exit(1);
}
cb.cb_sortcol = sortcol;
cb.cb_recurse = recurse;
cb.cb_proplist = proplist;
cb.cb_types = types;
if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) {
(void) fprintf(stderr,
gettext("internal error: out of memory\n"));
exit(1);
}
if (argc == 0) {
/*
* If given no arguments, iterate over all datasets.
*/
cb.cb_recurse = 1;
ret = zfs_iter_root(g_zfs, zfs_callback, &cb);
} else {
int i;
zfs_handle_t *zhp;
zfs_type_t argtype;
/*
* If we're recursive, then we always allow filesystems as
* arguments. If we also are interested in snapshots, then we
* can take volumes as well.
*/
argtype = types;
if (recurse) {
argtype |= ZFS_TYPE_FILESYSTEM;
if (types & ZFS_TYPE_SNAPSHOT)
argtype |= ZFS_TYPE_VOLUME;
}
for (i = 0; i < argc; i++) {
if (args_can_be_paths) {
zhp = zfs_path_to_zhandle(g_zfs, argv[i],
argtype);
} else {
zhp = zfs_open(g_zfs, argv[i], argtype);
}
if (zhp != NULL)
ret |= zfs_callback(zhp, &cb);
else
ret = 1;
}
}
/*
* At this point we've got our AVL tree full of zfs handles, so iterate
* over each one and execute the real user callback.
*/
for (node = uu_avl_first(cb.cb_avl); node != NULL;
node = uu_avl_next(cb.cb_avl, node))
ret |= callback(node->zn_handle, data);
/*
* Finally, clean up the AVL tree.
*/
if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) {
(void) fprintf(stderr,
gettext("internal error: out of memory"));
exit(1);
}
while ((node = uu_avl_walk_next(walk)) != NULL) {
uu_avl_remove(cb.cb_avl, node);
zfs_close(node->zn_handle);
free(node);
}
uu_avl_walk_end(walk);
uu_avl_destroy(cb.cb_avl);
uu_avl_pool_destroy(avl_pool);
return (ret);
}

View File

@ -0,0 +1,52 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef ZFS_ITER_H
#define ZFS_ITER_H
#pragma ident "%Z%%M% %I% %E% SMI"
#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;
int zfs_for_each(int, char **, boolean_t, zfs_type_t, zfs_sort_column_t *,
zfs_proplist_t **, zfs_iter_f, void *, boolean_t);
int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t);
void zfs_free_sort_columns(zfs_sort_column_t *);
#ifdef __cplusplus
}
#endif
#endif /* ZFS_ITER_H */

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,245 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <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;
};
/* 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) {
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, zpool_proplist_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();
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 && add_pool(zhp, zlp) == 0) {
if (proplist &&
zpool_expand_proplist(zhp, proplist) != 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,
zpool_proplist_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

View File

@ -0,0 +1,79 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "zpool_util.h"
/*
* Utility function to guarantee malloc() success.
*/
void *
safe_malloc(size_t size)
{
void *data;
if ((data = calloc(1, size)) == NULL) {
(void) fprintf(stderr, "internal error: out of memory\n");
exit(1);
}
return (data);
}
/*
* Same as above, but for strdup()
*/
char *
safe_strdup(const char *str)
{
char *ret;
if ((ret = strdup(str)) == NULL) {
(void) fprintf(stderr, "internal error: out of memory\n");
exit(1);
}
return (ret);
}
/*
* Display an out of memory error message and abort the current program.
*/
void
zpool_no_memory(void)
{
assert(errno == ENOMEM);
(void) fprintf(stderr,
gettext("internal error: out of memory\n"));
exit(1);
}

View File

@ -0,0 +1,72 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef ZPOOL_UTIL_H
#define ZPOOL_UTIL_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libnvpair.h>
#include <libzfs.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Basic utility functions
*/
void *safe_malloc(size_t);
char *safe_strdup(const char *);
void zpool_no_memory(void);
/*
* Virtual device functions
*/
nvlist_t *make_root_vdev(nvlist_t *poolconfig, int force, int check_rep,
boolean_t isreplace, int argc, char **argv);
/*
* Pool list functions
*/
int for_each_pool(int, char **, boolean_t unavail, zpool_proplist_t **,
zpool_iter_f, void *);
typedef struct zpool_list zpool_list_t;
zpool_list_t *pool_list_get(int, char **, zpool_proplist_t **, int *);
void pool_list_update(zpool_list_t *);
int pool_list_iter(zpool_list_t *, int unavail, zpool_iter_f, void *);
void pool_list_free(zpool_list_t *);
int pool_list_count(zpool_list_t *);
void pool_list_remove(zpool_list_t *, zpool_handle_t *);
libzfs_handle_t *g_zfs;
#ifdef __cplusplus
}
#endif
#endif /* ZPOOL_UTIL_H */

View File

@ -0,0 +1,850 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Functions to convert between a list of vdevs and an nvlist representing the
* configuration. Each entry in the list can be one of:
*
* Device vdevs
* disk=(path=..., devid=...)
* file=(path=...)
*
* Group vdevs
* raidz[1|2]=(...)
* mirror=(...)
*
* Hot spares
*
* While the underlying implementation supports it, group vdevs cannot contain
* other group vdevs. All userland verification of devices is contained within
* this file. If successful, the nvlist returned can be passed directly to the
* kernel; we've done as much verification as possible in userland.
*
* Hot spares are a special case, and passed down as an array of disk vdevs, at
* the same level as the root of the vdev tree.
*
* The only function exported by this file is 'get_vdev_spec'. The function
* performs several passes:
*
* 1. Construct the vdev specification. Performs syntax validation and
* makes sure each device is valid.
* 2. Check for devices in use. Using libdiskmgt, makes sure that no
* devices are also in use. Some can be overridden using the 'force'
* flag, others cannot.
* 3. Check for replication errors if the 'force' flag is not specified.
* validates that the replication level is consistent across the
* entire pool.
*/
#include <assert.h>
#include <devid.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <libnvpair.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <paths.h>
#include <sys/stat.h>
#include <sys/disk.h>
#include <sys/mntent.h>
#include <libgeom.h>
#include <libzfs.h>
#include "zpool_util.h"
/*
* For any given vdev specification, we can have multiple errors. The
* vdev_error() function keeps track of whether we have seen an error yet, and
* prints out a header if its the first error we've seen.
*/
boolean_t error_seen;
boolean_t is_force;
/*PRINTFLIKE1*/
static void
vdev_error(const char *fmt, ...)
{
va_list ap;
if (!error_seen) {
(void) fprintf(stderr, gettext("invalid vdev specification\n"));
if (!is_force)
(void) fprintf(stderr, gettext("use '-f' to override "
"the following errors:\n"));
else
(void) fprintf(stderr, gettext("the following errors "
"must be manually repaired:\n"));
error_seen = B_TRUE;
}
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
}
/*
* Validate a GEOM provider.
*/
static int
check_provider(const char *name, boolean_t force, boolean_t isspare)
{
struct gmesh mesh;
struct gclass *mp;
struct ggeom *gp;
struct gprovider *pp;
int rv;
/* XXX: What to do with isspare? */
if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
name += sizeof(_PATH_DEV) - 1;
rv = geom_gettree(&mesh);
assert(rv == 0);
pp = NULL;
LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
if (strcmp(pp->lg_name, name) == 0)
goto out;
}
}
}
out:
rv = -1;
if (pp == NULL)
vdev_error("no such provider %s\n", name);
else {
int acr, acw, ace;
VERIFY(sscanf(pp->lg_mode, "r%dw%de%d", &acr, &acw, &ace) == 3);
if (acw == 0 && ace == 0)
rv = 0;
else
vdev_error("%s is in use (%s)\n", name, pp->lg_mode);
}
geom_deletetree(&mesh);
return (rv);
}
static boolean_t
is_provider(const char *name)
{
off_t mediasize;
int fd;
fd = open(name, O_RDONLY);
if (fd == -1)
return (B_FALSE);
if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) {
close(fd);
return (B_FALSE);
}
close(fd);
return (B_TRUE);
}
/*
* Create a leaf vdev. Determine if this is a GEOM provider.
* Valid forms for a leaf vdev are:
*
* /dev/xxx Complete path to a GEOM provider
* xxx Shorthand for /dev/xxx
*/
nvlist_t *
make_leaf_vdev(const char *arg)
{
char path[MAXPATHLEN];
nvlist_t *vdev = NULL;
char *type = NULL;
if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
strlcpy(path, arg, sizeof (path));
else
snprintf(path, sizeof (path), "%s%s", _PATH_DEV, arg);
if (is_provider(path))
type = VDEV_TYPE_DISK;
else {
(void) fprintf(stderr, gettext("cannot use '%s': must be a "
"GEOM provider\n"), path);
return (NULL);
}
/*
* Finally, we have the complete device or file, and we know that it is
* acceptable to use. Construct the nvlist to describe this vdev. All
* vdevs have a 'path' element, and devices also have a 'devid' element.
*/
verify(nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) == 0);
verify(nvlist_add_string(vdev, ZPOOL_CONFIG_PATH, path) == 0);
verify(nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE, type) == 0);
if (strcmp(type, VDEV_TYPE_DISK) == 0)
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
(uint64_t)B_FALSE) == 0);
return (vdev);
}
/*
* Go through and verify the replication level of the pool is consistent.
* Performs the following checks:
*
* For the new spec, verifies that devices in mirrors and raidz are the
* same size.
*
* If the current configuration already has inconsistent replication
* levels, ignore any other potential problems in the new spec.
*
* Otherwise, make sure that the current spec (if there is one) and the new
* spec have consistent replication levels.
*/
typedef struct replication_level {
char *zprl_type;
uint64_t zprl_children;
uint64_t zprl_parity;
} replication_level_t;
/*
* Given a list of toplevel vdevs, return the current replication level. If
* the config is inconsistent, then NULL is returned. If 'fatal' is set, then
* an error message will be displayed for each self-inconsistent vdev.
*/
replication_level_t *
get_replication(nvlist_t *nvroot, boolean_t fatal)
{
nvlist_t **top;
uint_t t, toplevels;
nvlist_t **child;
uint_t c, children;
nvlist_t *nv;
char *type;
replication_level_t lastrep, rep, *ret;
boolean_t dontreport;
ret = safe_malloc(sizeof (replication_level_t));
verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
&top, &toplevels) == 0);
lastrep.zprl_type = NULL;
for (t = 0; t < toplevels; t++) {
nv = top[t];
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) != 0) {
/*
* This is a 'file' or 'disk' vdev.
*/
rep.zprl_type = type;
rep.zprl_children = 1;
rep.zprl_parity = 0;
} else {
uint64_t vdev_size;
/*
* This is a mirror or RAID-Z vdev. Go through and make
* sure the contents are all the same (files vs. disks),
* keeping track of the number of elements in the
* process.
*
* We also check that the size of each vdev (if it can
* be determined) is the same.
*/
rep.zprl_type = type;
rep.zprl_children = 0;
if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
verify(nvlist_lookup_uint64(nv,
ZPOOL_CONFIG_NPARITY,
&rep.zprl_parity) == 0);
assert(rep.zprl_parity != 0);
} else {
rep.zprl_parity = 0;
}
/*
* The 'dontreport' variable indicatest that we've
* already reported an error for this spec, so don't
* bother doing it again.
*/
type = NULL;
dontreport = 0;
vdev_size = -1ULL;
for (c = 0; c < children; c++) {
nvlist_t *cnv = child[c];
char *path;
struct stat64 statbuf;
uint64_t size = -1ULL;
char *childtype;
int fd, err;
rep.zprl_children++;
verify(nvlist_lookup_string(cnv,
ZPOOL_CONFIG_TYPE, &childtype) == 0);
/*
* If this is a a replacing or spare vdev, then
* get the real first child of the vdev.
*/
if (strcmp(childtype,
VDEV_TYPE_REPLACING) == 0 ||
strcmp(childtype, VDEV_TYPE_SPARE) == 0) {
nvlist_t **rchild;
uint_t rchildren;
verify(nvlist_lookup_nvlist_array(cnv,
ZPOOL_CONFIG_CHILDREN, &rchild,
&rchildren) == 0);
assert(rchildren == 2);
cnv = rchild[0];
verify(nvlist_lookup_string(cnv,
ZPOOL_CONFIG_TYPE,
&childtype) == 0);
}
verify(nvlist_lookup_string(cnv,
ZPOOL_CONFIG_PATH, &path) == 0);
/*
* If we have a raidz/mirror that combines disks
* with files, report it as an error.
*/
if (!dontreport && type != NULL &&
strcmp(type, childtype) != 0) {
if (ret != NULL)
free(ret);
ret = NULL;
if (fatal)
vdev_error(gettext(
"mismatched replication "
"level: %s contains both "
"files and devices\n"),
rep.zprl_type);
else
return (NULL);
dontreport = B_TRUE;
}
/*
* According to stat(2), the value of 'st_size'
* is undefined for block devices and character
* devices. But there is no effective way to
* determine the real size in userland.
*
* Instead, we'll take advantage of an
* implementation detail of spec_size(). If the
* device is currently open, then we (should)
* return a valid size.
*
* If we still don't get a valid size (indicated
* by a size of 0 or MAXOFFSET_T), then ignore
* this device altogether.
*/
if ((fd = open(path, O_RDONLY)) >= 0) {
err = fstat64(fd, &statbuf);
(void) close(fd);
} else {
err = stat64(path, &statbuf);
}
if (err != 0 || statbuf.st_size == 0)
continue;
size = statbuf.st_size;
/*
* Also check the size of each device. If they
* differ, then report an error.
*/
if (!dontreport && vdev_size != -1ULL &&
size != vdev_size) {
if (ret != NULL)
free(ret);
ret = NULL;
if (fatal)
vdev_error(gettext(
"%s contains devices of "
"different sizes\n"),
rep.zprl_type);
else
return (NULL);
dontreport = B_TRUE;
}
type = childtype;
vdev_size = size;
}
}
/*
* At this point, we have the replication of the last toplevel
* vdev in 'rep'. Compare it to 'lastrep' to see if its
* different.
*/
if (lastrep.zprl_type != NULL) {
if (strcmp(lastrep.zprl_type, rep.zprl_type) != 0) {
if (ret != NULL)
free(ret);
ret = NULL;
if (fatal)
vdev_error(gettext(
"mismatched replication level: "
"both %s and %s vdevs are "
"present\n"),
lastrep.zprl_type, rep.zprl_type);
else
return (NULL);
} else if (lastrep.zprl_parity != rep.zprl_parity) {
if (ret)
free(ret);
ret = NULL;
if (fatal)
vdev_error(gettext(
"mismatched replication level: "
"both %llu and %llu device parity "
"%s vdevs are present\n"),
lastrep.zprl_parity,
rep.zprl_parity,
rep.zprl_type);
else
return (NULL);
} else if (lastrep.zprl_children != rep.zprl_children) {
if (ret)
free(ret);
ret = NULL;
if (fatal)
vdev_error(gettext(
"mismatched replication level: "
"both %llu-way and %llu-way %s "
"vdevs are present\n"),
lastrep.zprl_children,
rep.zprl_children,
rep.zprl_type);
else
return (NULL);
}
}
lastrep = rep;
}
if (ret != NULL)
*ret = rep;
return (ret);
}
/*
* Check the replication level of the vdev spec against the current pool. Calls
* get_replication() to make sure the new spec is self-consistent. If the pool
* has a consistent replication level, then we ignore any errors. Otherwise,
* report any difference between the two.
*/
int
check_replication(nvlist_t *config, nvlist_t *newroot)
{
replication_level_t *current = NULL, *new;
int ret;
/*
* If we have a current pool configuration, check to see if it's
* self-consistent. If not, simply return success.
*/
if (config != NULL) {
nvlist_t *nvroot;
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
if ((current = get_replication(nvroot, B_FALSE)) == NULL)
return (0);
}
/*
* Get the replication level of the new vdev spec, reporting any
* inconsistencies found.
*/
if ((new = get_replication(newroot, B_TRUE)) == NULL) {
free(current);
return (-1);
}
/*
* Check to see if the new vdev spec matches the replication level of
* the current pool.
*/
ret = 0;
if (current != NULL) {
if (strcmp(current->zprl_type, new->zprl_type) != 0) {
vdev_error(gettext(
"mismatched replication level: pool uses %s "
"and new vdev is %s\n"),
current->zprl_type, new->zprl_type);
ret = -1;
} else if (current->zprl_parity != new->zprl_parity) {
vdev_error(gettext(
"mismatched replication level: pool uses %llu "
"device parity and new vdev uses %llu\n"),
current->zprl_parity, new->zprl_parity);
ret = -1;
} else if (current->zprl_children != new->zprl_children) {
vdev_error(gettext(
"mismatched replication level: pool uses %llu-way "
"%s and new vdev uses %llu-way %s\n"),
current->zprl_children, current->zprl_type,
new->zprl_children, new->zprl_type);
ret = -1;
}
}
free(new);
if (current != NULL)
free(current);
return (ret);
}
/*
* Determine if the given path is a hot spare within the given configuration.
*/
static boolean_t
is_spare(nvlist_t *config, const char *path)
{
int fd;
pool_state_t state;
char *name = NULL;
nvlist_t *label;
uint64_t guid, spareguid;
nvlist_t *nvroot;
nvlist_t **spares;
uint_t i, nspares;
boolean_t inuse;
if ((fd = open(path, O_RDONLY)) < 0)
return (B_FALSE);
if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
!inuse ||
state != POOL_STATE_SPARE ||
zpool_read_label(fd, &label) != 0) {
free(name);
(void) close(fd);
return (B_FALSE);
}
free(name);
(void) close(fd);
verify(nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) == 0);
nvlist_free(label);
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
for (i = 0; i < nspares; i++) {
verify(nvlist_lookup_uint64(spares[i],
ZPOOL_CONFIG_GUID, &spareguid) == 0);
if (spareguid == guid)
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Go through and find any devices that are in use. We rely on libdiskmgt for
* the majority of this task.
*/
int
check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing,
int isspare)
{
nvlist_t **child;
uint_t c, children;
char *type, *path;
int ret;
char buf[MAXPATHLEN];
uint64_t wholedisk;
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) != 0) {
verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
/*
* As a generic check, we look to see if this is a replace of a
* hot spare within the same pool. If so, we allow it
* regardless of what libdiskmgt or zpool_in_use() says.
*/
if (isreplacing) {
(void) strlcpy(buf, path, sizeof (buf));
if (is_spare(config, buf))
return (0);
}
if (strcmp(type, VDEV_TYPE_DISK) == 0)
ret = check_provider(path, force, isspare);
return (ret);
}
for (c = 0; c < children; c++)
if ((ret = check_in_use(config, child[c], force,
isreplacing, B_FALSE)) != 0)
return (ret);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
&child, &children) == 0)
for (c = 0; c < children; c++)
if ((ret = check_in_use(config, child[c], force,
isreplacing, B_TRUE)) != 0)
return (ret);
return (0);
}
const char *
is_grouping(const char *type, int *mindev)
{
if (strcmp(type, "raidz") == 0 || strcmp(type, "raidz1") == 0) {
if (mindev != NULL)
*mindev = 2;
return (VDEV_TYPE_RAIDZ);
}
if (strcmp(type, "raidz2") == 0) {
if (mindev != NULL)
*mindev = 3;
return (VDEV_TYPE_RAIDZ);
}
if (strcmp(type, "mirror") == 0) {
if (mindev != NULL)
*mindev = 2;
return (VDEV_TYPE_MIRROR);
}
if (strcmp(type, "spare") == 0) {
if (mindev != NULL)
*mindev = 1;
return (VDEV_TYPE_SPARE);
}
return (NULL);
}
/*
* Construct a syntactically valid vdev specification,
* and ensure that all devices and files exist and can be opened.
* Note: we don't bother freeing anything in the error paths
* because the program is just going to exit anyway.
*/
nvlist_t *
construct_spec(int argc, char **argv)
{
nvlist_t *nvroot, *nv, **top, **spares;
int t, toplevels, mindev, nspares;
const char *type;
top = NULL;
toplevels = 0;
spares = NULL;
nspares = 0;
while (argc > 0) {
nv = NULL;
/*
* If it's a mirror or raidz, the subsequent arguments are
* its leaves -- until we encounter the next mirror or raidz.
*/
if ((type = is_grouping(argv[0], &mindev)) != NULL) {
nvlist_t **child = NULL;
int c, children = 0;
if (strcmp(type, VDEV_TYPE_SPARE) == 0 &&
spares != NULL) {
(void) fprintf(stderr, gettext("invalid vdev "
"specification: 'spare' can be "
"specified only once\n"));
return (NULL);
}
for (c = 1; c < argc; c++) {
if (is_grouping(argv[c], NULL) != NULL)
break;
children++;
child = realloc(child,
children * sizeof (nvlist_t *));
if (child == NULL)
zpool_no_memory();
if ((nv = make_leaf_vdev(argv[c])) == NULL)
return (NULL);
child[children - 1] = nv;
}
if (children < mindev) {
(void) fprintf(stderr, gettext("invalid vdev "
"specification: %s requires at least %d "
"devices\n"), argv[0], mindev);
return (NULL);
}
argc -= c;
argv += c;
if (strcmp(type, VDEV_TYPE_SPARE) == 0) {
spares = child;
nspares = children;
continue;
} else {
verify(nvlist_alloc(&nv, NV_UNIQUE_NAME,
0) == 0);
verify(nvlist_add_string(nv, ZPOOL_CONFIG_TYPE,
type) == 0);
if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
verify(nvlist_add_uint64(nv,
ZPOOL_CONFIG_NPARITY,
mindev - 1) == 0);
}
verify(nvlist_add_nvlist_array(nv,
ZPOOL_CONFIG_CHILDREN, child,
children) == 0);
for (c = 0; c < children; c++)
nvlist_free(child[c]);
free(child);
}
} else {
/*
* We have a device. Pass off to make_leaf_vdev() to
* construct the appropriate nvlist describing the vdev.
*/
if ((nv = make_leaf_vdev(argv[0])) == NULL)
return (NULL);
argc--;
argv++;
}
toplevels++;
top = realloc(top, toplevels * sizeof (nvlist_t *));
if (top == NULL)
zpool_no_memory();
top[toplevels - 1] = nv;
}
if (toplevels == 0 && nspares == 0) {
(void) fprintf(stderr, gettext("invalid vdev "
"specification: at least one toplevel vdev must be "
"specified\n"));
return (NULL);
}
/*
* Finally, create nvroot and add all top-level vdevs to it.
*/
verify(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) == 0);
verify(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
VDEV_TYPE_ROOT) == 0);
verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
top, toplevels) == 0);
if (nspares != 0)
verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
spares, nspares) == 0);
for (t = 0; t < toplevels; t++)
nvlist_free(top[t]);
for (t = 0; t < nspares; t++)
nvlist_free(spares[t]);
if (spares)
free(spares);
free(top);
return (nvroot);
}
/*
* Get and validate the contents of the given vdev specification. This ensures
* that the nvlist returned is well-formed, that all the devices exist, and that
* they are not currently in use by any other known consumer. The 'poolconfig'
* parameter is the current configuration of the pool when adding devices
* existing pool, and is used to perform additional checks, such as changing the
* replication level of the pool. It can be 'NULL' to indicate that this is a
* new pool. The 'force' flag controls whether devices should be forcefully
* added, even if they appear in use.
*/
nvlist_t *
make_root_vdev(nvlist_t *poolconfig, int force, int check_rep,
boolean_t isreplacing, int argc, char **argv)
{
nvlist_t *newroot;
is_force = force;
/*
* Construct the vdev specification. If this is successful, we know
* that we have a valid specification, and that all devices can be
* opened.
*/
if ((newroot = construct_spec(argc, argv)) == NULL)
return (NULL);
/*
* Validate each device to make sure that its not shared with another
* subsystem. We do this even if 'force' is set, because there are some
* uses (such as a dedicated dump device) that even '-f' cannot
* override.
*/
if (check_in_use(poolconfig, newroot, force, isreplacing,
B_FALSE) != 0) {
nvlist_free(newroot);
return (NULL);
}
/*
* Check the replication level of the given vdevs and report any errors
* found. We include the existing pool spec, if any, as we need to
* catch changes against the existing replication level.
*/
if (check_rep && check_replication(poolconfig, newroot) != 0) {
nvlist_free(newroot);
return (NULL);
}
return (newroot);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
/*
* 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 (c) 1988 AT&T */
/* All Rights Reserved */
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _ASSERT_H
#define _ASSERT_H
#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6.1.4 */
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
extern void __assert_c99(const char *, const char *, int, const char *);
#else
extern void __assert(const char *, const char *, int);
#endif /* __STDC_VERSION__ - 0 >= 199901L */
#else
extern void _assert();
#endif
#ifdef __cplusplus
}
#endif
#endif /* _ASSERT_H */
/*
* Note that the ANSI C Standard requires all headers to be idempotent except
* <assert.h> which is explicitly required not to be idempotent (section 4.1.2).
* Therefore, it is by intent that the header guards (#ifndef _ASSERT_H) do
* not span this entire file.
*/
#undef assert
#ifdef NDEBUG
#define assert(EX) ((void)0)
#else
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
#define assert(EX) (void)((EX) || \
(__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
#else
#define assert(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#endif /* __STDC_VERSION__ - 0 >= 199901L */
#else
#define assert(EX) (void)((EX) || (_assert("EX", __FILE__, __LINE__), 0))
#endif /* __STDC__ */
#endif /* NDEBUG */

View File

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

View File

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

View File

@ -0,0 +1,32 @@
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _STDIO_EXT_H
#define _STDIO_EXT_H
#define enable_extended_FILE_stdio(x,y) (0)
#endif

View File

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

View File

@ -0,0 +1,99 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _THREAD_H
#define _THREAD_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <pthread.h>
#include <assert.h>
/*
* Compatibility thread stuff needed for Solaris -> Linux port
*/
typedef pthread_t thread_t;
typedef pthread_mutex_t mutex_t;
typedef pthread_cond_t cond_t;
typedef pthread_rwlock_t rwlock_t;
#define USYNC_THREAD 0
#define thr_self() (unsigned long)pthread_self()
#define thr_equal(a,b) pthread_equal(a,b)
#define thr_join(t,d,s) pthread_join(t,s)
#define thr_exit(r) pthread_exit(r)
#define _mutex_init(l,f,a) pthread_mutex_init(l,NULL)
#define _mutex_destroy(l) pthread_mutex_destroy(l)
#define mutex_lock(l) pthread_mutex_lock(l)
#define mutex_trylock(l) pthread_mutex_trylock(l)
#define mutex_unlock(l) pthread_mutex_unlock(l)
#define rwlock_init(l,f,a) pthread_rwlock_init(l,NULL)
#define rwlock_destroy(l) pthread_rwlock_destroy(l)
#define rw_rdlock(l) pthread_rwlock_rdlock(l)
#define rw_wrlock(l) pthread_rwlock_wrlock(l)
#define rw_tryrdlock(l) pthread_rwlock_tryrdlock(l)
#define rw_trywrlock(l) pthread_rwlock_trywrlock(l)
#define rw_unlock(l) pthread_rwlock_unlock(l)
#define cond_init(l,f,a) pthread_cond_init(l,NULL)
#define cond_destroy(l) pthread_cond_destroy(l)
#define cond_wait(l,m) pthread_cond_wait(l,m)
#define cond_signal(l) pthread_cond_signal(l)
#define cond_broadcast(l) pthread_cond_broadcast(l)
#define THR_BOUND 0x00000001 /* = PTHREAD_SCOPE_SYSTEM */
#define THR_NEW_LWP 0x00000002
#define THR_DETACHED 0x00000040 /* = PTHREAD_CREATE_DETACHED */
#define THR_SUSPENDED 0x00000080
#define THR_DAEMON 0x00000100
static __inline int
thr_create(void *stack_base, size_t stack_size, void *(*start_func) (void*),
void *arg, long flags, thread_t *new_thread_ID)
{
int ret;
assert(stack_base == NULL);
assert(stack_size == 0);
assert((flags & ~THR_BOUND & ~THR_DETACHED) == 0);
pthread_attr_t attr;
pthread_attr_init(&attr);
if(flags & THR_DETACHED)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* This function ignores the THR_BOUND flag, since NPTL doesn't seem to support PTHREAD_SCOPE_PROCESS */
ret = pthread_create(new_thread_ID, &attr, start_func, arg);
pthread_attr_destroy(&attr);
return (ret);
}
#endif /* _THREAD_H */

View File

@ -0,0 +1,266 @@
/*
* 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 <unistd.h>
#include <strings.h>
#include "libnvpair.h"
/*
* libnvpair - A tools library for manipulating <name, value> pairs.
*
* This library provides routines packing an unpacking nv pairs
* for transporting data across process boundaries, transporting
* between kernel and userland, and possibly saving onto disk files.
*/
static void
indent(FILE *fp, int depth)
{
while (depth-- > 0)
(void) fprintf(fp, "\t");
}
/*
* nvlist_print - Prints elements in an event buffer
*/
static
void
nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
{
int i;
char *name;
uint_t nelem;
nvpair_t *nvp;
if (nvl == NULL)
return;
indent(fp, depth);
(void) fprintf(fp, "nvlist version: %d\n", NVL_VERSION(nvl));
nvp = nvlist_next_nvpair(nvl, NULL);
while (nvp) {
data_type_t type = nvpair_type(nvp);
indent(fp, depth);
name = nvpair_name(nvp);
(void) fprintf(fp, "\t%s =", name);
nelem = 0;
switch (type) {
case DATA_TYPE_BOOLEAN: {
(void) fprintf(fp, " 1");
break;
}
case DATA_TYPE_BOOLEAN_VALUE: {
boolean_t val;
(void) nvpair_value_boolean_value(nvp, &val);
(void) fprintf(fp, " %d", val);
break;
}
case DATA_TYPE_BYTE: {
uchar_t val;
(void) nvpair_value_byte(nvp, &val);
(void) fprintf(fp, " 0x%2.2x", val);
break;
}
case DATA_TYPE_INT8: {
int8_t val;
(void) nvpair_value_int8(nvp, &val);
(void) fprintf(fp, " %d", val);
break;
}
case DATA_TYPE_UINT8: {
uint8_t val;
(void) nvpair_value_uint8(nvp, &val);
(void) fprintf(fp, " 0x%x", val);
break;
}
case DATA_TYPE_INT16: {
int16_t val;
(void) nvpair_value_int16(nvp, &val);
(void) fprintf(fp, " %d", val);
break;
}
case DATA_TYPE_UINT16: {
uint16_t val;
(void) nvpair_value_uint16(nvp, &val);
(void) fprintf(fp, " 0x%x", val);
break;
}
case DATA_TYPE_INT32: {
int32_t val;
(void) nvpair_value_int32(nvp, &val);
(void) fprintf(fp, " %d", val);
break;
}
case DATA_TYPE_UINT32: {
uint32_t val;
(void) nvpair_value_uint32(nvp, &val);
(void) fprintf(fp, " 0x%x", val);
break;
}
case DATA_TYPE_INT64: {
int64_t val;
(void) nvpair_value_int64(nvp, &val);
(void) fprintf(fp, " %lld", (longlong_t)val);
break;
}
case DATA_TYPE_UINT64: {
uint64_t val;
(void) nvpair_value_uint64(nvp, &val);
(void) fprintf(fp, " 0x%llx", (u_longlong_t)val);
break;
}
case DATA_TYPE_STRING: {
char *val;
(void) nvpair_value_string(nvp, &val);
(void) fprintf(fp, " %s", val);
break;
}
case DATA_TYPE_BOOLEAN_ARRAY: {
boolean_t *val;
(void) nvpair_value_boolean_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " %d", val[i]);
break;
}
case DATA_TYPE_BYTE_ARRAY: {
uchar_t *val;
(void) nvpair_value_byte_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " 0x%2.2x", val[i]);
break;
}
case DATA_TYPE_INT8_ARRAY: {
int8_t *val;
(void) nvpair_value_int8_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " %d", val[i]);
break;
}
case DATA_TYPE_UINT8_ARRAY: {
uint8_t *val;
(void) nvpair_value_uint8_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " 0x%x", val[i]);
break;
}
case DATA_TYPE_INT16_ARRAY: {
int16_t *val;
(void) nvpair_value_int16_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " %d", val[i]);
break;
}
case DATA_TYPE_UINT16_ARRAY: {
uint16_t *val;
(void) nvpair_value_uint16_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " 0x%x", val[i]);
break;
}
case DATA_TYPE_INT32_ARRAY: {
int32_t *val;
(void) nvpair_value_int32_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " %d", val[i]);
break;
}
case DATA_TYPE_UINT32_ARRAY: {
uint32_t *val;
(void) nvpair_value_uint32_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " 0x%x", val[i]);
break;
}
case DATA_TYPE_INT64_ARRAY: {
int64_t *val;
(void) nvpair_value_int64_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " %lld", (longlong_t)val[i]);
break;
}
case DATA_TYPE_UINT64_ARRAY: {
uint64_t *val;
(void) nvpair_value_uint64_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " 0x%llx",
(u_longlong_t)val[i]);
break;
}
case DATA_TYPE_STRING_ARRAY: {
char **val;
(void) nvpair_value_string_array(nvp, &val, &nelem);
for (i = 0; i < nelem; i++)
(void) fprintf(fp, " %s", val[i]);
break;
}
case DATA_TYPE_HRTIME: {
hrtime_t val;
(void) nvpair_value_hrtime(nvp, &val);
(void) fprintf(fp, " 0x%llx", val);
break;
}
case DATA_TYPE_NVLIST: {
nvlist_t *val;
(void) nvpair_value_nvlist(nvp, &val);
(void) fprintf(fp, " (embedded nvlist)\n");
nvlist_print_with_indent(fp, val, depth + 1);
indent(fp, depth + 1);
(void) fprintf(fp, "(end %s)\n", name);
break;
}
case DATA_TYPE_NVLIST_ARRAY: {
nvlist_t **val;
(void) nvpair_value_nvlist_array(nvp, &val, &nelem);
(void) fprintf(fp, " (array of embedded nvlists)\n");
for (i = 0; i < nelem; i++) {
indent(fp, depth + 1);
(void) fprintf(fp,
"(start %s[%d])\n", name, i);
nvlist_print_with_indent(fp, val[i], depth + 1);
indent(fp, depth + 1);
(void) fprintf(fp, "(end %s[%d])\n", name, i);
}
break;
}
default:
(void) fprintf(fp, " unknown data type (%d)", type);
break;
}
(void) fprintf(fp, "\n");
nvp = nvlist_next_nvpair(nvl, nvp);
}
}
void
nvlist_print(FILE *fp, nvlist_t *nvl)
{
nvlist_print_with_indent(fp, nvl, 0);
}

View File

@ -0,0 +1,46 @@
/*
* 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 _LIBNVPAIR_H
#define _LIBNVPAIR_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/nvpair.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
void nvlist_print(FILE *, nvlist_t *);
#ifdef __cplusplus
}
#endif
#endif /* _LIBNVPAIR_H */

View File

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

View File

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

View File

@ -0,0 +1,46 @@
/*
* 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 2006 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 <solaris.h>
/*
* We don't bind to the internal libc interfaces if this is a
* native build.
*/
#ifndef NATIVE_BUILD
#include "c_synonyms.h"
#endif
#include <libuutil.h>
#include <libuutil_impl.h>
#endif /* _LIBUUTIL_COMMON_H */

View File

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

View File

@ -0,0 +1,85 @@
/*
* 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 <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_msprintf(const char *format, ...)
{
va_list args;
char attic[1];
uint_t M, m;
char *b;
va_start(args, format);
M = vsnprintf(attic, 1, format, args);
va_end(args);
for (;;) {
m = M;
if ((b = uu_zalloc(m + 1)) == NULL)
return (NULL);
va_start(args, format);
M = vsnprintf(b, m + 1, format, args);
va_end(args);
if (M == m)
break; /* sizes match */
uu_free(b);
}
return (b);
}

View File

@ -0,0 +1,567 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "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",
sizeof (pp->uap_name), pp->uap_name, pp);
}
}
(void) pthread_mutex_lock(&uu_apool_list_lock);
pp->uap_next->uap_prev = pp->uap_prev;
pp->uap_prev->uap_next = pp->uap_next;
(void) pthread_mutex_unlock(&uu_apool_list_lock);
pp->uap_prev = NULL;
pp->uap_next = NULL;
uu_free(pp);
}
void
uu_avl_node_init(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
{
uintptr_t *na = (uintptr_t *)np;
if (pp->uap_debug) {
uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
if (offset + sizeof (*np) > pp->uap_objsize) {
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't fit in object (size %ld)\n",
base, np, pp, pp->uap_name, offset,
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, np, pp, pp->uap_name, offset,
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, np, 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, np, 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", 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",
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", 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",
ap, elem, idx);
if (na[0] == 0)
uu_panic("uu_avl_insert(%p, %p, %p): node not "
"initialized\n",
ap, elem, idx);
if (na[0] != POOL_TO_MARKER(pp))
uu_panic("uu_avl_insert(%p, %p, %p): node from "
"other pool, or corrupt\n",
ap, elem, idx);
if (!INDEX_VALID(ap, idx))
uu_panic("uu_avl_insert(%p, %p, %p): %s\n",
ap, elem, 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",
ap, 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",
ap, idx, INDEX_CHECK(idx)? "outdated index" :
"invalid index");
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_BEFORE));
}
/*
* called from uu_lockup() and uu_release(), as part of our fork1()-safety.
*/
void
uu_avl_lockup(void)
{
uu_avl_pool_t *pp;
(void) pthread_mutex_lock(&uu_apool_list_lock);
for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
pp = pp->uap_next)
(void) pthread_mutex_lock(&pp->uap_lock);
}
void
uu_avl_release(void)
{
uu_avl_pool_t *pp;
for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
pp = pp->uap_next)
(void) pthread_mutex_unlock(&pp->uap_lock);
(void) pthread_mutex_unlock(&uu_apool_list_lock);
}

View File

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

View File

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

View File

@ -0,0 +1,711 @@
/*
* 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 <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",
sizeof (pp->ulp_name), pp->ulp_name, 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, np, pp, pp->ulp_name, offset,
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, np, pp, pp->ulp_name, offset,
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, np_arg, 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, np_arg, 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",
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",
lp);
}
if (lp->ul_numnodes != 0) {
uu_panic("uu_list_destroy(%p): numnodes is nonzero, "
"but list is empty\n", 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",
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", lp, next, 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",
lp, NODE_TO_ELEM(lp, np), 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",
lp, elem, 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", lp, elem, 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",
lp, 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", lp, 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",
lp, 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", lp, 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) {
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",
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", 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", lp,
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",
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", 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",
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", lp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (-1);
}
list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);
return (0);
}
size_t
uu_list_numnodes(uu_list_t *lp)
{
return (lp->ul_numnodes);
}
void *
uu_list_first(uu_list_t *lp)
{
uu_list_node_impl_t *n = lp->ul_null_node.uln_next;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
void *
uu_list_last(uu_list_t *lp)
{
uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
void *
uu_list_next(uu_list_t *lp, void *elem)
{
uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
n = n->uln_next;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
void *
uu_list_prev(uu_list_t *lp, void *elem)
{
uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
n = n->uln_prev;
if (n == &lp->ul_null_node)
return (NULL);
return (NODE_TO_ELEM(lp, n));
}
/*
* called from uu_lockup() and uu_release(), as part of our fork1()-safety.
*/
void
uu_list_lockup(void)
{
uu_list_pool_t *pp;
(void) pthread_mutex_lock(&uu_lpool_list_lock);
for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
pp = pp->ulp_next)
(void) pthread_mutex_lock(&pp->ulp_lock);
}
void
uu_list_release(void)
{
uu_list_pool_t *pp;
for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
pp = pp->ulp_next)
(void) pthread_mutex_unlock(&pp->ulp_lock);
(void) pthread_mutex_unlock(&uu_lpool_list_lock);
}

View File

@ -0,0 +1,250 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libuutil_common.h"
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/debug.h>
#include <thread.h>
#include <unistd.h>
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
/*
* All of the old code under !defined(PTHREAD_ONCE_KEY_NP)
* is here to enable the building of a native version of
* libuutil.so when the build machine has not yet been upgraded
* to a version of libc that provides pthread_key_create_once_np().
* It should all be deleted when solaris_nevada ships.
* The code is not MT-safe in a relaxed memory model.
*/
#if defined(PTHREAD_ONCE_KEY_NP)
static pthread_key_t uu_error_key = PTHREAD_ONCE_KEY_NP;
#else /* PTHREAD_ONCE_KEY_NP */
static pthread_key_t uu_error_key = 0;
static pthread_mutex_t uu_key_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* PTHREAD_ONCE_KEY_NP */
static int uu_error_key_setup = 0;
static pthread_mutex_t uu_panic_lock = PTHREAD_MUTEX_INITIALIZER;
/* LINTED static unused */
static const char *uu_panic_format;
/* LINTED static unused */
static va_list uu_panic_args;
static pthread_t uu_panic_thread;
static uint32_t _uu_main_error;
void
uu_set_error(uint_t code)
{
#if 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);
}

View File

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

View File

@ -0,0 +1,205 @@
/*
* 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);
}

View File

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

View File

@ -0,0 +1,441 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBZFS_H
#define _LIBZFS_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <libnvpair.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/fs/zfs.h>
#include <sys/zfs_ioctl.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Miscellaneous ZFS constants
*/
#define ZFS_MAXNAMELEN MAXNAMELEN
#define ZPOOL_MAXNAMELEN MAXNAMELEN
#define ZFS_MAXPROPLEN MAXPATHLEN
/*
* libzfs errors
*/
enum {
EZFS_NOMEM = 2000, /* out of memory */
EZFS_BADPROP, /* invalid property value */
EZFS_PROPREADONLY, /* cannot set readonly property */
EZFS_PROPTYPE, /* property does not apply to dataset type */
EZFS_PROPNONINHERIT, /* property is not inheritable */
EZFS_PROPSPACE, /* bad quota or reservation */
EZFS_BADTYPE, /* dataset is not of appropriate type */
EZFS_BUSY, /* pool or dataset is busy */
EZFS_EXISTS, /* pool or dataset already exists */
EZFS_NOENT, /* no such pool or dataset */
EZFS_BADSTREAM, /* bad backup stream */
EZFS_DSREADONLY, /* dataset is readonly */
EZFS_VOLTOOBIG, /* volume is too large for 32-bit system */
EZFS_VOLHASDATA, /* volume already contains data */
EZFS_INVALIDNAME, /* invalid dataset name */
EZFS_BADRESTORE, /* unable to restore to destination */
EZFS_BADBACKUP, /* backup failed */
EZFS_BADTARGET, /* bad attach/detach/replace target */
EZFS_NODEVICE, /* no such device in pool */
EZFS_BADDEV, /* invalid device to add */
EZFS_NOREPLICAS, /* no valid replicas */
EZFS_RESILVERING, /* currently resilvering */
EZFS_BADVERSION, /* unsupported version */
EZFS_POOLUNAVAIL, /* pool is currently unavailable */
EZFS_DEVOVERFLOW, /* too many devices in one vdev */
EZFS_BADPATH, /* must be an absolute path */
EZFS_CROSSTARGET, /* rename or clone across pool or dataset */
EZFS_ZONED, /* used improperly in local zone */
EZFS_MOUNTFAILED, /* failed to mount dataset */
EZFS_UMOUNTFAILED, /* failed to unmount dataset */
EZFS_UNSHARENFSFAILED, /* unshare(1M) failed */
EZFS_SHARENFSFAILED, /* share(1M) failed */
EZFS_DEVLINKS, /* failed to create zvol links */
EZFS_PERM, /* permission denied */
EZFS_NOSPC, /* out of space */
EZFS_IO, /* I/O error */
EZFS_INTR, /* signal received */
EZFS_ISSPARE, /* device is a hot spare */
EZFS_INVALCONFIG, /* invalid vdev configuration */
EZFS_RECURSIVE, /* recursive dependency */
EZFS_NOHISTORY, /* no history object */
EZFS_UNSHAREISCSIFAILED, /* iscsitgtd failed request to unshare */
EZFS_SHAREISCSIFAILED, /* iscsitgtd failed request to share */
EZFS_POOLPROPS, /* couldn't retrieve pool props */
EZFS_POOL_NOTSUP, /* ops not supported for this type of pool */
EZFS_POOL_INVALARG, /* invalid argument for this pool operation */
EZFS_UNKNOWN
};
/*
* Basic handle types
*/
typedef struct zfs_handle zfs_handle_t;
typedef struct zpool_handle zpool_handle_t;
typedef struct libzfs_handle libzfs_handle_t;
/*
* Library initialization
*/
extern libzfs_handle_t *libzfs_init(void);
extern void libzfs_fini(libzfs_handle_t *);
extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *);
extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
extern int libzfs_errno(libzfs_handle_t *);
extern const char *libzfs_error_action(libzfs_handle_t *);
extern const char *libzfs_error_description(libzfs_handle_t *);
/*
* Basic handle functions
*/
extern zpool_handle_t *zpool_open(libzfs_handle_t *, const char *);
extern zpool_handle_t *zpool_open_canfail(libzfs_handle_t *, const char *);
extern void zpool_close(zpool_handle_t *);
extern const char *zpool_get_name(zpool_handle_t *);
extern uint64_t zpool_get_guid(zpool_handle_t *);
extern uint64_t zpool_get_space_used(zpool_handle_t *);
extern uint64_t zpool_get_space_total(zpool_handle_t *);
extern int zpool_get_root(zpool_handle_t *, char *, size_t);
extern int zpool_get_state(zpool_handle_t *);
extern uint64_t zpool_get_version(zpool_handle_t *);
/*
* Iterate over all active pools in the system.
*/
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
/*
* Functions to create and destroy pools
*/
extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
const char *);
extern int zpool_destroy(zpool_handle_t *);
extern int zpool_add(zpool_handle_t *, nvlist_t *);
/*
* Functions to manipulate pool and vdev state
*/
extern int zpool_scrub(zpool_handle_t *, pool_scrub_type_t);
extern int zpool_vdev_online(zpool_handle_t *, const char *);
extern int zpool_vdev_offline(zpool_handle_t *, const char *, int);
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_clear(zpool_handle_t *, const char *);
extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *);
/*
* 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 *, zfs_prop_t, char *,
size_t proplen, zfs_source_t *);
extern const char *zpool_prop_to_name(zpool_prop_t);
extern const char *zpool_prop_values(zpool_prop_t);
/*
* Pool health statistics.
*/
typedef enum {
/*
* The following correspond to faults as defined in the (fault.fs.zfs.*)
* event namespace. Each is associated with a corresponding message ID.
*/
ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */
ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */
ZPOOL_STATUS_MISSING_DEV_NR, /* missing device with no replicas */
ZPOOL_STATUS_CORRUPT_LABEL_R, /* bad device label with replicas */
ZPOOL_STATUS_CORRUPT_LABEL_NR, /* bad device label with no replicas */
ZPOOL_STATUS_BAD_GUID_SUM, /* sum of device guids didn't match */
ZPOOL_STATUS_CORRUPT_POOL, /* pool metadata is corrupted */
ZPOOL_STATUS_CORRUPT_DATA, /* data errors in user (meta)data */
ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */
/*
* The following are not faults per se, but still an error possibly
* requiring administrative attention. There is no corresponding
* message ID.
*/
ZPOOL_STATUS_VERSION_OLDER, /* older on-disk version */
ZPOOL_STATUS_RESILVERING, /* device being resilvered */
ZPOOL_STATUS_OFFLINE_DEV, /* device online */
/*
* Finally, the following indicates a healthy pool.
*/
ZPOOL_STATUS_OK
} zpool_status_t;
extern zpool_status_t zpool_get_status(zpool_handle_t *, char **);
extern zpool_status_t zpool_import_status(nvlist_t *, char **);
/*
* Statistics and configuration functions.
*/
extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
/*
* Import and export functions
*/
extern int zpool_export(zpool_handle_t *);
extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
const char *);
/*
* Search for pools to import
*/
extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
/*
* Miscellaneous pool functions
*/
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *);
extern int zpool_upgrade(zpool_handle_t *);
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
extern void zpool_log_history(libzfs_handle_t *, int, char **, const char *,
boolean_t, boolean_t);
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
size_t len);
/*
* Basic handle manipulations. These functions do not create or destroy the
* underlying datasets, only the references to them.
*/
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
extern void zfs_close(zfs_handle_t *);
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
extern const char *zfs_get_name(const zfs_handle_t *);
/*
* Property management functions. Some functions are shared with the kernel,
* and are found in sys/fs/zfs.h.
*/
extern const char *zfs_prop_to_name(zfs_prop_t);
extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
zfs_source_t *, char *, size_t, boolean_t);
extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
zfs_source_t *, char *, size_t);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern const char *zfs_prop_get_string(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *);
extern const char *zfs_prop_values(zfs_prop_t);
extern int zfs_prop_valid_for_type(zfs_prop_t, int);
extern const char *zfs_prop_default_string(zfs_prop_t prop);
extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
extern int zfs_prop_is_string(zfs_prop_t prop);
extern const char *zfs_prop_column_name(zfs_prop_t);
extern boolean_t zfs_prop_align_right(zfs_prop_t);
extern void nicebool(int value, char *buf, size_t buflen);
typedef struct zfs_proplist {
zfs_prop_t pl_prop;
char *pl_user_prop;
struct zfs_proplist *pl_next;
boolean_t pl_all;
size_t pl_width;
boolean_t pl_fixed;
} zfs_proplist_t;
typedef zfs_proplist_t zpool_proplist_t;
extern int zfs_get_proplist(libzfs_handle_t *, char *, zfs_proplist_t **);
extern int zpool_get_proplist(libzfs_handle_t *, char *, zpool_proplist_t **);
extern int zfs_expand_proplist(zfs_handle_t *, zfs_proplist_t **);
extern int zpool_expand_proplist(zpool_handle_t *, zpool_proplist_t **);
extern void zfs_free_proplist(zfs_proplist_t *);
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
#define ZFS_MOUNTPOINT_NONE "none"
#define ZFS_MOUNTPOINT_LEGACY "legacy"
/*
* Functions for printing properties from zfs/zpool
*/
typedef struct libzfs_get_cbdata {
int cb_sources;
int cb_columns[4];
int cb_colwidths[5];
boolean_t cb_scripted;
boolean_t cb_literal;
boolean_t cb_first;
zfs_proplist_t *cb_proplist;
} libzfs_get_cbdata_t;
void libzfs_print_one_property(const char *, libzfs_get_cbdata_t *,
const char *, const char *, zfs_source_t, const char *);
#define GET_COL_NAME 1
#define GET_COL_PROPERTY 2
#define GET_COL_VALUE 3
#define GET_COL_SOURCE 4
/*
* Iterator functions.
*/
typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
/*
* Functions to create and destroy datasets.
*/
extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
nvlist_t *);
extern int zfs_destroy(zfs_handle_t *);
extern int zfs_destroy_snaps(zfs_handle_t *, char *);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, int);
extern int zfs_rename(zfs_handle_t *, const char *);
extern int zfs_send(zfs_handle_t *, const char *, int);
extern int zfs_receive(libzfs_handle_t *, const char *, int, int, int,
boolean_t, int);
extern int zfs_promote(zfs_handle_t *);
/*
* Miscellaneous functions.
*/
extern const char *zfs_type_to_name(zfs_type_t);
extern void zfs_refresh_properties(zfs_handle_t *);
extern int zfs_name_valid(const char *, zfs_type_t);
extern int zfs_disable(zfs_handle_t *);
extern int zfs_enable(zfs_handle_t *);
extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
/*
* Mount support functions.
*/
extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
extern boolean_t zfs_is_mounted(zfs_handle_t *, char **);
extern int zfs_mount(zfs_handle_t *, const char *, int);
extern int zfs_unmount(zfs_handle_t *, const char *, int);
extern int zfs_unmountall(zfs_handle_t *, int);
/*
* Share support functions.
*/
extern boolean_t zfs_is_shared(zfs_handle_t *);
extern int zfs_share(zfs_handle_t *);
extern int zfs_unshare(zfs_handle_t *);
/*
* Protocol-specifc share support functions.
*/
extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
extern int zfs_share_nfs(zfs_handle_t *);
extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
extern int zfs_unshareall_nfs(zfs_handle_t *);
extern boolean_t zfs_is_shared_iscsi(zfs_handle_t *);
extern int zfs_share_iscsi(zfs_handle_t *);
extern int zfs_unshare_iscsi(zfs_handle_t *);
/*
* 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 *);
/*
* Pool destroy special. Remove the device information without destroying
* the underlying dataset.
*/
extern int zfs_remove_link(zfs_handle_t *);
/*
* Given a device or file, determine if it is part of a pool.
*/
extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
boolean_t *);
/*
* ftyp special. Read the label from a given device.
*/
extern int zpool_read_label(int, nvlist_t **);
/*
* Create and remove zvol /dev links.
*/
extern int zpool_create_zvol_links(zpool_handle_t *);
extern int zpool_remove_zvol_links(zpool_handle_t *);
/*
* Enable and disable datasets within a pool by mounting/unmounting and
* sharing/unsharing them.
*/
extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
#ifdef __FreeBSD__
extern int zmount(const char *, const char *, int, char *, char *, int, char *,
int);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _LIBZFS_H */

View File

@ -0,0 +1,606 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libintl.h>
#include <libuutil.h>
#include <stddef.h>
#include <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;
uu_list_node_t cn_listnode;
} prop_changenode_t;
struct prop_changelist {
zfs_prop_t cl_prop;
zfs_prop_t cl_realprop;
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_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.
*/
int
changelist_prefix(prop_changelist_t *clp)
{
prop_changenode_t *cn;
int ret = 0;
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
/*
* If we are in the global zone, but this dataset is exported
* to a local zone, do nothing.
*/
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
continue;
if (ZFS_IS_VOLUME(cn->cn_handle)) {
switch (clp->cl_realprop) {
case ZFS_PROP_NAME:
/*
* If this was a rename, unshare the zvol, and
* remove the /dev/zvol links.
*/
(void) zfs_unshare_iscsi(cn->cn_handle);
if (zvol_remove_link(cn->cn_handle->zfs_hdl,
cn->cn_handle->zfs_name) != 0)
ret = -1;
break;
case ZFS_PROP_VOLSIZE:
/*
* If this was a change to the volume size, we
* need to unshare and reshare the volume.
*/
(void) zfs_unshare_iscsi(cn->cn_handle);
break;
}
} else if (zfs_unmount(cn->cn_handle, NULL, clp->cl_flags) != 0)
ret = -1;
}
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 ret = 0;
/*
* If we're changing the mountpoint, attempt to destroy the underlying
* mountpoint. All other datasets will have inherited from this dataset
* (in which case their mountpoints exist in the filesystem in the new
* location), or have explicit mountpoints set (in which case they won't
* be in the changelist).
*/
if ((cn = uu_list_last(clp->cl_list)) == NULL)
return (0);
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT)
remove_mountpoint(cn->cn_handle);
/*
* We walk the datasets in reverse, because we want to mount any parent
* datasets before mounting the children.
*/
for (cn = uu_list_last(clp->cl_list); cn != NULL;
cn = uu_list_prev(clp->cl_list, cn)) {
/*
* 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;
zfs_refresh_properties(cn->cn_handle);
if (ZFS_IS_VOLUME(cn->cn_handle)) {
/*
* If we're doing a rename, recreate the /dev/zvol
* links.
*/
if (clp->cl_realprop == ZFS_PROP_NAME &&
zvol_create_link(cn->cn_handle->zfs_hdl,
cn->cn_handle->zfs_name) != 0) {
ret = -1;
} else if (cn->cn_shared ||
clp->cl_prop == ZFS_PROP_SHAREISCSI) {
if (zfs_prop_get(cn->cn_handle,
ZFS_PROP_SHAREISCSI, shareopts,
sizeof (shareopts), NULL, NULL, 0,
B_FALSE) == 0 &&
strcmp(shareopts, "off") == 0) {
ret = zfs_unshare_iscsi(cn->cn_handle);
} else {
ret = zfs_share_iscsi(cn->cn_handle);
}
}
continue;
}
if ((clp->cl_waslegacy || cn->cn_mounted) &&
!zfs_is_mounted(cn->cn_handle, NULL) &&
zfs_mount(cn->cn_handle, NULL, 0) != 0)
ret = -1;
/*
* We always re-share even if the filesystem is currently
* shared, so that we can adopt any new options.
*/
if (cn->cn_shared ||
(clp->cl_prop == ZFS_PROP_SHARENFS && clp->cl_waslegacy)) {
if (zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
shareopts, sizeof (shareopts), NULL, NULL, 0,
B_FALSE) == 0 && strcmp(shareopts, "off") == 0) {
ret = zfs_unshare_nfs(cn->cn_handle, NULL);
} else {
ret = zfs_share_nfs(cn->cn_handle);
}
}
}
return (ret);
}
/*
* Is this "dataset" a child of "parent"?
*/
static boolean_t
isa_child_of(const char *dataset, const char *parent)
{
int len;
len = strlen(parent);
if (strncmp(dataset, parent, len) == 0 &&
(dataset[len] == '@' || dataset[len] == '/' ||
dataset[len] == '\0'))
return (B_TRUE);
else
return (B_FALSE);
}
/*
* If we rename a filesystem, child filesystem handles are no longer valid
* since we identify each dataset by its name in the ZFS namespace. As a
* result, we have to go through and fix up all the names appropriately. We
* could do this automatically if libzfs kept track of all open handles, but
* this is a lot less work.
*/
void
changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
{
prop_changenode_t *cn;
char newname[ZFS_MAXNAMELEN];
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
/*
* Do not rename a clone that's not in the source hierarchy.
*/
if (!isa_child_of(cn->cn_handle->zfs_name, src))
continue;
/*
* Destroy the previous mountpoint if needed.
*/
remove_mountpoint(cn->cn_handle);
(void) strlcpy(newname, dst, sizeof (newname));
(void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
(void) strlcpy(cn->cn_handle->zfs_name, newname,
sizeof (cn->cn_handle->zfs_name));
}
}
/*
* Given a gathered changelist for the 'sharenfs' property, unshare all the
* datasets in the list.
*/
int
changelist_unshare(prop_changelist_t *clp)
{
prop_changenode_t *cn;
int ret = 0;
if (clp->cl_prop != ZFS_PROP_SHARENFS)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
if (zfs_unshare_nfs(cn->cn_handle, NULL) != 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(zfs_handle_t *zhp, prop_changelist_t *clp)
{
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, zhp->zfs_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;
uu_list_walk_t *walk;
if (clp->cl_list) {
verify((walk = uu_list_walk_start(clp->cl_list,
UU_WALK_ROBUST)) != NULL);
while ((cn = uu_list_walk_next(walk)) != NULL) {
uu_list_remove(clp->cl_list, cn);
zfs_close(cn->cn_handle);
free(cn);
}
uu_list_walk_end(walk);
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;
zfs_source_t 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 (clp->cl_alldependents || clp->cl_allchildren ||
sourcetype == ZFS_SRC_DEFAULT || sourcetype == ZFS_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 = zfs_is_mounted(zhp, NULL);
cn->cn_shared = zfs_is_shared(zhp);
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
/* Indicate if any child is exported to a local zone. */
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
clp->cl_haszonedchild = B_TRUE;
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
if (clp->cl_sorted) {
uu_list_index_t idx;
(void) uu_list_find(clp->cl_list, cn, NULL,
&idx);
uu_list_insert(clp->cl_list, cn, idx);
} else {
ASSERT(!clp->cl_alldependents);
verify(uu_list_insert_before(clp->cl_list,
uu_list_first(clp->cl_list), cn) == 0);
}
if (!clp->cl_alldependents)
return (zfs_iter_children(zhp, change_one, data));
} else {
zfs_close(zhp);
}
return (0);
}
/*ARGSUSED*/
static int
compare_mountpoints(const void *a, const void *b, void *unused)
{
const prop_changenode_t *ca = a;
const prop_changenode_t *cb = b;
char mounta[MAXPATHLEN];
char mountb[MAXPATHLEN];
boolean_t hasmounta, hasmountb;
/*
* When unsharing or unmounting filesystems, we need to do it in
* mountpoint order. This allows the user to have a mountpoint
* hierarchy that is different from the dataset hierarchy, and still
* allow it to be changed. However, if either dataset doesn't have a
* mountpoint (because it is a volume or a snapshot), we place it at the
* end of the list, because it doesn't affect our change at all.
*/
hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
if (!hasmounta && hasmountb)
return (-1);
else if (hasmounta && !hasmountb)
return (1);
else if (!hasmounta && !hasmountb)
return (0);
else
return (strcmp(mountb, mounta));
}
/*
* Given a ZFS handle and a property, construct a complete list of datasets
* that need to be modified as part of this process. For anything but the
* 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
* Otherwise, we iterate over all children and look for any datasets that
* inherit the property. For each such dataset, we add it to the list and
* mark whether it was shared beforehand.
*/
prop_changelist_t *
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
{
prop_changelist_t *clp;
prop_changenode_t *cn;
zfs_handle_t *temp;
char property[ZFS_MAXPROPLEN];
uu_compare_fn_t *compare = NULL;
if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
return (NULL);
/*
* For mountpoint-related tasks, we want to sort everything by
* mountpoint, so that we mount and unmount them in the appropriate
* order, regardless of their position in the hierarchy.
*/
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
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_flags = 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_SHAREISCSI)
return (clp);
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_ANY)) == 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 = zfs_is_mounted(temp, NULL);
cn->cn_shared = zfs_is_shared(temp);
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
if (clp->cl_sorted) {
uu_list_index_t idx;
(void) uu_list_find(clp->cl_list, cn, NULL, &idx);
uu_list_insert(clp->cl_list, cn, idx);
} else {
verify(uu_list_insert_after(clp->cl_list,
uu_list_last(clp->cl_list), cn) == 0);
}
/*
* If the property was previously 'legacy' or 'none', record this fact,
* as the behavior of changelist_postfix() will be different.
*/
if (zfs_prop_get(zhp, prop, property, sizeof (property),
NULL, NULL, 0, B_FALSE) == 0 &&
(strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0 ||
strcmp(property, "off") == 0))
clp->cl_waslegacy = B_TRUE;
return (clp);
}

View File

@ -0,0 +1,374 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* The pool configuration repository is stored in /etc/zfs/zpool.cache as a
* single packed nvlist. While it would be nice to just read in this
* file from userland, this wouldn't work from a local zone. So we have to have
* a zpool ioctl to return the complete configuration for all pools. In the
* global zone, this will be identical to reading the file and unpacking it in
* userland.
*/
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <libintl.h>
#include <libuutil.h>
#include "libzfs_impl.h"
typedef struct config_node {
char *cn_name;
nvlist_t *cn_config;
uu_avl_node_t cn_avl;
} config_node_t;
/* ARGSUSED */
static int
config_node_compare(const void *a, const void *b, void *unused)
{
int ret;
const config_node_t *ca = (config_node_t *)a;
const config_node_t *cb = (config_node_t *)b;
ret = strcmp(ca->cn_name, cb->cn_name);
if (ret < 0)
return (-1);
else if (ret > 0)
return (1);
else
return (0);
}
void
namespace_clear(libzfs_handle_t *hdl)
{
if (hdl->libzfs_ns_avl) {
uu_avl_walk_t *walk;
config_node_t *cn;
if ((walk = uu_avl_walk_start(hdl->libzfs_ns_avl,
UU_WALK_ROBUST)) == NULL)
return;
while ((cn = uu_avl_walk_next(walk)) != NULL) {
uu_avl_remove(hdl->libzfs_ns_avl, cn);
nvlist_free(cn->cn_config);
free(cn->cn_name);
free(cn);
}
uu_avl_walk_end(walk);
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 };
uu_avl_walk_t *walk;
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.
*/
if ((walk = uu_avl_walk_start(hdl->libzfs_ns_avl,
UU_WALK_ROBUST)) == NULL) {
nvlist_free(config);
return (no_memory(hdl));
}
while ((cn = uu_avl_walk_next(walk)) != NULL) {
uu_avl_remove(hdl->libzfs_ns_avl, cn);
nvlist_free(cn->cn_config);
free(cn->cn_name);
free(cn);
}
uu_avl_walk_end(walk);
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);
}
/*
* Retrive the configuration for the given pool. The configuration is a nvlist
* describing the vdevs, as well as the statistics associated with each one.
*/
nvlist_t *
zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
{
if (oldconfig)
*oldconfig = zhp->zpool_old_config;
return (zhp->zpool_config);
}
/*
* Refresh the vdev statistics associated with the given pool. This is used in
* iostat to show configuration changes and determine the delta from the last
* time the function was called. This function can fail, in case the pool has
* been destroyed.
*/
int
zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
{
zfs_cmd_t zc = { 0 };
int error;
nvlist_t *config;
libzfs_handle_t *hdl = zhp->zpool_hdl;
*missing = B_FALSE;
(void) strcpy(zc.zc_name, zhp->zpool_name);
if (zhp->zpool_config_size == 0)
zhp->zpool_config_size = 1 << 16;
if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0)
return (-1);
for (;;) {
if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_STATS,
&zc) == 0) {
/*
* The real error is returned in the zc_cookie field.
*/
error = zc.zc_cookie;
break;
}
if (errno == ENOMEM) {
if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
} else {
zcmd_free_nvlists(&zc);
if (errno == ENOENT || errno == EINVAL)
*missing = B_TRUE;
zhp->zpool_state = POOL_STATE_UNAVAIL;
return (0);
}
}
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
zcmd_free_nvlists(&zc);
zhp->zpool_config_size = zc.zc_nvlist_dst_size;
if (zhp->zpool_config != NULL) {
uint64_t oldtxg, newtxg;
verify(nvlist_lookup_uint64(zhp->zpool_config,
ZPOOL_CONFIG_POOL_TXG, &oldtxg) == 0);
verify(nvlist_lookup_uint64(config,
ZPOOL_CONFIG_POOL_TXG, &newtxg) == 0);
if (zhp->zpool_old_config != NULL)
nvlist_free(zhp->zpool_old_config);
if (oldtxg != newtxg) {
nvlist_free(zhp->zpool_config);
zhp->zpool_old_config = NULL;
} else {
zhp->zpool_old_config = zhp->zpool_config;
}
}
zhp->zpool_config = config;
if (error)
zhp->zpool_state = POOL_STATE_UNAVAIL;
else
zhp->zpool_state = POOL_STATE_ACTIVE;
return (0);
}
/*
* Iterate over all pools in the system.
*/
int
zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
{
config_node_t *cn;
zpool_handle_t *zhp;
int ret;
if (namespace_reload(hdl) != 0)
return (-1);
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0)
return (-1);
if (zhp == NULL)
continue;
if ((ret = func(zhp, data)) != 0)
return (ret);
}
return (0);
}
/*
* Iterate over root datasets, calling the given function for each. The zfs
* handle passed each time must be explicitly closed by the callback.
*/
int
zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
{
config_node_t *cn;
zfs_handle_t *zhp;
int ret;
if (namespace_reload(hdl) != 0)
return (-1);
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
continue;
if ((ret = func(zhp, data)) != 0)
return (ret);
}
return (0);
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,171 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBFS_IMPL_H
#define _LIBFS_IMPL_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/dmu.h>
#include <sys/fs/zfs.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_acl.h>
#include <sys/nvpair.h>
#include <libuutil.h>
#include <libzfs.h>
#ifdef __cplusplus
extern "C" {
#endif
struct libzfs_handle {
int libzfs_error;
int libzfs_fd;
FILE *libzfs_mnttab;
FILE *libzfs_sharetab;
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;
};
struct zfs_handle {
libzfs_handle_t *zfs_hdl;
char zfs_name[ZFS_MAXNAMELEN];
zfs_type_t zfs_type; /* type including snapshot */
zfs_type_t zfs_head_type; /* type excluding snapshot */
dmu_objset_stats_t zfs_dmustats;
nvlist_t *zfs_props;
nvlist_t *zfs_user_props;
boolean_t zfs_mntcheck;
char *zfs_mntopts;
char zfs_root[MAXPATHLEN];
};
/*
* 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;
char zpool_name[ZPOOL_MAXNAMELEN];
int zpool_state;
size_t zpool_config_size;
nvlist_t *zpool_config;
nvlist_t *zpool_old_config;
nvlist_t *zpool_props;
};
int zfs_error(libzfs_handle_t *, int, const char *);
int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
void zfs_error_aux(libzfs_handle_t *, const char *, ...);
void *zfs_alloc(libzfs_handle_t *, size_t);
void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t);
char *zfs_strdup(libzfs_handle_t *, const char *);
int no_memory(libzfs_handle_t *);
int zfs_standard_error(libzfs_handle_t *, int, const char *);
int zfs_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int zpool_standard_error(libzfs_handle_t *, int, const char *);
int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
int zfs_expand_proplist_common(libzfs_handle_t *, zfs_proplist_t **,
zfs_type_t);
int zfs_get_proplist_common(libzfs_handle_t *, char *, zfs_proplist_t **,
zfs_type_t);
zfs_prop_t zfs_prop_iter_common(zfs_prop_f, void *, zfs_type_t, boolean_t);
zfs_prop_t zfs_name_to_prop_common(const char *, zfs_type_t);
nvlist_t *zfs_validate_properties(libzfs_handle_t *, zfs_type_t, char *,
nvlist_t *, uint64_t, zfs_handle_t *zhp, const char *errbuf);
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 *, size_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(zfs_handle_t *, prop_changelist_t *);
void changelist_free(prop_changelist_t *);
prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int);
int changelist_unshare(prop_changelist_t *);
int changelist_haszonedchild(prop_changelist_t *);
void remove_mountpoint(zfs_handle_t *);
zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
int zvol_create_link(libzfs_handle_t *, const char *);
int zvol_remove_link(libzfs_handle_t *, const char *);
int zpool_iter_zvol(zpool_handle_t *, int (*)(const char *, void *), void *);
void namespace_clear(libzfs_handle_t *);
#ifdef __FreeBSD__
/*
* 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.
*/
static __inline int
zcmd_ioctl(int fd, unsigned long cmd, zfs_cmd_t *zc)
{
size_t oldsize;
int ret;
oldsize = zc->zc_nvlist_dst_size;
ret = ioctl(fd, cmd, zc);
if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) {
ret = -1;
errno = ENOMEM;
}
return (ret);
}
#define ioctl(fd, cmd, zc) zcmd_ioctl((fd), (cmd), (zc))
#endif
#ifdef __cplusplus
}
#endif
#endif /* _LIBFS_IMPL_H */

View File

@ -0,0 +1,994 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Pool import support functions.
*
* To import a pool, we rely on reading the configuration information from the
* ZFS label of each device. If we successfully read the label, then we
* organize the configuration information in the following hierarchy:
*
* pool guid -> toplevel vdev guid -> label txg
*
* Duplicate entries matching this same tuple will be discarded. Once we have
* examined every device, we pick the best label txg config for each toplevel
* vdev. We then arrange these toplevel vdevs into a complete pool config, and
* update any paths that have changed. Finally, we attempt to import the pool
* using our derived config, and record the results.
*/
#include <devid.h>
#include <dirent.h>
#include <errno.h>
#include <libintl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgeom.h>
#include <sys/vdev_impl.h>
#include "libzfs.h"
#include "libzfs_impl.h"
/*
* Intermediate structures used to gather configuration information.
*/
typedef struct config_entry {
uint64_t ce_txg;
nvlist_t *ce_config;
struct config_entry *ce_next;
} config_entry_t;
typedef struct vdev_entry {
uint64_t ve_guid;
config_entry_t *ve_configs;
struct vdev_entry *ve_next;
} vdev_entry_t;
typedef struct pool_entry {
uint64_t pe_guid;
vdev_entry_t *pe_vdevs;
struct pool_entry *pe_next;
} pool_entry_t;
typedef struct name_entry {
char *ne_name;
uint64_t ne_guid;
struct name_entry *ne_next;
} name_entry_t;
typedef struct pool_list {
pool_entry_t *pools;
name_entry_t *names;
} pool_list_t;
static char *
get_devid(const char *path)
{
int fd;
ddi_devid_t devid;
char *minor, *ret;
if ((fd = open(path, O_RDONLY)) < 0)
return (NULL);
minor = NULL;
ret = NULL;
if (devid_get(fd, &devid) == 0) {
if (devid_get_minor_name(fd, &minor) == 0)
ret = devid_str_encode(devid, minor);
if (minor != NULL)
devid_str_free(minor);
devid_free(devid);
}
(void) close(fd);
return (ret);
}
/*
* Go through and fix up any path and/or devid information for the given vdev
* configuration.
*/
static int
fix_paths(nvlist_t *nv, name_entry_t *names)
{
nvlist_t **child;
uint_t c, children;
uint64_t guid;
name_entry_t *ne, *best;
char *path, *devid;
int matched;
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) == 0) {
for (c = 0; c < children; c++)
if (fix_paths(child[c], names) != 0)
return (-1);
return (0);
}
/*
* This is a leaf (file or disk) vdev. In either case, go through
* the name list and see if we find a matching guid. If so, replace
* the path and see if we can calculate a new devid.
*
* There may be multiple names associated with a particular guid, in
* which case we have overlapping slices or multiple paths to the same
* disk. If this is the case, then we want to pick the path that is
* the most similar to the original, where "most similar" is the number
* of matching characters starting from the end of the path. This will
* preserve slice numbers even if the disks have been reorganized, and
* will also catch preferred disk names if multiple paths exist.
*/
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
path = NULL;
matched = 0;
best = NULL;
for (ne = names; ne != NULL; ne = ne->ne_next) {
if (ne->ne_guid == guid) {
const char *src, *dst;
int count;
if (path == NULL) {
best = ne;
break;
}
src = ne->ne_name + strlen(ne->ne_name) - 1;
dst = path + strlen(path) - 1;
for (count = 0; src >= ne->ne_name && dst >= path;
src--, dst--, count++)
if (*src != *dst)
break;
/*
* At this point, 'count' is the number of characters
* matched from the end.
*/
if (count > matched || best == NULL) {
best = ne;
matched = count;
}
}
}
if (best == NULL)
return (0);
if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0)
return (-1);
if ((devid = get_devid(best->ne_name)) == NULL) {
(void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
} else {
if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0)
return (-1);
devid_str_free(devid);
}
return (0);
}
/*
* Add the given configuration to the list of known devices.
*/
static int
add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path,
nvlist_t *config)
{
uint64_t pool_guid, vdev_guid, top_guid, txg, state;
pool_entry_t *pe;
vdev_entry_t *ve;
config_entry_t *ce;
name_entry_t *ne;
/*
* If this is a hot spare not currently in use, add it to the list of
* names to translate, but don't do anything else.
*/
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
&state) == 0 && state == POOL_STATE_SPARE &&
nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) {
if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
return (-1);
if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) {
free(ne);
return (-1);
}
ne->ne_guid = vdev_guid;
ne->ne_next = pl->names;
pl->names = ne;
return (0);
}
/*
* If we have a valid config but cannot read any of these fields, then
* it means we have a half-initialized label. In vdev_label_init()
* we write a label with txg == 0 so that we can identify the device
* in case the user refers to the same disk later on. If we fail to
* create the pool, we'll be left with a label in this state
* which should not be considered part of a valid pool.
*/
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&pool_guid) != 0 ||
nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
&vdev_guid) != 0 ||
nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID,
&top_guid) != 0 ||
nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
&txg) != 0 || txg == 0) {
nvlist_free(config);
return (0);
}
/*
* First, see if we know about this pool. If not, then add it to the
* list of known pools.
*/
for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
if (pe->pe_guid == pool_guid)
break;
}
if (pe == NULL) {
if ((pe = zfs_alloc(hdl, sizeof (pool_entry_t))) == NULL) {
nvlist_free(config);
return (-1);
}
pe->pe_guid = pool_guid;
pe->pe_next = pl->pools;
pl->pools = pe;
}
/*
* Second, see if we know about this toplevel vdev. Add it if its
* missing.
*/
for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
if (ve->ve_guid == top_guid)
break;
}
if (ve == NULL) {
if ((ve = zfs_alloc(hdl, sizeof (vdev_entry_t))) == NULL) {
nvlist_free(config);
return (-1);
}
ve->ve_guid = top_guid;
ve->ve_next = pe->pe_vdevs;
pe->pe_vdevs = ve;
}
/*
* Third, see if we have a config with a matching transaction group. If
* so, then we do nothing. Otherwise, add it to the list of known
* configs.
*/
for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) {
if (ce->ce_txg == txg)
break;
}
if (ce == NULL) {
if ((ce = zfs_alloc(hdl, sizeof (config_entry_t))) == NULL) {
nvlist_free(config);
return (-1);
}
ce->ce_txg = txg;
ce->ce_config = config;
ce->ce_next = ve->ve_configs;
ve->ve_configs = ce;
} else {
nvlist_free(config);
}
/*
* At this point we've successfully added our config to the list of
* known configs. The last thing to do is add the vdev guid -> path
* mappings so that we can fix up the configuration as necessary before
* doing the import.
*/
if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
return (-1);
if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) {
free(ne);
return (-1);
}
ne->ne_guid = vdev_guid;
ne->ne_next = pl->names;
pl->names = ne;
return (0);
}
/*
* Returns true if the named pool matches the given GUID.
*/
static int
pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid,
boolean_t *isactive)
{
zpool_handle_t *zhp;
uint64_t theguid;
if (zpool_open_silent(hdl, name, &zhp) != 0)
return (-1);
if (zhp == NULL) {
*isactive = B_FALSE;
return (0);
}
verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID,
&theguid) == 0);
zpool_close(zhp);
*isactive = (theguid == guid);
return (0);
}
/*
* Convert our list of pools into the definitive set of configurations. We
* start by picking the best config for each toplevel vdev. Once that's done,
* we assemble the toplevel vdevs into a full config for the pool. We make a
* pass to fix up any incorrect paths, and then add it to the main list to
* return to the user.
*/
static nvlist_t *
get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
{
pool_entry_t *pe;
vdev_entry_t *ve;
config_entry_t *ce;
nvlist_t *ret = NULL, *config = NULL, *tmp, *nvtop, *nvroot;
nvlist_t **spares;
uint_t i, nspares;
boolean_t config_seen;
uint64_t best_txg;
char *name;
zfs_cmd_t zc = { 0 };
uint64_t version, guid;
size_t len;
int err;
uint_t children = 0;
nvlist_t **child = NULL;
uint_t c;
boolean_t isactive;
if (nvlist_alloc(&ret, 0, 0) != 0)
goto nomem;
for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
uint64_t id;
if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0)
goto nomem;
config_seen = B_FALSE;
/*
* Iterate over all toplevel vdevs. Grab the pool configuration
* from the first one we find, and then go through the rest and
* add them as necessary to the 'vdevs' member of the config.
*/
for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
/*
* Determine the best configuration for this vdev by
* selecting the config with the latest transaction
* group.
*/
best_txg = 0;
for (ce = ve->ve_configs; ce != NULL;
ce = ce->ce_next) {
if (ce->ce_txg > best_txg) {
tmp = ce->ce_config;
best_txg = ce->ce_txg;
}
}
if (!config_seen) {
/*
* Copy the relevant pieces of data to the pool
* configuration:
*
* version
* pool guid
* name
* pool state
*/
uint64_t state;
verify(nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_VERSION, &version) == 0);
if (nvlist_add_uint64(config,
ZPOOL_CONFIG_VERSION, version) != 0)
goto nomem;
verify(nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_POOL_GUID, &guid) == 0);
if (nvlist_add_uint64(config,
ZPOOL_CONFIG_POOL_GUID, guid) != 0)
goto nomem;
verify(nvlist_lookup_string(tmp,
ZPOOL_CONFIG_POOL_NAME, &name) == 0);
if (nvlist_add_string(config,
ZPOOL_CONFIG_POOL_NAME, name) != 0)
goto nomem;
verify(nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
if (nvlist_add_uint64(config,
ZPOOL_CONFIG_POOL_STATE, state) != 0)
goto nomem;
config_seen = B_TRUE;
}
/*
* Add this top-level vdev to the child array.
*/
verify(nvlist_lookup_nvlist(tmp,
ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0);
verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID,
&id) == 0);
if (id >= children) {
nvlist_t **newchild;
newchild = zfs_alloc(hdl, (id + 1) *
sizeof (nvlist_t *));
if (newchild == NULL)
goto nomem;
for (c = 0; c < children; c++)
newchild[c] = child[c];
free(child);
child = newchild;
children = id + 1;
}
if (nvlist_dup(nvtop, &child[id], 0) != 0)
goto nomem;
}
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&guid) == 0);
/*
* Look for any missing top-level vdevs. If this is the case,
* create a faked up 'missing' vdev as a placeholder. We cannot
* simply compress the child array, because the kernel performs
* certain checks to make sure the vdev IDs match their location
* in the configuration.
*/
for (c = 0; c < children; c++)
if (child[c] == NULL) {
nvlist_t *missing;
if (nvlist_alloc(&missing, NV_UNIQUE_NAME,
0) != 0)
goto nomem;
if (nvlist_add_string(missing,
ZPOOL_CONFIG_TYPE,
VDEV_TYPE_MISSING) != 0 ||
nvlist_add_uint64(missing,
ZPOOL_CONFIG_ID, c) != 0 ||
nvlist_add_uint64(missing,
ZPOOL_CONFIG_GUID, 0ULL) != 0) {
nvlist_free(missing);
goto nomem;
}
child[c] = missing;
}
/*
* Put all of this pool's top-level vdevs into a root vdev.
*/
if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0)
goto nomem;
if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
VDEV_TYPE_ROOT) != 0 ||
nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 ||
nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 ||
nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
child, children) != 0) {
nvlist_free(nvroot);
goto nomem;
}
for (c = 0; c < children; c++)
nvlist_free(child[c]);
free(child);
children = 0;
child = NULL;
/*
* Go through and fix up any paths and/or devids based on our
* known list of vdev GUID -> path mappings.
*/
if (fix_paths(nvroot, pl->names) != 0) {
nvlist_free(nvroot);
goto nomem;
}
/*
* Add the root vdev to this pool's configuration.
*/
if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
nvroot) != 0) {
nvlist_free(nvroot);
goto nomem;
}
nvlist_free(nvroot);
/*
* Determine if this pool is currently active, in which case we
* can't actually import it.
*/
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&guid) == 0);
if (pool_active(hdl, name, guid, &isactive) != 0)
goto error;
if (isactive) {
nvlist_free(config);
config = NULL;
continue;
}
/*
* Try to do the import in order to get vdev state.
*/
if (zcmd_write_src_nvlist(hdl, &zc, config, &len) != 0)
goto error;
nvlist_free(config);
config = NULL;
if (zcmd_alloc_dst_nvlist(hdl, &zc, len * 2) != 0) {
zcmd_free_nvlists(&zc);
goto error;
}
while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT,
&zc)) != 0 && errno == ENOMEM) {
if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
zcmd_free_nvlists(&zc);
goto error;
}
}
if (err) {
(void) zpool_standard_error(hdl, errno,
dgettext(TEXT_DOMAIN, "cannot discover pools"));
zcmd_free_nvlists(&zc);
goto error;
}
if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
zcmd_free_nvlists(&zc);
goto error;
}
zcmd_free_nvlists(&zc);
/*
* Go through and update the paths for spares, now that we have
* them.
*/
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
for (i = 0; i < nspares; i++) {
if (fix_paths(spares[i], pl->names) != 0)
goto nomem;
}
}
/*
* Add this pool to the list of configs.
*/
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
if (nvlist_add_nvlist(ret, name, config) != 0)
goto nomem;
nvlist_free(config);
config = NULL;
}
return (ret);
nomem:
(void) no_memory(hdl);
error:
nvlist_free(config);
nvlist_free(ret);
for (c = 0; c < children; c++)
nvlist_free(child[c]);
free(child);
return (NULL);
}
/*
* Return the offset of the given label.
*/
static uint64_t
label_offset(size_t size, int l)
{
return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ?
0 : size - VDEV_LABELS * sizeof (vdev_label_t)));
}
/*
* Given a file descriptor, read the label information and return an nvlist
* describing the configuration, if there is one.
*/
int
zpool_read_label(int fd, nvlist_t **config)
{
struct stat64 statbuf;
int l;
vdev_label_t *label;
uint64_t state, txg;
*config = NULL;
if (fstat64(fd, &statbuf) == -1)
return (0);
if ((label = malloc(sizeof (vdev_label_t))) == NULL)
return (-1);
for (l = 0; l < VDEV_LABELS; l++) {
if (pread(fd, label, sizeof (vdev_label_t),
label_offset(statbuf.st_size, l)) != sizeof (vdev_label_t))
continue;
if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0)
continue;
if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
&state) != 0 || state > POOL_STATE_SPARE) {
nvlist_free(*config);
continue;
}
if (state != POOL_STATE_SPARE &&
(nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
&txg) != 0 || txg == 0)) {
nvlist_free(*config);
continue;
}
free(label);
return (0);
}
free(label);
*config = NULL;
return (0);
}
/*
* Given a list of directories to search, find all pools stored on disk. This
* includes partial pools which are not available to import. If no args are
* given (argc is 0), then the default directory (/dev) is searched.
*/
nvlist_t *
zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
{
int i;
DIR *dirp;
char path[MAXPATHLEN];
nvlist_t *ret = NULL, *config;
int fd;
pool_list_t pools = { 0 };
pool_entry_t *pe, *penext;
vdev_entry_t *ve, *venext;
config_entry_t *ce, *cenext;
name_entry_t *ne, *nenext;
struct gmesh mesh;
struct gclass *mp;
struct ggeom *gp;
struct gprovider *pp;
/*
* Go through and read the label configuration information from every
* possible device, organizing the information according to pool GUID
* and toplevel GUID.
*/
fd = geom_gettree(&mesh);
assert(fd == 0);
LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
(void) snprintf(path, sizeof (path), "%s%s",
_PATH_DEV, pp->lg_name);
if ((fd = open64(path, O_RDONLY)) < 0)
continue;
if ((zpool_read_label(fd, &config)) != 0) {
(void) no_memory(hdl);
goto error;
}
(void) close(fd);
if (config == NULL)
continue;
if (add_config(hdl, &pools, path, config) != 0)
goto error;
}
}
}
geom_deletetree(&mesh);
ret = get_configs(hdl, &pools);
error:
for (pe = pools.pools; pe != NULL; pe = penext) {
penext = pe->pe_next;
for (ve = pe->pe_vdevs; ve != NULL; ve = venext) {
venext = ve->ve_next;
for (ce = ve->ve_configs; ce != NULL; ce = cenext) {
cenext = ce->ce_next;
if (ce->ce_config)
nvlist_free(ce->ce_config);
free(ce);
}
free(ve);
}
free(pe);
}
for (ne = pools.names; ne != NULL; ne = nenext) {
nenext = ne->ne_next;
if (ne->ne_name)
free(ne->ne_name);
free(ne);
}
return (ret);
}
boolean_t
find_guid(nvlist_t *nv, uint64_t guid)
{
uint64_t tmp;
nvlist_t **child;
uint_t c, children;
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &tmp) == 0);
if (tmp == guid)
return (B_TRUE);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) == 0) {
for (c = 0; c < children; c++)
if (find_guid(child[c], guid))
return (B_TRUE);
}
return (B_FALSE);
}
typedef struct spare_cbdata {
uint64_t cb_guid;
zpool_handle_t *cb_zhp;
} spare_cbdata_t;
static int
find_spare(zpool_handle_t *zhp, void *data)
{
spare_cbdata_t *cbp = data;
nvlist_t **spares;
uint_t i, nspares;
uint64_t guid;
nvlist_t *nvroot;
verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
for (i = 0; i < nspares; i++) {
verify(nvlist_lookup_uint64(spares[i],
ZPOOL_CONFIG_GUID, &guid) == 0);
if (guid == cbp->cb_guid) {
cbp->cb_zhp = zhp;
return (1);
}
}
}
zpool_close(zhp);
return (0);
}
/*
* Determines if the pool is in use. If so, it returns true and the state of
* the pool as well as the name of the pool. Both strings are allocated and
* must be freed by the caller.
*/
int
zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
boolean_t *inuse)
{
nvlist_t *config;
char *name;
boolean_t ret;
uint64_t guid, vdev_guid;
zpool_handle_t *zhp;
nvlist_t *pool_config;
uint64_t stateval, isspare;
spare_cbdata_t cb = { 0 };
boolean_t isactive;
*inuse = B_FALSE;
if (zpool_read_label(fd, &config) != 0) {
(void) no_memory(hdl);
return (-1);
}
if (config == NULL)
return (0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
&stateval) == 0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
&vdev_guid) == 0);
if (stateval != POOL_STATE_SPARE) {
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&guid) == 0);
}
switch (stateval) {
case POOL_STATE_EXPORTED:
ret = B_TRUE;
break;
case POOL_STATE_ACTIVE:
/*
* For an active pool, we have to determine if it's really part
* of a currently active pool (in which case the pool will exist
* and the guid will be the same), or whether it's part of an
* active pool that was disconnected without being explicitly
* exported.
*/
if (pool_active(hdl, name, guid, &isactive) != 0) {
nvlist_free(config);
return (-1);
}
if (isactive) {
/*
* Because the device may have been removed while
* offlined, we only report it as active if the vdev is
* still present in the config. Otherwise, pretend like
* it's not in use.
*/
if ((zhp = zpool_open_canfail(hdl, name)) != NULL &&
(pool_config = zpool_get_config(zhp, NULL))
!= NULL) {
nvlist_t *nvroot;
verify(nvlist_lookup_nvlist(pool_config,
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
ret = find_guid(nvroot, vdev_guid);
} else {
ret = B_FALSE;
}
/*
* If this is an active spare within another pool, we
* treat it like an unused hot spare. This allows the
* user to create a pool with a hot spare that currently
* in use within another pool. Since we return B_TRUE,
* libdiskmgt will continue to prevent generic consumers
* from using the device.
*/
if (ret && nvlist_lookup_uint64(config,
ZPOOL_CONFIG_IS_SPARE, &isspare) == 0 && isspare)
stateval = POOL_STATE_SPARE;
if (zhp != NULL)
zpool_close(zhp);
} else {
stateval = POOL_STATE_POTENTIALLY_ACTIVE;
ret = B_TRUE;
}
break;
case POOL_STATE_SPARE:
/*
* For a hot spare, it can be either definitively in use, or
* potentially active. To determine if it's in use, we iterate
* over all pools in the system and search for one with a spare
* with a matching guid.
*
* Due to the shared nature of spares, we don't actually report
* the potentially active case as in use. This means the user
* can freely create pools on the hot spares of exported pools,
* but to do otherwise makes the resulting code complicated, and
* we end up having to deal with this case anyway.
*/
cb.cb_zhp = NULL;
cb.cb_guid = vdev_guid;
if (zpool_iter(hdl, find_spare, &cb) == 1) {
name = (char *)zpool_get_name(cb.cb_zhp);
ret = TRUE;
} else {
ret = FALSE;
}
break;
default:
ret = B_FALSE;
}
if (ret) {
if ((*namestr = zfs_strdup(hdl, name)) == NULL) {
nvlist_free(config);
return (-1);
}
*state = (pool_state_t)stateval;
}
if (cb.cb_zhp)
zpool_close(cb.cb_zhp);
nvlist_free(config);
*inuse = ret;
return (0);
}

View File

@ -0,0 +1,986 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Routines to manage ZFS mounts. We separate all the nasty routines that have
* to deal with the OS. The following functions are the main entry points --
* they are used by mount and unmount and when changing a filesystem's
* mountpoint.
*
* zfs_is_mounted()
* zfs_mount()
* zfs_unmount()
* zfs_unmountall()
*
* This file also contains the functions used to manage sharing filesystems via
* NFS and iSCSI:
*
* zfs_is_shared()
* zfs_share()
* zfs_unshare()
*
* zfs_is_shared_nfs()
* zfs_share_nfs()
* zfs_unshare_nfs()
* zfs_unshareall_nfs()
* zfs_is_shared_iscsi()
* zfs_share_iscsi()
* zfs_unshare_iscsi()
*
* The following functions are available for pool consumers, and will
* mount/unmount and share/unshare all datasets within pool:
*
* zpool_enable_datasets()
* zpool_disable_datasets()
*/
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <zone.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <libzfs.h>
#include "libzfs_impl.h"
static int (*iscsitgt_zfs_share)(const char *);
static int (*iscsitgt_zfs_unshare)(const char *);
static int (*iscsitgt_zfs_is_shared)(const char *);
#pragma init(zfs_iscsi_init)
static void
zfs_iscsi_init(void)
{
void *libiscsitgt;
if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1",
RTLD_LAZY | RTLD_GLOBAL)) == NULL ||
(iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt,
"iscsitgt_zfs_share")) == NULL ||
(iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt,
"iscsitgt_zfs_unshare")) == NULL ||
(iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt,
"iscsitgt_zfs_is_shared")) == NULL) {
iscsitgt_zfs_share = NULL;
iscsitgt_zfs_unshare = NULL;
iscsitgt_zfs_is_shared = NULL;
}
}
/*
* Search the sharetab for the given mountpoint, returning true if it is found.
*/
static boolean_t
is_shared(libzfs_handle_t *hdl, const char *mountpoint)
{
char buf[MAXPATHLEN], *tab;
if (hdl->libzfs_sharetab == NULL)
return (0);
(void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
/* the mountpoint is the first entry on each line */
if ((tab = strchr(buf, '\t')) != NULL) {
*tab = '\0';
if (strcmp(buf, mountpoint) == 0)
return (B_TRUE);
}
}
return (B_FALSE);
}
#if 0
/*
* Returns true if the specified directory is empty. If we can't open the
* directory at all, return true so that the mount can fail with a more
* informative error message.
*/
static boolean_t
dir_is_empty(const char *dirname)
{
DIR *dirp;
struct dirent64 *dp;
if ((dirp = opendir(dirname)) == NULL)
return (B_TRUE);
while ((dp = readdir64(dirp)) != NULL) {
if (strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, "..") == 0)
continue;
(void) closedir(dirp);
return (B_FALSE);
}
(void) closedir(dirp);
return (B_TRUE);
}
#endif
/*
* Checks to see if the mount is active. If the filesystem is mounted, we fill
* in 'where' with the current mountpoint, and return 1. Otherwise, we return
* 0.
*/
boolean_t
is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where)
{
struct mnttab search = { 0 }, entry;
/*
* Search for the entry in /etc/mnttab. We don't bother getting the
* mountpoint, as we can just search for the special device. This will
* also let us find mounts when the mountpoint is 'legacy'.
*/
search.mnt_special = (char *)special;
search.mnt_fstype = MNTTYPE_ZFS;
rewind(zfs_hdl->libzfs_mnttab);
if (getmntany(zfs_hdl->libzfs_mnttab, &entry, &search) != 0)
return (B_FALSE);
if (where != NULL)
*where = zfs_strdup(zfs_hdl, entry.mnt_mountp);
return (B_TRUE);
}
boolean_t
zfs_is_mounted(zfs_handle_t *zhp, char **where)
{
return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where));
}
/*
* Returns true if the given dataset is mountable, false otherwise. Returns the
* mountpoint in 'buf'.
*/
static boolean_t
zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
zfs_source_t *source)
{
char sourceloc[ZFS_MAXNAMELEN];
zfs_source_t sourcetype;
if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type))
return (B_FALSE);
verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen,
&sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0);
if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 ||
strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
return (B_FALSE);
if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT))
return (B_FALSE);
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
getzoneid() == GLOBAL_ZONEID)
return (B_FALSE);
if (source)
*source = sourcetype;
return (B_TRUE);
}
/*
* Mount the given filesystem.
*/
int
zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
{
struct stat buf;
char mountpoint[ZFS_MAXPROPLEN];
char mntopts[MNT_LINE_MAX];
libzfs_handle_t *hdl = zhp->zfs_hdl;
if (options == NULL)
mntopts[0] = '\0';
else
(void) strlcpy(mntopts, options, sizeof (mntopts));
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
return (0);
/* Create the directory if it doesn't already exist */
if (lstat(mountpoint, &buf) != 0) {
if (mkdirp(mountpoint, 0755) != 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"failed to create mountpoint"));
return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
mountpoint));
}
}
#if 0 /* FreeBSD: overlay mounts are not checked. */
/*
* Determine if the mountpoint is empty. If so, refuse to perform the
* mount. We don't perform this check if MS_OVERLAY is specified, which
* would defeat the point. We also avoid this check if 'remount' is
* specified.
*/
if ((flags & MS_OVERLAY) == 0 &&
strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
!dir_is_empty(mountpoint)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"directory is not empty"));
return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
}
#endif
/* perform the mount */
if (zmount(zfs_get_name(zhp), mountpoint, flags,
MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
/*
* Generic errors are nasty, but there are just way too many
* from mount(), and they're well-understood. We pick a few
* common ones to improve upon.
*/
if (errno == EBUSY)
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"mountpoint or dataset is busy"));
else
zfs_error_aux(hdl, strerror(errno));
return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
zhp->zfs_name));
}
return (0);
}
/*
* Unmount a single filesystem.
*/
static int
unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
{
if (unmount(mountpoint, flags) != 0) {
zfs_error_aux(hdl, strerror(errno));
return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
mountpoint));
}
return (0);
}
/*
* Unmount the given filesystem.
*/
int
zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
{
struct mnttab search = { 0 }, entry;
/* check to see if need to unmount the filesystem */
search.mnt_special = zhp->zfs_name;
search.mnt_fstype = MNTTYPE_ZFS;
rewind(zhp->zfs_hdl->libzfs_mnttab);
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
if (mountpoint == NULL)
mountpoint = entry.mnt_mountp;
/*
* Unshare and unmount the filesystem
*/
if (zfs_unshare_nfs(zhp, mountpoint) != 0 ||
unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0)
return (-1);
}
return (0);
}
/*
* Unmount this filesystem and any children inheriting the mountpoint property.
* To do this, just act like we're changing the mountpoint property, but don't
* remount the filesystems afterwards.
*/
int
zfs_unmountall(zfs_handle_t *zhp, int flags)
{
prop_changelist_t *clp;
int ret;
clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags);
if (clp == NULL)
return (-1);
ret = changelist_prefix(clp);
changelist_free(clp);
return (ret);
}
boolean_t
zfs_is_shared(zfs_handle_t *zhp)
{
if (ZFS_IS_VOLUME(zhp))
return (zfs_is_shared_iscsi(zhp));
return (zfs_is_shared_nfs(zhp, NULL));
}
int
zfs_share(zfs_handle_t *zhp)
{
if (ZFS_IS_VOLUME(zhp))
return (zfs_share_iscsi(zhp));
return (zfs_share_nfs(zhp));
}
int
zfs_unshare(zfs_handle_t *zhp)
{
if (ZFS_IS_VOLUME(zhp))
return (zfs_unshare_iscsi(zhp));
return (zfs_unshare_nfs(zhp, NULL));
}
/*
* Check to see if the filesystem is currently shared.
*/
boolean_t
zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
{
char *mountpoint;
if (!zfs_is_mounted(zhp, &mountpoint))
return (B_FALSE);
if (is_shared(zhp->zfs_hdl, mountpoint)) {
if (where != NULL)
*where = mountpoint;
else
free(mountpoint);
return (B_TRUE);
} else {
free(mountpoint);
return (B_FALSE);
}
}
/*
* Share the given filesystem according to the options in 'sharenfs'. We rely
* on share(1M) to the dirty work for us.
*/
int
zfs_share_nfs(zfs_handle_t *zhp)
{
char mountpoint[ZFS_MAXPROPLEN];
char shareopts[ZFS_MAXPROPLEN];
char buf[MAXPATHLEN];
FILE *fp;
libzfs_handle_t *hdl = zhp->zfs_hdl;
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
return (0);
/*
* Return success if there are no share options.
*/
if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
NULL, NULL, 0, B_FALSE) != 0 ||
strcmp(shareopts, "off") == 0)
return (0);
/*
* If the 'zoned' property is set, then zfs_is_mountable() will have
* already bailed out if we are in the global zone. But local
* zones cannot be NFS servers, so we ignore it for local zones as well.
*/
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
return (0);
#ifdef __FreeBSD__
{
int error;
if (strcmp(shareopts, "on") == 0)
error = fsshare(ZFS_EXPORTS_PATH, mountpoint, "");
else
error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts);
if (error != 0) {
zfs_error_aux(hdl, "%s", strerror(error));
(void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
zfs_get_name(zhp));
return (-1);
}
}
#else
/*
* Invoke the share(1M) command. We always do this, even if it's
* currently shared, as the options may have changed.
*/
if (strcmp(shareopts, "on") == 0)
(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
"-F nfs \"%s\" 2>&1", mountpoint);
else
(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
"-F nfs -o \"%s\" \"%s\" 2>&1", shareopts,
mountpoint);
if ((fp = popen(buf, "r")) == NULL)
return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
zfs_get_name(zhp)));
/*
* share(1M) should only produce output if there is some kind
* of error. All output begins with "share_nfs: ", so we trim
* this off to get to the real error.
*/
if (fgets(buf, sizeof (buf), fp) != NULL) {
char *colon = strchr(buf, ':');
while (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
if (colon != NULL)
zfs_error_aux(hdl, colon + 2);
(void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
zfs_get_name(zhp));
verify(pclose(fp) != 0);
return (-1);
}
verify(pclose(fp) == 0);
#endif
return (0);
}
/*
* Unshare a filesystem by mountpoint.
*/
static int
unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
{
char buf[MAXPATHLEN];
FILE *fp;
#ifdef __FreeBSD__
{
int error;
error = fsunshare(ZFS_EXPORTS_PATH, mountpoint);
if (error != 0) {
zfs_error_aux(hdl, "%s", strerror(error));
return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
dgettext(TEXT_DOMAIN,
"cannot unshare '%s'"), name));
}
}
#else
(void) snprintf(buf, sizeof (buf),
"/usr/sbin/unshare \"%s\" 2>&1",
mountpoint);
if ((fp = popen(buf, "r")) == NULL)
return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
dgettext(TEXT_DOMAIN,
"cannot unshare '%s'"), name));
/*
* unshare(1M) should only produce output if there is
* some kind of error. All output begins with "unshare
* nfs: ", so we trim this off to get to the real error.
*/
if (fgets(buf, sizeof (buf), fp) != NULL) {
char *colon = strchr(buf, ':');
while (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
if (colon != NULL)
zfs_error_aux(hdl, colon + 2);
verify(pclose(fp) != 0);
return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
dgettext(TEXT_DOMAIN,
"cannot unshare '%s'"), name));
}
verify(pclose(fp) == 0);
#endif
return (0);
}
/*
* Unshare the given filesystem.
*/
int
zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
{
struct mnttab search = { 0 }, entry;
/* check to see if need to unmount the filesystem */
search.mnt_special = (char *)zfs_get_name(zhp);
search.mnt_fstype = MNTTYPE_ZFS;
rewind(zhp->zfs_hdl->libzfs_mnttab);
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
if (mountpoint == NULL)
mountpoint = entry.mnt_mountp;
if (is_shared(zhp->zfs_hdl, mountpoint) &&
unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0)
return (-1);
}
return (0);
}
/*
* Same as zfs_unmountall(), but for NFS unshares.
*/
int
zfs_unshareall_nfs(zfs_handle_t *zhp)
{
prop_changelist_t *clp;
int ret;
clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0);
if (clp == NULL)
return (-1);
ret = changelist_unshare(clp);
changelist_free(clp);
return (ret);
}
/*
* Remove the mountpoint associated with the current dataset, if necessary.
* We only remove the underlying directory if:
*
* - The mountpoint is not 'none' or 'legacy'
* - The mountpoint is non-empty
* - The mountpoint is the default or inherited
* - The 'zoned' property is set, or we're in a local zone
*
* Any other directories we leave alone.
*/
void
remove_mountpoint(zfs_handle_t *zhp)
{
char mountpoint[ZFS_MAXPROPLEN];
zfs_source_t source;
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
&source))
return;
if (source == ZFS_SRC_DEFAULT ||
source == ZFS_SRC_INHERITED) {
/*
* Try to remove the directory, silently ignoring any errors.
* The filesystem may have since been removed or moved around,
* and this error isn't really useful to the administrator in
* any way.
*/
(void) rmdir(mountpoint);
}
}
boolean_t
zfs_is_shared_iscsi(zfs_handle_t *zhp)
{
return (iscsitgt_zfs_is_shared != NULL &&
iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
}
int
zfs_share_iscsi(zfs_handle_t *zhp)
{
char shareopts[ZFS_MAXPROPLEN];
const char *dataset = zhp->zfs_name;
libzfs_handle_t *hdl = zhp->zfs_hdl;
/*
* Return success if there are no share options.
*/
if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts,
sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 ||
strcmp(shareopts, "off") == 0)
return (0);
/* We don't support iSCSI on FreeBSD yet. */
#ifdef TODO
if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0)
return (zfs_error_fmt(hdl, EZFS_SHAREISCSIFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset));
#endif
return (0);
}
int
zfs_unshare_iscsi(zfs_handle_t *zhp)
{
const char *dataset = zfs_get_name(zhp);
libzfs_handle_t *hdl = zhp->zfs_hdl;
/* We don't support iSCSI on FreeBSD yet. */
#ifdef TODO
/*
* Return if the volume is not shared
*/
if (!zfs_is_shared_iscsi(zhp))
return (0);
/*
* If this fails with ENODEV it indicates that zvol wasn't shared so
* we should return success in that case.
*/
if (iscsitgt_zfs_unshare == NULL ||
(iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV))
return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED,
dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset));
#endif
return (0);
}
typedef struct mount_cbdata {
zfs_handle_t **cb_datasets;
int cb_used;
int cb_alloc;
} mount_cbdata_t;
static int
mount_cb(zfs_handle_t *zhp, void *data)
{
mount_cbdata_t *cbp = data;
if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) {
zfs_close(zhp);
return (0);
}
if (cbp->cb_alloc == cbp->cb_used) {
void *ptr;
if ((ptr = zfs_realloc(zhp->zfs_hdl,
cbp->cb_datasets, cbp->cb_alloc * sizeof (void *),
cbp->cb_alloc * 2 * sizeof (void *))) == NULL)
return (-1);
cbp->cb_datasets = ptr;
cbp->cb_alloc *= 2;
}
cbp->cb_datasets[cbp->cb_used++] = zhp;
return (zfs_iter_children(zhp, mount_cb, cbp));
}
static int
dataset_cmp(const void *a, const void *b)
{
zfs_handle_t **za = (zfs_handle_t **)a;
zfs_handle_t **zb = (zfs_handle_t **)b;
char mounta[MAXPATHLEN];
char mountb[MAXPATHLEN];
boolean_t gota, gotb;
if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0)
verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0)
verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
if (gota && gotb)
return (strcmp(mounta, mountb));
if (gota)
return (-1);
if (gotb)
return (1);
return (strcmp(zfs_get_name(a), zfs_get_name(b)));
}
/*
* Mount and share all datasets within the given pool. This assumes that no
* datasets within the pool are currently mounted. Because users can create
* complicated nested hierarchies of mountpoints, we first gather all the
* datasets and mountpoints within the pool, and sort them by mountpoint. Once
* we have the list of all filesystems, we iterate over them in order and mount
* and/or share each one.
*/
#pragma weak zpool_mount_datasets = zpool_enable_datasets
int
zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
{
mount_cbdata_t cb = { 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
zfs_handle_t *zfsp;
int i, ret = -1;
/*
* Gather all datasets within the pool.
*/
if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
return (-1);
cb.cb_alloc = 4;
if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL)
goto out;
cb.cb_datasets[0] = zfsp;
cb.cb_used = 1;
if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
goto out;
/*
* Sort the datasets by mountpoint.
*/
qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp);
/*
* And mount all the datasets.
*/
ret = 0;
for (i = 0; i < cb.cb_used; i++) {
if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 ||
zfs_share(cb.cb_datasets[i]) != 0)
ret = -1;
}
out:
for (i = 0; i < cb.cb_used; i++)
zfs_close(cb.cb_datasets[i]);
free(cb.cb_datasets);
return (ret);
}
static int
zvol_cb(const char *dataset, void *data)
{
libzfs_handle_t *hdl = data;
zfs_handle_t *zhp;
/*
* Ignore snapshots and ignore failures from non-existant datasets.
*/
if (strchr(dataset, '@') != NULL ||
(zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL)
return (0);
(void) zfs_unshare_iscsi(zhp);
zfs_close(zhp);
return (0);
}
static int
mountpoint_compare(const void *a, const void *b)
{
const char *mounta = *((char **)a);
const char *mountb = *((char **)b);
return (strcmp(mountb, mounta));
}
/*
* Unshare and unmount all datasets within the given pool. We don't want to
* rely on traversing the DSL to discover the filesystems within the pool,
* because this may be expensive (if not all of them are mounted), and can fail
* arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and
* gather all the filesystems that are currently mounted.
*/
#pragma weak zpool_unmount_datasets = zpool_disable_datasets
int
zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
{
int used, alloc;
struct statfs *sfs;
size_t namelen;
char **mountpoints = NULL;
zfs_handle_t **datasets = NULL;
libzfs_handle_t *hdl = zhp->zpool_hdl;
int i, j, n;
int ret = -1;
int flags = (force ? MS_FORCE : 0);
/*
* First unshare all zvols.
*/
if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0)
return (-1);
namelen = strlen(zhp->zpool_name);
used = alloc = 0;
if ((n = getmntinfo(&sfs, MNT_WAIT)) == 0) {
fprintf(stderr, "getmntinfo(): %s\n", strerror(errno));
return (-1);
}
for (j = 0; j < n; j++) {
/*
* Ignore non-ZFS entries.
*/
if (strcmp(sfs[j].f_fstypename, MNTTYPE_ZFS) != 0)
continue;
/*
* Ignore filesystems not within this pool.
*/
if (strncmp(sfs[j].f_mntfromname, zhp->zpool_name, namelen) != 0 ||
(sfs[j].f_mntfromname[namelen] != '/' &&
sfs[j].f_mntfromname[namelen] != '\0'))
continue;
/*
* At this point we've found a filesystem within our pool. Add
* it to our growing list.
*/
if (used == alloc) {
if (alloc == 0) {
if ((mountpoints = zfs_alloc(hdl,
8 * sizeof (void *))) == NULL)
goto out;
if ((datasets = zfs_alloc(hdl,
8 * sizeof (void *))) == NULL)
goto out;
alloc = 8;
} else {
void *ptr;
if ((ptr = zfs_realloc(hdl, mountpoints,
alloc * sizeof (void *),
alloc * 2 * sizeof (void *))) == NULL)
goto out;
mountpoints = ptr;
if ((ptr = zfs_realloc(hdl, datasets,
alloc * sizeof (void *),
alloc * 2 * sizeof (void *))) == NULL)
goto out;
datasets = ptr;
alloc *= 2;
}
}
if ((mountpoints[used] = zfs_strdup(hdl,
sfs[j].f_mntonname)) == NULL)
goto out;
/*
* This is allowed to fail, in case there is some I/O error. It
* is only used to determine if we need to remove the underlying
* mountpoint, so failure is not fatal.
*/
datasets[used] = make_dataset_handle(hdl, sfs[j].f_mntfromname);
used++;
}
/*
* At this point, we have the entire list of filesystems, so sort it by
* mountpoint.
*/
qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
/*
* Walk through and first unshare everything.
*/
for (i = 0; i < used; i++) {
if (is_shared(hdl, mountpoints[i]) &&
unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0)
goto out;
}
/*
* Now unmount everything, removing the underlying directories as
* appropriate.
*/
for (i = 0; i < used; i++) {
if (unmount_one(hdl, mountpoints[i], flags) != 0)
goto out;
}
for (i = 0; i < used; i++) {
if (datasets[i])
remove_mountpoint(datasets[i]);
}
ret = 0;
out:
for (i = 0; i < used; i++) {
if (datasets[i])
zfs_close(datasets[i]);
free(mountpoints[i]);
}
free(datasets);
free(mountpoints);
return (ret);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,289 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* 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 descsribes 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 "libzfs_impl.h"
/*
* Message ID table. This must be kep 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 *msgid_table[] = {
"ZFS-8000-14",
"ZFS-8000-2Q",
"ZFS-8000-3C",
"ZFS-8000-4J",
"ZFS-8000-5E",
"ZFS-8000-6X",
"ZFS-8000-72",
"ZFS-8000-8A",
"ZFS-8000-9P",
"ZFS-8000-A5"
};
/*
* If the pool is active, a certain class of static errors is overridden by the
* faults as analayzed by FMA. These faults have separate knowledge articles,
* and the article referred to by 'zpool status' must match that indicated by
* the syslog error message. We override missing data as well as corrupt pool.
*/
static char *msgid_table_active[] = {
"ZFS-8000-14",
"ZFS-8000-D3", /* overridden */
"ZFS-8000-D3", /* overridden */
"ZFS-8000-4J",
"ZFS-8000-5E",
"ZFS-8000-6X",
"ZFS-8000-CS", /* overridden */
"ZFS-8000-8A",
"ZFS-8000-9P",
"ZFS-8000-CS", /* overridden */
};
#define NMSGID (sizeof (msgid_table) / sizeof (msgid_table[0]))
/* ARGSUSED */
static int
vdev_missing(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_CANT_OPEN &&
aux == VDEV_AUX_OPEN_FAILED);
}
/* ARGSUSED */
static int
vdev_errors(uint64_t state, uint64_t aux, uint64_t errs)
{
return (errs != 0);
}
/* ARGSUSED */
static int
vdev_broken(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_CANT_OPEN);
}
/* ARGSUSED */
static int
vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs)
{
return (state == VDEV_STATE_OFFLINE);
}
/*
* Detect if any leaf devices that have seen errors or could not be opened.
*/
static boolean_t
find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t))
{
nvlist_t **child;
vdev_stat_t *vs;
uint_t c, children;
char *type;
/*
* Ignore problems within a 'replacing' vdev, since we're presumably in
* the process of repairing any such errors, and don't want to call them
* out again. We'll pick up the fact that a resilver is happening
* later.
*/
verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0);
if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
return (B_FALSE);
if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,
&children) == 0) {
for (c = 0; c < children; c++)
if (find_vdev_problem(child[c], func))
return (B_TRUE);
} else {
verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &c) == 0);
if (func(vs->vs_state, vs->vs_aux,
vs->vs_read_errors +
vs->vs_write_errors +
vs->vs_checksum_errors))
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Active pool health status.
*
* To determine the status for a pool, we make several passes over the config,
* picking the most egregious error we find. In order of importance, we do the
* following:
*
* - Check for a complete and valid configuration
* - Look for any missing devices in a non-replicated config
* - Check for any data errors
* - Check for any missing devices in a replicated config
* - Look for any devices showing errors
* - Check for any resilvering devices
*
* There can obviously be multiple errors within a single pool, so this routine
* only picks the most damaging of all the current errors to report.
*/
static zpool_status_t
check_status(nvlist_t *config, boolean_t isimport)
{
nvlist_t *nvroot;
vdev_stat_t *vs;
uint_t vsc;
uint64_t nerr;
uint64_t version;
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
&version) == 0);
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &vsc) == 0);
/*
* Newer on-disk version.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_VERSION_NEWER)
return (ZPOOL_STATUS_VERSION_NEWER);
/*
* Check that the config is complete.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_BAD_GUID_SUM)
return (ZPOOL_STATUS_BAD_GUID_SUM);
/*
* Missing devices in non-replicated config.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
find_vdev_problem(nvroot, vdev_missing))
return (ZPOOL_STATUS_MISSING_DEV_NR);
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
find_vdev_problem(nvroot, vdev_broken))
return (ZPOOL_STATUS_CORRUPT_LABEL_NR);
/*
* Corrupted pool metadata
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
return (ZPOOL_STATUS_CORRUPT_POOL);
/*
* Persistent data errors.
*/
if (!isimport) {
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
&nerr) == 0 && nerr != 0)
return (ZPOOL_STATUS_CORRUPT_DATA);
}
/*
* Missing devices in a replicated config.
*/
if (find_vdev_problem(nvroot, vdev_missing))
return (ZPOOL_STATUS_MISSING_DEV_R);
if (find_vdev_problem(nvroot, vdev_broken))
return (ZPOOL_STATUS_CORRUPT_LABEL_R);
/*
* Devices with errors
*/
if (!isimport && find_vdev_problem(nvroot, vdev_errors))
return (ZPOOL_STATUS_FAILING_DEV);
/*
* Offlined devices
*/
if (find_vdev_problem(nvroot, vdev_offlined))
return (ZPOOL_STATUS_OFFLINE_DEV);
/*
* Currently resilvering
*/
if (!vs->vs_scrub_complete && vs->vs_scrub_type == POOL_SCRUB_RESILVER)
return (ZPOOL_STATUS_RESILVERING);
/*
* Outdated, but usable, version
*/
if (version < ZFS_VERSION)
return (ZPOOL_STATUS_VERSION_OLDER);
return (ZPOOL_STATUS_OK);
}
zpool_status_t
zpool_get_status(zpool_handle_t *zhp, char **msgid)
{
zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE);
if (ret >= NMSGID)
*msgid = NULL;
else
*msgid = msgid_table_active[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 = msgid_table[ret];
return (ret);
}

View File

@ -0,0 +1,827 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Internal utility routines for the ZFS library.
*/
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/types.h>
#include <libzfs.h>
#include "libzfs_impl.h"
int
libzfs_errno(libzfs_handle_t *hdl)
{
return (hdl->libzfs_error);
}
const char *
libzfs_error_action(libzfs_handle_t *hdl)
{
return (hdl->libzfs_action);
}
const char *
libzfs_error_description(libzfs_handle_t *hdl)
{
if (hdl->libzfs_desc[0] != '\0')
return (hdl->libzfs_desc);
switch (hdl->libzfs_error) {
case EZFS_NOMEM:
return (dgettext(TEXT_DOMAIN, "out of memory"));
case EZFS_BADPROP:
return (dgettext(TEXT_DOMAIN, "invalid property value"));
case EZFS_PROPREADONLY:
return (dgettext(TEXT_DOMAIN, "read only property"));
case EZFS_PROPTYPE:
return (dgettext(TEXT_DOMAIN, "property doesn't apply to "
"datasets of this type"));
case EZFS_PROPNONINHERIT:
return (dgettext(TEXT_DOMAIN, "property cannot be inherited"));
case EZFS_PROPSPACE:
return (dgettext(TEXT_DOMAIN, "invalid quota or reservation"));
case EZFS_BADTYPE:
return (dgettext(TEXT_DOMAIN, "operation not applicable to "
"datasets of this type"));
case EZFS_BUSY:
return (dgettext(TEXT_DOMAIN, "pool or dataset is busy"));
case EZFS_EXISTS:
return (dgettext(TEXT_DOMAIN, "pool or dataset exists"));
case EZFS_NOENT:
return (dgettext(TEXT_DOMAIN, "no such pool or dataset"));
case EZFS_BADSTREAM:
return (dgettext(TEXT_DOMAIN, "invalid backup stream"));
case EZFS_DSREADONLY:
return (dgettext(TEXT_DOMAIN, "dataset is read only"));
case EZFS_VOLTOOBIG:
return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for "
"this system"));
case EZFS_VOLHASDATA:
return (dgettext(TEXT_DOMAIN, "volume has data"));
case EZFS_INVALIDNAME:
return (dgettext(TEXT_DOMAIN, "invalid name"));
case EZFS_BADRESTORE:
return (dgettext(TEXT_DOMAIN, "unable to restore to "
"destination"));
case EZFS_BADBACKUP:
return (dgettext(TEXT_DOMAIN, "backup failed"));
case EZFS_BADTARGET:
return (dgettext(TEXT_DOMAIN, "invalid target vdev"));
case EZFS_NODEVICE:
return (dgettext(TEXT_DOMAIN, "no such device in pool"));
case EZFS_BADDEV:
return (dgettext(TEXT_DOMAIN, "invalid device"));
case EZFS_NOREPLICAS:
return (dgettext(TEXT_DOMAIN, "no valid replicas"));
case EZFS_RESILVERING:
return (dgettext(TEXT_DOMAIN, "currently resilvering"));
case EZFS_BADVERSION:
return (dgettext(TEXT_DOMAIN, "unsupported version"));
case EZFS_POOLUNAVAIL:
return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
case EZFS_DEVOVERFLOW:
return (dgettext(TEXT_DOMAIN, "too many devices in one vdev"));
case EZFS_BADPATH:
return (dgettext(TEXT_DOMAIN, "must be an absolute path"));
case EZFS_CROSSTARGET:
return (dgettext(TEXT_DOMAIN, "operation crosses datasets or "
"pools"));
case EZFS_ZONED:
return (dgettext(TEXT_DOMAIN, "dataset in use by local zone"));
case EZFS_MOUNTFAILED:
return (dgettext(TEXT_DOMAIN, "mount failed"));
case EZFS_UMOUNTFAILED:
return (dgettext(TEXT_DOMAIN, "umount failed"));
case EZFS_UNSHARENFSFAILED:
return (dgettext(TEXT_DOMAIN, "unshare(1M) failed"));
case EZFS_SHARENFSFAILED:
return (dgettext(TEXT_DOMAIN, "share(1M) failed"));
case EZFS_DEVLINKS:
return (dgettext(TEXT_DOMAIN, "failed to create /dev links"));
case EZFS_PERM:
return (dgettext(TEXT_DOMAIN, "permission denied"));
case EZFS_NOSPC:
return (dgettext(TEXT_DOMAIN, "out of space"));
case EZFS_IO:
return (dgettext(TEXT_DOMAIN, "I/O error"));
case EZFS_INTR:
return (dgettext(TEXT_DOMAIN, "signal received"));
case EZFS_ISSPARE:
return (dgettext(TEXT_DOMAIN, "device is reserved as a hot "
"spare"));
case EZFS_INVALCONFIG:
return (dgettext(TEXT_DOMAIN, "invalid vdev configuration"));
case EZFS_RECURSIVE:
return (dgettext(TEXT_DOMAIN, "recursive dataset dependency"));
case EZFS_NOHISTORY:
return (dgettext(TEXT_DOMAIN, "no history available"));
case EZFS_UNSHAREISCSIFAILED:
return (dgettext(TEXT_DOMAIN,
"iscsitgtd failed request to unshare"));
case EZFS_SHAREISCSIFAILED:
return (dgettext(TEXT_DOMAIN,
"iscsitgtd failed request to share"));
case EZFS_POOLPROPS:
return (dgettext(TEXT_DOMAIN, "failed to retrieve "
"pool properties"));
case EZFS_POOL_NOTSUP:
return (dgettext(TEXT_DOMAIN, "operation not supported "
"on this type of pool"));
case EZFS_POOL_INVALARG:
return (dgettext(TEXT_DOMAIN, "invalid argument for "
"this pool operation"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
assert(hdl->libzfs_error == 0);
return (dgettext(TEXT_DOMAIN, "no error"));
}
}
/*PRINTFLIKE2*/
void
zfs_error_aux(libzfs_handle_t *hdl, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void) vsnprintf(hdl->libzfs_desc, sizeof (hdl->libzfs_desc),
fmt, ap);
hdl->libzfs_desc_active = 1;
va_end(ap);
}
static void
zfs_verror(libzfs_handle_t *hdl, int error, const char *fmt, va_list ap)
{
(void) vsnprintf(hdl->libzfs_action, sizeof (hdl->libzfs_action),
fmt, ap);
hdl->libzfs_error = error;
if (hdl->libzfs_desc_active)
hdl->libzfs_desc_active = 0;
else
hdl->libzfs_desc[0] = '\0';
if (hdl->libzfs_printerr) {
if (error == EZFS_UNKNOWN) {
(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "internal "
"error: %s\n"), libzfs_error_description(hdl));
abort();
}
(void) fprintf(stderr, "%s: %s\n", hdl->libzfs_action,
libzfs_error_description(hdl));
if (error == EZFS_NOMEM)
exit(1);
}
}
int
zfs_error(libzfs_handle_t *hdl, int error, const char *msg)
{
return (zfs_error_fmt(hdl, error, "%s", msg));
}
/*PRINTFLIKE3*/
int
zfs_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
zfs_verror(hdl, error, fmt, ap);
va_end(ap);
return (-1);
}
static int
zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
va_list ap)
{
switch (error) {
case EPERM:
case EACCES:
zfs_verror(hdl, EZFS_PERM, fmt, ap);
return (-1);
case EIO:
zfs_verror(hdl, EZFS_IO, fmt, ap);
return (-1);
case EINTR:
zfs_verror(hdl, EZFS_INTR, fmt, ap);
return (-1);
}
return (0);
}
int
zfs_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
{
return (zfs_standard_error_fmt(hdl, error, "%s", msg));
}
/*PRINTFLIKE3*/
int
zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (zfs_common_error(hdl, error, fmt, ap) != 0) {
va_end(ap);
return (-1);
}
switch (error) {
case ENXIO:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
case ENOENT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset does not exist"));
zfs_verror(hdl, EZFS_NOENT, fmt, ap);
break;
case ENOSPC:
case EDQUOT:
zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
return (-1);
case EEXIST:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset already exists"));
zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
break;
case EBUSY:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset is busy"));
zfs_verror(hdl, EZFS_BUSY, fmt, ap);
break;
default:
zfs_error_aux(hdl, strerror(errno));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
break;
}
va_end(ap);
return (-1);
}
int
zpool_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
{
return (zpool_standard_error_fmt(hdl, error, "%s", msg));
}
/*PRINTFLIKE3*/
int
zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (zfs_common_error(hdl, error, fmt, ap) != 0) {
va_end(ap);
return (-1);
}
switch (error) {
case ENODEV:
zfs_verror(hdl, EZFS_NODEVICE, fmt, ap);
break;
case ENOENT:
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "no such pool or dataset"));
zfs_verror(hdl, EZFS_NOENT, fmt, ap);
break;
case EEXIST:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"pool already exists"));
zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
break;
case EBUSY:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool is busy"));
zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
break;
case ENXIO:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"one or more devices is currently unavailable"));
zfs_verror(hdl, EZFS_BADDEV, fmt, ap);
break;
case ENAMETOOLONG:
zfs_verror(hdl, EZFS_DEVOVERFLOW, fmt, ap);
break;
case ENOTSUP:
zfs_verror(hdl, EZFS_POOL_NOTSUP, fmt, ap);
break;
case EINVAL:
zfs_verror(hdl, EZFS_POOL_INVALARG, fmt, ap);
break;
default:
zfs_error_aux(hdl, strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
}
va_end(ap);
return (-1);
}
/*
* Display an out of memory error message and abort the current program.
*/
int
no_memory(libzfs_handle_t *hdl)
{
return (zfs_error(hdl, EZFS_NOMEM, "internal error"));
}
/*
* A safe form of malloc() which will die if the allocation fails.
*/
void *
zfs_alloc(libzfs_handle_t *hdl, size_t size)
{
void *data;
if ((data = calloc(1, size)) == NULL)
(void) no_memory(hdl);
return (data);
}
/*
* A safe form of realloc(), which also zeroes newly allocated space.
*/
void *
zfs_realloc(libzfs_handle_t *hdl, void *ptr, size_t oldsize, size_t newsize)
{
void *ret;
if ((ret = realloc(ptr, newsize)) == NULL) {
(void) no_memory(hdl);
free(ptr);
return (NULL);
}
bzero((char *)ret + oldsize, (newsize - oldsize));
return (ret);
}
/*
* A safe form of strdup() which will die if the allocation fails.
*/
char *
zfs_strdup(libzfs_handle_t *hdl, const char *str)
{
char *ret;
if ((ret = strdup(str)) == NULL)
(void) no_memory(hdl);
return (ret);
}
/*
* Convert a number to an appropriately human-readable output.
*/
void
zfs_nicenum(uint64_t num, char *buf, size_t buflen)
{
uint64_t n = num;
int index = 0;
char u;
while (n >= 1024) {
n /= 1024;
index++;
}
u = " KMGTPE"[index];
if (index == 0) {
(void) snprintf(buf, buflen, "%llu", n);
} else if ((num & ((1ULL << 10 * index) - 1)) == 0) {
/*
* If this is an even multiple of the base, always display
* without any decimal precision.
*/
(void) snprintf(buf, buflen, "%llu%c", n, u);
} else {
/*
* We want to choose a precision that reflects the best choice
* for fitting in 5 characters. This can get rather tricky when
* we have numbers that are very close to an order of magnitude.
* For example, when displaying 10239 (which is really 9.999K),
* we want only a single place of precision for 10.0K. We could
* develop some complex heuristics for this, but it's much
* easier just to try each combination in turn.
*/
int i;
for (i = 2; i >= 0; i--) {
(void) snprintf(buf, buflen, "%.*f%c", i,
(double)num / (1ULL << 10 * index), u);
if (strlen(buf) <= 5)
break;
}
}
}
void
libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
{
hdl->libzfs_printerr = printerr;
}
libzfs_handle_t *
libzfs_init(void)
{
libzfs_handle_t *hdl;
if ((hdl = calloc(sizeof (libzfs_handle_t), 1)) == NULL) {
return (NULL);
}
if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
free(hdl);
return (NULL);
}
if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) {
(void) close(hdl->libzfs_fd);
free(hdl);
return (NULL);
}
hdl->libzfs_sharetab = fopen(ZFS_EXPORTS_PATH, "r");
return (hdl);
}
void
libzfs_fini(libzfs_handle_t *hdl)
{
(void) close(hdl->libzfs_fd);
if (hdl->libzfs_mnttab)
(void) fclose(hdl->libzfs_mnttab);
if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab);
namespace_clear(hdl);
free(hdl);
}
libzfs_handle_t *
zpool_get_handle(zpool_handle_t *zhp)
{
return (zhp->zpool_hdl);
}
libzfs_handle_t *
zfs_get_handle(zfs_handle_t *zhp)
{
return (zhp->zfs_hdl);
}
/*
* Given a name, determine whether or not it's a valid path
* (starts with '/' or "./"). If so, walk the mnttab trying
* to match the device number. If not, treat the path as an
* fs/vol/snap name.
*/
zfs_handle_t *
zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
{
struct statfs statbuf;
if (path[0] != '/' && strncmp(path, "./", strlen("./")) != 0) {
/*
* It's not a valid path, assume it's a name of type 'argtype'.
*/
return (zfs_open(hdl, path, argtype));
}
if (statfs(path, &statbuf) != 0) {
(void) fprintf(stderr, "%s: %s\n", path, strerror(errno));
return (NULL);
}
if (strcmp(statbuf.f_fstypename, MNTTYPE_ZFS) != 0) {
(void) fprintf(stderr, gettext("'%s': not a ZFS filesystem\n"),
path);
return (NULL);
}
return (zfs_open(hdl, statbuf.f_mntfromname, ZFS_TYPE_FILESYSTEM));
}
/*
* Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
* an ioctl().
*/
int
zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
{
if (len == 0)
len = 2048;
zc->zc_nvlist_dst_size = len;
if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
return (-1);
return (0);
}
/*
* Called when an ioctl() which returns an nvlist fails with ENOMEM. This will
* expand the nvlist to the size specified in 'zc_nvlist_dst_size', which was
* filled in by the kernel to indicate the actual required size.
*/
int
zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
{
free((void *)(uintptr_t)zc->zc_nvlist_dst);
if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
zfs_alloc(hdl, zc->zc_nvlist_dst_size))
== 0)
return (-1);
return (0);
}
/*
* Called to free the src and dst nvlists stored in the command structure.
*/
void
zcmd_free_nvlists(zfs_cmd_t *zc)
{
free((void *)(uintptr_t)zc->zc_nvlist_src);
free((void *)(uintptr_t)zc->zc_nvlist_dst);
}
int
zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl,
size_t *size)
{
char *packed;
size_t len;
verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0);
if ((packed = zfs_alloc(hdl, len)) == NULL)
return (-1);
verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
zc->zc_nvlist_src = (uint64_t)(uintptr_t)packed;
zc->zc_nvlist_src_size = len;
if (size)
*size = len;
return (0);
}
/*
* Unpacks an nvlist from the ZFS ioctl command structure.
*/
int
zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
{
if (nvlist_unpack((void *)(uintptr_t)zc->zc_nvlist_dst,
zc->zc_nvlist_dst_size, nvlp, 0) != 0)
return (no_memory(hdl));
return (0);
}
static void
zfs_print_prop_headers(libzfs_get_cbdata_t *cbp)
{
zfs_proplist_t *pl = cbp->cb_proplist;
int i;
char *title;
size_t len;
cbp->cb_first = B_FALSE;
if (cbp->cb_scripted)
return;
/*
* Start with the length of the column headers.
*/
cbp->cb_colwidths[GET_COL_NAME] = strlen(dgettext(TEXT_DOMAIN, "NAME"));
cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(dgettext(TEXT_DOMAIN,
"PROPERTY"));
cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
"VALUE"));
cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
"SOURCE"));
/*
* Go through and calculate the widths for each column. For the
* 'source' column, we kludge it up by taking the worst-case scenario of
* inheriting from the longest name. This is acceptable because in the
* majority of cases 'SOURCE' is the last column displayed, and we don't
* use the width anyway. Note that the 'VALUE' column can be oversized,
* if the name of the property is much longer the any values we find.
*/
for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
/*
* 'PROPERTY' column
*/
if (pl->pl_prop != ZFS_PROP_INVAL) {
len = strlen(zfs_prop_to_name(pl->pl_prop));
if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
cbp->cb_colwidths[GET_COL_PROPERTY] = len;
} else {
len = strlen(pl->pl_user_prop);
if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
cbp->cb_colwidths[GET_COL_PROPERTY] = len;
}
/*
* 'VALUE' column
*/
if ((pl->pl_prop != ZFS_PROP_NAME || !pl->pl_all) &&
pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
/*
* 'NAME' and 'SOURCE' columns
*/
if (pl->pl_prop == ZFS_PROP_NAME &&
pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width +
strlen(dgettext(TEXT_DOMAIN, "inherited from"));
}
}
/*
* Now go through and print the headers.
*/
for (i = 0; i < 4; i++) {
switch (cbp->cb_columns[i]) {
case GET_COL_NAME:
title = dgettext(TEXT_DOMAIN, "NAME");
break;
case GET_COL_PROPERTY:
title = dgettext(TEXT_DOMAIN, "PROPERTY");
break;
case GET_COL_VALUE:
title = dgettext(TEXT_DOMAIN, "VALUE");
break;
case GET_COL_SOURCE:
title = dgettext(TEXT_DOMAIN, "SOURCE");
break;
default:
title = NULL;
}
if (title != NULL) {
if (i == 3 || cbp->cb_columns[i + 1] == 0)
(void) printf("%s", title);
else
(void) printf("%-*s ",
cbp->cb_colwidths[cbp->cb_columns[i]],
title);
}
}
(void) printf("\n");
}
/*
* Display a single line of output, according to the settings in the callback
* structure.
*/
void
libzfs_print_one_property(const char *name, libzfs_get_cbdata_t *cbp,
const char *propname, const char *value, zfs_source_t sourcetype,
const char *source)
{
int i;
const char *str;
char buf[128];
/*
* Ignore those source types that the user has chosen to ignore.
*/
if ((sourcetype & cbp->cb_sources) == 0)
return;
if (cbp->cb_first)
zfs_print_prop_headers(cbp);
for (i = 0; i < 4; i++) {
switch (cbp->cb_columns[i]) {
case GET_COL_NAME:
str = name;
break;
case GET_COL_PROPERTY:
str = propname;
break;
case GET_COL_VALUE:
str = value;
break;
case GET_COL_SOURCE:
switch (sourcetype) {
case ZFS_SRC_NONE:
str = "-";
break;
case ZFS_SRC_DEFAULT:
str = "default";
break;
case ZFS_SRC_LOCAL:
str = "local";
break;
case ZFS_SRC_TEMPORARY:
str = "temporary";
break;
case ZFS_SRC_INHERITED:
(void) snprintf(buf, sizeof (buf),
"inherited from %s", source);
str = buf;
break;
}
break;
default:
continue;
}
if (cbp->cb_columns[i + 1] == 0)
(void) printf("%s", str);
else if (cbp->cb_scripted)
(void) printf("%s\t", str);
else
(void) printf("%-*s ",
cbp->cb_colwidths[cbp->cb_columns[i]],
str);
}
(void) printf("\n");
}

View File

@ -0,0 +1,830 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <sys/spa.h>
#include <sys/stat.h>
#include <sys/processor.h>
#include <sys/zfs_context.h>
#include <sys/zmod.h>
/*
* Emulation of kernel services in userland.
*/
int hz = 119; /* frequency when using gethrtime() >> 23 for lbolt */
uint64_t physmem;
vnode_t *rootdir = (vnode_t *)0xabcd1234;
/*
* =========================================================================
* threads
* =========================================================================
*/
/*ARGSUSED*/
kthread_t *
zk_thread_create(void (*func)(), void *arg)
{
thread_t tid;
VERIFY(thr_create(0, 0, (void *(*)(void *))func, arg, THR_DETACHED,
&tid) == 0);
return ((void *)(uintptr_t)tid);
}
/*
* =========================================================================
* kstats
* =========================================================================
*/
/*ARGSUSED*/
kstat_t *
kstat_create(char *module, int instance, char *name, char *class,
uchar_t type, ulong_t ndata, uchar_t ks_flag)
{
return (NULL);
}
/*ARGSUSED*/
void
kstat_install(kstat_t *ksp)
{}
/*ARGSUSED*/
void
kstat_delete(kstat_t *ksp)
{}
/*
* =========================================================================
* mutexes
* =========================================================================
*/
void
zmutex_init(kmutex_t *mp)
{
mp->m_owner = NULL;
(void) _mutex_init(&mp->m_lock, USYNC_THREAD, NULL);
}
void
zmutex_destroy(kmutex_t *mp)
{
ASSERT(mp->m_owner == NULL);
(void) _mutex_destroy(&(mp)->m_lock);
mp->m_owner = (void *)-1UL;
}
void
mutex_enter(kmutex_t *mp)
{
ASSERT(mp->m_owner != (void *)-1UL);
ASSERT(mp->m_owner != curthread);
VERIFY(mutex_lock(&mp->m_lock) == 0);
ASSERT(mp->m_owner == NULL);
mp->m_owner = curthread;
}
int
mutex_tryenter(kmutex_t *mp)
{
ASSERT(mp->m_owner != (void *)-1UL);
if (mutex_trylock(&mp->m_lock) == 0) {
ASSERT(mp->m_owner == NULL);
mp->m_owner = curthread;
return (1);
} else {
return (0);
}
}
void
mutex_exit(kmutex_t *mp)
{
ASSERT(mp->m_owner == curthread);
mp->m_owner = NULL;
VERIFY(mutex_unlock(&mp->m_lock) == 0);
}
void *
mutex_owner(kmutex_t *mp)
{
return (mp->m_owner);
}
/*
* =========================================================================
* rwlocks
* =========================================================================
*/
/*ARGSUSED*/
void
rw_init(krwlock_t *rwlp, char *name, int type, void *arg)
{
rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL);
rwlp->rw_owner = NULL;
rwlp->rw_count = 0;
}
void
rw_destroy(krwlock_t *rwlp)
{
rwlock_destroy(&rwlp->rw_lock);
rwlp->rw_owner = (void *)-1UL;
rwlp->rw_count = -2;
}
void
rw_enter(krwlock_t *rwlp, krw_t rw)
{
//ASSERT(!RW_LOCK_HELD(rwlp));
ASSERT(rwlp->rw_owner != (void *)-1UL);
ASSERT(rwlp->rw_owner != curthread);
if (rw == RW_READER) {
(void) rw_rdlock(&rwlp->rw_lock);
ASSERT(rwlp->rw_count >= 0);
atomic_add_int(&rwlp->rw_count, 1);
} else {
(void) rw_wrlock(&rwlp->rw_lock);
ASSERT(rwlp->rw_count == 0);
rwlp->rw_count = -1;
rwlp->rw_owner = curthread;
}
}
void
rw_exit(krwlock_t *rwlp)
{
ASSERT(rwlp->rw_owner != (void *)-1UL);
if (rwlp->rw_owner == curthread) {
/* Write locked. */
ASSERT(rwlp->rw_count == -1);
rwlp->rw_count = 0;
rwlp->rw_owner = NULL;
} else {
/* Read locked. */
ASSERT(rwlp->rw_count > 0);
atomic_add_int(&rwlp->rw_count, -1);
}
(void) rw_unlock(&rwlp->rw_lock);
}
int
rw_tryenter(krwlock_t *rwlp, krw_t rw)
{
int rv;
ASSERT(rwlp->rw_owner != (void *)-1UL);
ASSERT(rwlp->rw_owner != curthread);
if (rw == RW_READER)
rv = rw_tryrdlock(&rwlp->rw_lock);
else
rv = rw_trywrlock(&rwlp->rw_lock);
if (rv == 0) {
ASSERT(rwlp->rw_owner == NULL);
if (rw == RW_READER) {
ASSERT(rwlp->rw_count >= 0);
atomic_add_int(&rwlp->rw_count, 1);
} else {
ASSERT(rwlp->rw_count == 0);
rwlp->rw_count = -1;
rwlp->rw_owner = curthread;
}
return (1);
}
return (0);
}
/*ARGSUSED*/
int
rw_tryupgrade(krwlock_t *rwlp)
{
ASSERT(rwlp->rw_owner != (void *)-1UL);
return (0);
}
int
rw_lock_held(krwlock_t *rwlp)
{
return (rwlp->rw_count != 0);
}
/*
* =========================================================================
* condition variables
* =========================================================================
*/
/*ARGSUSED*/
void
cv_init(kcondvar_t *cv, char *name, int type, void *arg)
{
VERIFY(cond_init(cv, name, NULL) == 0);
}
void
cv_destroy(kcondvar_t *cv)
{
VERIFY(cond_destroy(cv) == 0);
}
void
cv_wait(kcondvar_t *cv, kmutex_t *mp)
{
ASSERT(mutex_owner(mp) == curthread);
mp->m_owner = NULL;
int ret = cond_wait(cv, &mp->m_lock);
VERIFY(ret == 0 || ret == EINTR);
mp->m_owner = curthread;
}
clock_t
cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime)
{
int error;
struct timespec ts;
struct timeval tv;
clock_t delta;
ASSERT(abstime > 0);
top:
delta = abstime - lbolt;
if (delta <= 0)
return (-1);
if (gettimeofday(&tv, NULL) != 0)
assert(!"gettimeofday() failed");
ts.tv_sec = tv.tv_sec + delta / hz;
ts.tv_nsec = tv.tv_usec * 1000 + (delta % hz) * (NANOSEC / hz);
ASSERT(ts.tv_nsec >= 0);
if(ts.tv_nsec >= NANOSEC) {
ts.tv_sec++;
ts.tv_nsec -= NANOSEC;
}
ASSERT(mutex_owner(mp) == curthread);
mp->m_owner = NULL;
error = pthread_cond_timedwait(cv, &mp->m_lock, &ts);
mp->m_owner = curthread;
if (error == EINTR)
goto top;
if (error == ETIMEDOUT)
return (-1);
ASSERT(error == 0);
return (1);
}
void
cv_signal(kcondvar_t *cv)
{
VERIFY(cond_signal(cv) == 0);
}
void
cv_broadcast(kcondvar_t *cv)
{
VERIFY(cond_broadcast(cv) == 0);
}
/*
* =========================================================================
* vnode operations
* =========================================================================
*/
/*
* Note: for the xxxat() versions of these functions, we assume that the
* starting vp is always rootdir (which is true for spa_directory.c, the only
* ZFS consumer of these interfaces). We assert this is true, and then emulate
* them by adding '/' in front of the path.
*/
/*ARGSUSED*/
int
vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
{
int fd;
vnode_t *vp;
int old_umask;
char realpath[MAXPATHLEN];
struct stat64 st;
/*
* If we're accessing a real disk from userland, we need to use
* the character interface to avoid caching. This is particularly
* important if we're trying to look at a real in-kernel storage
* pool from userland, e.g. via zdb, because otherwise we won't
* see the changes occurring under the segmap cache.
* On the other hand, the stupid character device returns zero
* for its size. So -- gag -- we open the block device to get
* its size, and remember it for subsequent VOP_GETATTR().
*/
if (strncmp(path, "/dev/", 5) == 0) {
char *dsk;
fd = open64(path, O_RDONLY);
if (fd == -1)
return (errno);
if (fstat64(fd, &st) == -1) {
close(fd);
return (errno);
}
close(fd);
(void) sprintf(realpath, "%s", path);
dsk = strstr(path, "/dsk/");
if (dsk != NULL)
(void) sprintf(realpath + (dsk - path) + 1, "r%s",
dsk + 1);
} else {
(void) sprintf(realpath, "%s", path);
if (!(flags & FCREAT) && stat64(realpath, &st) == -1)
return (errno);
}
if (flags & FCREAT)
old_umask = umask(0);
/*
* The construct 'flags - FREAD' conveniently maps combinations of
* FREAD and FWRITE to the corresponding O_RDONLY, O_WRONLY, and O_RDWR.
*/
fd = open64(realpath, flags - FREAD, mode);
if (flags & FCREAT)
(void) umask(old_umask);
if (fd == -1)
return (errno);
if (fstat64(fd, &st) == -1) {
close(fd);
return (errno);
}
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
*vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
vp->v_fd = fd;
vp->v_size = st.st_size;
vp->v_path = spa_strdup(path);
return (0);
}
int
vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
int x3, vnode_t *startvp)
{
char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL);
int ret;
ASSERT(startvp == rootdir);
(void) sprintf(realpath, "/%s", path);
ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3);
umem_free(realpath, strlen(path) + 2);
return (ret);
}
/*ARGSUSED*/
int
vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp)
{
ssize_t iolen, split;
if (uio == UIO_READ) {
iolen = pread64(vp->v_fd, addr, len, offset);
} else {
/*
* To simulate partial disk writes, we split writes into two
* system calls so that the process can be killed in between.
*/
split = (len > 0 ? rand() % len : 0);
iolen = pwrite64(vp->v_fd, addr, split, offset);
iolen += pwrite64(vp->v_fd, (char *)addr + split,
len - split, offset + split);
}
if (iolen == -1)
return (errno);
if (residp)
*residp = len - iolen;
else if (iolen != len)
return (EIO);
return (0);
}
void
vn_close(vnode_t *vp)
{
close(vp->v_fd);
spa_strfree(vp->v_path);
umem_free(vp, sizeof (vnode_t));
}
#ifdef ZFS_DEBUG
/*
* =========================================================================
* Figure out which debugging statements to print
* =========================================================================
*/
static char *dprintf_string;
static int dprintf_print_all;
int
dprintf_find_string(const char *string)
{
char *tmp_str = dprintf_string;
int len = strlen(string);
/*
* Find out if this is a string we want to print.
* String format: file1.c,function_name1,file2.c,file3.c
*/
while (tmp_str != NULL) {
if (strncmp(tmp_str, string, len) == 0 &&
(tmp_str[len] == ',' || tmp_str[len] == '\0'))
return (1);
tmp_str = strchr(tmp_str, ',');
if (tmp_str != NULL)
tmp_str++; /* Get rid of , */
}
return (0);
}
void
dprintf_setup(int *argc, char **argv)
{
int i, j;
/*
* Debugging can be specified two ways: by setting the
* environment variable ZFS_DEBUG, or by including a
* "debug=..." argument on the command line. The command
* line setting overrides the environment variable.
*/
for (i = 1; i < *argc; i++) {
int len = strlen("debug=");
/* First look for a command line argument */
if (strncmp("debug=", argv[i], len) == 0) {
dprintf_string = argv[i] + len;
/* Remove from args */
for (j = i; j < *argc; j++)
argv[j] = argv[j+1];
argv[j] = NULL;
(*argc)--;
}
}
if (dprintf_string == NULL) {
/* Look for ZFS_DEBUG environment variable */
dprintf_string = getenv("ZFS_DEBUG");
}
/*
* Are we just turning on all debugging?
*/
if (dprintf_find_string("on"))
dprintf_print_all = 1;
}
/*
* =========================================================================
* debug printfs
* =========================================================================
*/
void
__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
{
const char *newfile;
va_list adx;
/*
* Get rid of annoying "../common/" prefix to filename.
*/
newfile = strrchr(file, '/');
if (newfile != NULL) {
newfile = newfile + 1; /* Get rid of leading / */
} else {
newfile = file;
}
if (dprintf_print_all ||
dprintf_find_string(newfile) ||
dprintf_find_string(func)) {
/* Print out just the function name if requested */
flockfile(stdout);
if (dprintf_find_string("pid"))
(void) printf("%d ", getpid());
if (dprintf_find_string("tid"))
(void) printf("%u ", thr_self());
#if 0
if (dprintf_find_string("cpu"))
(void) printf("%u ", getcpuid());
#endif
if (dprintf_find_string("time"))
(void) printf("%llu ", gethrtime());
if (dprintf_find_string("long"))
(void) printf("%s, line %d: ", newfile, line);
(void) printf("%s: ", func);
va_start(adx, fmt);
(void) vprintf(fmt, adx);
va_end(adx);
funlockfile(stdout);
}
}
#endif /* ZFS_DEBUG */
/*
* =========================================================================
* cmn_err() and panic()
* =========================================================================
*/
static char ce_prefix[CE_IGNORE][10] = { "", "NOTICE: ", "WARNING: ", "" };
static char ce_suffix[CE_IGNORE][2] = { "", "\n", "\n", "" };
void
vpanic(const char *fmt, va_list adx)
{
(void) fprintf(stderr, "error: ");
(void) vfprintf(stderr, fmt, adx);
(void) fprintf(stderr, "\n");
abort(); /* think of it as a "user-level crash dump" */
}
void
panic(const char *fmt, ...)
{
va_list adx;
va_start(adx, fmt);
vpanic(fmt, adx);
va_end(adx);
}
void
vcmn_err(int ce, const char *fmt, va_list adx)
{
if (ce == CE_PANIC)
vpanic(fmt, adx);
if (ce != CE_NOTE) { /* suppress noise in userland stress testing */
(void) fprintf(stderr, "%s", ce_prefix[ce]);
(void) vfprintf(stderr, fmt, adx);
(void) fprintf(stderr, "%s", ce_suffix[ce]);
}
}
/*PRINTFLIKE2*/
void
cmn_err(int ce, const char *fmt, ...)
{
va_list adx;
va_start(adx, fmt);
vcmn_err(ce, fmt, adx);
va_end(adx);
}
/*
* =========================================================================
* kobj interfaces
* =========================================================================
*/
struct _buf *
kobj_open_file(char *name)
{
struct _buf *file;
vnode_t *vp;
/* set vp as the _fd field of the file */
if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir) != 0)
return ((void *)-1UL);
file = umem_zalloc(sizeof (struct _buf), UMEM_NOFAIL);
file->_fd = (intptr_t)vp;
return (file);
}
int
kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off)
{
ssize_t resid;
vn_rdwr(UIO_READ, (vnode_t *)file->_fd, buf, size, (offset_t)off,
UIO_SYSSPACE, 0, 0, 0, &resid);
return (size - resid);
}
void
kobj_close_file(struct _buf *file)
{
vn_close((vnode_t *)file->_fd);
umem_free(file, sizeof (struct _buf));
}
int
kobj_get_filesize(struct _buf *file, uint64_t *size)
{
struct stat64 st;
vnode_t *vp = (vnode_t *)file->_fd;
if (fstat64(vp->v_fd, &st) == -1) {
vn_close(vp);
return (errno);
}
*size = st.st_size;
return (0);
}
/*
* =========================================================================
* misc routines
* =========================================================================
*/
void
delay(clock_t ticks)
{
poll(0, 0, ticks * (1000 / hz));
}
#if 0
/*
* Find highest one bit set.
* Returns bit number + 1 of highest bit that is set, otherwise returns 0.
* High order bit is 31 (or 63 in _LP64 kernel).
*/
int
highbit(ulong_t i)
{
register int h = 1;
if (i == 0)
return (0);
#ifdef _LP64
if (i & 0xffffffff00000000ul) {
h += 32; i >>= 32;
}
#endif
if (i & 0xffff0000) {
h += 16; i >>= 16;
}
if (i & 0xff00) {
h += 8; i >>= 8;
}
if (i & 0xf0) {
h += 4; i >>= 4;
}
if (i & 0xc) {
h += 2; i >>= 2;
}
if (i & 0x2) {
h += 1;
}
return (h);
}
#endif
static int
random_get_bytes_common(uint8_t *ptr, size_t len, char *devname)
{
int fd = open(devname, O_RDONLY);
size_t resid = len;
ssize_t bytes;
ASSERT(fd != -1);
while (resid != 0) {
bytes = read(fd, ptr, resid);
ASSERT(bytes >= 0);
ptr += bytes;
resid -= bytes;
}
close(fd);
return (0);
}
int
random_get_bytes(uint8_t *ptr, size_t len)
{
return (random_get_bytes_common(ptr, len, "/dev/random"));
}
int
random_get_pseudo_bytes(uint8_t *ptr, size_t len)
{
return (random_get_bytes_common(ptr, len, "/dev/urandom"));
}
/*
* =========================================================================
* kernel emulation setup & teardown
* =========================================================================
*/
static int
umem_out_of_memory(void)
{
char errmsg[] = "out of memory -- generating core dump\n";
write(fileno(stderr), errmsg, sizeof (errmsg));
abort();
return (0);
}
void
kernel_init(int mode)
{
umem_nofail_callback(umem_out_of_memory);
physmem = sysconf(_SC_PHYS_PAGES);
dprintf("physmem = %llu pages (%.2f GB)\n", physmem,
(double)physmem * sysconf(_SC_PAGE_SIZE) / (1ULL << 30));
spa_init(mode);
}
void
kernel_fini(void)
{
spa_fini();
}
int
z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen)
{
int ret;
uLongf len = *dstlen;
if ((ret = uncompress(dst, &len, src, srclen)) == Z_OK)
*dstlen = (size_t)len;
return (ret);
}
int
z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen,
int level)
{
int ret;
uLongf len = *dstlen;
if ((ret = compress2(dst, &len, src, srclen, level)) == Z_OK)
*dstlen = (size_t)len;
return (ret);
}

View File

@ -0,0 +1,497 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_ZFS_CONTEXT_H
#define _SYS_ZFS_CONTEXT_H
#pragma ident "%Z%%M% %I% %E% SMI"
#ifdef __cplusplus
extern "C" {
#endif
#define _SYS_MUTEX_H
#define _SYS_RWLOCK_H
#define _SYS_CONDVAR_H
#define _SYS_SYSTM_H
#define _SYS_DEBUG_H
#define _SYS_T_LOCK_H
#define _SYS_VNODE_H
#define _SYS_VFS_H
#define _SYS_SUNDDI_H
#define _SYS_CALLB_H
#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 <vmem.h>
#include <fsshare.h>
#include <sys/note.h>
#include <sys/types.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/debug.h>
#include <sys/sdt.h>
#include <sys/kstat.h>
#include <sys/kernel.h>
#include <machine/atomic.h>
#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 *, ...);
extern void vpanic(const char *, __va_list);
/* This definition is copied from assert.h. */
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
#define verify(EX) (void)((EX) || \
(__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
#else
#define verify(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#endif /* __STDC_VERSION__ - 0 >= 199901L */
#else
#define verify(EX) (void)((EX) || (_assert("EX", __FILE__, __LINE__), 0))
#endif /* __STDC__ */
#define VERIFY verify
#define ASSERT assert
extern void __assert(const char *, const char *, int);
#ifdef lint
#define VERIFY3_IMPL(x, y, z, t) if (x == z) ((void)0)
#else
/* BEGIN CSTYLED */
#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE) do { \
const TYPE __left = (TYPE)(LEFT); \
const TYPE __right = (TYPE)(RIGHT); \
if (!(__left OP __right)) { \
char *__buf = alloca(256); \
(void) snprintf(__buf, 256, "%s %s %s (0x%llx %s 0x%llx)", \
#LEFT, #OP, #RIGHT, \
(u_longlong_t)__left, #OP, (u_longlong_t)__right); \
__assert(__buf, __FILE__, __LINE__); \
} \
_NOTE(CONSTCOND) } while (0)
/* END CSTYLED */
#endif /* lint */
#define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t)
#define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t)
#define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t)
#ifdef NDEBUG
#define ASSERT3S(x, y, z) ((void)0)
#define ASSERT3U(x, y, z) ((void)0)
#define ASSERT3P(x, y, z) ((void)0)
#else
#define ASSERT3S(x, y, z) VERIFY3S(x, y, z)
#define ASSERT3U(x, y, z) VERIFY3U(x, y, z)
#define ASSERT3P(x, y, z) VERIFY3P(x, y, z)
#endif
/*
* Dtrace SDT probes have different signatures in userland than they do in
* kernel. If they're being used in kernel code, re-define them out of
* existence for their counterparts in libzpool.
*/
#ifdef DTRACE_PROBE1
#undef DTRACE_PROBE1
#define DTRACE_PROBE1(a, b, c) ((void)0)
#endif /* DTRACE_PROBE1 */
#ifdef DTRACE_PROBE2
#undef DTRACE_PROBE2
#define DTRACE_PROBE2(a, b, c, d, e) ((void)0)
#endif /* DTRACE_PROBE2 */
#ifdef DTRACE_PROBE3
#undef DTRACE_PROBE3
#define DTRACE_PROBE3(a, b, c, d, e, f, g) ((void)0)
#endif /* DTRACE_PROBE3 */
#ifdef DTRACE_PROBE4
#undef DTRACE_PROBE4
#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0)
#endif /* DTRACE_PROBE4 */
/*
* Threads
*/
#define curthread ((void *)(uintptr_t)thr_self())
typedef struct kthread kthread_t;
#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
zk_thread_create(func, arg)
#define thread_exit() thr_exit(NULL)
extern kthread_t *zk_thread_create(void (*func)(), void *arg);
#define issig(why) (FALSE)
#define ISSIG(thr, why) (FALSE)
/*
* Mutexes
*/
typedef struct kmutex {
void *m_owner;
mutex_t m_lock;
} kmutex_t;
#define MUTEX_DEFAULT USYNC_THREAD
#undef MUTEX_HELD
#define MUTEX_HELD(m) ((m)->m_owner == curthread)
/*
* Argh -- we have to get cheesy here because the kernel and userland
* have different signatures for the same routine.
*/
//extern int _mutex_init(mutex_t *mp, int type, void *arg);
//extern int _mutex_destroy(mutex_t *mp);
#define mutex_init(mp, b, c, d) zmutex_init((kmutex_t *)(mp))
#define mutex_destroy(mp) zmutex_destroy((kmutex_t *)(mp))
extern void zmutex_init(kmutex_t *mp);
extern void zmutex_destroy(kmutex_t *mp);
extern void mutex_enter(kmutex_t *mp);
extern void mutex_exit(kmutex_t *mp);
extern int mutex_tryenter(kmutex_t *mp);
extern void *mutex_owner(kmutex_t *mp);
/*
* RW locks
*/
typedef struct krwlock {
int rw_count;
void *rw_owner;
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
#undef RW_WRITE_HELD
#define RW_WRITE_HELD(x) ((x)->rw_owner == curthread)
#define RW_LOCK_HELD(x) rw_lock_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)
/*
* Condition variables
*/
typedef cond_t kcondvar_t;
#define CV_DEFAULT USYNC_THREAD
extern void cv_init(kcondvar_t *cv, char *name, int type, void *arg);
extern void cv_destroy(kcondvar_t *cv);
extern void cv_wait(kcondvar_t *cv, kmutex_t *mp);
extern clock_t cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime);
extern void cv_signal(kcondvar_t *cv);
extern void cv_broadcast(kcondvar_t *cv);
/*
* Kernel memory
*/
#define KM_SLEEP UMEM_NOFAIL
#define KM_NOSLEEP UMEM_DEFAULT
#define KMC_NODEBUG UMC_NODEBUG
#define kmem_alloc(_s, _f) umem_alloc(_s, _f)
#define kmem_zalloc(_s, _f) umem_zalloc(_s, _f)
#define kmem_free(_b, _s) umem_free(_b, _s)
#define kmem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i) \
umem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i)
#define kmem_cache_destroy(_c) umem_cache_destroy(_c)
#define kmem_cache_alloc(_c, _f) umem_cache_alloc(_c, _f)
#define kmem_cache_free(_c, _b) umem_cache_free(_c, _b)
#define kmem_debugging() 0
#define kmem_cache_reap_now(c)
typedef umem_cache_t kmem_cache_t;
/*
* Task queues
*/
typedef struct taskq taskq_t;
typedef uintptr_t taskqid_t;
typedef void (task_func_t)(void *);
#define TASKQ_PREPOPULATE 0x0001
#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
#define 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 */
extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
extern void taskq_destroy(taskq_t *);
extern void taskq_wait(taskq_t *);
extern int taskq_member(taskq_t *, void *);
/*
* vnodes
*/
typedef struct vnode {
uint64_t v_size;
int v_fd;
char *v_path;
} vnode_t;
typedef struct vattr {
uint_t va_mask; /* bit-mask of attributes */
u_offset_t va_size; /* file size in bytes */
} vattr_t;
#define AT_TYPE 0x0001
#define AT_MODE 0x0002
#define AT_UID 0x0004
#define AT_GID 0x0008
#define AT_FSID 0x0010
#define AT_NODEID 0x0020
#define AT_NLINK 0x0040
#define AT_SIZE 0x0080
#define AT_ATIME 0x0100
#define AT_MTIME 0x0200
#define AT_CTIME 0x0400
#define AT_RDEV 0x0800
#define AT_BLKSIZE 0x1000
#define AT_NBLOCKS 0x2000
#define AT_SEQ 0x8000
#define CRCREAT 0
#define VOP_CLOSE(vp, f, c, o, cr) 0
#define VOP_PUTPAGE(vp, of, sz, fl, cr) 0
#define VOP_GETATTR(vp, vap, fl, cr) ((vap)->va_size = (vp)->v_size, 0)
#define VOP_FSYNC(vp, f, cr) fsync((vp)->v_fd)
#define VN_RELE(vp) vn_close(vp)
extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp,
int x2, int x3);
extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp,
int x2, int x3, vnode_t *vp);
extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len,
offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp);
extern void vn_close(vnode_t *vp);
#define vn_remove(path, x1, x2) remove(path)
#define vn_rename(from, to, seg) rename((from), (to))
#define vn_is_readonly(vp) B_FALSE
extern vnode_t *rootdir;
#include <sys/file.h> /* for FREAD, FWRITE, etc */
#define FTRUNC O_TRUNC
/*
* Random stuff
*/
#define lbolt (gethrtime() >> 23)
#define lbolt64 (gethrtime() >> 23)
//#define hz 119 /* frequency when using gethrtime() >> 23 for lbolt */
extern void delay(clock_t ticks);
#define gethrestime_sec() time(NULL)
#define max_ncpus 64
#define minclsyspri 60
#define maxclsyspri 99
#define CPU_SEQID (thr_self() & (max_ncpus - 1))
#define kcred NULL
#define CRED() NULL
extern uint64_t physmem;
extern int highbit(ulong_t i);
extern int random_get_bytes(uint8_t *ptr, size_t len);
extern int random_get_pseudo_bytes(uint8_t *ptr, size_t len);
extern void kernel_init(int);
extern void kernel_fini(void);
struct spa;
extern void nicenum(uint64_t num, char *buf);
extern void show_pool_stats(struct spa *);
typedef struct callb_cpr {
kmutex_t *cc_lockp;
} callb_cpr_t;
#define CALLB_CPR_INIT(cp, lockp, func, name) { \
(cp)->cc_lockp = lockp; \
}
#define CALLB_CPR_SAFE_BEGIN(cp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
}
#define CALLB_CPR_SAFE_END(cp, lockp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
}
#define CALLB_CPR_EXIT(cp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
mutex_exit((cp)->cc_lockp); \
}
#define zone_dataset_visible(x, y) (1)
#define INGLOBALZONE(z) (1)
#ifdef __cplusplus
}
#endif
/* ZFS Boot Related stuff. */
struct _buf {
intptr_t _fd;
};
struct bootstat {
uint64_t st_size;
};
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);
/* Random compatibility stuff. */
#define lbolt (gethrtime() >> 23)
#define lbolt64 (gethrtime() >> 23)
extern int hz;
extern uint64_t physmem;
#define gethrestime_sec() time(NULL)
#define open64(...) open(__VA_ARGS__)
#define pread64(d, p, n, o) pread(d, p, n, o)
#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)
struct file {
void *dummy;
};
#define FCREAT O_CREAT
#define FOFFMAX 0x0
#define SX_SYSINIT(name, lock, desc)
#define SYSCTL_DECL(...)
#define SYSCTL_INT(...)
#define SYSCTL_NODE(...)
#ifdef TUNABLE_INT
#undef TUNABLE_INT
#endif
#define TUNABLE_INT(...)
/* Errors */
#ifndef ERESTART
#define ERESTART (-1)
#endif
#endif /* _SYS_ZFS_CONTEXT_H */

View File

@ -0,0 +1,250 @@
/*
* 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 <sys/zfs_context.h>
int taskq_now;
typedef struct task {
struct task *task_next;
struct task *task_prev;
task_func_t *task_func;
void *task_arg;
} task_t;
#define TASKQ_ACTIVE 0x00010000
struct taskq {
kmutex_t tq_lock;
krwlock_t tq_threadlock;
kcondvar_t tq_dispatch_cv;
kcondvar_t tq_wait_cv;
thread_t *tq_threadlist;
int tq_flags;
int tq_active;
int tq_nthreads;
int tq_nalloc;
int tq_minalloc;
int tq_maxalloc;
task_t *tq_freelist;
task_t tq_task;
};
static task_t *
task_alloc(taskq_t *tq, int tqflags)
{
task_t *t;
if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
tq->tq_freelist = t->task_next;
} else {
mutex_exit(&tq->tq_lock);
if (tq->tq_nalloc >= tq->tq_maxalloc) {
if (!(tqflags & KM_SLEEP)) {
mutex_enter(&tq->tq_lock);
return (NULL);
}
/*
* We don't want to exceed tq_maxalloc, but we can't
* wait for other tasks to complete (and thus free up
* task structures) without risking deadlock with
* the caller. So, we just delay for one second
* to throttle the allocation rate.
*/
delay(hz);
}
t = kmem_alloc(sizeof (task_t), tqflags);
mutex_enter(&tq->tq_lock);
if (t != NULL)
tq->tq_nalloc++;
}
return (t);
}
static void
task_free(taskq_t *tq, task_t *t)
{
if (tq->tq_nalloc <= tq->tq_minalloc) {
t->task_next = tq->tq_freelist;
tq->tq_freelist = t;
} else {
tq->tq_nalloc--;
mutex_exit(&tq->tq_lock);
kmem_free(t, sizeof (task_t));
mutex_enter(&tq->tq_lock);
}
}
taskqid_t
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
{
task_t *t;
if (taskq_now) {
func(arg);
return (1);
}
mutex_enter(&tq->tq_lock);
ASSERT(tq->tq_flags & TASKQ_ACTIVE);
if ((t = task_alloc(tq, tqflags)) == NULL) {
mutex_exit(&tq->tq_lock);
return (0);
}
t->task_next = &tq->tq_task;
t->task_prev = tq->tq_task.task_prev;
t->task_next->task_prev = t;
t->task_prev->task_next = t;
t->task_func = func;
t->task_arg = arg;
cv_signal(&tq->tq_dispatch_cv);
mutex_exit(&tq->tq_lock);
return (1);
}
void
taskq_wait(taskq_t *tq)
{
mutex_enter(&tq->tq_lock);
while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
mutex_exit(&tq->tq_lock);
}
static void *
taskq_thread(void *arg)
{
taskq_t *tq = arg;
task_t *t;
mutex_enter(&tq->tq_lock);
while (tq->tq_flags & TASKQ_ACTIVE) {
if ((t = tq->tq_task.task_next) == &tq->tq_task) {
if (--tq->tq_active == 0)
cv_broadcast(&tq->tq_wait_cv);
cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
tq->tq_active++;
continue;
}
t->task_prev->task_next = t->task_next;
t->task_next->task_prev = t->task_prev;
mutex_exit(&tq->tq_lock);
rw_enter(&tq->tq_threadlock, RW_READER);
t->task_func(t->task_arg);
rw_exit(&tq->tq_threadlock);
mutex_enter(&tq->tq_lock);
task_free(tq, t);
}
tq->tq_nthreads--;
cv_broadcast(&tq->tq_wait_cv);
mutex_exit(&tq->tq_lock);
return (NULL);
}
/*ARGSUSED*/
taskq_t *
taskq_create(const char *name, int nthreads, pri_t pri,
int minalloc, int maxalloc, uint_t flags)
{
taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
int t;
rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
tq->tq_flags = flags | TASKQ_ACTIVE;
tq->tq_active = nthreads;
tq->tq_nthreads = nthreads;
tq->tq_minalloc = minalloc;
tq->tq_maxalloc = maxalloc;
tq->tq_task.task_next = &tq->tq_task;
tq->tq_task.task_prev = &tq->tq_task;
tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
if (flags & TASKQ_PREPOPULATE) {
mutex_enter(&tq->tq_lock);
while (minalloc-- > 0)
task_free(tq, task_alloc(tq, KM_SLEEP));
mutex_exit(&tq->tq_lock);
}
for (t = 0; t < nthreads; t++)
(void) thr_create(0, 0, taskq_thread,
tq, THR_BOUND, &tq->tq_threadlist[t]);
return (tq);
}
void
taskq_destroy(taskq_t *tq)
{
int t;
int nthreads = tq->tq_nthreads;
taskq_wait(tq);
mutex_enter(&tq->tq_lock);
tq->tq_flags &= ~TASKQ_ACTIVE;
cv_broadcast(&tq->tq_dispatch_cv);
while (tq->tq_nthreads != 0)
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
tq->tq_minalloc = 0;
while (tq->tq_nalloc != 0) {
ASSERT(tq->tq_freelist != NULL);
task_free(tq, task_alloc(tq, KM_SLEEP));
}
mutex_exit(&tq->tq_lock);
for (t = 0; t < nthreads; t++)
(void) thr_join(tq->tq_threadlist[t], NULL, NULL);
kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
rw_destroy(&tq->tq_threadlock);
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);
}

View File

@ -0,0 +1,144 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <sys/zfs_context.h>
#include <sys/avl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/spa.h>
#include <sys/fs/zfs.h>
#include <sys/refcount.h>
/*
* Routines needed by more than one client of libzpool.
*/
void
nicenum(uint64_t num, char *buf)
{
uint64_t n = num;
int index = 0;
char u;
while (n >= 1024) {
n = (n + (1024 / 2)) / 1024; /* Round up or down */
index++;
}
u = " KMGTPE"[index];
if (index == 0) {
(void) sprintf(buf, "%llu", (u_longlong_t)n);
} else if (n < 10 && (num & (num - 1)) != 0) {
(void) sprintf(buf, "%.2f%c",
(double)num / (1ULL << 10 * index), u);
} else if (n < 100 && (num & (num - 1)) != 0) {
(void) sprintf(buf, "%.1f%c",
(double)num / (1ULL << 10 * index), u);
} else {
(void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
}
}
static void
show_vdev_stats(const char *desc, nvlist_t *nv, int indent)
{
nvlist_t **child;
uint_t c, children;
vdev_stat_t *vs;
uint64_t sec;
char used[6], avail[6];
char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6];
if (indent == 0) {
(void) printf(" "
" capacity operations bandwidth ---- errors ----\n");
(void) printf("description "
"used avail read write read write read write cksum\n");
}
VERIFY(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &c) == 0);
sec = MAX(1, vs->vs_timestamp / NANOSEC);
nicenum(vs->vs_alloc, used);
nicenum(vs->vs_space - vs->vs_alloc, avail);
nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops);
nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops);
nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes);
nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes);
nicenum(vs->vs_read_errors, rerr);
nicenum(vs->vs_write_errors, werr);
nicenum(vs->vs_checksum_errors, cerr);
(void) printf("%*s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
indent, "",
indent - 19 - (vs->vs_space ? 0 : 12), desc,
vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
rops, wops, rbytes, wbytes, rerr, werr, cerr);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&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, cnv, indent + 2);
free(tname);
}
}
void
show_pool_stats(spa_t *spa)
{
nvlist_t *config, *nvroot;
char *name;
spa_config_enter(spa, RW_READER, FTAG);
config = spa_config_generate(spa, NULL, -1ULL, B_TRUE);
spa_config_exit(spa, FTAG);
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, nvroot, 0);
}

20
cddl/lib/Makefile Normal file
View File

@ -0,0 +1,20 @@
# $FreeBSD$
.include <bsd.own.mk>
SUBDIR= libavl
SUBDIR+=libnvpair
SUBDIR+=libumem
SUBDIR+=libuutil
.if ${MK_CDDL} != "no"
SUBDIR+=libzfs
SUBDIR+=${_libzpool}
.endif
.if ${MACHINE_ARCH} == "i386"
.if ${MACHINE} == "i386"
_libzpool= libzpool
.endif
.endif
.include <bsd.subdir.mk>

5
cddl/lib/Makefile.inc Normal file
View File

@ -0,0 +1,5 @@
# $FreeBSD$
SHLIBDIR?= /lib
.include "../Makefile.inc"

13
cddl/lib/libavl/Makefile Normal file
View File

@ -0,0 +1,13 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/common/avl
LIB= avl
SHLIB_MAJOR?=1
SRCS= avl.c
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
.include <bsd.lib.mk>

View File

@ -0,0 +1,18 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../contrib/opensolaris/lib/libnvpair
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/common/nvpair
LIB= nvpair
SHLIB_MAJOR?=1
SRCS= libnvpair.c
SRCS+= nvpair_alloc_system.c
SRCS+= nvpair_alloc_fixed.c
SRCS+= nvpair.c
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../include
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
.include <bsd.lib.mk>

12
cddl/lib/libumem/Makefile Normal file
View File

@ -0,0 +1,12 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../compat/opensolaris/lib/libumem
LIB= umem
SHLIB_MAJOR?=1
SRCS= umem.c
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
.include <bsd.lib.mk>

View File

@ -0,0 +1,27 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../contrib/opensolaris/lib/libuutil/common
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/common/avl
LIB= uutil
SHLIB_MAJOR?=1
SRCS= avl.c
SRCS+= uu_alloc.c
SRCS+= uu_avl.c
SRCS+= uu_dprintf.c
SRCS+= uu_ident.c
SRCS+= uu_list.c
SRCS+= uu_misc.c
SRCS+= uu_open.c
SRCS+= uu_pname.c
SRCS+= uu_strtoint.c
CFLAGS+=-DNATIVE_BUILD
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libuutil/common
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
.include <bsd.lib.mk>

49
cddl/lib/libzfs/Makefile Normal file
View File

@ -0,0 +1,49 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../compat/opensolaris/misc
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/common/zfs
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
.PATH: ${.CURDIR}/../../../contrib/opensolaris/lib/libzfs/common
LIB= zfs
SHLIB_MAJOR?=1
DPADD= ${LIBUTIL}
LDADD= -lutil
SRCS= mnttab.c
SRCS+= mkdirp.c
SRCS+= zmount.c
SRCS+= fsshare.c
SRCS+= zone.c
SRCS+= zfs_namecheck.c
SRCS+= zfs_prop.c
SRCS+= libzfs_dataset.c
SRCS+= libzfs_util.c
SRCS+= libzfs_graph.c
SRCS+= libzfs_mount.c
SRCS+= libzfs_pool.c
SRCS+= libzfs_changelist.c
SRCS+= libzfs_config.c
SRCS+= libzfs_import.c
SRCS+= libzfs_status.c
CFLAGS+=-DZFS_NO_ACL
CFLAGS+=-I${.CURDIR}/../../../sbin/mount
CFLAGS+=-I${.CURDIR}/../../../cddl/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/common/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/sys
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libnvpair
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libuutil/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzfs/common
CFLAGS+=-I${.CURDIR}/../../../sys
.include <bsd.lib.mk>

View File

@ -0,0 +1,46 @@
# $FreeBSD$
.include "${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/Makefile.files"
# ZFS_COMMON_SRCS
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
# ZFS_SHARED_SRCS
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/common/zfs
# KERNEL_SRCS
.PATH: ${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
# LIST_SRCS
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/os
# ATOMIC_SRCS
.PATH: ${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/arch/${MACHINE_ARCH}
LIB= zpool
SHLIB_MAJOR?=1
ZFS_COMMON_SRCS= ${ZFS_COMMON_OBJS:C/.o$/.c/} vdev_file.c
ZFS_SHARED_SRCS= ${ZFS_SHARED_OBJS:C/.o$/.c/}
KERNEL_SRCS= kernel.c taskq.c util.c
LIST_SRCS= list.c
ATOMIC_SRCS= atomic.S
SRCS= ${ZFS_COMMON_SRCS} ${ZFS_SHARED_SRCS} ${KERNEL_SRCS} ${LIST_SRCS} ${ATOMIC_SRCS}
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/sys
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/common/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
CFLAGS+=-I${.CURDIR}/../../../cddl/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libnvpair
CFLAGS+=-I${.CURDIR}/../../../sys
DPADD= ${LIBTHR} ${LIBZ}
LDADD= -lthr -lz
# atomic.S doesn't like profiling.
NO_PROFILE=
.include <bsd.lib.mk>

11
cddl/sbin/Makefile Normal file
View File

@ -0,0 +1,11 @@
# $FreeBSD$
.include <bsd.own.mk>
SUBDIR=
.if ${MK_ZFS} != "no"
SUBDIR+=zfs
SUBDIR+=zpool
.endif
.include <bsd.subdir.mk>

5
cddl/sbin/Makefile.inc Normal file
View File

@ -0,0 +1,5 @@
# $FreeBSD$
BINDIR?= /sbin
.include "../Makefile.inc"

27
cddl/sbin/zfs/Makefile Normal file
View File

@ -0,0 +1,27 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../contrib/opensolaris/cmd/zfs
PROG= zfs
MAN= zfs.8
SRCS= zfs_main.c
SRCS+= zfs_iter.c
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libuutil/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzfs/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libumem/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libnvpair
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/sys
CFLAGS+=-I${.CURDIR}/../../../sys
DPADD= ${LIBGEOM} ${LIBM} ${LIBNVPAIR} ${LIBUUTIL} ${LIBZFS}
LDADD= -lgeom -lm -lnvpair -luutil -lzfs
.include <bsd.prog.mk>

29
cddl/sbin/zpool/Makefile Normal file
View File

@ -0,0 +1,29 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../contrib/opensolaris/cmd/zpool
PROG= zpool
MAN= zpool.8
SRCS= zpool_main.c
SRCS+= zpool_vdev.c
SRCS+= zpool_iter.c
SRCS+= zpool_util.c
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libuutil/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libumem/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzfs/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libnvpair
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/sys
CFLAGS+=-I${.CURDIR}/../../../sys
DPADD= ${LIBAVL} ${LIBGEOM} ${LIBM} ${LIBNVPAIR} ${LIBUUTIL} ${LIBZFS}
LDADD= -lavl -lgeom -lm -lnvpair -luutil -lzfs
.include <bsd.prog.mk>

15
cddl/usr.bin/Makefile Normal file
View File

@ -0,0 +1,15 @@
# $FreeBSD$
.include <bsd.own.mk>
.if ${MK_ZFS} != "no"
SUBDIR= ${_ztest}
.endif
.if ${MACHINE_ARCH} == "i386"
.if ${MACHINE} == "i386"
_ztest= ztest
.endif
.endif
.include <bsd.subdir.mk>

View File

@ -0,0 +1,5 @@
# $FreeBSD$
BINDIR?= /usr/bin
.include "../Makefile.inc"

View File

@ -0,0 +1,23 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../contrib/opensolaris/cmd/ztest
PROG= ztest
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/sys
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
CFLAGS+=-I${.CURDIR}/../../lib/libumem
CFLAGS+=-I${.CURDIR}/../../../sys
DPADD= ${LIBAVL} ${LIBM} ${LIBNVPAIR} ${LIBTHR} ${LIBUMEM} ${LIBZPOOL}
LDADD= -lavl -lm -lnvpair -lthr -lumem -lzpool
NO_MAN=
.include <bsd.prog.mk>

16
cddl/usr.sbin/Makefile Normal file
View File

@ -0,0 +1,16 @@
# $FreeBSD$
.include <bsd.own.mk>
SUBDIR=
.if ${MK_ZFS} != "no"
SUBDIR+=${_zdb}
.endif
.if ${MACHINE_ARCH} == "i386"
.if ${MACHINE} == "i386"
_zdb= zdb
.endif
.endif
.include <bsd.subdir.mk>

View File

@ -0,0 +1,5 @@
# $FreeBSD$
BINDIR?= /usr/sbin
.include "../Makefile.inc"

View File

@ -0,0 +1,23 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../contrib/opensolaris/cmd/zdb
PROG= zdb
MAN= zdb.8
SRCS+= zdb.c zdb_il.c
CFLAGS+=-I${.CURDIR}/../../../sys/compat/opensolaris
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/include
CFLAGS+=-I${.CURDIR}/../../../compat/opensolaris/lib/libumem
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/lib/libzpool/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common
CFLAGS+=-I${.CURDIR}/../../../sys/contrib/opensolaris/uts/common/sys
CFLAGS+=-I${.CURDIR}/../../../contrib/opensolaris/head
CFLAGS+=-I${.CURDIR}/../../lib/libumem
CFLAGS+=-I${.CURDIR}/../../../sys
DPADD= ${LIBAVL} ${LIBM} ${LIBNVPAIR} ${LIBTHR} ${LIBUMEM} ${LIBZPOOL}
LDADD= -lavl -lm -lnvpair -lthr -lumem -lzpool
.include <bsd.prog.mk>

View File

@ -0,0 +1,49 @@
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _OPENSOLARIS_DEVID_H_
#define _OPENSOLARIS_DEVID_H_
#include <sys/types.h>
#include <stdlib.h>
typedef int ddi_devid_t;
typedef struct devid_nmlist {
char *devname;
dev_t dev;
} devid_nmlist_t;
static inline int devid_str_decode(char *devidstr, ddi_devid_t *retdevid, char **retminor_name) { abort(); }
static inline int devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name, devid_nmlist_t **retlist) { abort(); }
static inline void devid_str_free(char *str) { abort(); }
static inline void devid_free(ddi_devid_t devid) { abort(); }
static inline void devid_free_nmlist(devid_nmlist_t *list) { abort(); }
static inline int devid_get(int fd, ddi_devid_t *retdevid) { return -1; }
static inline int devid_get_minor_name(int fd, char **retminor_name) { abort(); }
static inline char *devid_str_encode(ddi_devid_t devid, char *minor_name) { abort(); }
#endif /* !_OPENSOLARIS_DEVID_H_ */

View File

@ -0,0 +1,35 @@
/*-
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _OPENSOLARIS_FSSHARE_H_
#define _OPENSOLARIS_FSSHARE_H_
int fsshare(const char *, const char *, const char *);
int fsunshare(const char *, const char *);
#endif /* !_OPENSOLARIS_FSSHARE_H_ */

View File

@ -0,0 +1,11 @@
#ifndef _LIBINTL_H_
#define _LIBINTL_H_
#include <sys/cdefs.h>
#include <stdio.h>
#define textdomain(domain) 0
#define gettext(...) (__VA_ARGS__)
#define dgettext(domain, ...) (__VA_ARGS__)
#endif /* !_SOLARIS_H_ */

View File

@ -0,0 +1,19 @@
#ifndef _OPENSOLARIS_MNTTAB_H_
#define _OPENSOLARIS_MNTTAB_H_
#include <stdio.h>
#include <paths.h>
#define MNTTAB _PATH_DEVNULL
#define MNT_LINE_MAX 1024
struct mnttab {
char *mnt_special;
char *mnt_mountp;
char *mnt_fstype;
char *mnt_mntopts;
};
int getmntany(FILE *fd, struct mnttab *mgetp, struct mnttab *mrefp);
#endif /* !_OPENSOLARIS_MNTTAB_H_ */

View File

@ -0,0 +1,18 @@
#ifndef _OPENSOLARIS_PRIV_H_
#define _OPENSOLARIS_PRIV_H_
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#define PRIV_SYS_CONFIG 0
static __inline int
priv_ineffect(priv)
{
assert(priv == PRIV_SYS_CONFIG);
return (geteuid() == 0);
}
#endif /* !_OPENSOLARIS_PRIV_H_ */

View File

@ -0,0 +1,8 @@
#ifndef _SOLARIS_H_
#define _SOLARIS_H_
#include <sys/ccompile.h>
#define dirent64 dirent
#endif /* !_SOLARIS_H_ */

View File

@ -0,0 +1,6 @@
#ifndef _ZONE_H_
#define _ZONE_H_
#include <sys/zone.h>
#endif /* !_ZONE_H_ */

View File

@ -0,0 +1,167 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Ricardo Correia. All rights reserved.
* Use is subject to license terms.
*/
#include <umem.h>
#include <stdlib.h>
#include <assert.h>
static umem_nofail_callback_t *nofail_cb = NULL;
struct umem_cache {
umem_constructor_t *constructor;
umem_destructor_t *destructor;
void *callback_data;
size_t bufsize;
};
/*
* Simple stub for umem_alloc(). The callback isn't expected to return.
*/
void *umem_alloc(size_t size, int flags)
{
assert(flags == UMEM_DEFAULT || flags == UMEM_NOFAIL);
if(size == 0)
return NULL;
void *ret = malloc(size);
if(ret == NULL) {
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
return ret;
}
/*
* Simple stub for umem_zalloc().
*/
void *umem_zalloc(size_t size, int flags)
{
assert(flags == UMEM_DEFAULT || flags == UMEM_NOFAIL);
if(size == 0)
return NULL;
void *ret = calloc(1, size);
if(ret == NULL) {
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
return ret;
}
/*
* Simple stub for umem_free().
*/
void umem_free(void *buf, size_t size)
{
free(buf);
}
/*
* Simple stub for umem_nofail_callback().
*/
void umem_nofail_callback(umem_nofail_callback_t *callback)
{
nofail_cb = callback;
}
/*
* Simple stub for umem_cache_create().
*/
umem_cache_t *umem_cache_create(char *debug_name, size_t bufsize, size_t align, umem_constructor_t *constructor, umem_destructor_t *destructor, umem_reclaim_t *reclaim, void *callback_data, void *source, int cflags)
{
assert(source == NULL);
umem_cache_t *cache = malloc(sizeof(umem_cache_t));
if(cache == NULL)
return NULL;
cache->constructor = constructor;
cache->destructor = destructor;
cache->callback_data = callback_data;
cache->bufsize = bufsize;
return cache;
}
/*
* Simple stub for umem_cache_alloc(). The nofail callback isn't expected to return.
*/
void *umem_cache_alloc(umem_cache_t *cache, int flags)
{
void *buf = malloc(cache->bufsize);
if(buf == NULL) {
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
if(cache->constructor != NULL) {
if(cache->constructor(buf, cache->callback_data, flags) != 0) {
free(buf);
if(!(flags & UMEM_NOFAIL))
return NULL;
if(nofail_cb != NULL)
nofail_cb();
abort();
}
}
return buf;
}
/*
* Simple stub for umem_cache_free().
*/
void umem_cache_free(umem_cache_t *cache, void *buffer)
{
if(cache->destructor != NULL)
cache->destructor(buffer, cache->callback_data);
free(buffer);
}
/*
* Simple stub for umem_cache_destroy().
*/
void umem_cache_destroy(umem_cache_t *cache)
{
free(cache);
}

View File

@ -0,0 +1,85 @@
/*
* 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.
*/
#ifndef _UMEM_H
#define _UMEM_H
#include <sys/types.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define UMEM_DEFAULT 0x0000 /* normal -- may fail */
#define UMEM_NOFAIL 0x0100 /* Never fails -- may call exit(2) */
#define UMEM_FLAGS 0xffff /* all settable umem flags */
extern void *umem_alloc(size_t, int);
extern void *umem_alloc_align(size_t, size_t, int);
extern void *umem_zalloc(size_t, int);
extern void umem_free(void *, size_t);
extern void umem_free_align(void *, size_t);
/*
* Flags for umem_cache_create()
*/
#define UMC_NOTOUCH 0x00010000
#define UMC_NODEBUG 0x00020000
#define UMC_NOMAGAZINE 0x00040000
#define UMC_NOHASH 0x00080000
struct umem_cache; /* cache structure is opaque to umem clients */
typedef struct umem_cache umem_cache_t;
typedef int umem_constructor_t(void *, void *, int);
typedef void umem_destructor_t(void *, void *);
typedef void umem_reclaim_t(void *);
typedef int umem_nofail_callback_t(void);
#define UMEM_CALLBACK_RETRY 0
#define UMEM_CALLBACK_EXIT(status) (0x100 | ((status) & 0xFF))
extern void umem_nofail_callback(umem_nofail_callback_t *);
extern umem_cache_t *umem_cache_create(char *, size_t,
size_t, umem_constructor_t *, umem_destructor_t *, umem_reclaim_t *,
void *, void *, int);
extern void umem_cache_destroy(umem_cache_t *);
extern void *umem_cache_alloc(umem_cache_t *, int);
extern void umem_cache_free(umem_cache_t *, void *);
extern void umem_reap(void);
#ifdef __cplusplus
}
#endif
#endif /* _UMEM_H */

View File

@ -0,0 +1,242 @@
/*-
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <libutil.h>
#include <assert.h>
#include <pathnames.h> /* _PATH_MOUNTDPID */
#include <fsshare.h>
#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n"
#define OPTSSIZE 1024
#define MAXLINESIZE (PATH_MAX + OPTSSIZE)
static void
restart_mountd(void)
{
struct pidfh *pfh;
pid_t mountdpid;
pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid);
if (pfh != NULL) {
/* Mountd is not running. */
pidfile_remove(pfh);
return;
}
if (errno != EEXIST) {
/* Cannot open pidfile for some reason. */
return;
}
/* We have mountd(8) PID in mountdpid varible. */
kill(mountdpid, SIGHUP);
}
/*
* Read one line from a file. Skip comments, empty lines and a line with a
* mountpoint specified in the 'skip' argument.
*/
static char *
getline(FILE *fd, const char *skip)
{
static char line[MAXLINESIZE];
size_t len, skiplen;
char *s, last;
if (skip != NULL)
skiplen = strlen(skip);
for (;;) {
s = fgets(line, sizeof(line), fd);
if (s == NULL)
return (NULL);
/* Skip empty lines and comments. */
if (line[0] == '\n' || line[0] == '#')
continue;
len = strlen(line);
if (line[len - 1] == '\n')
line[len - 1] = '\0';
last = line[skiplen];
/* Skip the given mountpoint. */
if (skip != NULL && strncmp(skip, line, skiplen) == 0 &&
(last == '\t' || last == ' ' || last == '\0')) {
continue;
}
break;
}
return (line);
}
/*
* Function translate options to a format acceptable by exports(5), eg.
*
* -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0
*
* Accepted input formats:
*
* ro,network=192.168.0.0,mask=255.255.255.0,maproot=0
* ro network=192.168.0.0 mask=255.255.255.0 maproot=0
* -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0
* -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0
*/
static char *
translate_opts(const char *shareopts)
{
static char newopts[OPTSSIZE];
char oldopts[OPTSSIZE], opt[64];
char *o, *s = NULL;
strlcpy(oldopts, shareopts, sizeof(oldopts));
newopts[0] = '\0';
s = oldopts;
while ((o = strsep(&s, "-, ")) != NULL) {
if (o[0] == '\0')
continue;
snprintf(opt, sizeof(opt), "-%s ", o);
strlcat(newopts, opt, sizeof(newopts));
}
return (newopts);
}
static int
fsshare_main(const char *file, const char *mountpoint, const char *shareopts,
int share)
{
char tmpfile[PATH_MAX];
char *line;
FILE *newfd, *oldfd;
int fd, error;
newfd = oldfd = NULL;
error = 0;
/*
* Create temporary file in the same directory, so we can atomically
* rename it.
*/
if (strlcpy(tmpfile, file, sizeof(tmpfile)) >= sizeof(tmpfile))
return (ENAMETOOLONG);
if (strlcat(tmpfile, ".XXXXXXXX", sizeof(tmpfile)) >= sizeof(tmpfile))
return (ENAMETOOLONG);
fd = mkstemp(tmpfile);
if (fd == -1)
return (errno);
/*
* File name is random, so we don't really need file lock now, but it
* will be needed after rename(2).
*/
error = flock(fd, LOCK_EX);
assert(error == 0 || (error == -1 && errno == EOPNOTSUPP));
newfd = fdopen(fd, "r+");
assert(newfd != NULL);
/* Open old exports file. */
oldfd = fopen(file, "r");
if (oldfd == NULL) {
if (share) {
if (errno != ENOENT) {
error = errno;
goto out;
}
} else {
/* If there is no exports file, ignore the error. */
if (errno == ENOENT)
errno = 0;
error = errno;
goto out;
}
} else {
error = flock(fileno(oldfd), LOCK_EX);
assert(error == 0 || (error == -1 && errno == EOPNOTSUPP));
error = 0;
}
/* Place big, fat warning at the begining of the file. */
fprintf(newfd, "%s", FILE_HEADER);
while (oldfd != NULL && (line = getline(oldfd, mountpoint)) != NULL)
fprintf(newfd, "%s\n", line);
if (oldfd != NULL && ferror(oldfd) != 0) {
error = ferror(oldfd);
goto out;
}
if (ferror(newfd) != 0) {
error = ferror(newfd);
goto out;
}
if (share) {
fprintf(newfd, "%s\t%s\n", mountpoint,
translate_opts(shareopts));
}
out:
if (error != 0)
unlink(tmpfile);
else {
if (rename(tmpfile, file) == -1) {
error = errno;
unlink(tmpfile);
} else {
/*
* Send SIGHUP to mountd, but unlock exports file later.
*/
restart_mountd();
}
}
if (oldfd != NULL) {
flock(fileno(oldfd), LOCK_UN);
fclose(oldfd);
}
if (newfd != NULL) {
flock(fileno(newfd), LOCK_UN);
fclose(newfd);
}
return (error);
}
/*
* Add the given mountpoint to the given exports file.
*/
int
fsshare(const char *file, const char *mountpoint, const char *shareopts)
{
return (fsshare_main(file, mountpoint, shareopts, 1));
}
/*
* Remove the given mountpoint from the given exports file.
*/
int
fsunshare(const char *file, const char *mountpoint)
{
return (fsshare_main(file, mountpoint, NULL, 0));
}

View File

@ -0,0 +1,213 @@
/*
* 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 (c) 1988 AT&T */
/* All Rights Reserved */
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)mkdirp.c 1.15 06/01/04 SMI"
/*
* Creates directory and it's parents if the parents do not
* exist yet.
*
* Returns -1 if fails for reasons other than non-existing
* parents.
* Does NOT simplify pathnames with . or .. in them.
*/
#include <sys/types.h>
#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
static char *simplify(const char *str);
int
mkdirp(const char *d, mode_t mode)
{
char *endptr, *ptr, *slash, *str;
str = simplify(d);
/* If space couldn't be allocated for the simplified names, return. */
if (str == NULL)
return (-1);
/* Try to make the directory */
if (mkdir(str, mode) == 0) {
free(str);
return (0);
}
if (errno != ENOENT) {
free(str);
return (-1);
}
endptr = strrchr(str, '\0');
slash = strrchr(str, '/');
/* Search upward for the non-existing parent */
while (slash != NULL) {
ptr = slash;
*ptr = '\0';
/* If reached an existing parent, break */
if (access(str, F_OK) == 0)
break;
/* If non-existing parent */
else {
slash = strrchr(str, '/');
/* If under / or current directory, make it. */
if (slash == NULL || slash == str) {
if (mkdir(str, mode) != 0 && errno != EEXIST) {
free(str);
return (-1);
}
break;
}
}
}
/* Create directories starting from upmost non-existing parent */
while ((ptr = strchr(str, '\0')) != endptr) {
*ptr = '/';
if (mkdir(str, mode) != 0 && errno != EEXIST) {
/*
* If the mkdir fails because str already
* exists (EEXIST), then str has the form
* "existing-dir/..", and this is really
* ok. (Remember, this loop is creating the
* portion of the path that didn't exist)
*/
free(str);
return (-1);
}
}
free(str);
return (0);
}
/*
* simplify - given a pathname, simplify that path by removing
* duplicate contiguous slashes.
*
* A simplified copy of the argument is returned to the
* caller, or NULL is returned on error.
*
* The caller should handle error reporting based upon the
* returned vlaue, and should free the returned value,
* when appropriate.
*/
static char *
simplify(const char *str)
{
int i;
size_t mbPathlen; /* length of multi-byte path */
size_t wcPathlen; /* length of wide-character path */
wchar_t *wptr; /* scratch pointer */
wchar_t *wcPath; /* wide-character version of the path */
char *mbPath; /* The copy fo the path to be returned */
/*
* bail out if there is nothing there.
*/
if (!str)
return (NULL);
/*
* Get a copy of the argument.
*/
if ((mbPath = strdup(str)) == NULL) {
return (NULL);
}
/*
* convert the multi-byte version of the path to a
* wide-character rendering, for doing our figuring.
*/
mbPathlen = strlen(mbPath);
if ((wcPath = calloc(sizeof (wchar_t), mbPathlen+1)) == NULL) {
free(mbPath);
return (NULL);
}
if ((wcPathlen = mbstowcs(wcPath, mbPath, mbPathlen)) == (size_t)-1) {
free(mbPath);
free(wcPath);
return (NULL);
}
/*
* remove duplicate slashes first ("//../" -> "/")
*/
for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
*wptr++ = wcPath[i];
if (wcPath[i] == '/') {
i++;
while (wcPath[i] == '/') {
i++;
}
i--;
}
}
*wptr = '\0';
/*
* now convert back to the multi-byte format.
*/
if (wcstombs(mbPath, wcPath, mbPathlen) == (size_t)-1) {
free(mbPath);
free(wcPath);
return (NULL);
}
free(wcPath);
return (mbPath);
}

View File

@ -0,0 +1,160 @@
/*-
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This file implements Solaris compatible getmntany() and hasmntopt()
* functions.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <stdio.h>
static char *
mntopt(char **p)
{
char *cp = *p;
char *retstr;
while (*cp && isspace(*cp))
cp++;
retstr = cp;
while (*cp && *cp != ',')
cp++;
if (*cp) {
*cp = '\0';
cp++;
}
*p = cp;
return (retstr);
}
char *
hasmntopt(struct mnttab *mnt, char *opt)
{
char tmpopts[MNT_LINE_MAX];
char *f, *opts = tmpopts;
if (mnt->mnt_mntopts == NULL)
return (NULL);
(void) strcpy(opts, mnt->mnt_mntopts);
f = mntopt(&opts);
for (; *f; f = mntopt(&opts)) {
if (strncmp(opt, f, strlen(opt)) == 0)
return (f - tmpopts + mnt->mnt_mntopts);
}
return (NULL);
}
static void
optadd(char *mntopts, size_t size, const char *opt)
{
if (mntopts[0] != '\0')
strlcat(mntopts, ",", size);
strlcat(mntopts, opt, size);
}
int
getmntany(FILE *fd __unused, struct mnttab *mgetp, struct mnttab *mrefp)
{
static struct statfs *sfs = NULL;
static char mntopts[MNTMAXSTR];
struct opt *o;
long i, n, flags;
if (sfs != NULL) {
free(sfs);
sfs = NULL;
}
mntopts[0] = '\0';
n = getfsstat(NULL, 0, MNT_NOWAIT);
if (n == -1)
return (-1);
n = sizeof(*sfs) * (n + 8);
sfs = malloc(n);
if (sfs == NULL)
return (-1);
n = getfsstat(sfs, n, MNT_WAIT);
if (n == -1) {
free(sfs);
sfs = NULL;
return (-1);
}
for (i = 0; i < n; i++) {
if (mrefp->mnt_special != NULL &&
strcmp(mrefp->mnt_special, sfs[i].f_mntfromname) != 0) {
continue;
}
if (mrefp->mnt_mountp != NULL &&
strcmp(mrefp->mnt_mountp, sfs[i].f_mntonname) != 0) {
continue;
}
if (mrefp->mnt_fstype != NULL &&
strcmp(mrefp->mnt_fstype, sfs[i].f_fstypename) != 0) {
continue;
}
flags = sfs[i].f_flags;
#define OPTADD(opt) optadd(mntopts, sizeof(mntopts), (opt))
if (flags & MNT_RDONLY)
OPTADD(MNTOPT_RO);
else
OPTADD(MNTOPT_RW);
if (flags & MNT_NOSUID)
OPTADD(MNTOPT_NOSUID);
else
OPTADD(MNTOPT_SETUID);
if (flags & MNT_UPDATE)
OPTADD(MNTOPT_REMOUNT);
if (flags & MNT_NOATIME)
OPTADD(MNTOPT_NOATIME);
else
OPTADD(MNTOPT_ATIME);
OPTADD(MNTOPT_NOXATTR);
if (flags & MNT_NOEXEC)
OPTADD(MNTOPT_NOEXEC);
else
OPTADD(MNTOPT_EXEC);
#undef OPTADD
mgetp->mnt_special = sfs[i].f_mntfromname;
mgetp->mnt_mountp = sfs[i].f_mntonname;
mgetp->mnt_fstype = sfs[i].f_fstypename;
mgetp->mnt_mntopts = mntopts;
return (0);
}
free(sfs);
sfs = NULL;
return (-1);
}

View File

@ -0,0 +1,102 @@
/*-
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This file implements Solaris compatible zmount() function.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/uio.h>
#include <sys/mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
static void
build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
size_t len)
{
int i;
if (*iovlen < 0)
return;
i = *iovlen;
*iov = realloc(*iov, sizeof(**iov) * (i + 2));
if (*iov == NULL) {
*iovlen = -1;
return;
}
(*iov)[i].iov_base = strdup(name);
(*iov)[i].iov_len = strlen(name) + 1;
i++;
(*iov)[i].iov_base = val;
if (len == (size_t)-1) {
if (val != NULL)
len = strlen(val) + 1;
else
len = 0;
}
(*iov)[i].iov_len = (int)len;
*iovlen = ++i;
}
int
zmount(const char *spec, const char *dir, int mflag, char *fstype,
char *dataptr, int datalen, char *optptr, int optlen)
{
struct iovec *iov;
char *optstr, *os, *p;
int iovlen, rv;
assert(spec != NULL);
assert(dir != NULL);
assert(mflag == 0);
assert(fstype != NULL);
assert(strcmp(fstype, MNTTYPE_ZFS) == 0);
assert(dataptr == NULL);
assert(datalen == 0);
assert(optptr != NULL);
assert(optlen > 0);
optstr = strdup(optptr);
assert(optptr != NULL);
iov = NULL;
iovlen = 0;
build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, dir),
(size_t)-1);
build_iovec(&iov, &iovlen, "from", __DECONST(char *, spec), (size_t)-1);
for (p = optstr; p != NULL; strsep(&p, ",/ "))
build_iovec(&iov, &iovlen, p, NULL, (size_t)-1);
rv = nmount(iov, iovlen, 0);
free(optstr);
return (rv);
}

View File

@ -0,0 +1,46 @@
/*-
* Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/zone.h>
int
getzoneid(void)
{
size_t size;
int jailid;
/* Information that we are in jail or not is enough for our needs. */
size = sizeof(jailid);
if (sysctlbyname("security.jail.jailed", &jailid, &size, NULL, 0) == -1)
assert(!"No security.jail.jailed sysctl!");
return (jailid);
}

View File

@ -0,0 +1,97 @@
'\" te
.\" 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, Sun Microsystems, Inc. All Rights Reserved.
.TH zdb 1M "31 Oct 2005" "SunOS 5.11" "System Administration Commands"
.SH NAME
zdb \- ZFS debugger
.SH SYNOPSIS
.LP
.nf
\fBzdb\fR \fIpool\fR
.fi
.SH DESCRIPTION
.LP
The \fBzdb\fR command is used by support engineers to diagnose failures and gather statistics. Since the \fBZFS\fR file system is always consistent on disk and is self-repairing, \fBzdb\fR should only be run under the direction by a support engineer.
.LP
If no arguments are specified, \fBzdb\fR, performs basic consistency checks on the pool and associated datasets, and report any problems detected.
.LP
Any options supported by this command are internal to Sun and subject to change at any time.
.SH EXIT STATUS
.LP
The following exit values are returned:
.sp
.ne 2
.mk
.na
\fB\fB0\fR\fR
.ad
.RS 5n
.rt
The pool is consistent.
.RE
.sp
.ne 2
.mk
.na
\fB\fB1\fR\fR
.ad
.RS 5n
.rt
An error was detected.
.RE
.sp
.ne 2
.mk
.na
\fB\fB2\fR\fR
.ad
.RS 5n
.rt
Invalid command line options were specified.
.RE
.SH ATTRIBUTES
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
.sp
.TS
tab() box;
cw(2.75i) |cw(2.75i)
lw(2.75i) |lw(2.75i)
.
ATTRIBUTE TYPEATTRIBUTE VALUE
_
AvailabilitySUNWzfsu
_
Interface StabilityUnstable
.TE
.SH SEE ALSO
.LP
\fBzfs\fR(1M), \fBzpool\fR(1M), \fBattributes\fR(5)

Some files were not shown because too many files have changed in this diff Show More