Skip to content

Commit

Permalink
Refactoring of function nvlist_find_value
Browse files Browse the repository at this point in the history
It's needed to allow iteration of nvlists in other functions.

Ported to GRUB2 from IllumOS changeset 13700    2889e2596bd6

Author: Christopher Siden <chris.siden@delphix.com>
Ported-by: Massimo Maggi <me@massimo-maggi.eu>
  • Loading branch information
maxximino committed Jan 26, 2013
1 parent 595d76e commit 5834403
Showing 1 changed file with 158 additions and 72 deletions.
230 changes: 158 additions & 72 deletions grub-core/fs/zfs/zfs.c
Expand Up @@ -2,6 +2,7 @@
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc.
* Copyright 2010 Sun Microsystems, Inc.
* Copyright (c) 2012 by Delphix. All rights reserved.
*
* GRUB is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -767,6 +768,155 @@ fill_vdev_info (struct grub_zfs_data *data,
diskdesc, inserted, 0xffffffff);
}

/*
* 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) :
*
* encoding method/host endian (4 bytes)
* 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)
*
*/

/*
* The nvlist_next_nvpair() function returns a handle to the next nvpair in the
* list following nvpair. If nvpair is NULL, the first pair is returned. If
* nvpair is the last pair in the nvlist, NULL is returned.
*/
static const char *
nvlist_next_nvpair(const char *nvl, const char *nvpair)
{
const char *nvp;
int encode_size;
int name_len;
if (nvl == NULL)
return (NULL);

if (nvpair == NULL) {
/* skip over header, nvl_version and nvl_nvflag */
nvpair = nvl + 4 * 3;
} else {
/* skip to the next nvpair */
encode_size = grub_be_to_cpu32 (grub_get_unaligned32(nvpair));
nvpair += encode_size;
}
/* 8 bytes of 0 marks the end of the list */
if (*(grub_uint64_t*)nvpair == 0)
return (NULL);
/*consistency checks*/
if (nvpair + 4 * 3 >= nvl + VDEV_PHYS_SIZE)
{
grub_dprintf ("zfs", "nvlist overflow\n");
grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
return (NULL);
}
encode_size = grub_be_to_cpu32 (grub_get_unaligned32(nvpair));

nvp = nvpair + 4*2;
name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp));
nvp += 4;

nvp = nvp + ((name_len + 3) & ~3); // align
if (nvp + 4 >= nvl + VDEV_PHYS_SIZE
|| encode_size < 0
|| nvp + 4 + encode_size > nvl + VDEV_PHYS_SIZE)
{
grub_dprintf ("zfs", "nvlist overflow\n");
grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
return (NULL);
}
/* end consistency checks */

return (nvpair);
}
/*
* This function returns 0 on success and 1 on failure. On success, a string
* containing the name of nvpair is saved in buf.
*/
static int
nvpair_name(const char *nvp, char **buf, int* buflen)
{
int len;

/* skip over encode/decode size */
nvp += 4 * 2;

len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp));
nvp=nvp+4;

*buf=(char*)nvp;
*buflen=len;

return (0);
}
/*
* This function retrieves the value of the nvpair in the form of enumerated
* type data_type_t.
*/
static int
nvpair_type(const char *nvp)
{
int name_len, type;

/* skip over encode/decode size */
nvp += 4 * 2;

/* skip over name_len */
name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp));
nvp += 4;

/* skip over name */
nvp = nvp + ((name_len + 3) & ~3); /* align */

type = grub_be_to_cpu32 (grub_get_unaligned32 (nvp));

return (type);
}
static int
nvpair_value(const char *nvp,char **val,
grub_size_t *size_out, grub_size_t *nelm_out)
{
int name_len,nelm,encode_size;

/* skip over encode/decode size */
encode_size = grub_be_to_cpu32 (grub_get_unaligned32(nvp));
nvp += 8;

/* skip over name_len */
name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp));
nvp += 4;

/* skip over name */
nvp = nvp + ((name_len + 3) & ~3); /* align */

/* skip over type */
nvp += 4;
nelm = grub_be_to_cpu32 (grub_get_unaligned32 (nvp));
nvp +=4;
if (nelm < 1)
{
grub_error (GRUB_ERR_BAD_FS, "empty nvpair");
return 0;
}
*val = (char *) nvp;
*size_out = encode_size;
if (nelm_out)
*nelm_out = nelm;

return 1;
}

/*
* Check the disk label information and retrieve needed vdev name-value pairs.
*
Expand Down Expand Up @@ -3064,34 +3214,14 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
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) :
*
* encoding method/host endian (4 bytes)
* 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
nvlist_find_value (const char *nvlist_in, const char *name,
int valtype, char **val,
grub_size_t *size_out, grub_size_t *nelm_out)
{
int name_len, type, encode_size;
const char *nvpair, *nvp_name, *nvlist = nvlist_in;
int name_len, type ;
const char *nvpair=NULL,*nvlist=nvlist_in;
char *nvp_name;

/* Verify if the 1st and 2nd byte in the nvlist are valid. */
/* NOTE: independently of what endianness header announces all
Expand All @@ -3104,62 +3234,18 @@ nvlist_find_value (const char *nvlist_in, const char *name,
return 0;
}

/* skip the header, nvl_version, and nvl_nvflag */
nvlist = nvlist + 4 * 3;
/*
* Loop thru the nvpair list
* The XDR representation of an integer is in big-endian byte order.
*/
while ((encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (nvlist))))
while ((nvpair=nvlist_next_nvpair(nvlist,nvpair)))
{
int nelm;

if (nvlist + 4 * 4 >= nvlist_in + VDEV_PHYS_SIZE)
nvpair_name(nvpair,&nvp_name,&name_len);
type = nvpair_type(nvpair);
if ((grub_strncmp (nvp_name, name, grub_strlen(name)) == 0) && type == valtype)
{
grub_dprintf ("zfs", "nvlist overflow\n");
grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
return 0;
}

nvpair = nvlist + 4 * 2; /* skip the encode/decode size */

name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
nvpair += 4;

nvp_name = nvpair;
nvpair = nvpair + ((name_len + 3) & ~3); /* align */

if (nvpair + 8 >= nvlist_in + VDEV_PHYS_SIZE
|| encode_size < 0
|| nvpair + 8 + encode_size > nvlist_in + VDEV_PHYS_SIZE)
{
grub_dprintf ("zfs", "nvlist overflow\n");
grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
return 0;
return nvpair_value(nvpair,val,size_out,nelm_out);
}

type = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
nvpair += 4;

nelm = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
if (nelm < 1)
{
grub_error (GRUB_ERR_BAD_FS, "empty nvpair");
return 0;
}

nvpair += 4;

if ((grub_strncmp (nvp_name, name, name_len) == 0) && type == valtype)
{
*val = (char *) nvpair;
*size_out = encode_size;
if (nelm_out)
*nelm_out = nelm;
return 1;
}

nvlist += encode_size; /* goto the next nvpair */
}
return 0;
}
Expand Down

0 comments on commit 5834403

Please sign in to comment.