]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
merge mainline into zfs
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 26 Oct 2011 17:26:24 +0000 (19:26 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 26 Oct 2011 17:26:24 +0000 (19:26 +0200)
1  2 
Makefile.util.def
grub-core/fs/zfs/zfs.c
grub-core/fs/zfs/zfsinfo.c
util/grub-fstest.c

Simple merge
index 327e99a908b48631c51d28fb63865279855dc2be,6721cddc1ce78e6ce8a058ae7b5face283eed61b..e248a1114592446366b03338d83d450491768636
@@@ -432,519 -409,16 +434,520 @@@ get_psize (blkptr_t * bp, grub_zfs_endi
          << SPA_MINBLOCKSHIFT);
  }
  
 -static grub_uint64_t
 -dva_get_offset (dva_t * dva, grub_zfs_endian_t endian)
 +static grub_uint64_t
 +dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian)
 +{
 +  grub_dprintf ("zfs", "dva=%llx, %llx\n", 
 +              (unsigned long long) dva->dva_word[0], 
 +              (unsigned long long) dva->dva_word[1]);
 +  return grub_zfs_to_cpu64 ((dva)->dva_word[1], 
 +                          endian) << SPA_MINBLOCKSHIFT;
 +}
 +
 +static grub_err_t
 +zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist)
 +{
 +  grub_err_t err;
 +
 +  *nvlist = grub_malloc (VDEV_PHYS_SIZE);
 +  if (!diskdesc->dev)
 +    return grub_error (GRUB_ERR_BAD_FS, "member drive unknown");
 +
 +  /* Read in the vdev name-value pair list (112K). */
 +  err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0,
 +                      VDEV_PHYS_SIZE, *nvlist);
 +  if (err)
 +    {
 +      grub_free (*nvlist);
 +      *nvlist = 0;
 +      return err;
 +    }
 +  return GRUB_ERR_NONE;
 +}
 +
 +static grub_err_t
 +fill_vdev_info_real (struct grub_zfs_data *data,
 +                   const char *nvlist,
 +                   struct grub_zfs_device_desc *fill,
 +                   struct grub_zfs_device_desc *insert)
 +{
 +  char *type;
 +
 +  type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE);
 +
 +  if (!type)
 +    return grub_errno;
 +
 +  if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &(fill->id)))
 +    return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
 +
 +  if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &(fill->guid)))
 +    return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
 +
 +  if (grub_strcmp (type, VDEV_TYPE_DISK) == 0
 +      || grub_strcmp (type, VDEV_TYPE_FILE) == 0)
 +    {
 +      fill->type = DEVICE_LEAF;
 +
 +      if (!fill->dev && fill->guid == insert->guid)
 +      {
 +        fill->dev = insert->dev;
 +        fill->vdev_phys_sector = insert->vdev_phys_sector;
 +        fill->current_uberblock = insert->current_uberblock;
 +        fill->original = insert->original;
 +      }
 +      if (!data->device_original)
 +      data->device_original = fill;
 +
 +      return GRUB_ERR_NONE;
 +    }
 +
 +  if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0
 +      || grub_strcmp (type, VDEV_TYPE_RAIDZ) == 0)
 +    {
 +      int nelm, i;
 +
 +      if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0)
 +      fill->type = DEVICE_MIRROR;
 +      else
 +      {
 +        grub_uint64_t par;
 +        fill->type = DEVICE_RAIDZ;
 +        if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par))
 +          return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity");
 +        fill->nparity = par;
 +      }
 +
 +      nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist, ZPOOL_CONFIG_CHILDREN);
 +
 +      if (nelm <= 0)
 +      return grub_error (GRUB_ERR_BAD_FS, "incorrect mirror VDEV");
 +
 +      if (!fill->children)
 +      {
 +        fill->n_children = nelm;
 +        
 +        fill->children = grub_zalloc (fill->n_children
 +                                      * sizeof (fill->children[0]));
 +      }
 +
 +      for (i = 0; i < nelm; i++)
 +      {
 +        char *child;
 +        grub_err_t err;
 +
 +        child = grub_zfs_nvlist_lookup_nvlist_array
 +          (nvlist, ZPOOL_CONFIG_CHILDREN, i);
 +
 +        err = fill_vdev_info_real (data, child, &fill->children[i], insert);
 +
 +        grub_free (child);
 +
 +        if (err)
 +          return err;
 +      }
 +      return GRUB_ERR_NONE;
 +    }
 +
 +  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "vdev %s isn't supported",
 +                   type);
 +}
 +
 +static grub_err_t
 +fill_vdev_info (struct grub_zfs_data *data,
 +              char *nvlist, struct grub_zfs_device_desc *diskdesc)
 +{
 +  grub_uint64_t id;
 +  unsigned i;
 +
 +  if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &id))
 +    return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
 +
 +  for (i = 0; i < data->n_devices_attached; i++)
 +    if (data->devices_attached[i].id == id)
 +      return fill_vdev_info_real (data, nvlist, &data->devices_attached[i],
 +                                diskdesc);
 +
 +  data->n_devices_attached++;
 +  if (data->n_devices_attached > data->n_devices_allocated)
 +    {
 +      void *tmp;
 +      data->n_devices_allocated = 2 * data->n_devices_attached + 1;
 +      data->devices_attached
 +      = grub_realloc (tmp = data->devices_attached,
 +                      data->n_devices_allocated
 +                      * sizeof (data->devices_attached[0]));
 +      if (!data->devices_attached)
 +      {
 +        data->devices_attached = tmp;
 +        return grub_errno;
 +      }
 +    }
 +
 +  grub_memset (&data->devices_attached[data->n_devices_attached - 1],
 +             0, sizeof (data->devices_attached[data->n_devices_attached - 1]));
 +
 +  return fill_vdev_info_real (data, nvlist,
 +                            &data->devices_attached[data->n_devices_attached - 1],
 +                            diskdesc);
 +}
 +
 +/*
 + * Check the disk label information and retrieve needed vdev name-value pairs.
 + *
 + */
 +static grub_err_t
 +check_pool_label (struct grub_zfs_data *data,
 +                struct grub_zfs_device_desc *diskdesc)
 +{
 +  grub_uint64_t pool_state, txg = 0;
 +  char *nvlist;
 +#if 0
 +  char *nv;
 +#endif
 +  grub_uint64_t poolguid;
 +  grub_uint64_t version;
 +  int found;
 +  grub_err_t err;
 +
 +  err = zfs_fetch_nvlist (diskdesc, &nvlist);
 +  if (err)
 +    return err;
 +
 +  grub_dprintf ("zfs", "check 2 passed\n");
 +
 +  found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE,
 +                                       &pool_state);
 +  if (! found)
 +    {
 +      grub_free (nvlist);
 +      if (! grub_errno)
 +      grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found");
 +      return grub_errno;
 +    }
 +  grub_dprintf ("zfs", "check 3 passed\n");
 +
 +  if (pool_state == POOL_STATE_DESTROYED)
 +    {
 +      grub_free (nvlist);
 +      return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed");
 +    }
 +  grub_dprintf ("zfs", "check 4 passed\n");
 +
 +  found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg);
 +  if (!found)
 +    {
 +      grub_free (nvlist);
 +      if (! grub_errno)
 +      grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found");
 +      return grub_errno;
 +    }
 +  grub_dprintf ("zfs", "check 6 passed\n");
 +
 +  /* not an active device */
 +  if (txg == 0)
 +    {
 +      grub_free (nvlist);
 +      return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active");
 +    }
 +  grub_dprintf ("zfs", "check 7 passed\n");
 +
 +  found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION,
 +                                       &version);
 +  if (! found)
 +    {
 +      grub_free (nvlist);
 +      if (! grub_errno)
 +      grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found");
 +      return grub_errno;
 +    }
 +  grub_dprintf ("zfs", "check 8 passed\n");
 +
 +  if (version > SPA_VERSION)
 +    {
 +      grub_free (nvlist);
 +      return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 +                       "too new version %llu > %llu",
 +                       (unsigned long long) version,
 +                       (unsigned long long) SPA_VERSION);
 +    }
 +  grub_dprintf ("zfs", "check 9 passed\n");
 +
 +  found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID,
 +                                       &(diskdesc->guid));
 +  if (! found)
 +    {
 +      grub_free (nvlist);
 +      if (! grub_errno)
 +      grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found");
 +      return grub_errno;
 +    }
 +
 +  found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID,
 +                                       &poolguid);
 +  if (! found)
 +    {
 +      grub_free (nvlist);
 +      if (! grub_errno)
 +      grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found");
 +      return grub_errno;
 +    }
 +
 +  grub_dprintf ("zfs", "check 11 passed\n");
 +
 +  if (data->mounted && data->guid != poolguid)
 +    return grub_error (GRUB_ERR_BAD_FS, "another zpool");
 +  else
 +    data->guid = poolguid;
 +
 +  {
 +    char *nv;
 +    nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
 +
 +    if (!nv)
 +      {
 +      grub_free (nvlist);
 +      return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev tree");
 +      }
 +    err = fill_vdev_info (data, nv, diskdesc);
 +    if (err)
 +      {
 +      grub_free (nvlist);
 +      return err;
 +      }
 +  }
 +  grub_dprintf ("zfs", "check 10 passed\n");
 +
 +  grub_free (nvlist);
 +
 +  return GRUB_ERR_NONE;
 +}
 +
 +static grub_err_t
 +scan_disk (grub_device_t dev, struct grub_zfs_data *data,
 +         int original)
 +{
 +  int label = 0;
 +  uberblock_phys_t *ub_array, *ubbest = NULL;
 +  vdev_boot_header_t *bh;
 +  grub_err_t err;
 +  int vdevnum;
 +  struct grub_zfs_device_desc desc;
 +
 +  ub_array = grub_malloc (VDEV_UBERBLOCK_RING);
 +  if (!ub_array)
 +    return grub_errno;
 +
 +  bh = grub_malloc (VDEV_BOOT_HEADER_SIZE);
 +  if (!bh)
 +    {
 +      grub_free (ub_array);
 +      return grub_errno;
 +    }
 +
 +  vdevnum = VDEV_LABELS;
 +
 +  desc.dev = dev;
 +  desc.original = original;
 +
 +  /* Don't check back labels on CDROM.  */
 +  if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN)
 +    vdevnum = VDEV_LABELS / 2;
 +
 +  for (label = 0; ubbest == NULL && label < vdevnum; label++)
 +    {
 +      desc.vdev_phys_sector
 +      = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)
 +      + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT)
 +      + (label < VDEV_LABELS / 2 ? 0 : grub_disk_get_size (dev->disk)
 +         - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT));
 +
 +      /* Read in the uberblock ring (128K). */
 +      err = grub_disk_read (dev->disk, desc.vdev_phys_sector
 +                          + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
 +                          0, VDEV_UBERBLOCK_RING, (char *) ub_array);
 +      if (err)
 +      {
 +        grub_errno = GRUB_ERR_NONE;
 +        continue;
 +      }
 +      grub_dprintf ("zfs", "label ok %d\n", label);
 +
 +      ubbest = find_bestub (ub_array, desc.vdev_phys_sector);
 +      if (!ubbest)
 +      {
 +        grub_dprintf ("zfs", "No uberblock found\n");
 +        grub_errno = GRUB_ERR_NONE;
 +        continue;
 +      }
 +
 +      grub_memmove (&(desc.current_uberblock),
 +                  &ubbest->ubp_uberblock, sizeof (uberblock_t));
 +      if (original)
 +      grub_memmove (&(data->current_uberblock),
 +                    &ubbest->ubp_uberblock, sizeof (uberblock_t));
 +
 +      err = check_pool_label (data, &desc);
 +      if (err)
 +      {
 +        grub_errno = GRUB_ERR_NONE;
 +        continue;
 +      }
 +#if 0
 +      if (find_best_root &&
 +        vdev_uberblock_compare (&ubbest->ubp_uberblock,
 +                                &(current_uberblock)) <= 0)
 +      continue;
 +#endif
 +      grub_free (ub_array);
 +      grub_free (bh);
 +      return GRUB_ERR_NONE;
 +    }
 +  
 +  grub_free (ub_array);
 +  grub_free (bh);
 +
 +  return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label");
 +}
 +
 +static grub_err_t
 +scan_devices (struct grub_zfs_data *data)
 +{
 +  auto int hook (const char *name);
 +  int hook (const char *name)
 +  {
 +    grub_device_t dev;
 +    grub_err_t err;
 +    dev = grub_device_open (name);
 +    if (!dev)
 +      return 0;
 +    if (!dev->disk)
 +      {
 +      grub_device_close (dev);
 +      return 0;
 +      }
 +    err = scan_disk (dev, data, 0);
 +    if (err == GRUB_ERR_BAD_FS)
 +      {
 +      grub_device_close (dev);
 +      grub_errno = GRUB_ERR_NONE;
 +      return 0;
 +      }
 +    if (err)
 +      {
 +      grub_device_close (dev);
 +      grub_print_error ();
 +      return 0;
 +      }
 +    
 +    return 0;
 +  }
 +  grub_device_iterate (hook);
 +  return GRUB_ERR_NONE;
 +}
 +
 +static grub_err_t
 +read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
 +           grub_uint32_t asize, grub_size_t len, void *buf)
 +{
 +  switch (desc->type)
 +    {
 +    case DEVICE_LEAF:
 +      {
 +      grub_uint64_t sector;
 +      sector = DVA_OFFSET_TO_PHYS_SECTOR (offset);
 +      if (!desc->dev)
 +        {
 +          return grub_error (GRUB_ERR_BAD_FS, "member drive unknown");
 +        }
 +      /* read in a data block */
 +      return grub_disk_read (desc->dev->disk, sector, 0, len, buf);
 +      }
 +    case DEVICE_MIRROR:
 +      {
 +      grub_err_t err;
 +      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++)
 +        {
 +          err = read_device (offset, &desc->children[i], asize,
 +                             len, buf);
 +          if (!err)
 +            break;
 +          grub_errno = GRUB_ERR_NONE;
 +        }
 +      return (grub_errno = err);
 +      }
 +    case DEVICE_RAIDZ:
 +      {
 +      grub_uint64_t sector;
 +      grub_uint32_t bsize;
 +      unsigned c = 0;
 +
 +      bsize = (asize + desc->nparity) / desc->n_children;
 +      sector = offset >> 9;
 +      while (len > 0)
 +        {
 +          grub_size_t csize;
 +          grub_uint64_t high;
-           grub_uint32_t devn;
++          grub_uint64_t devn;
 +          grub_err_t err;
 +          high = grub_divmod64 (sector + (asize > 2) + c, desc->n_children,
 +                                &devn);
 +          csize = bsize << 9;
 +          if (csize > len)
 +            csize = len;
 +          grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T
-                         "+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%x)\n",
++                        "+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%"
++                        PRIxGRUB_UINT64_T ")\n",
 +                        sector,(asize > 2), c, high, devn);
 +          err = read_device (high << 9, &desc->children[devn],
 +                             bsize, csize, buf);
 +          if (err)
 +            return err;
 +          c++;
 +          buf = (char *) buf + csize;
 +          len -= csize;
 +        }
 +      return GRUB_ERR_NONE;       
 +      }
 +      return GRUB_ERR_NONE;
 +    }
 +  return grub_error (GRUB_ERR_BAD_FS, "unsupported device type");
 +}
 +
 +static grub_err_t
 +read_dva (const dva_t *dva,
 +        grub_zfs_endian_t endian, struct grub_zfs_data *data,
 +        void *buf, grub_size_t len)
  {
 -  grub_dprintf ("zfs", "dva=%llx, %llx\n", 
 -              (unsigned long long) dva->dva_word[0], 
 -              (unsigned long long) dva->dva_word[1]);
 -  return grub_zfs_to_cpu64 ((dva)->dva_word[1], 
 -                          endian) << SPA_MINBLOCKSHIFT;
 -}
 +  grub_uint64_t offset;
 +  unsigned i;
 +  grub_err_t err;
 +  int try = 0;
 +  offset = dva_get_offset (dva, endian);
  
 +  for (try = 0; try < 2; try++)
 +    {
 +      for (i = 0; i < data->n_devices_attached; i++)
 +      if (data->devices_attached[i].id == DVA_GET_VDEV (dva))
 +        {
 +          err = read_device (offset, &data->devices_attached[i],
 +                             dva->dva_word[0] & 0xffffff, len, buf);
 +          if (!err)
 +            return GRUB_ERR_NONE;
 +          break;
 +        }
 +      if (try == 1)
 +      break;
 +      err = scan_devices (data);
 +      if (err)
 +      return err;
 +    }
 +  return err;
 +}
  
  /*
   * Read a block of data based on the gang block address dva,
@@@ -1369,10 -858,8 +1374,9 @@@ zap_leaf_lookup (zap_leaf_phys_t * l, g
                                name))
        {
          struct zap_leaf_array *la;
-         grub_uint8_t *ip;
  
 -        if (le->le_int_size != 8 || le->le_value_length != 1)
 +        if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length,
 +                                                       endian) != 1)
            return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry");
  
          /* get the uint64_t property value */
