Hi.

Attached is my work on reiserfs handling.

As I don't plan to work on it anytime soon, and as I was advised on IRC, I 
port it here, in case someone want to use it as a base.

It does not follow the coding style, some part looks quite ugly (big hex 
constants as bitmasks, grub_reiserfs_get_item and grub_reiserfe_iterate_dir 
functions), has old commented out code (grub_reiserfs_read) and some debug 
printfs commented out (grub_reiserfe_iterate_dir), conditionaly compiled 
(GRUB_REISERFS_DEBUG), or always available.

As such, it was good enough to read tens of megabyte big files on a reiserfs 
3.5 disk image on a PPC32 box. It handles symlinks as such, and showed no 
read errors as far as I could tell. It was only tested in grub-emu, and never 
on a live system.

To build grub2 with reiserfs module, add this file in the "fs" folder and add 
a reference to this file in conf/*.rmk files .

Sorry for not finishing that work.

-- 
Vincent Pelletier
/* reiserfs.c - ReiserFS versions up to 3.6 */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
  TODO:
  implement journal handling (ram replay)
  implement symlinks support
  support items bigger than roughly (blocksize / 4) * blocksize
    !! that goes for directories aswell, size in such case is metadata size.
  test tail packing & direct files
  validate partition label position
*/
#warning "TODO : journal, tail packing (?)"

// #define GRUB_REISERFS_KEYV2_BITFIELD
// #define GRUB_REISERFS_DEBUG
// #define GRUB_REISERFS_JOURNALING
// #define GRUB_HEXDUMP

#include <grub/err.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/disk.h>
#include <grub/dl.h>
#include <grub/types.h>
#include <grub/fshelp.h>

#define MIN(a, b) (((a)<(b))?(a):(b))
#define MAX(a, b) (((a)>(b))?(a):(b))

#define REISERFS_SUPER_BLOCK_OFFSET 0x10000
#define REISERFS_MAGIC_LEN 12
#define REISERFS_MAGIC_STRING "ReIsEr2Fs\0\0\0"
/* If the 3rd bit of an item state is set, then it's visible.  */
#define GRUB_REISERFS_VISIBLE_MASK ((grub_uint16_t)0x04)
#define REISERFS_MAX_LABEL_LENGTH 16
#define REISERFS_LABEL_OFFSET 0x64

#define S_IFLNK 0xA000

#ifndef GRUB_UTIL
static grub_dl_t my_mod;
#endif

enum grub_reiserfs_item_type
{
  GRUB_REISERFS_STAT,
  GRUB_REISERFS_DIRECTORY,
  GRUB_REISERFS_DIRECT,
  GRUB_REISERFS_INDIRECT,
  GRUB_REISERFS_ANY, /* Matches both _DIRECT and _INDIRECT when searching.  */
  GRUB_REISERFS_UNKNOWN
};

struct grub_reiserfs_superblock
{
  grub_uint32_t block_count;
  grub_uint32_t block_free_count;
  grub_uint32_t root_block;
  grub_uint32_t journal_block;
  grub_uint32_t journal_device;
  grub_uint32_t journal_original_size;
  grub_uint32_t journal_max_transaction_size;
  grub_uint32_t journal_block_count;
  grub_uint32_t journal_max_batch;
  grub_uint32_t journal_max_commit_age;
  grub_uint32_t journal_max_transaction_age;
  grub_uint16_t block_size;
  grub_uint16_t oid_max_size;
  grub_uint16_t oid_current_size;
  grub_uint16_t state;
  grub_uint8_t magic_string[REISERFS_MAGIC_LEN];
  grub_uint32_t function_hash_code;
  grub_uint16_t tree_height;
  grub_uint16_t bitmap_number;
  grub_uint16_t version;
  grub_uint16_t reserved;
  grub_uint32_t inode_generation;
} __attribute__ ((packed));

#ifdef GRUB_REISERFS_JOURNALING
#error "Journaling not yet supported."
struct grub_reiserfs_journal_header
{
  grub_uint32_t last_flush_uid;
  grub_uint32_t unflushed_offset;
  grub_uint32_t mount_id;
} __attribute__ ((packed));

struct grub_reiserfs_transaction_header
{
  grub_uint32_t id;
  grub_uint32_t len;
  grub_uint32_t mount_id;
  char *data;
  char checksum[12];
} __attribute__ ((packed));
#endif

struct grub_reiserfs_stat_item_v1
{
  grub_uint16_t mode;
  grub_uint16_t hardlink_count;
  grub_uint16_t uid;
  grub_uint16_t gid;
  grub_uint32_t size;
  grub_uint32_t atime;
  grub_uint32_t mtime;
  grub_uint32_t ctime;
  grub_uint32_t rdev;
  grub_uint32_t first_direct_byte;
} __attribute__ ((packed));

struct grub_reiserfs_stat_item_v2
{
  grub_uint16_t mode;
  grub_uint16_t reserved;
  grub_uint32_t hardlink_count;
  grub_uint64_t size;
  grub_uint32_t uid;
  grub_uint32_t gid;
  grub_uint32_t atime;
  grub_uint32_t mtime;
  grub_uint32_t ctime;
  grub_uint32_t blocks;
  grub_uint32_t first_direct_byte;
} __attribute__ ((packed));

struct grub_reiserfs_key
{
  grub_uint32_t directory_id;
  grub_uint32_t object_id;
  union
    {
      struct
        {
          grub_uint32_t offset;
          grub_uint32_t type;
        } v1 __attribute__ ((packed));
      struct
        {
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
          grub_uint64_t offset:60;
          grub_uint64_t type:4;
#else
          grub_uint64_t offset_type;
#endif
        } v2 __attribute__ ((packed));
    } u;
} __attribute__ ((packed));

