/*-
* Copyright (c) 2007 Doug Rabson
* 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
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/*
* Stand-alone ZFS file reader.
*/
#include "zfsimpl.h"
#include "zfssubr.c"
struct zfsmount {
};
/*
* List of all vdevs, chained through v_alllink.
*/
/*
* List of ZFS features supported for read
*/
static const char *features_for_read[] = {
"org.illumos:lz4_compress",
"com.delphix:hole_birth",
"com.delphix:extensible_dataset",
"com.delphix:embedded_data",
"org.open-zfs:large_blocks",
"org.illumos:sha512",
};
/*
* List of all pools, chained through spa_link.
*/
static char *dnode_cache_buf;
static char *zap_scratch;
static void
zfs_init(void)
{
zfs_init_crc();
}
static void *
{
char *ptr;
printf("ZFS: out of temporary buffer space\n");
for (;;) ;
}
ptr = zfs_temp_ptr;
zfs_temp_ptr += size;
return (ptr);
}
static void
{
zfs_temp_ptr -= size;
if (zfs_temp_ptr != ptr) {
printf("ZFS: zfs_alloc()/zfs_free() mismatch\n");
for (;;) ;
}
}
static int
{
| ((*xdr)[3] << 0);
(*xdr) += 4;
return (0);
}
static int
{
| ((*xdr)[3] << 0);
(*xdr) += 4;
return (0);
}
static int
{
return (0);
}
static int
{
const unsigned char *p, *pair;
int junk;
p = nvlist;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
while (encoded_size && decoded_size) {
const char *pairname;
pairname = (const char*) p;
if (elementsp)
if (type == DATA_TYPE_UINT64) {
return (0);
} else if (type == DATA_TYPE_STRING) {
int len;
(*(const char**) valuep) = (const char*) p;
return (0);
} else if (type == DATA_TYPE_NVLIST
|| type == DATA_TYPE_NVLIST_ARRAY) {
(*(const unsigned char**) valuep) =
(const unsigned char*) p;
return (0);
} else {
return (EIO);
}
} else {
/*
* Not the pair we are looking for, skip to the next one.
*/
p = pair + encoded_size;
}
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
}
return (EIO);
}
static int
{
const unsigned char *p, *pair;
int junk;
int rc;
rc = 0;
p = nvlist;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
while (encoded_size && decoded_size) {
const char *pairname;
int i, found;
found = 0;
pairname = (const char*) p;
for (i = 0; features_for_read[i] != NULL; i++) {
found = 1;
break;
}
}
if (!found) {
}
p = pair + encoded_size;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
}
return (rc);
}
/*
* Return the next nvlist in an nvlist array.
*/
static const unsigned char *
{
const unsigned char *p, *pair;
int junk;
p = nvlist;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
while (encoded_size && decoded_size) {
p = pair + encoded_size;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
}
return p;
}
#ifdef TEST
static const unsigned char *
{
static const char* typenames[] = {
"DATA_TYPE_UNKNOWN",
"DATA_TYPE_BOOLEAN",
"DATA_TYPE_BYTE",
"DATA_TYPE_INT16",
"DATA_TYPE_UINT16",
"DATA_TYPE_INT32",
"DATA_TYPE_UINT32",
"DATA_TYPE_INT64",
"DATA_TYPE_UINT64",
"DATA_TYPE_STRING",
"DATA_TYPE_BYTE_ARRAY",
"DATA_TYPE_INT16_ARRAY",
"DATA_TYPE_UINT16_ARRAY",
"DATA_TYPE_INT32_ARRAY",
"DATA_TYPE_UINT32_ARRAY",
"DATA_TYPE_INT64_ARRAY",
"DATA_TYPE_UINT64_ARRAY",
"DATA_TYPE_STRING_ARRAY",
"DATA_TYPE_HRTIME",
"DATA_TYPE_NVLIST",
"DATA_TYPE_NVLIST_ARRAY",
"DATA_TYPE_BOOLEAN_VALUE",
"DATA_TYPE_INT8",
"DATA_TYPE_UINT8",
"DATA_TYPE_BOOLEAN_ARRAY",
"DATA_TYPE_INT8_ARRAY",
"DATA_TYPE_UINT8_ARRAY"
};
unsigned int i, j;
const unsigned char *p, *pair;
int junk;
p = nvlist;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
while (encoded_size && decoded_size) {
const char *pairname;
pairname = (const char*) p;
for (i = 0; i < indent; i++)
printf(" ");
switch (pairtype) {
case DATA_TYPE_UINT64: {
xdr_uint64_t(&p, &val);
break;
}
case DATA_TYPE_STRING: {
int len;
printf(" = \"%s\"\n", p);
break;
}
case DATA_TYPE_NVLIST:
printf("\n");
break;
case DATA_TYPE_NVLIST_ARRAY:
for (j = 0; j < elements; j++) {
printf("[%d]\n", j);
if (j != elements - 1) {
for (i = 0; i < indent; i++)
printf(" ");
}
}
break;
default:
printf("\n");
}
p = pair + encoded_size;
pair = p;
xdr_int(&p, &encoded_size);
xdr_int(&p, &decoded_size);
}
return p;
}
#endif
static int
{
int rc;
if (!vdev->v_phys_read)
return (EIO);
if (bp) {
} else {
}
/*printf("ZFS: reading %d bytes at 0x%jx to %p\n", psize, (uintmax_t)offset, buf);*/
if (rc)
return (rc);
return (EIO);
return (0);
}
static int
{
}
static int
{
int rc;
continue;
if (!rc)
return (0);
}
return (rc);
}
static int
{
/*
* Here we should have two kids:
* First one which is the one we are replacing and we can trust
* only this one to have valid data, but it might not be present.
* Second one is that one we are replacing with. It is most likely
* healthy, but we can't trust it has needed data, so we won't use it.
*/
return (EIO);
return (EIO);
}
static vdev_t *
{
return (vdev);
return (0);
}
static vdev_t *
{
vdev->v_phys_read = 0;
vdev->v_read_priv = 0;
return (vdev);
}
static int
{
int rc;
const char *type;
const char *path;
const unsigned char *kids;
DATA_TYPE_UINT64, 0, &guid)
DATA_TYPE_UINT64, 0, &id)
DATA_TYPE_STRING, 0, &type)) {
printf("ZFS: can't find vdev details\n");
return (ENOENT);
}
#ifdef ZFS_TEST
#endif
printf("ZFS: can only boot from disk, mirror, raidz1, raidz2 and raidz3 vdevs\n");
return (EIO);
}
&is_offline);
&is_removed);
&is_faulted);
&is_degraded);
&isnt_present);
if (!vdev) {
is_new = 1;
else
DATA_TYPE_UINT64, 0, &ashift) == 0)
else
DATA_TYPE_UINT64, 0, &nparity) == 0)
else
DATA_TYPE_STRING, 0, &path) == 0) {
path += 9;
DATA_TYPE_STRING, 0, &path) == 0)
else
DATA_TYPE_STRING, 0, &path) == 0)
else
} else {
else {
printf("ZFS: can only boot from disk, mirror, raidz1, raidz2 and raidz3 vdevs\n");
return (EIO);
}
} else {
}
}
} else {
is_new = 0;
}
/*
* This is either new vdev or we've already seen this vdev,
* but from an older vdev label, so let's refresh its state
* from the newer label.
*/
if (is_offline)
else if (is_removed)
else if (is_faulted)
else if (is_degraded)
else if (isnt_present)
}
/*
* Its ok if we don't have any kids.
*/
if (rc == 0) {
for (i = 0; i < nkids; i++) {
if (rc)
return (rc);
if (is_new)
}
} else {
vdev->v_nchildren = 0;
}
if (vdevp)
return (0);
}
static void
{
int good_kids;
int bad_kids;
/*
* A mirror or raidz is healthy if all its kids are healthy. A
* mirror is degraded if any of its kids is healthy; a raidz
* is degraded if at most nparity kids are offline.
*/
good_kids = 0;
bad_kids = 0;
good_kids++;
else
bad_kids++;
}
if (bad_kids == 0) {
} else {
if (good_kids) {
} else {
}
} else {
}
}
}
}
}
static spa_t *
{
return (spa);
return (0);
}
static spa_t *
{
return (spa);
return (0);
}
spa_t *
spa_get_primary(void)
{
return (STAILQ_FIRST(&zfs_pools));
}
vdev_t *
{
spa = spa_get_primary();
return (NULL);
return (NULL);
return (vdev);
}
static spa_t *
{
return (spa);
}
static const char *
{
static const char* names[] = {
"UNKNOWN",
"CLOSED",
"OFFLINE",
"REMOVED",
"CANT_OPEN",
"FAULTED",
"DEGRADED",
"ONLINE"
};
}
static int
{
return (pager_output(line));
}
static int
{
int i;
buf[0] = 0;
for (i = 0; i < indent; i++)
}
static int
{
int ret;
if (ret != 0)
return (ret);
if (ret != 0)
return (ret);
}
return (ret);
}
static int
{
if (ret != 0)
return (ret);
if (bootfs[0] == '\0')
else
bootfs);
if (ret != 0)
return (ret);
}
if (ret != 0)
return (ret);
if (ret != 0)
return (ret);
good_kids = 0;
degraded_kids = 0;
bad_kids = 0;
good_kids++;
else
bad_kids++;
}
else if ((good_kids + degraded_kids) > 0)
if (ret != 0)
return (ret);
if (ret != 0)
return (ret);
}
return (ret);
}
int
spa_all_status(void)
{
if (!first) {
if (ret != 0)
return (ret);
}
first = 0;
if (ret != 0)
return (ret);
}
return (ret);
}
static int
{
const unsigned char *nvlist;
const char *pool_name;
const unsigned char *vdevs;
const unsigned char *features;
char *upbuf;
/*
* Load the vdev label and figure out which
* uberblock is most current.
*/
return (EIO);
return (EIO);
}
if (nvlist_find(nvlist,
DATA_TYPE_UINT64, 0, &val)) {
return (EIO);
}
if (!SPA_VERSION_IS_SUPPORTED(val)) {
printf("ZFS: unsupported ZFS version %u (should be %u)\n",
(unsigned) val, (unsigned) SPA_VERSION);
return (EIO);
}
/* Check ZFS features for read */
if (nvlist_find(nvlist,
DATA_TYPE_NVLIST, 0, &features) == 0
&& nvlist_check_features_for_read(features) != 0)
return (EIO);
if (nvlist_find(nvlist,
DATA_TYPE_UINT64, 0, &val)) {
return (EIO);
}
if (val == POOL_STATE_DESTROYED) {
/* We don't boot only from destroyed pools. */
return (EIO);
}
if (nvlist_find(nvlist,
DATA_TYPE_UINT64, 0, &pool_txg)
|| nvlist_find(nvlist,
DATA_TYPE_UINT64, 0, &pool_guid)
|| nvlist_find(nvlist,
DATA_TYPE_STRING, 0, &pool_name)) {
/*
* Cache and spare devices end up here - just ignore
* them.
*/
/*printf("ZFS: can't find pool details\n");*/
return (EIO);
}
is_log = 0;
&is_log);
if (is_log)
return (EIO);
/*
* Create the pool if this is the first time we've seen it.
*/
if (!spa) {
}
is_newer = 1;
} else
is_newer = 0;
/*
* Get the vdev tree and create our in-core copy of it.
* If we already have a vdev with this guid, this must
* be some kind of alias (overlapping slices, dangerously dedicated
* disks etc).
*/
if (nvlist_find(nvlist,
DATA_TYPE_UINT64, 0, &guid)) {
return (EIO);
}
return (EIO);
if (nvlist_find(nvlist,
DATA_TYPE_NVLIST, 0, &vdevs)) {
return (EIO);
}
if (rc)
return (rc);
/*
* Add the toplevel vdev to the pool if its not already there.
*/
break;
/*
* We should already have created an incomplete vdev for this
* vdev. Find it and initialise it with our read proc.
*/
if (vdev) {
} else {
printf("ZFS: inconsistent nvlist contents\n");
return (EIO);
}
/*
* Re-evaluate top-level vdev state.
*/
/*
* Ok, we are happy with the pool so far. Lets find
* the best uberblock and then we can actually access
* the contents of the pool.
*/
for (i = 0;
i < VDEV_UBERBLOCK_COUNT(vdev);
i++) {
continue;
continue;
continue;
}
}
if (spap)
return (0);
}
static int
ilog2(int n)
{
int v;
for (v = 0; v < 32; v++)
if (n == (1 << v))
return v;
return -1;
}
static int
{
char *pbuf;
int i;
/* Artificial BP for gang block header. */
for (i = 0; i < SPA_DVAS_PER_BP; i++)
/* Read gang header block using the artificial BP. */
return (EIO);
for (i = 0; i < SPA_GBH_NBLKPTRS; i++) {
if (BP_IS_HOLE(gbp))
continue;
return (EIO);
}
return (EIO);
return (0);
}
static int
{
void *pbuf;
int i, error;
/*
* Process data embedded in block pointer
*/
if (BP_IS_EMBEDDED(bp)) {
if (cpfunc != ZIO_COMPRESS_OFF)
else
error = 0;
if (cpfunc != ZIO_COMPRESS_OFF) {
}
if (error != 0)
printf("ZFS: i/o error - unable to decompress block pointer data, error %d\n",
error);
return (error);
}
for (i = 0; i < SPA_DVAS_PER_BP; i++) {
int vdevid;
continue;
break;
}
continue;
}
else
if (DVA_GET_GANG(dva))
else
if (error == 0) {
if (cpfunc != ZIO_COMPRESS_OFF)
}
if (error == 0)
break;
}
if (error != 0)
printf("ZFS: i/o error - all block copies unavailable\n");
return (error);
}
static int
{
int i, rc;
if (bsize > SPA_MAXBLOCKSIZE) {
printf("ZFS: I/O error - blocks larger than %llu are not "
"supported\n", SPA_MAXBLOCKSIZE);
return (EIO);
}
/*
* Note: bsize may not be a power of two here so we need to do an
* actual divide rather than a bitshift.
*/
while (buflen > 0) {
int ibn;
printf("warning: zfs bug: bn %llx > dn_maxblkid %llx\n",
(unsigned long long)bn,
(unsigned long long)dnode->dn_maxblkid);
/*
* zfs bug, will not return error
* return (EIO);
*/
}
goto cached;
for (i = 0; i < nlevels; i++) {
/*
* Copy the bp from the indirect array so that
* we can re-use the scratch buffer for multi-level
* objects.
*/
if (BP_IS_HOLE(&bp)) {
break;
}
if (rc)
return (rc);
}
dnode_cache_bn = bn;
/*
* The buffer contains our data block. Copy what we
* need from it and loop.
*/
offset += i;
buflen -= i;
}
return (0);
}
/*
* Lookup a value in a microzap directory. Assumes that the zap
* scratch buffer contains the directory contents.
*/
static int
{
int chunks, i;
/*
* Microzap objects use exactly one block. Read the whole
* thing.
*/
for (i = 0; i < chunks; i++) {
return (0);
}
}
return (ENOENT);
}
/*
* Compare a name with a zap leaf entry. Return non-zero if the name
* matches.
*/
static int
{
const char *p;
p = name;
while (namelen > 0) {
if (len > ZAP_LEAF_ARRAY_BYTES)
return (0);
p += len;
}
return 1;
}
/*
* Extract a uint64_t value from a zap leaf entry.
*/
static uint64_t
{
int i;
const uint8_t *p;
}
return value;
}
/*
* Lookup a value in a fatzap directory. Assumes that the zap scratch
* buffer contains the directory header.
*/
static int
{
fat_zap_t z;
int rc;
return (EIO);
/*
* Figure out where the pointer table is and read it in if necessary.
*/
zap_scratch, bsize);
if (rc)
return (rc);
} else {
ptrtbl = &ZAP_EMBEDDED_PTRTBL_ENT(&z, 0);
}
if (rc)
return (rc);
/*
* Make sure this chunk matches our hash.
*/
return (ENOENT);
/*
* Hash within the chunk to find our entry.
*/
if (h == 0xffff)
return (ENOENT);
zc = 0;
break;
}
}
return (E2BIG);
return (0);
}
return (ENOENT);
}
/*
* Lookup a name in a zap object and return its value as a uint64_t.
*/
static int
{
int rc;
if (rc)
return (rc);
else if (zap_type == ZBT_HEADER)
return (EIO);
}
/*
* List a microzap directory. Assumes that the zap scratch buffer contains
* the directory contents.
*/
static int
{
/*
* Microzap objects use exactly one block. Read the whole
* thing.
*/
for (i = 0; i < chunks; i++) {
if (rc != 0)
return (rc);
}
}
return (0);
}
/*
* List a fatzap directory. Assumes that the zap scratch buffer contains
* the directory header.
*/
static int
{
fat_zap_t z;
int i, j, rc;
return (EIO);
/*
* This assumes that the leaf blocks start at block 1. The
* documentation isn't exactly clear on this.
*/
for (i = 0; i < zh.zap_num_leafs; i++) {
return (EIO);
for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
int namelen;
continue;
/*
* Paste the name back together.
*/
p = name;
while (namelen > 0) {
int len;
if (len > ZAP_LEAF_ARRAY_BYTES)
p += len;
}
/*
* Assume the first eight bytes of the value are
* a uint64_t.
*/
//printf("%s 0x%jx\n", name, (uintmax_t)value);
if (rc != 0)
return (rc);
}
}
return (0);
}
{
return (0);
}
/*
* List a zap directory.
*/
static int
{
return (EIO);
else
}
static int
{
dnode, sizeof(dnode_phys_t));
}
static int
{
int chunks, i;
/*
* Microzap objects use exactly one block. Read the whole
* thing.
*/
for (i = 0; i < chunks; i++) {
return (0);
}
}
return (ENOENT);
}
static void
{
char *p;
p = name;
while (namelen > 0) {
if (len > ZAP_LEAF_ARRAY_BYTES)
p += len;
}
*p = '\0';
}
static int
{
fat_zap_t z;
int i, j;
return (EIO);
/*
* This assumes that the leaf blocks start at block 1. The
* documentation isn't exactly clear on this.
*/
for (i = 0; i < zh.zap_num_leafs; i++) {
return (EIO);
for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
continue;
continue;
return (0);
}
}
}
return (ENOENT);
}
static int
{
int rc;
if (rc)
return (rc);
else
}
static int
{
char *p;
int len;
*p = '\0';
return (EIO);
}
for (;;) {
return (EIO);
/* Actual loop condition. */
if (parent_obj == 0)
break;
return (EIO);
return (EIO);
return (EIO);
p -= len;
--p;
*p = '/';
/* Actual loop iteration. */
}
if (*p != '\0')
++p;
return (0);
}
static int
{
const char *p, *q;
return (EIO);
return (EIO);
p = name;
for (;;) {
return (EIO);
while (*p == '/')
p++;
/* Actual loop condition #1. */
if (*p == '\0')
break;
q = strchr(p, '/');
if (q) {
element[q - p] = '\0';
p = q + 1;
} else {
p += strlen(p);
}
return (EIO);
/* Actual loop condition #2. */
return (ENOENT);
}
return (0);
}
static int
{
return (EIO);
}
return (EIO);
}
return (EIO);
}
}
int
{
int err;
if (err != 0) {
return (err);
}
if (err != 0) {
return (err);
}
if (err != 0) {
return (err);
}
if (err != 0)
return (err);
else
}
/*
* Find the object set given the object number of its dataset object
* and return its details in *objset
*/
static int
{
return (EIO);
}
printf("ZFS: can't read object set for dataset %ju\n",
return (EIO);
}
return (0);
}
/*
* Find the object set pointed to by the BOOTFS property or the root
* dataset if there is none and return its details in *objset
*/
static int
{
*objid = 0;
/*
* Start with the MOS directory object.
*/
printf("ZFS: can't read MOS object directory\n");
return (EIO);
}
/*
* Lookup the pool_props and see if we can find a bootfs.
*/
&& bootfs != 0)
{
return (0);
}
/*
* Lookup the root dataset directory
*/
printf("ZFS: can't find root dsl_dir\n");
return (EIO);
}
/*
* Use the information from the dataset directory's bonus buffer
* to find the dataset object and from that the object set itself.
*/
return (0);
}
static int
{
/*
* Find the root object set if not explicitly provided
*/
printf("ZFS: can't find root filesystem\n");
return (EIO);
}
printf("ZFS: can't open root filesystem\n");
return (EIO);
}
return (0);
}
/*
* callback function for feature name checks.
*/
static int
{
int i;
if (value == 0)
return (0);
if (name[0] == '\0')
return (0);
for (i = 0; features_for_read[i] != NULL; i++) {
return (0);
}
return (EIO);
}
/*
* Checks whether the MOS features that are active are supported.
*/
static int
{
int rc;
&dir)) != 0)
return (rc);
&objnum)) != 0) {
/*
* It is older pool without features. As we have already
* tested the label, just return without raising the error.
*/
rc = 0;
return (rc);
}
return (rc);
return (EIO);
return (EIO);
else
return (rc);
}
static int
{
int rc;
return (EIO);
}
return (EIO);
}
if (rc != 0) {
}
return (rc);
}
static int
{
} else {
int hdrsize;
if (dn->dn_bonuslen != 0)
else {
int error;
if (error != 0) {
return (error);
}
} else {
return (EIO);
}
}
}
return (0);
}
/*
* Lookup a file and return its dnode.
*/
static int
{
int rc;
const char *p, *q;
int symlinks_followed = 0;
printf("ZFS: unexpected object set type %ju\n",
return (EIO);
}
/*
* Get the root directory dnode.
*/
if (rc)
return (rc);
if (rc)
return (rc);
if (rc)
return (rc);
p = upath;
while (p && *p) {
while (*p == '/')
p++;
if (!*p)
break;
q = strchr(p, '/');
if (q) {
element[q - p] = 0;
p = q;
} else {
p = 0;
}
if (rc)
return (rc);
return (ENOTDIR);
if (rc)
return (rc);
if (rc)
return (rc);
/*
* Check for symlink.
*/
if (rc)
return (rc);
if (symlinks_followed > 10)
return (EMLINK);
/*
* Read the link value and copy the tail of our
* current path onto the end.
*/
if (p)
else
/*
* Second test is purely to silence bogus compiler
* warning about accessing past the end of dn_bonus.
*/
} else {
if (rc)
return (rc);
}
/*
* Restart with the new path, starting either at
* the root or at the parent depending whether or
* not the link is relative.
*/
p = path;
if (*p == '/')
else
}
}
return (0);
}