@@@ -1477,13 -964,18 +1481,19 @@@ fzap_iterate (dnode_end_t * zap_dnode, 
        grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
        return 0;
      }
-   for (idx = 0; idx < grub_zfs_to_cpu64 (zap->zap_num_leafs,
-                                        zap_dnode->endian); idx++)
+   for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++)
      {
 -      blkid = ((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))];
 +      blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))],
 +                                       zap_dnode->endian);
  
-       err = dmu_read (zap_dnode, blkid, (void **) &l, &endian, data);
+       for (idx2 = 0; idx2 < idx; idx2++)
+       if (blkid == ((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))])
+         break;
+       if (idx2 != idx)
+       continue;
+       err = dmu_read (zap_dnode, blkid, &l_in, &endian, data);
+       l = l_in;
        if (err)
        {
          grub_errno = GRUB_ERR_NONE;
Simple merge
index 122465d7a6665ba3c7512ce6a5edf6478b9ee639,ad64701a2d3ca375f4365d2485af0d8d7c862647..b6ed2ef0cbf3d95ef112f88e78682bced2374b7a
@@@ -54,14 -54,17 +54,17 @@@ execute_command (char *name, int n, cha
    return (cmd->func) (cmd, n, args);
  }
  
- #define CMD_LS          1
- #define CMD_CP          2
- #define CMD_CMP         3
- #define CMD_HEX         4
- #define CMD_CRC         6
- #define CMD_BLOCKLIST   7
- #define CMD_ZFSINFO     8
+ enum {
+   CMD_LS = 1,
+   CMD_CP,
+   CMD_CAT,
+   CMD_CMP,
+   CMD_HEX,
+   CMD_CRC,
+   CMD_BLOCKLIST,
 -  CMD_TESTLOAD
++  CMD_TESTLOAD,
++  CMD_ZFSINFO
+ };
 -
  #define BUF_SIZE  32256
  
  static grub_disk_addr_t skip, leng;