struct grub_reiserfs_item_header
{
  struct grub_reiserfs_key key;
  union
    {
      grub_uint16_t free_space;
      grub_uint16_t entry_count;
    } u __attribute__ ((packed));
  grub_uint16_t item_size;
  grub_uint16_t item_location;
  grub_uint16_t version;
} __attribute__ ((packed));

struct grub_reiserfs_block_header
{
  grub_uint16_t level;
  grub_uint16_t item_count;
  grub_uint16_t free_space;
  grub_uint16_t reserved;
  struct grub_reiserfs_key block_right_delimiting_key;
} __attribute__ ((packed));

struct grub_reiserfs_disk_child
{
  grub_uint32_t block_number;
  grub_uint16_t size;
  grub_uint16_t reserved;
} __attribute__ ((packed));

struct grub_reiserfs_directory_header
{
  grub_uint32_t offset;
  grub_uint32_t directory_id;
  grub_uint32_t object_id;
  grub_uint16_t location;
  grub_uint16_t state;
} __attribute__ ((packed));

struct grub_reiserfs_node_body
{
  union
    {
      struct
        {
          struct grub_reiserfs_key *key_list;
          struct grub_reiserfs_disk_child *child_list;
        } internal;
      struct
        {
          struct grub_reiserfs_item_header *item_header_list;
        } leaf;
    } u;
};

struct grub_fshelp_node
{
  struct grub_reiserfs_data *data;
  grub_uint32_t block_number; /* 0 if node is not found.  */
  grub_uint16_t block_position;
  enum grub_reiserfs_item_type type; /* To know how to read the header.  */
  struct grub_reiserfs_item_header header;
};

/* Returned when opening a file.  */
struct grub_reiserfs_data
{
  struct grub_reiserfs_superblock superblock;
  grub_disk_t disk;
};

/* Internal-only functions. Not to be used outside of this file.  */

/* Return the type of given v2 key.  */
static enum grub_reiserfs_item_type
grub_reiserfs_get_key_v2_type (const struct grub_reiserfs_key *key)
{
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
  switch (key->u.v2.type)
#else
  switch (key->u.v2.offset_type & 0xF)
#endif
    {
      case 0:
        return GRUB_REISERFS_STAT;
      case 15:
        return GRUB_REISERFS_ANY;
      case 3:
        return GRUB_REISERFS_DIRECTORY;
      case 2:
        return GRUB_REISERFS_DIRECT;
      case 1:
        return GRUB_REISERFS_INDIRECT;
    }
  return GRUB_REISERFS_UNKNOWN;
}

/* Return the type of given v1 key.  */
static enum grub_reiserfs_item_type
grub_reiserfs_get_key_v1_type (const struct grub_reiserfs_key *key)
{
  switch (grub_le_to_cpu32 (key->u.v1.type))
    {
      case 0:
        return GRUB_REISERFS_STAT;
      case 555:
        return GRUB_REISERFS_ANY;
      case 500:
        return GRUB_REISERFS_DIRECTORY;
      case 0x20000000:
      case 0xFFFFFFFF:
        return GRUB_REISERFS_DIRECT;
      case 0x10000000:
      case 0xFFFFFFFE:
        return GRUB_REISERFS_INDIRECT;
    }
  return GRUB_REISERFS_UNKNOWN;
}

/* Return 1 if the given key is version 1 key, 2 otherwise.  */
static int
grub_reiserfs_get_key_version (const struct grub_reiserfs_key *key)
{
  return (grub_reiserfs_get_key_v1_type (key) == GRUB_REISERFS_UNKNOWN) ? 2 : 1;
}

#ifdef GRUB_HEXDUMP
static void
grub_hexdump (char *buffer, grub_size_t len)
{
  unsigned int a;
  for (a = 0; a < len; a++)
    {
      if (!(a & 0x0F))
        grub_printf ("\n%08x  ", a);
      grub_printf ("%02x ", ((unsigned int) ((unsigned char *) buffer)[a]) & 0xFF);
    }
  grub_printf ("\n");
}
#endif

#ifdef GRUB_REISERFS_DEBUG
static grub_uint64_t
grub_reiserfs_get_key_offset (const struct grub_reiserfs_key *key);

static enum grub_reiserfs_item_type
grub_reiserfs_get_key_type (const struct grub_reiserfs_key *key);

static void
grub_reiserfs_print_key (const struct grub_reiserfs_key *key)
{
  unsigned int a;
  char *reiserfs_type_strings[6] = \
  {"stat     ",
   "directory",
   "direct   ",
   "indirect ",
   "any      ",
   "unknown  "};
  for (a = 0; a < sizeof(struct grub_reiserfs_key); a++)
    grub_printf ("%02x ", ((unsigned int) ((char *)key)[a]) & 0xFF);
  grub_printf("parent id = 0x%08x, self id = 0x%08x, type = %s, offset = ",
              grub_le_to_cpu32 (key->directory_id),
              grub_le_to_cpu32 (key->object_id),
              reiserfs_type_strings [grub_reiserfs_get_key_type(key)]);
  if (grub_reiserfs_get_key_version (key) == 1)
    grub_printf("%08x", (unsigned int) grub_reiserfs_get_key_offset(key));
  else
    grub_printf("0x%07x%08x",
                (unsigned int) (grub_reiserfs_get_key_offset(key) >> 32),
                (unsigned int) (grub_reiserfs_get_key_offset(key) & 0xFFFFFFFF));
  grub_printf ("\n");
}
#endif

