/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/partition.h>
GRUB_MOD_LICENSE ("GPLv3+");
/*
* For nvlist manipulation. (from nvpair.h)
*/
#define NV_ENCODE_NATIVE 0
#define NV_BIG_ENDIAN 0
#ifndef GRUB_UTIL
#endif
static inline grub_disk_addr_t
{
}
/*
* FAT ZAP data structures
*/
static inline grub_uint64_t
{
return (((n) == 0) ? 0 : ((hash) >> (64 - (n))));
}
/*
* The amount of space within the chunk available for the array is:
* chunk size - space for type (1) - space for next pointer (2)
*/
static inline int
{
return bs - 5;
}
static inline int
{
}
static inline grub_size_t
{
}
/*
* The amount of space available for chunks is:
* block size shift - hash entry size (2) * number of hash
* entries - header space (2*chunksize)
*/
static inline int
{
ZAP_LEAF_CHUNKSIZE - 2);
}
/*
* The chunks start immediately after the hash table. The end of the
* hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a
* chunk_t.
*/
static inline zap_leaf_chunk_t *
{
return &((zap_leaf_chunk_t *) (l->l_entries
/ sizeof (grub_properly_aligned_t)))[idx];
}
static inline struct zap_leaf_entry *
{
}
/*
* Decompression Entry - lzjb
*/
typedef struct decomp_entry
{
const char *name;
/*
* Signature for checksum functions.
*/
/*
* Information about each checksum function.
*/
typedef struct zio_checksum_info {
typedef struct dnode_end
{
} dnode_end_t;
struct grub_zfs_device_desc
{
/* Valid only for non-leafs. */
unsigned n_children;
/* Valid only for RAIDZ. */
unsigned nparity;
/* Valid for all */
unsigned ashift;
/* Valid only for leaf devices. */
int original;
};
struct subvolume
{
struct
{
} *keyring;
};
struct grub_zfs_data
{
/* cache for a file block of the currently zfs_open()-ed file */
char *file_buf;
/* cache for a dnode block */
unsigned n_devices_attached;
unsigned n_devices_allocated;
int mounted;
int mount_count;
char *dev_name;
};
/* cache list for ZFS mount */
struct zfs_mount_cache
{
char *zcache_dev_name;
unsigned long zcache_dev_id;
/* cache list for disk dev with ZFS uberblock being found */
struct zfs_dev_ublock
{
char *dev_name;
unsigned long dev_id;
/* cache list for non-zfs disk dev */
struct zfs_dev_notzfs
{
char *dev_name;
unsigned long dev_id;
void *nonce,
const grub_uint32_t *expected_mac,
static grub_err_t
zlib_decompress (void *s, void *d,
{
return grub_errno;
return GRUB_ERR_NONE;
}
static grub_err_t
zle_decompress (void *s, void *d,
{
{
if (*iptr & 0x80)
else
{
iptr++;
continue;
}
}
return GRUB_ERR_NONE;
}
};
struct grub_zfs_data *data);
/*
* Our own version of log2(). Same thing as highbit()-1.
*/
static int
{
int i = 0;
while (num > 1)
{
i++;
}
return (i);
}
/* Checksum Functions */
static void
zio_cksum_t * zcp)
{
ZIO_SET_CHECKSUM (zcp, 0, 0, 0, 0);
}
/* Checksum Table and Values */
{NULL, 0, 0, "inherit"},
{NULL, 0, 0, "on"},
{zio_checksum_off, 0, 0, "off"},
{NULL, 0, 0, "zilog"},
{fletcher_2, 0, 0, "fletcher2"},
{NULL, 0, 0, "zilog2"},
};
/*
* zio_checksum_verify: Provides support for checksum verification.
*
* Fletcher2, Fletcher4, and SHA256 are supported.
*
*/
static grub_err_t
{
{
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"unknown checksum function %d", checksum);
}
{
zc = expected_cksum;
}
else
{
(unsigned long long) actual_cksum.zc_word[0],
}
return GRUB_ERR_NONE;
}
/*
* vdev_uberblock_compare takes two uberblock structures and returns an integer
* indicating the more recent of the two.
* Return Value = 1 if ub2 is more recent
* Return Value = -1 if ub1 is more recent
* The most recent uberblock is determined using its transaction number and
* timestamp. The uberblock with the highest transaction number is
* considered "newer". If the transaction numbers of the two blocks match, the
* timestamps are compared to determine the "newer" of the two.
*/
static int
{
== UBERBLOCK_MAGIC)
else
== UBERBLOCK_MAGIC)
else
return (1);
return (-1);
return (1);
return (-1);
return (0);
}
/*
* Three pieces of information are needed to verify an uberblock: the magic
* number, the version number, and the checksum.
*
* Currently Implemented: version number, magic number
* Need to Implement: checksum
*
*/
static grub_err_t
{
<= SPA_VERSION)
<= SPA_VERSION)
if (endian == GRUB_ZFS_UNKNOWN_ENDIAN)
return err;
}
/*
* Find the best uberblock.
* Return:
* Success - Pointer to the best uberblock.
* Failure - NULL
*/
static uberblock_t *
{
{
if (err)
{
continue;
}
}
if (!ubbest)
grub_errno = err;
return (ubbest);
}
static inline grub_size_t
{
<< SPA_MINBLOCKSHIFT);
}
static grub_uint64_t
{
endian) << SPA_MINBLOCKSHIFT;
}
static grub_err_t
{
/* Read in the vdev name-value pair list (112K). */
VDEV_PHYS_SIZE, *nvlist);
if (err)
{
*nvlist = 0;
return err;
}
return GRUB_ERR_NONE;
}
/*
* Check if this vdev is online and is in a good state.
*/
static int
{
{
return (1);
}
{
return (1);
}
{
return (1);
}
return (0);
}
static grub_err_t
const char *nvlist,
struct grub_zfs_device_desc *fill,
struct grub_zfs_device_desc *insert,
int *inserted)
{
char *type;
if (!type)
return grub_errno;
{
}
{
}
{
{
}
else
{
*inserted = 1;
}
if (vdev_validate(nvlist))
else
return GRUB_ERR_NONE;
}
{
int nelm, i;
else
{
{
}
{
}
}
if (nelm <= 0)
{
}
{
}
for (i = 0; i < nelm; i++)
{
char *child;
(nvlist, ZPOOL_CONFIG_CHILDREN, i);
if (! child)
{
return grub_errno;
}
inserted);
if (err)
{
return err;
}
}
return GRUB_ERR_NONE;
}
return grub_errno;
}
static grub_err_t
int *inserted)
{
unsigned i;
*inserted = 0;
}
(unsigned long long)id);
for (i = 0; i < data->n_devices_attached; i++)
{
{
return grub_errno;
}
}
}
static grub_err_t
char **ret_nvlist)
{
char *nvlist;
int found;
if (err) {
return err;
}
&pool_state);
if (! found)
{
if (! grub_errno)
return grub_errno;
}
if (pool_state == POOL_STATE_DESTROYED)
{
}
if (!found)
{
if (! grub_errno)
return grub_errno;
}
/* not an active device */
if (txg == 0)
{
}
&version);
if (! found)
{
if (! grub_errno)
return grub_errno;
}
if (version > SPA_VERSION)
{
SPA_VERSION, (unsigned long long)version);
"ZFS pool version (%llu) is too new for the GRUB2 ZFS module (maximum version\n"
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"ZFS SPA version is later than GRUB2 ZFS "
"reader code (%llu > %llu)",
(unsigned long long) version,
(unsigned long long) SPA_VERSION);
}
if (! found)
{
if (! grub_errno)
return grub_errno;
}
poolguid);
if (! found)
{
if (! grub_errno)
return grub_errno;
}
if (ret_nvlist == NULL)
else
*ret_nvlist = nvlist;
return (GRUB_ERR_NONE);
}
/*
* Check the disk label information and retrieve needed vdev name-value pairs.
*
*/
static grub_err_t
struct grub_zfs_device_desc *diskdesc,
int *inserted)
{
*inserted = 0;
if (err)
return err;
else
{
char *nv;
if (!nv)
{
}
if (err)
{
return err;
}
}
return GRUB_ERR_NONE;
}
static int
{
unsigned int i, j;
int ret = 0;
{
{
(unsigned long long)*vdev_guid,
i,
break;
}
{
{
(unsigned long long)*vdev_guid,
i, j,
{
ret = 1;
break;
}
}
}
}
return (ret);
}
static grub_uint64_t
{
if (!err) {
if (vdev_nv) {
}
} else
return ashift;
}
static grub_err_t
{
*better_found = 0;
if (!ub_array)
return grub_errno;
{
return 0;
/* search cached non-zfs disk */
{
return 0;
}
if (!dev)
return 0;
/* the mirrored root pool should be on other disk(s) */
{
return 0;
}
/* the partition name for the mirrored root pool should be same */
{
return 0;
}
{
{
return 0;
}
else
{
&dev_ub->current_uberblock) > 0)
{
*better_found = 1;
return 1; /* Terminate iteration */
}
else
return 0;
}
}
/* Don't check back labels on CDROM. */
{
/* Read in the uberblock ring (128K). */
+ (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
0, VDEV_UBERBLOCK_RING, (char *) ub_array);
if (err)
{
continue;
}
/* Make sure that ashift is filled in desc before calling find_bestub */
if (!ubbest)
{
continue;
}
if (err)
{
continue;
}
if (dev_ub)
{
if (zfs_dev_ublock_list)
else
}
{
(unsigned long long) poolguid,
return 0;
}
/*
* zfs_pool_validate() has the side effect of filling in the VDEV
* guid for this VDEV, so ensure that this VDEV is a member of the
* pool.
*/
{
*better_found = 1;
return 1; /* Terminate iteration */
}
else
{
return 0;
}
}
if (dev_notzfs)
{
if (zfs_dev_notzfs_list)
else
}
return 0;
}
return GRUB_ERR_NONE;
}
static grub_err_t
{
int label = 0;
int better_found = 0;
int vdevnum;
/* search for cached non-zfs dev first */
{
}
if (!ub_array)
{
return grub_errno;
}
if (original)
{
{
goto check_label;
}
}
/* Don't check back labels on CDROM. */
{
/* Read in the uberblock ring (128K). */
+ (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
0, VDEV_UBERBLOCK_RING, (char *) ub_array);
if (err)
{
continue;
}
/* Make sure that ashift is filled in desc before calling find_bestub */
if (!ubbest)
{
continue;
}
if (original)
if (err)
{
continue;
}
{
if (dev_ub)
{
if (zfs_dev_ublock_list)
else
}
}
better_found != 0)
{
grub_printf("The %s device is too old and should not be trusted as part of this zpool\n",
}
return GRUB_ERR_NONE;
}
if (original)
{
if (dev_notzfs)
{
if (zfs_dev_notzfs_list)
else
}
}
}
static grub_err_t
{
{
int inserted;
if (!dev)
return 0;
{
return 0;
}
if (err == GRUB_ERR_BAD_FS)
{
return 0;
}
if (err)
{
grub_print_error ();
return 0;
}
if (!inserted)
return 1;
}
if (!dev_found)
return grub_error (GRUB_ERR_BAD_FS,
return GRUB_ERR_NONE;
}
/* x**y. */
/* Such an s that x**s = y */
/* perform the operation a ^= b * (x ** (known_idx * recovery_pow) ) */
static inline void
int known_idx, int recovery_pow)
{
int add;
/* Simple xor. */
if (known_idx == 0 || recovery_pow == 0)
{
grub_crypto_xor (a, a, b, s);
return;
}
for (;s--; b++, a++)
if (*b)
}
static inline grub_uint8_t
{
if (a == 0 || b == 0)
return 0;
}
static inline grub_err_t
const unsigned *powers,
const int *idx)
{
/* Now we have */
/* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/
/* Let's invert the matrix in question. */
switch (nbufs)
{
/* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])). */
case 1:
{
int add;
grub_uint8_t *a;
return GRUB_ERR_NONE;
for (a = bufs[0]; s--; a++)
if (*a)
return GRUB_ERR_NONE;
}
/* Case 2x2: Let's use the determinant formula. */
case 2:
{
unsigned i;
/* The determinant is: */
if (det == 0)
for (i = 0; i < s; i++)
{
}
return GRUB_ERR_NONE;
}
/* Otherwise use Gauss. */
default:
{
int i, j, k;
for (i = 0; i < nbufs; i++)
for (j = 0; j < nbufs; j++)
for (i = 0; i < nbufs; i++)
for (j = 0; j < nbufs; j++)
matrix2[i][j] = 0;
for (i = 0; i < nbufs; i++)
matrix2[i][i] = 1;
for (i = 0; i < nbufs; i++)
{
for (j = i; j < nbufs; j++)
if (matrix1[i][j])
break;
if (j == nbufs)
if (j != i)
{
int xchng;
xchng = j;
for (j = 0; j < nbufs; j++)
{
grub_uint8_t t;
matrix1[i][j] = t;
}
for (j = 0; j < nbufs; j++)
{
grub_uint8_t t;
matrix2[i][j] = t;
}
}
for (j = 0; j < nbufs; j++)
for (j = 0; j < nbufs; j++)
for (j = i + 1; j < nbufs; j++)
{
for (k = 0; k < nbufs; k++)
for (k = 0; k < nbufs; k++)
}
}
for (i = nbufs - 1; i >= 0; i--)
{
for (j = 0; j < i; j++)
{
for (k = 0; k < nbufs; k++)
for (k = 0; k < nbufs; k++)
}
}
for (i = 0; i < (int) s; i++)
{
grub_uint8_t b[nbufs];
for (j = 0; j < nbufs; j++)
b[j] = bufs[j][i];
for (j = 0; j < nbufs; j++)
{
bufs[j][i] = 0;
for (k = 0; k < nbufs; k++)
}
}
return GRUB_ERR_NONE;
}
}
}
static grub_err_t
{
{
case DEVICE_LEAF:
{
{
}
{
}
/* read in a data block */
}
case DEVICE_MIRROR:
{
unsigned i;
if (desc->n_children <= 0)
return grub_error (GRUB_ERR_BAD_FS,
"non-positive number of mirror children");
for (i = 0; i < desc->n_children; i++)
{
if (!err)
break;
}
return (grub_errno = err);
}
case DEVICE_RAIDZ:
{
unsigned c = 0;
unsigned failed_devices = 0;
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
s = orig_s;
desc->n_children, &m);
c = 2;
c = 3;
else
while (len > 0)
{
c++;
PRIxGRUB_UINT64_T ")\n",
devn);
{
grub_errno = err = 0;
}
if (err)
return err;
c++;
idx--;
s--;
}
if (failed_devices)
{
unsigned cur_redundancy_pow = 0;
unsigned n_redundancy = 0;
unsigned i, j;
/* Compute mul. x**s has a period of 255. */
if (powx[0] == 0)
{
for (i = 0; i < 255; i++)
{
if (cur & 0x80)
else
cur <<= 1;
}
}
/* Read redundancy data. */
for (n_redundancy = 0, cur_redundancy_pow = 0;
{
& 1)),
/* Ignore error if we may still have enough devices. */
>= failed_devices)
{
continue;
}
if (err)
return err;
n_redundancy++;
}
/* Now xor-our the parts we already know. */
s = orig_s;
while (len > 0)
{
for (j = 0; j < failed_devices; j++)
if (buf == recovery_buf[j])
break;
if (j == failed_devices)
for (j = 0; j < failed_devices; j++)
idx, redundancy_pow[j]);
s--;
idx--;
}
for (i = 0; i < failed_devices
&& recovery_len[i] == recovery_len[0];
i++);
/* Since the chunks have variable length handle the last block
separately. */
if (i != failed_devices)
{
for (j = 0; j < i; j++)
err = recovery (tmp_recovery_buf, recovery_len[0] - recovery_len[failed_devices - 1], i, redundancy_pow,
if (err)
return err;
}
if (err)
return err;
}
return GRUB_ERR_NONE;
}
}
}
static grub_err_t
{
unsigned i;
int try;
{
for (i = 0; i < data->n_devices_attached; i++)
if (try == 1)
break;
if (err)
return err;
}
return err;
}
/*
* Read a block of data based on the gang block address dva,
* and put its data in buf.
*/
static grub_err_t
struct grub_zfs_data *data)
{
int i;
/* pick a good dva from the block pointer */
for (i = 0; i < BP_GET_NDVAS(bp); i++)
{
return GRUB_ERR_NONE;
}
}
/*
* Read gang block header, verify its checksum, loop through all gang blocks
* to collect its data based on the gang block address dva and put it in buf.
*
*/
static grub_err_t
struct grub_zfs_data *data)
{
unsigned i;
if (!zio_gb)
return grub_errno;
:"big-endian gang\n");
if (err)
{
return err;
}
/* XXX */
/* self checksuming the gang block header */
(char *) zio_gb, SPA_GANGBLOCKSIZE);
if (err)
{
return err;
}
for (i = 0; i < SPA_GBH_NBLKPTRS; i++)
{
continue;
if (err)
{
return err;
}
}
return GRUB_ERR_NONE;
}
/*
* Read in a block of raw data to buf.
*/
static grub_err_t
{
int psize;
else
return err;
}
/*
* Loop through DVAs to read in a block of raw data to buf and verify
* the checksum.
*/
static grub_err_t
struct grub_zfs_data *data)
{
int i, psize;
/* pick a good dva from the block pointer */
for (i = 0; i < BP_GET_NDVAS(bp); i++)
{
{
continue;
}
if (err)
{
continue;
}
/* if no errors, return from here */
return GRUB_ERR_NONE;
}
grub_errno = err;
return err;
}
/*
* Read in a block of data, decompress if needed,
* and put the uncompressed data in buf.
*/
static grub_err_t
{
<< SPA_MINBLOCKSHIFT));
if (size)
if (comp >= ZIO_COMPRESS_FUNCTIONS)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"compression algorithm %u not supported\n", (unsigned int) comp);
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
if (comp != ZIO_COMPRESS_OFF)
{
/* It's not really necessary to align to 16, just for safety. */
if (! compbuf)
return grub_errno;
}
else
{
if (! compbuf)
return grub_errno;
}
if (err)
{
return err;
}
if (encrypted)
{
if (!grub_zfs_decrypt)
else
{
unsigned i, besti = 0;
{
besti = i;
}
if (bestval == 0)
{
endian));
}
endian));
endian);
}
if (err)
{
return err;
}
}
if (comp != ZIO_COMPRESS_OFF)
{
if (!*buf)
{
return grub_errno;
}
if (err)
{
return err;
}
}
return GRUB_ERR_NONE;
}
/*
* Get the block from a block id.
* push the block onto the stack.
*
*/
static grub_err_t
{
int level;
void *tmpbuf = 0;
{
if (BP_IS_HOLE (bp))
{
{
err = grub_errno;
break;
}
break;
}
if (level == 0)
{
break;
}
if (err)
break;
}
if (endian_out)
*endian_out = endian;
return err;
}
/*
* mzap_lookup: Looks up property described by "name" and returns the value
* in "value".
*/
static grub_err_t
int case_insensitive)
{
int i, chunks;
for (i = 0; i < chunks; i++)
{
{
return GRUB_ERR_NONE;
}
}
}
/*
* mzap_value_search: Looks up value and returns property name.
*/
static grub_err_t
{
int i, chunks;
for (i = 0; i < chunks; i++)
{
{
return GRUB_ERR_NONE;
}
}
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
"mzap_value_search: couldn't find %s", value);
}
static int
{
int i, chunks;
for (i = 0; i < chunks; i++)
{
return 1;
}
return 0;
}
static grub_uint64_t
int case_insensitive)
{
grub_uint8_t c;
if (table[128] == 0)
{
int i, j;
for (i = 0; i < 256; i++)
{
}
}
if (case_insensitive)
else
/*
* Only use 28 bits, since we need 4 bits in the cookie for the
* collision differentiator. We MUST use the high bits, since
* those are the onces that we first pay attention to when
* chosing the bucket.
*/
return (crc);
}
/*
* Only to be used on 8-bit arrays.
* array_len is actual len in bytes (not encoded le_value_length).
* buf is null-terminated.
*/
static inline int
int case_insensitive)
{
if (!case_insensitive)
while (n--)
{
t1++;
t2++;
}
return 0;
}
/* XXX */
static int
const char *buf, int case_insensitive)
{
{
if (toread > ZAP_LEAF_ARRAY_BYTES)
return 0;
case_insensitive) != 0)
break;
}
}
/* XXX */
static grub_err_t
{
{
if (toread > ZAP_LEAF_ARRAY_BYTES)
/* Don't use grub_error because this error is to be ignored. */
return GRUB_ERR_BAD_FS;
}
return GRUB_ERR_NONE;
}
/*
* Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the
* value for the property "name".
*
*/
/* XXX */
static grub_err_t
int blksft, grub_uint64_t h,
int case_insensitive)
{
/* Verify if this is a valid leaf block */
{
/* Verify the chunk entry */
continue;
{
endian) != 1)
/* get the uint64_t property value */
return GRUB_ERR_NONE;
}
}
}
/* Verify if this is a fat zap header block */
static grub_err_t
{
return GRUB_ERR_NONE;
}
/*
* Fat ZAP lookup
*
*/
/* XXX */
static grub_err_t
{
void *l;
if (err)
return err;
/* get block id from index */
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"external pointer tables not supported");
blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian);
/* Get the leaf block */
if (err)
return err;
grub_free (l);
return err;
}
/* XXX */
static int
const void *val_in,
struct grub_zfs_data *data)
{
zap_leaf_phys_t *l;
void *l_in;
return 0;
/* get block id from index */
{
"external pointer tables not supported");
return 0;
}
/* Get the leaf block */
{
return 0;
}
{
break;
continue;
l = l_in;
if (err)
{
continue;
}
/* Verify if this is a valid leaf block */
{
grub_free (l);
continue;
}
{
grub_free (l);
continue;
}
{
char *buf;
char *val;
/* Verify the chunk entry */
continue;
* name_elem_length + 1);
endian),
* name_elem_length, buf))
{
continue;
}
* (int) le->le_int_size);
endian),
val_length, val))
{
continue;
}
{
grub_free (l);
return 1;
}
}
grub_free (l);
}
return 0;
}
/*
* Read in the data of a zap object and find the value for a matching
* property name.
*
*/
static grub_err_t
{
int size;
void *zapbuf;
/* Read in the first block of the zap object data. */
if (err)
return err;
if (block_type == ZBT_MICRO)
{
return err;
}
else if (block_type == ZBT_HEADER)
{
/* this is a fat zap */
return err;
}
}
/*
* Read in the data of a zap object and find the property name for a
* matching value.
*
*/
static grub_err_t
struct grub_zfs_data *data)
{
int size;
void *zapbuf;
/* Read in the first block of the zap object data. */
if (err)
return err;
if (block_type == ZBT_MICRO)
{
return err;
}
else if (block_type == ZBT_HEADER)
{
/* this is a fat zap */
}
}
static int
struct grub_zfs_data *data)
{
int size;
void *zapbuf;
int ret;
const void *val_in,
const void *val_in,
{
return 0;
}
/* Read in the first block of the zap object data. */
if (err)
return 0;
if (block_type == ZBT_MICRO)
{
return ret;
}
else if (block_type == ZBT_HEADER)
{
/* this is a fat zap */
return ret;
}
return 0;
}
static int
const void *val_in,
struct grub_zfs_data *data)
{
void *zapbuf;
int ret;
/* Read in the first block of the zap object data. */
if (err)
return 0;
if (block_type == ZBT_MICRO)
{
return 0;
}
if (block_type == ZBT_HEADER)
{
/* this is a fat zap */
return ret;
}
return 0;
}
/*
* Get the dnode of an object number from the metadnode of an object set.
*
* Input
* mdn - metadnode to get the object dnode
* objnum - object number for the object dnode
* buf - data buffer that holds the returning dnode
*/
static grub_err_t
{
void *dnbuf;
sizeof (*mdn)) == 0
{
return GRUB_ERR_NONE;
}
(unsigned long long) blkid);
if (err)
return err;
{
}
else
{
}
return GRUB_ERR_NONE;
}
/*
* Get the file dnode for a given file name where mdn is the meta dnode
* for this ZFS object set. When found, place the file dnode in dn.
* The 'path' argument will be mangled.
*
*/
static grub_err_t
struct grub_zfs_data *data)
{
struct dnode_chain
{
};
if (! dn_new)
return grub_errno;
if (err)
{
return err;
}
data, 0);
if (err)
{
return err;
}
if (version > ZPL_VERSION)
{
}
data, 0);
if (err == GRUB_ERR_FILE_NOT_FOUND)
{
subvol->case_insensitive = 0;
}
if (err)
{
return err;
}
if (err)
{
return err;
}
if (!path_buf)
{
return grub_errno;
}
while (1)
{
/* skip leading slashes */
while (*path == '/')
path++;
if (!*path)
break;
/* get the next component name */
path++;
/* Skip dot. */
continue;
/* Handle double dot. */
{
{
dn_new = dnode_path;
}
else
{
"can't resolve ..");
break;
}
continue;
}
*path = 0; /* ensure null termination */
{
}
if (err)
break;
if (! dn_new)
{
err = grub_errno;
break;
}
dnode_path = dn_new;
if (err)
break;
&& ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
{
char *sym_value;
int free_symval = 0;
sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian);
{
<< SPA_MINBLOCKSHIFT);
if (!sym_value)
return grub_errno;
{
void *t;
if (err)
return err;
grub_free (t);
}
free_symval = 1;
}
if (!path_buf)
{
return grub_errno;
}
if (free_symval)
if (path[0] != '/')
{
dn_new = dnode_path;
}
else while (dnode_path != root)
{
dn_new = dnode_path;
}
}
{
void *sahdrp;
int hdrsize;
{
}
{
if (err)
return err;
}
else
{
}
+ hdrsize
+ SA_TYPE_OFFSET),
{
+ hdrsize
+ SA_SIZE_OFFSET),
if (!path_buf)
{
return grub_errno;
}
if (path[0] != '/')
{
dn_new = dnode_path;
}
else while (dnode_path != root)
{
dn_new = dnode_path;
}
}
}
}
if (!err)
while (dnode_path)
{
dnode_path = dn_new;
}
return err;
}
/*
* Get the default 'bootfs' dataset name using rootfs object number
*
*/
static grub_err_t
{
char *bootfs;
*bootfsname = 0;
return (grub_errno);
if (err)
return err;
return (grub_errno);
do
{
{
return (grub_errno);
}
{
return (grub_errno);
}
{
return (grub_errno);
}
{
return (GRUB_ERR_BAD_FS);
}
if (expected_len >= buf_size)
{
if (! tmp)
{
return (grub_errno);
}
}
/* create space for parent dataset name */
/* remove trailing slash */
*bootfsname = bootfs;
return (0);
}
/*
* Get the default 'bootfs' property value from the rootpool.
*
*/
static grub_err_t
struct grub_zfs_data *data)
{
return (grub_errno);
/*
* find the object number for 'pool_props', and get the dnode
* of the 'pool_props'.
*/
return (GRUB_ERR_BAD_FS);
return (grub_errno);
return (GRUB_ERR_BAD_FS);
if (!objnum)
return (GRUB_ERR_BAD_FS);
return (0);
}
/*
* Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
*
* If no fsname and no obj are given, return the DSL_DIR metadnode.
* If fsname is given, return its metadnode and its matching object number.
* If only obj is given, return the metadnode for this object number.
*
*/
static grub_err_t
{
if (err)
return err;
if (err)
return err;
if (err)
return err;
while (*fsname)
{
while (*fsname == '/')
fsname++;
break;
fsname++;
*fsname = 0;
childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *) DN_BONUS (&mdn->dn)))->dd_child_dir_zapobj, mdn->endian);
if (childobj == 0)
if (err)
return err;
if (err)
return err;
if (err)
return err;
}
return GRUB_ERR_NONE;
}
static grub_err_t
{
void *osp;
if (err)
return err;
if (ospsize < OBJSET_PHYS_SIZE_V14)
{
}
return GRUB_ERR_NONE;
}
static grub_err_t
struct grub_zfs_data *data)
{
int keyn = 0;
const void *val_in,
{
return 0;
}
const void *val_in,
const void *val_in,
{
if (namelen != 1)
{
namelen);
return 0;
}
if (elemsize != 1)
{
elemsize);
return 0;
}
keyn++;
return 0;
}
if (! ptr_at)
{
*isfs = 1;
filename = 0;
snapname = 0;
}
else
{
*isfs = 0;
if (!fsname)
return grub_errno;
{
if (!snapname)
{
return grub_errno;
}
}
else
snapname = 0;
if (ptr_slash)
else
filename = "/";
}
if (err)
{
return err;
}
headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian);
data);
if (err)
{
return err;
}
if (grub_zfs_load_key && keychainobj)
{
propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian);
if (err)
{
return err;
}
if (err == GRUB_ERR_FILE_NOT_FOUND)
{
err = 0;
grub_errno = 0;
salt = 0;
}
if (err)
{
return err;
}
&keychain_dn, data);
if (err)
{
return err;
}
{
return err;
}
}
if (snapname)
{
snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian);
if (!err)
if (!err)
if (err)
{
return err;
}
}
if (*isfs)
{
return GRUB_ERR_NONE;
}
return err;
}
/*
* For a given XDR packed nvlist, verify the first 4 bytes and move on.
*
* An XDR packed nvlist is encoded as (comments from nvs_xdr_create) :
*
* nvl_version (4 bytes)
* nvl_nvflag (4 bytes)
* encoded nvpairs:
* encoded size of the nvpair (4 bytes)
* decoded size of the nvpair (4 bytes)
* name string size (4 bytes)
* name string data (sizeof(NV_ALIGN4(string))
* data type (4 bytes)
* # of elements in the nvpair (4 bytes)
* data
* 2 zero's for the last nvpair
* (end of the entire list) (8 bytes)
*
*/
static int
{
/* Verify if the 1st and 2nd byte in the nvlist are valid. */
/* NOTE: independently of what endianness header announces all
subsequent values are big-endian. */
{
return 0;
}
/* skip the header, nvl_version, and nvl_nvflag */
/*
* Loop thru the nvpair list
* The XDR representation of an integer is in big-endian byte order.
*/
{
int nelm;
{
return 0;
}
nvpair += 4;
|| encode_size < 0
{
return 0;
}
nvpair += 4;
if (nelm < 1)
nvpair += 4;
{
*size_out = encode_size;
if (nelm_out)
return 1;
}
}
return 0;
}
int
grub_uint64_t * out)
{
char *nvpair;
int found;
if (!found)
return 0;
if (size < sizeof (grub_uint64_t))
{
return 0;
}
return 1;
}
char *
{
char *nvpair;
char *ret;
int found;
if (!found)
return 0;
if (size < 4)
{
return 0;
}
if (!ret)
return 0;
return ret;
}
char *
{
char *nvpair;
char *ret;
int found;
&size, 0);
if (!found)
return 0;
if (!ret)
return 0;
return ret;
}
int
const char *name)
{
char *nvpair;
int found;
if (! found)
return -1;
return nelm;
}
static int
{
const char *ptr;
ptr += 8;
}
char *
{
int found;
char *ret;
unsigned i;
int elemsize = 0;
if (!found)
return 0;
{
return 0;
}
for (i = 0; i < index; i++)
{
int r;
if (r < 0)
{
return NULL;
}
nvpairptr += r;
}
if (elemsize < 0)
{
return 0;
}
if (!ret)
return 0;
return ret;
}
static void
{
unsigned i;
{
case DEVICE_LEAF:
return;
case DEVICE_RAIDZ:
case DEVICE_MIRROR:
for (i = 0; i < desc->n_children; i++)
return;
}
}
static void
{
unsigned i;
data->mount_count--;
{
for (i = 0; i < data->n_devices_attached; i++)
{
}
}
}
/*
* Free all the caches
*/
static void
zfs_free_caches (void)
{
unsigned int i;
/* free the zfs mount cache list */
{
if (data)
{
for (i = 0; i < data->n_devices_attached; i++)
{
}
}
zcache->zcache_dev_id = 0;
}
/* free the cache list for disk dev with ZFS uberblock */
{
}
/* free the cache list for non-zfs disk dev */
while (dev_notzfs != NULL)
{
}
}
/*
* zfs_mount() locates a valid uberblock of the root pool and
* read in its MOS to the device grub_zfs_data structure.
*
* zfs_mount() is called frequently by GRUB2 zfs interfaces, so
* a cache mechanism is implemented to save the successful zfs
* mount data for devices. This is good for zfs mount performance.
*
*/
static struct grub_zfs_data *
{
int inserted;
unsigned int i, j;
{
return 0;
}
if (partname)
{
}
else
/* check the zfs mount cache list first */
{
continue;
/* use the previous mount data */
if (data->mount_count == 0)
{
/* below items will be re-assigned out of zfs_mount() */
data->file_start = 0;
data->dnode_start = 0;
data->dnode_endian = 0;
/* assign to the new grub device instance */
if (data->devices_attached)
{
for (i = 0; i < data->n_devices_attached; i++)
{
{
for (j = 0; j < desc->n_children; j++)
}
}
}
data->mount_count++;
return (data);
}
}
/* allocate the zfs mount data structure */
if (! data)
{
return 0;
}
/* need to do a new mount */
data->mount_count++;
* data->n_devices_allocated);
data->n_devices_attached = 0;
if (err)
{
zfs_unmount (data);
return NULL;
}
for (i = 0; i < data->n_devices_attached; i++)
if (err)
{
zfs_unmount (data);
return NULL;
}
if (ospsize < OBJSET_PHYS_SIZE_V14)
{
zfs_unmount (data);
return NULL;
}
/* Got the MOS. Save it to data->mos. */
else
{
}
else
{
}
return data;
}
{
if (!zfs)
return grub_errno;
if (err)
zfs_unmount (zfs);
return err;
}
/*
* Returns the pool name.
*/
static grub_err_t
{
char *nvlist;
if (! data)
return grub_errno;
if (err)
{
zfs_unmount (data);
return err;
}
zfs_unmount (data);
return grub_errno;
}
/*
* Returns the pool GUID.
*/
static grub_err_t
{
*uuid = 0;
if (! data)
return grub_errno;
zfs_unmount (data);
if (! *uuid)
return grub_errno;
return GRUB_ERR_NONE;
}
static grub_err_t
{
*mt = 0;
if (! data)
return grub_errno;
zfs_unmount (data);
return GRUB_ERR_NONE;
}
/*
* zfs_open() locates a file in the rootpool by following the
* MOS and places the dnode of the file in the device
* grub_zfs_data structure.
*/
static grub_err_t
{
int isfs;
if (! data)
return grub_errno;
if (err)
{
zfs_unmount (data);
return err;
}
if (isfs)
{
zfs_unmount (data);
}
/* We found the dnode for this file. Verify if it is a plain file. */
{
zfs_unmount (data);
}
/* get the file size and set the file position to 0 */
/*
* For DMU_OT_SA we will need to locate the SIZE attribute
* attribute, which could be either in the bonus buffer
* or the "spill" block.
*/
{
void *sahdrp;
int hdrsize;
{
}
{
if (err)
return err;
}
else
{
}
file->size = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian);
}
{
file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian);
}
else
#ifndef GRUB_UTIL
#endif
return GRUB_ERR_NONE;
}
static grub_ssize_t
{
/*
* If offset is in memory, move it into the buffer provided and return.
*/
{
len);
return len;
}
/*
* Entire Dnode is too big to fit into the space available. We
* will need to read it in chunks. This could be optimized to
* read in as large a chunk as there is space available, but for
* now, this only reads in one data block at a time.
*/
read = 0;
while (length)
{
void *t;
/*
* Find requested blkid and the offset within that block.
*/
0, data);
if (err)
{
return -1;
}
}
return len;
}
static grub_err_t
{
#ifndef GRUB_UTIL
#endif
return GRUB_ERR_NONE;
}
{
int isfs;
if (! data)
return grub_errno;
zfs_unmount (data);
return err;
}
{
if (! data)
return grub_errno;
zfs_unmount (data);
return err;
}
{
if (! data)
return grub_errno;
if (! err)
zfs_unmount (data);
return err;
}
static void
{
{
headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian);
if (err)
{
return;
}
}
if (err)
{
return;
}
if (err)
{
return;
}
if (err)
{
return;
}
{
void *sahdrp;
int hdrsize;
{
}
{
if (err)
return;
}
else
{
return;
}
info->mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
}
{
}
return;
}
static grub_err_t
int (*hook) (const char *, const struct grub_dirhook_info *))
{
int isfs;
{
{
void *sahdrp;
int hdrsize;
{
}
{
if (err)
{
grub_print_error ();
return 0;
}
}
else
{
grub_print_error ();
return 0;
}
info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
}
{
}
}
{
if (err)
return 0;
return 0;
}
{
char *name2;
int ret;
if (err)
return 0;
return 0;
name2[0] = '@';
return ret;
}
if (! data)
return grub_errno;
if (err)
{
zfs_unmount (data);
return err;
}
if (isfs)
{
childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian);
headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian);
if (err)
{
zfs_unmount (data);
return err;
}
if (err)
{
zfs_unmount (data);
return err;
}
snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&dn.dn))->ds_snapnames_zapobj, dn.endian);
if (err)
{
zfs_unmount (data);
return err;
}
}
else
{
{
zfs_unmount (data);
}
}
zfs_unmount (data);
return grub_errno;
}
#ifdef GRUB_UTIL
static grub_err_t
unsigned int *nsectors,
{
unsigned i;
if (embed_type != GRUB_EMBED_PCBIOS)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"ZFS currently supports only PC-BIOS embedding");
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Your core.img is unusually large. "
"It won't fit in the embedding area.");
if (!*sectors)
return grub_errno;
for (i = 0; i < *nsectors; i++)
return GRUB_ERR_NONE;
}
#endif
/* GRUB2 zfs FS module interface structure */
.name = "zfs",
.dir = grub_zfs_dir,
.open = grub_zfs_open,
.read = grub_zfs_read,
.close = grub_zfs_close,
#ifdef GRUB_UTIL
.embed = grub_zfs_embed,
.reserved_first_sector = 1,
#endif
.next = 0
};
{
#ifndef GRUB_UTIL
#endif
}
{
zfs_free_caches ();
}