/* Return the offset of given key.  */
static grub_uint64_t
grub_reiserfs_get_key_offset (const struct grub_reiserfs_key *key)
{
  if (grub_reiserfs_get_key_version (key) == 1)
    return grub_le_to_cpu32 (key->u.v1.offset);
  else
    {
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
      return key->u.v2.offset;
#else
      grub_uint64_t temp = grub_le_to_cpu64 (key->u.v2.offset_type & ~0xF);
      return ((temp & 0xF000000000000000LLU) >> 4) | (temp & 0x00FFFFFFFFFFFFFFLLU);
#endif
    }
}

/* Set the offset of given key.  */
static void
grub_reiserfs_set_key_offset (struct grub_reiserfs_key *key, grub_uint64_t value)
{
  if (grub_reiserfs_get_key_version (key) == 1)
    key->u.v1.offset = grub_cpu_to_le32 (value);
  else
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
    key->u.v2.offset = value;
#else
    key->u.v2.offset_type = (key->u.v2.offset_type & 0xF) | grub_cpu_to_le64(((value & 0x0F00000000000000LLU) << 4) | (value & 0x00FFFFFFFFFFFFFFLLU));
#endif
}

/* Return the type of given key.  */
static enum grub_reiserfs_item_type
grub_reiserfs_get_key_type (const struct grub_reiserfs_key *key)
{
  if (grub_reiserfs_get_key_version (key) == 1)
    return grub_reiserfs_get_key_v1_type (key);
  else
    return grub_reiserfs_get_key_v2_type (key);
}

/* Set the type of given key, with given version number.  */
static void
grub_reiserfs_set_key_type (struct grub_reiserfs_key *key,
                            enum grub_reiserfs_item_type grub_type,
                            int version)
{
  grub_uint32_t type;
  switch (grub_type)
    {
      case GRUB_REISERFS_STAT:
        type = 0;
        break;
      case GRUB_REISERFS_ANY:
        type = (version == 1) ? 555 : 15;
        break;
      case GRUB_REISERFS_DIRECTORY:
        type = (version == 1) ? 500 : 3;
        break;
      case GRUB_REISERFS_DIRECT:
        type = (version == 1) ? 0xFFFFFFFF : 2;
        break;
      case GRUB_REISERFS_INDIRECT:
        type = (version == 1) ? 0xFFFFFFFE : 1;
        break;
      default:
        return;
    }
  if (version == 1)
    key->u.v1.type = grub_cpu_to_le32 (type);
  else
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
    key->u.v2.type = type;
#else
    key->u.v2.offset_type = (key->u.v2.offset_type & ~0xF) | (type & 0xF);
#endif
}

/* -1 if key 1 if lower than key 2.
   0 if key 1 is equal to key 2.
   1 if key 1 is higher than key 2.  */
static int
grub_reiserfs_compare_keys (const struct grub_reiserfs_key *key1,
                            const struct grub_reiserfs_key *key2)
{
  grub_uint64_t offset1, offset2;
  enum grub_reiserfs_item_type type1, type2;
  grub_uint32_t id1, id2;
  if (!key1 || !key2)
    return -2;
  id1 = grub_le_to_cpu32 (key1->directory_id);
  id2 = grub_le_to_cpu32 (key2->directory_id);
  if (id1 < id2)
    return -1;
  if (id1 > id2)
    return 1;
  id1 = grub_le_to_cpu32 (key1->object_id);
  id2 = grub_le_to_cpu32 (key2->object_id);
  if (id1 < id2)
    return -1;
  if (id1 > id2)
    return 1;
  offset1 = grub_reiserfs_get_key_offset(key1);
  offset2 = grub_reiserfs_get_key_offset(key2);
  if (offset1 < offset2)
    return -1;
  if (offset1 > offset2)
    return 1;
  type1 = grub_reiserfs_get_key_type(key1);
  type2 = grub_reiserfs_get_key_type(key2);
  if ((type1 == GRUB_REISERFS_ANY && (type2 == GRUB_REISERFS_DIRECT || type2 == GRUB_REISERFS_INDIRECT)) ||
      (type2 == GRUB_REISERFS_ANY && (type1 == GRUB_REISERFS_DIRECT || type1 == GRUB_REISERFS_INDIRECT)))
    return 0;
  if (type1 < type2)
    return -1;
  if (type1 > type2)
    return 1;
  return 0;
}

/* Find the item identified by KEY in mounted filesystem DATA, and fill ITEM
   accordingly to what was found.  */
static grub_err_t
grub_reiserfs_get_item (struct grub_reiserfs_data *data,
                        const struct grub_reiserfs_key *key,
                        struct grub_fshelp_node *item)
{
  grub_uint32_t block_number;
  struct grub_reiserfs_node_body block_body;
  struct grub_reiserfs_block_header *block_header = 0;
  struct grub_reiserfs_key *block_key = 0;
  grub_uint16_t increment, block_size, item_count, current_level;
  grub_uint16_t previous_level = ~0;

  if (!data)
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, "data is NULL");
      goto fail;
    }

  if (!key)
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, "key is NULL");
      goto fail;
    }

  if (!item)
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, "item is NULL");
      goto fail;
    }
  block_size = grub_le_to_cpu16 (data->superblock.block_size);
  block_number = grub_le_to_cpu32 (data->superblock.root_block);
#ifdef GRUB_REISERFS_DEBUG
  grub_printf("Searching for ");
  grub_reiserfs_print_key (key);
#endif
  block_header = grub_malloc (block_size);
  if (!block_header)
    goto fail;
  do
    {
      grub_disk_read (data->disk,
                      (block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                      (block_number * block_size) % GRUB_DISK_SECTOR_SIZE,
                      block_size, (char *) block_header);
      if (grub_errno)
        goto fail;
      current_level = grub_le_to_cpu16 (block_header->level);
      grub_dprintf ("reiserfs_tree", " at level %d\n", current_level);
      if (current_level >= previous_level)
        {
          grub_dprintf ("reiserfs_tree", "level loop detected, aborting\n");
          goto fail;
        }
      previous_level = current_level;
      item_count = grub_le_to_cpu16 (block_header->item_count);
       grub_dprintf ("reiserfs_tree", " number of contained items : %d\n", item_count);
      if (current_level > 1)
        {
          /* Internal node.  Navigate to the child that should contain the searched key.  */
          block_body.u.internal.key_list = (struct grub_reiserfs_key *) (block_header + 1);
          block_body.u.internal.child_list = (struct grub_reiserfs_disk_child *) (block_body.u.internal.key_list + item_count);
          for (increment = 0;
               increment < item_count && grub_reiserfs_compare_keys (key, &(block_body.u.internal.key_list[increment])) >= 0;
               increment++)
            {
#ifdef GRUB_REISERFS_DEBUG
              grub_printf("i %03d/%03d ", increment + 1, item_count + 1);
              grub_reiserfs_print_key (&(block_body.u.internal.key_list[increment]));
#endif
            }
          block_number = grub_le_to_cpu32 (block_body.u.internal.child_list[increment].block_number);
#ifdef GRUB_REISERFS_DEBUG
          if (increment == item_count || grub_reiserfs_compare_keys (key, &(block_body.u.internal.key_list[increment])) == 0)
              grub_printf(">");
          else
              grub_printf("<");
          if (increment < item_count)
            {
              grub_printf (" %03d/%03d ", increment + 1, item_count + 1);
              grub_reiserfs_print_key (&(block_body.u.internal.key_list[increment]));
              if (increment + 1 < item_count)
                {
                  grub_printf ("+ %03d/%03d ", increment + 2, item_count);
    	      grub_reiserfs_print_key (&(block_body.u.internal.key_list[increment + 1]));
                }
            }
          else
            grub_printf ("Accessing rightmost child at block %d.\n", block_number);
#endif
        }
      else
        {
          /* Leaf node.  Check that the key is actually present.  */
          block_body.u.leaf.item_header_list = (struct grub_reiserfs_item_header *) (block_header + 1);
          for (increment = 0;
               increment < item_count && grub_reiserfs_compare_keys (key, &(block_body.u.leaf.item_header_list[increment].key)) != 0;
               increment++)
            {
#ifdef GRUB_REISERFS_DEBUG
              if (key->directory_id == block_body.u.leaf.item_header_list[increment].key.directory_id && \
                  key->object_id == block_body.u.leaf.item_header_list[increment].key.object_id)
                grub_printf("C");
              else
                grub_printf(" ");
              grub_printf(" %03d/%03d ", increment + 1, item_count);
              grub_reiserfs_print_key (&(block_body.u.leaf.item_header_list[increment].key));
#endif
            }
          if (increment < item_count)
            block_key = &(block_body.u.leaf.item_header_list[increment].key);
        }
    } while (current_level > 1);

  item->data = data;

  if (increment == item_count || grub_reiserfs_compare_keys (key, block_key))
    {
      item->block_number = 0;
      item->block_position = 0;
      item->type = GRUB_REISERFS_UNKNOWN;
#ifdef GRUB_REISERFS_DEBUG
      grub_printf("Not found.\n");
#endif
    }
  else
    {
      item->block_number = block_number;
      item->block_position = increment;
      item->type = grub_reiserfs_get_key_type (block_key);
      grub_memcpy (&(item->header),
                   &(block_body.u.leaf.item_header_list[increment]),
                   sizeof (struct grub_reiserfs_item_header));
#ifdef GRUB_REISERFS_DEBUG
      grub_printf("F %03d/%03d ", increment + 1, item_count);
      grub_reiserfs_print_key (block_key);
#endif
    }
  grub_free(block_header);
  return GRUB_ERR_NONE;

 fail:
  grub_free (block_header);
  return grub_errno;
}

/* Return the path of the file which is pointed at by symlink NODE.  */
static char *
grub_reiserfs_read_symlink (grub_fshelp_node_t node)
{
  char *symlink_buffer = 0;
  grub_uint16_t block_size;
  grub_size_t offset, len, block;
  struct grub_fshelp_node found;
  struct grub_reiserfs_key key;

  grub_memcpy (&key, &(node->header.key), sizeof(key));
  grub_reiserfs_set_key_offset (&key, 1);
  grub_reiserfs_set_key_type (&key, GRUB_REISERFS_DIRECT, grub_reiserfs_get_key_version(&key));

  if (grub_reiserfs_get_item (node->data, &key, &found) != GRUB_ERR_NONE)
    goto fail;

  if (found.block_number == 0)
    goto fail;

  block_size = grub_le_to_cpu16 (node->data->superblock.block_size);
  len = grub_le_to_cpu16 (found.header.item_size);
  block = (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE;
  offset = grub_le_to_cpu16 (found.header.item_location);

  symlink_buffer = grub_malloc (len);
  if (!symlink_buffer)
    goto fail;

  grub_disk_read(node->data->disk, block, offset, len, symlink_buffer);
  if (grub_errno)
    goto fail;

  return symlink_buffer;

 fail:
  grub_free (symlink_buffer);
  return 0;
}

/* Fill the mounted filesystem structure and return it.  */
static struct grub_reiserfs_data *
grub_reiserfs_mount (grub_disk_t disk)
{
  struct grub_reiserfs_data *data = 0;
  data = grub_malloc (sizeof (*data));
  if (!data)
    goto fail;
  grub_disk_read (disk, REISERFS_SUPER_BLOCK_OFFSET / GRUB_DISK_SECTOR_SIZE,
                  0, sizeof (data->superblock), (char *) &(data->superblock));
  if (grub_errno)
    goto fail;
  if (grub_memcmp (data->superblock.magic_string,
                   REISERFS_MAGIC_STRING, REISERFS_MAGIC_LEN))
    {
      grub_error (GRUB_ERR_BAD_FS, "not a reiserfs filesystem");
      goto fail;
    }
  data->disk = disk;
  return data;

 fail:
  grub_free (data);
  return 0;
}

/* Call HOOK for each file in directory ITEM.  */
static int
grub_reiserfs_iterate_dir (grub_fshelp_node_t item,
                           int NESTED_FUNC_ATTR
                           (*hook) (const char *filename,
                                    enum grub_fshelp_filetype filetype,
                                    grub_fshelp_node_t node))
{
  struct grub_reiserfs_data *data = item->data;
  struct grub_reiserfs_block_header *block_header = 0;
  grub_uint16_t block_size, block_position;
  grub_uint32_t block_number;

  if (item->type != GRUB_REISERFS_DIRECTORY)
    {
      grub_error (GRUB_ERR_BAD_FILE_TYPE,
                  "grub_reiserfs_iterate_dir called on a non-directory item");
      goto fail;
    }
  block_size = grub_le_to_cpu16 (data->superblock.block_size);
  block_header = grub_malloc (block_size);
  if (!block_header)
    goto fail;
  block_number = item->block_number;
  block_position = item->block_position;
  grub_dprintf ("reiserfs", "Iterating directory...\n");
  do
    {
      struct grub_reiserfs_directory_header *directory_header;
      struct grub_reiserfs_node_body block_body;
      struct grub_fshelp_node directory_item;
      grub_uint16_t entry_count, entry_number;

      grub_disk_read (data->disk,
                      (block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                      (block_number * block_size) % GRUB_DISK_SECTOR_SIZE,
                      block_size, (char *) block_header);
      if (grub_errno)
        goto fail;
//       if (grub_le_to_cpu16 (block_header->level) != 1)
//         {
//           grub_error (GRUB_ERR_TEST_FAILURE,
//                       "reiserfs: block %d is not a leaf block",
//                       block_number);
//           goto fail;
//         }
      block_body.u.leaf.item_header_list = (struct grub_reiserfs_item_header *) (block_header + 1);
      directory_header = (struct grub_reiserfs_directory_header *) (((char *) block_header) + grub_le_to_cpu16 (block_body.u.leaf.item_header_list[block_position].item_location));
      entry_count = grub_le_to_cpu16 (block_body.u.leaf.item_header_list[block_position].u.entry_count);
      for (entry_number = 0; entry_number < entry_count; entry_number++)
        {
          grub_uint16_t entry_state;

          entry_state = grub_le_to_cpu16 (directory_header[entry_number].state);
          if (entry_state & GRUB_REISERFS_VISIBLE_MASK)
            {
              struct grub_fshelp_node *entry_item = 0;
              struct grub_reiserfs_key entry_key;
              enum grub_reiserfs_item_type entry_type;
              char *entry_name;

              entry_item = grub_malloc (sizeof(*entry_item));
              if (!entry_item)
                goto fail;
              entry_name = ((char *) directory_header) + grub_le_to_cpu16 (directory_header[entry_number].location);
              entry_key.directory_id = directory_header[entry_number].directory_id;
              entry_key.object_id = directory_header[entry_number].object_id;
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
              entry_key.u.v2.offset = 0;
              entry_key.u.v2.type = 0;
#else
              entry_key.u.v2.offset_type = 0;
#endif
              grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_DIRECTORY, 2);
              grub_reiserfs_set_key_offset (&entry_key, 1);
              if (grub_reiserfs_get_item (data, &entry_key, entry_item) != GRUB_ERR_NONE)
                goto fail;
              if (entry_item->type == GRUB_REISERFS_DIRECTORY)
                entry_type = GRUB_FSHELP_DIR;
              else
                {
                  grub_uint32_t entry_block_number;
                  /* Order is very important here.
                     First set the offset to 0 using current key version.
                     Then change the key type, which influes on key version detection.  */
                  grub_reiserfs_set_key_offset (&entry_key, 0);
                  grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_STAT, 2);
                  if (grub_reiserfs_get_item (data, &entry_key, entry_item) != GRUB_ERR_NONE)
                    goto fail;
                  if (entry_item->block_number != 0)
                    {
                      grub_uint16_t entry_version;
                      entry_version = grub_le_to_cpu16 (entry_item->header.version);
                      entry_block_number = entry_item->block_number;
		      //grub_dprintf("reiserfs", "version %04x block %08x (%08x) position %08x\n", entry_version, entry_block_number, (entry_block_number * block_size) / GRUB_DISK_SECTOR_SIZE, grub_le_to_cpu16 (entry_item->header.item_location));
                      if (entry_version == 0) /* Version 1 stat item. */
                        {
                          struct grub_reiserfs_stat_item_v1 entry_v1_stat;
                          grub_disk_read (data->disk,
                                          (entry_block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                                          grub_le_to_cpu16 (entry_item->header.item_location),
                                          sizeof(entry_v1_stat), (char *) &entry_v1_stat);
                          if (grub_errno)
                            goto fail;
			  //grub_dprintf("reiserfs", "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", grub_le_to_cpu16 (entry_v1_stat.mode), grub_le_to_cpu16 (entry_v1_stat.hardlink_count), grub_le_to_cpu16 (entry_v1_stat.uid), grub_le_to_cpu16 (entry_v1_stat.gid), grub_le_to_cpu32 (entry_v1_stat.size), grub_le_to_cpu32 (entry_v1_stat.atime), grub_le_to_cpu32 (entry_v1_stat.mtime), grub_le_to_cpu32 (entry_v1_stat.ctime), grub_le_to_cpu32 (entry_v1_stat.rdev), grub_le_to_cpu32 (entry_v1_stat.first_direct_byte));
			  //grub_dprintf("reiserfs", "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", entry_v1_stat.mode, entry_v1_stat.hardlink_count, entry_v1_stat.uid, entry_v1_stat.gid, entry_v1_stat.size, entry_v1_stat.atime, entry_v1_stat.mtime, entry_v1_stat.ctime, entry_v1_stat.rdev, entry_v1_stat.first_direct_byte);
                          if ((grub_le_to_cpu16 (entry_v1_stat.mode) & S_IFLNK) == S_IFLNK)
                            entry_type = GRUB_FSHELP_SYMLINK;
                          else
                            entry_type = GRUB_FSHELP_REG;
                        }
                      else
                        {
                          struct grub_reiserfs_stat_item_v2 entry_v2_stat;
                          grub_disk_read (data->disk,
                                          (entry_block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                                          grub_le_to_cpu16 (entry_item->header.item_location),
                                          sizeof(entry_v2_stat), (char *) &entry_v2_stat);
                          if (grub_errno)
                            goto fail;
			  //grub_dprintf("reiserfs", "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", grub_le_to_cpu16 (entry_v2_stat.mode), grub_le_to_cpu16 (entry_v2_stat.reserved), grub_le_to_cpu32 (entry_v2_stat.hardlink_count), (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) >> 32), (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) && 0xFFFFFFFF), grub_le_to_cpu32 (entry_v2_stat.uid), grub_le_to_cpu32 (entry_v2_stat.gid), grub_le_to_cpu32 (entry_v2_stat.atime), grub_le_to_cpu32 (entry_v2_stat.mtime), grub_le_to_cpu32 (entry_v2_stat.ctime), grub_le_to_cpu32 (entry_v2_stat.blocks), grub_le_to_cpu32 (entry_v2_stat.first_direct_byte));
			  //grub_dprintf("reiserfs", "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", entry_v2_stat.mode, entry_v2_stat.reserved, entry_v2_stat.hardlink_count, (unsigned int) (entry_v2_stat.size >> 32), (unsigned int) (entry_v2_stat.size && 0xFFFFFFFF), entry_v2_stat.uid, entry_v2_stat.gid, entry_v2_stat.atime, entry_v2_stat.mtime, entry_v2_stat.ctime, entry_v2_stat.blocks, entry_v2_stat.first_direct_byte);
                          if ((grub_le_to_cpu16 (entry_v2_stat.mode) & S_IFLNK) == S_IFLNK)
                            entry_type = GRUB_FSHELP_SYMLINK;
                          else
                            entry_type = GRUB_FSHELP_REG;
                        }
                      }
                    else
                      {
                        /* Pseudo file ".." never has stat block.  */
                        if (grub_strcmp (entry_name, ".."))
                          grub_dprintf ("reiserfs",
                                        "Warning : %s has no stat block !\n",
                                        entry_name);
                        continue;
                      }
                }
              if (hook (entry_name, entry_type, entry_item))
                {
                  grub_dprintf ("reiserfs", "Found : %s, type=%d\n", entry_name, entry_type);
                  goto found;
                }
              //grub_free (entry_item);
              *entry_name = 0; /* Make sure next entry name (which is just
                                  before this one in disk order) stops before
                                  the current one.  */
            }
        }
      grub_reiserfs_set_key_offset (&(block_body.u.leaf.item_header_list[block_position].key), grub_reiserfs_get_key_offset (&(block_body.u.leaf.item_header_list[block_position].key)) + 1);
      if (grub_reiserfs_get_item (data, &(block_body.u.leaf.item_header_list[block_position].key), &directory_item) != GRUB_ERR_NONE)
        goto fail;
      block_number = directory_item.block_number;
      block_position = directory_item.block_position;
    } while (block_number);
  grub_free (block_header);
  return 0;

 found:
 fail:
  grub_free (block_header);
  return 1;
}

/****************************************************************************/
/* grub api functions */
/****************************************************************************/

/* Open a file named NAME and initialize FILE.  */
static grub_err_t
grub_reiserfs_open (struct grub_file *file, const char *name)
{
  struct grub_reiserfs_data *data = 0;
  struct grub_fshelp_node root, *found = 0, info;
  struct grub_reiserfs_key key;
  grub_uint32_t block_number;
  grub_uint16_t entry_version, block_size, entry_location;

#ifndef GRUB_UTIL
  grub_dl_ref (my_mod);
#endif
  data = grub_reiserfs_mount (file->device->disk);
  if (!data)
    goto fail;
  block_size = grub_le_to_cpu16 (data->superblock.block_size);
  key.directory_id = grub_cpu_to_le32 (1);
  key.object_id = grub_cpu_to_le32 (2);
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
  key.u.v2.offset = 0;
  key.u.v2.type = 0;
#else
  key.u.v2.offset_type = 0;
#endif
  grub_reiserfs_set_key_type (&key, GRUB_REISERFS_DIRECTORY, 2);
  grub_reiserfs_set_key_offset (&key, 1);
  if (grub_reiserfs_get_item (data, &key, &root) != GRUB_ERR_NONE)
    goto fail;
  if (root.block_number == 0)
    goto fail; /* Should never happen since checked at mount.  */
  grub_fshelp_find_file (name, &root, &found,
                         grub_reiserfs_iterate_dir,
                         grub_reiserfs_read_symlink, GRUB_FSHELP_REG);
  if (grub_errno)
    goto fail;
  key.directory_id = found->header.key.directory_id;
  key.object_id = found->header.key.object_id;
  grub_reiserfs_set_key_type (&key, GRUB_REISERFS_STAT, 2);
  grub_reiserfs_set_key_offset (&key, 0);
  if (grub_reiserfs_get_item (data, &key, &info) != GRUB_ERR_NONE)
    goto fail;
  if (info.block_number == 0)
    goto fail;
  entry_version = grub_le_to_cpu16 (info.header.version);
  entry_location = grub_le_to_cpu16 (info.header.item_location);
  block_number = info.block_number;
  if (entry_version == 0) /* Version 1 stat item. */
    {
      struct grub_reiserfs_stat_item_v1 entry_v1_stat;
      grub_disk_read (data->disk,
                      (block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                      entry_location + (block_number * block_size) % GRUB_DISK_SECTOR_SIZE,
                      sizeof(entry_v1_stat), (char *) &entry_v1_stat);
      if (grub_errno)
        goto fail;
      file->size = (grub_off_t) grub_le_to_cpu64 (entry_v1_stat.size);
    }
  else
    {
      struct grub_reiserfs_stat_item_v2 entry_v2_stat;
      grub_disk_read (data->disk,
                      (block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                      entry_location + (block_number * block_size) % GRUB_DISK_SECTOR_SIZE,
                        sizeof(entry_v2_stat), (char *) &entry_v2_stat);
      if (grub_errno)
        goto fail;
      file->size = (grub_off_t) grub_le_to_cpu64 (entry_v2_stat.size);
    }
  grub_dprintf ("reiserfs", "file size : %d (%08x%08x)\n", (unsigned int) file->size,
                (unsigned int) (file->size >> 32), (unsigned int) file->size);
  file->offset = 0;
  file->data = found;
  return GRUB_ERR_NONE;

 fail:
  grub_free (found);
  grub_free (data);
#ifndef GRUB_UTIL
  grub_dl_unref (my_mod);
#endif
  return grub_errno;
}

static grub_ssize_t
grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len)
{
  unsigned int indirect_block, indirect_block_count;
  struct grub_reiserfs_key key;
  struct grub_fshelp_node *node = file->data;
  struct grub_reiserfs_data *data = node->data;
  struct grub_fshelp_node found;
  //grub_size_t read, read_length = 0;
  grub_uint16_t block_size = grub_le_to_cpu16 (data->superblock.block_size),
                item_size;
  grub_uint32_t *indirect_block_ptr = 0;

  grub_uint64_t current_key_offset = 1;
  grub_size_t initial_position, current_position, final_position, block, length, offset;

  if (file->offset >= file->size)
    return 0;

  key.directory_id = node->header.key.directory_id;
  key.object_id = node->header.key.object_id;
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
  key.u.v2.offset = 0;
  key.u.v2.type = 0;
#else
  key.u.v2.offset_type = 0;
#endif
  grub_reiserfs_set_key_type (&key, GRUB_REISERFS_ANY, 2);
  initial_position = file->offset;
  current_position = 0;
  final_position = MIN(len + initial_position, file->size);
  grub_dprintf ("reiserfs", "Reading from %d to %d (%d instead of requested %d)\n", initial_position, final_position, final_position - initial_position, len);
  while (current_position < final_position)
    {
      grub_reiserfs_set_key_offset (&key, current_key_offset);
    
      if (grub_reiserfs_get_item (data, &key, &found) != GRUB_ERR_NONE)
        goto fail;
      if (found.block_number == 0)
        goto fail;
      item_size = grub_le_to_cpu16 (found.header.item_size);
      switch (found.type)
        {
          case GRUB_REISERFS_DIRECT:
	    block = (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE;
	    grub_dprintf ("reiserfs_blocktype", "D: %d\n", block);
            if (initial_position < current_position + item_size)
              {
                offset = MAX ((signed) (initial_position - current_position), 0);
                length = MIN (item_size, final_position - current_position) - offset;
		grub_dprintf ("reiserfs", "Reading direct block %d from %d to %d...\n", block, offset, offset + length);
		found.data->disk->read_hook = file->read_hook;
                grub_disk_read (found.data->disk, block, offset + grub_le_to_cpu16 (found.header.item_location), length, buf);
		found.data->disk->read_hook = 0;
                if (grub_errno)
                  goto fail;
                buf += length;
                current_position += offset + length;
              }
            else
              current_position += item_size;
            break;
          case GRUB_REISERFS_INDIRECT:
            indirect_block_count = item_size / sizeof (*indirect_block_ptr);
            indirect_block_ptr = grub_malloc (item_size);
            if (!indirect_block_ptr)
              goto fail;
            grub_disk_read (found.data->disk,
                            (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                            grub_le_to_cpu16 (found.header.item_location),
                            item_size, (char *) indirect_block_ptr);
            if (grub_errno)
              goto fail;
	    found.data->disk->read_hook = file->read_hook;
            for (indirect_block = 0;
                 indirect_block < indirect_block_count && current_position < final_position;
                 indirect_block++)
              {
	        block = (grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / GRUB_DISK_SECTOR_SIZE;
	        grub_dprintf ("reiserfs_blocktype", "I: %d\n", block);
                if (current_position + block_size >= initial_position)
                  {
                    offset = MAX ((signed) (initial_position - current_position), 0);
                    length = MIN (block_size, final_position - current_position) - offset;
		    grub_dprintf ("reiserfs", "Reading indirect block %d from %d to %d...\n", block, offset, offset + length);
                    //grub_dprintf ("reiserfs", "\nib=%04d/%04d, ip=%d, cp=%d, fp=%d, off=%d, l=%d, tl=%d\n", indirect_block + 1, indirect_block_count, initial_position, current_position, final_position, offset, length, len);
                    grub_disk_read (found.data->disk, block, offset, length, buf);
                    if (grub_errno)
                      goto fail;
                    buf += length;
                    current_position += offset + length;
                  }
                else
                  current_position += block_size;
              }
	    found.data->disk->read_hook = 0;
            grub_free (indirect_block_ptr);
            indirect_block_ptr = 0;
            break;
          default:
            goto fail;
        }
	current_key_offset = current_position + 1;
    }
  grub_dprintf("reiserfs", "Have successfully read %d bytes (%d requested)\n", current_position - initial_position, len);
  return current_position - initial_position;
/*
  switch (found.type)
    {
      case GRUB_REISERFS_DIRECT:
        read_length = MIN (len, item_size - file->offset);
        grub_disk_read (found.data->disk,
                        (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                        grub_le_to_cpu16 (found.header.item_location) + file->offset,
                        read_length, buf);
        if (grub_errno)
          goto fail;
        break;
      case GRUB_REISERFS_INDIRECT:
        indirect_block_count = item_size / sizeof (*indirect_block_ptr);
        indirect_block_ptr = grub_malloc (item_size);
        if (!indirect_block_ptr)
          goto fail;
        grub_disk_read (found.data->disk,
                        (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE,
                        grub_le_to_cpu16 (found.header.item_location),
                        item_size, (char *) indirect_block_ptr);
        if (grub_errno)
          goto fail;
        len = MIN (len, file->size - file->offset);
        for (indirect_block = file->offset / block_size;
             indirect_block < indirect_block_count && read_length < len;
             indirect_block++)
          {
            read = MIN (block_size, len - read_length);
            grub_disk_read (found.data->disk,
                            (grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / GRUB_DISK_SECTOR_SIZE,
                            file->offset % block_size, read,
                            ((void *) buf) + read_length);
            if (grub_errno)
              goto fail;
            read_length += read;
          }
        grub_free (indirect_block_ptr);
        break;
      default:
        goto fail;
    }

  return read_length;*/

 fail:
  grub_free (indirect_block_ptr);
  return 0;
}

/* Close the file FILE.  */
static grub_err_t
grub_reiserfs_close (grub_file_t file)
{
  struct grub_fshelp_node *node = file->data;
  struct grub_reiserfs_data *data = node->data;

  grub_free (data);
  grub_free (node);
#ifndef GRUB_UTIL
  grub_dl_unref (my_mod);
#endif
  return GRUB_ERR_NONE;
}

/* Call HOOK with each file under DIR.  */
static grub_err_t
grub_reiserfs_dir (grub_device_t device, const char *path,
                   int (*hook) (const char *filename, int dir))
{
  struct grub_reiserfs_data *data = 0;
  struct grub_fshelp_node root, *found;
  struct grub_reiserfs_key root_key;

  auto int NESTED_FUNC_ATTR iterate (const char *filename,
                                     enum grub_fshelp_filetype filetype,
                                     grub_fshelp_node_t node);

  int NESTED_FUNC_ATTR iterate (const char *filename,
                                enum grub_fshelp_filetype filetype,
                                grub_fshelp_node_t node)
    {
      grub_free (node);
      if (filetype == GRUB_FSHELP_DIR)
        return hook (filename, 1);
      else
        return hook (filename, 0);
    }
#ifndef GRUB_UTIL
  grub_dl_ref (my_mod);
#endif
  data = grub_reiserfs_mount (device->disk);
  if (!data)
    goto fail;
  root_key.directory_id = grub_cpu_to_le32 (1);
  root_key.object_id = grub_cpu_to_le32 (2);
#ifdef GRUB_REISERFS_KEYV2_BITFIELD
  root_key.u.v2.offset = 0;
  root_key.u.v2.type = 0;
#else
  root_key.u.v2.offset_type = 0;
#endif
  grub_reiserfs_set_key_type (&root_key, GRUB_REISERFS_DIRECTORY, 2);
  grub_reiserfs_set_key_offset (&root_key, 1);
  if (grub_reiserfs_get_item (data, &root_key, &root) != GRUB_ERR_NONE)
    goto fail;
  if (root.block_number == 0)
    goto fail;
  grub_fshelp_find_file (path, &root, &found, grub_reiserfs_iterate_dir,
                         grub_reiserfs_read_symlink, GRUB_FSHELP_DIR);
  if (grub_errno)
    goto fail;
  grub_reiserfs_iterate_dir (found, iterate);
  grub_free (data);
#ifndef GRUB_UTIL
  grub_dl_unref (my_mod);
#endif
  return GRUB_ERR_NONE;

 fail:
  grub_free (data);
#ifndef GRUB_UTIL
  grub_dl_unref (my_mod);
#endif
  return grub_errno;
}

/* Return the label of the device DEVICE in LABEL.  The label is
   returned in a grub_malloc'ed buffer and should be freed by the
   caller.  */
static grub_err_t
grub_reiserfs_label (grub_device_t device, char **label)
{
  *label = grub_malloc (REISERFS_MAX_LABEL_LENGTH);
  if (*label)
    {
      grub_disk_read (device->disk,
                      REISERFS_SUPER_BLOCK_OFFSET / GRUB_DISK_SECTOR_SIZE,
                      REISERFS_LABEL_OFFSET, REISERFS_MAX_LABEL_LENGTH,
                      *label);
    }
  return grub_errno;
}

static struct grub_fs grub_reiserfs_fs =
  {
    .name = "reiserfs",
    .dir = grub_reiserfs_dir,
    .open = grub_reiserfs_open,
    .read = grub_reiserfs_read,
    .close = grub_reiserfs_close,
    .label = grub_reiserfs_label,
    .next = 0
  };

GRUB_MOD_INIT(reiserfs)
{
  grub_fs_register (&grub_reiserfs_fs);
#ifndef GRUB_UTIL
  my_mod = mod;
#endif
}

GRUB_MOD_FINI(reiserfs)
{
  grub_fs_unregister (&grub_reiserfs_fs);
}

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to