/* reiserfs.c - ReiserFS versions up to 3.6 */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2004,2005,2008 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/>.
*/
/*
TODO:
implement journal handling (ram replay)
test tail packing & direct files
validate partition label position
*/
#if 0
# define GRUB_REISERFS_DEBUG
# define GRUB_REISERFS_JOURNALING
# define GRUB_HEXDUMP
#endif
GRUB_MOD_LICENSE ("GPLv3+");
#define MIN(a, b) \
#define MAX(a, b) \
/* If the 3rd bit of an item state is set, then it's visible. */
static inline void
{
if (! boolean)
}
{
/* Matches both _DIRECT and _INDIRECT when searching. */
};
struct grub_reiserfs_superblock
{
} __attribute__ ((packed));
struct grub_reiserfs_journal_header
{
} __attribute__ ((packed));
{
} __attribute__ ((packed));
struct grub_reiserfs_commit_block
{
} __attribute__ ((packed));
struct grub_reiserfs_stat_item_v1
{
} __attribute__ ((packed));
struct grub_reiserfs_stat_item_v2
{
} __attribute__ ((packed));
struct grub_reiserfs_key
{
union
{
struct
{
struct
{
} u;
} __attribute__ ((packed));
struct grub_reiserfs_item_header
{
union
{
} u __attribute__ ((packed));
} __attribute__ ((packed));
struct grub_reiserfs_block_header
{
} __attribute__ ((packed));
struct grub_reiserfs_disk_child
{
} __attribute__ ((packed));
{
} __attribute__ ((packed));
struct grub_fshelp_node
{
};
/* Returned when opening a file. */
struct grub_reiserfs_data
{
};
/* Internal-only functions. Not to be used outside of this file. */
/* Return the type of given v2 key. */
static enum grub_reiserfs_item_type
{
{
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
{
{
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
{
}
#ifdef GRUB_HEXDUMP
static void
{
grub_size_t 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
static enum grub_reiserfs_item_type
static void
{
unsigned int a;
char *reiserfs_type_strings[] = {
"stat ",
"directory",
"direct ",
"indirect ",
"any ",
"unknown "
};
for (a = 0; a < sizeof (struct grub_reiserfs_key); a++)
grub_printf ("parent id = 0x%08x, self id = 0x%08x, type = %s, offset = ",
else
grub_printf("0x%07x%08x",
grub_printf ("\n");
}
#endif
/* Return the offset of given key. */
static grub_uint64_t
{
else
}
/* Set the offset of given key. */
static void
{
else
}
/* Return the type of given key. */
static enum grub_reiserfs_item_type
{
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
int version)
{
switch (grub_type)
{
case GRUB_REISERFS_STAT:
type = 0;
break;
case GRUB_REISERFS_ANY:
break;
case GRUB_REISERFS_DIRECTORY:
break;
case GRUB_REISERFS_DIRECT:
break;
case GRUB_REISERFS_INDIRECT:
break;
default:
return;
}
if (version == 1)
else
}
/* -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
const struct grub_reiserfs_key *key2)
{
return -2;
return -1;
return 1;
return -1;
return 1;
return -1;
return 1;
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;
return -1;
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
const struct grub_reiserfs_key *key,
struct grub_fshelp_node *item)
{
if (! data)
{
goto fail;
}
if (! key)
{
goto fail;
}
if (! item)
{
goto fail;
}
#ifdef GRUB_REISERFS_DEBUG
grub_printf("Searching for ");
#endif
if (! block_header)
goto fail;
item->next_offset = 0;
do
{
& (GRUB_DISK_SECTOR_SIZE - 1)),
if (grub_errno)
goto fail;
if (current_level >= previous_level)
{
goto fail;
}
if (current_level > 1)
{
/* Internal node. Navigate to the child that should contain
the searched key. */
= ((struct grub_reiserfs_disk_child *)
(keys + item_count));
for (i = 0;
i < item_count
i++)
{
#ifdef GRUB_REISERFS_DEBUG
grub_reiserfs_print_key (&(keys[i]));
#endif
}
#ifdef GRUB_REISERFS_DEBUG
if (i == item_count
grub_printf(">");
else
grub_printf("<");
if (i < item_count)
{
grub_reiserfs_print_key (&(keys[i]));
if (i + 1 < item_count)
{
}
}
else
grub_printf ("Accessing rightmost child at block %d.\n",
#endif
}
else
{
/* Leaf node. Check that the key is actually present. */
for (i = 0;
i < item_count
!= 0);
i++)
{
#ifdef GRUB_REISERFS_DEBUG
grub_printf("C");
else
grub_printf(" ");
#endif
}
if (i < item_count)
}
}
while (current_level > 1);
{
item->block_number = 0;
item->block_position = 0;
#ifdef GRUB_REISERFS_DEBUG
grub_printf("Not found.\n");
#endif
}
else
{
item->block_position = i;
sizeof (struct grub_reiserfs_item_header));
#ifdef GRUB_REISERFS_DEBUG
#endif
}
return GRUB_ERR_NONE;
fail:
return grub_errno;
}
/* Return the path of the file which is pointed at by symlink NODE. */
static char *
{
char *symlink_buffer = 0;
goto fail;
if (found.block_number == 0)
goto fail;
if (! symlink_buffer)
goto fail;
if (grub_errno)
goto fail;
symlink_buffer[len] = 0;
return symlink_buffer;
fail:
return 0;
}
/* Fill the mounted filesystem structure and return it. */
static struct grub_reiserfs_data *
{
if (! data)
goto fail;
if (grub_errno)
goto fail;
{
goto fail;
}
return data;
fail:
/* Disk is too small to contain a ReiserFS. */
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
return 0;
}
/* Call HOOK for each file in directory ITEM. */
static int
int NESTED_FUNC_ATTR
enum grub_fshelp_filetype filetype,
{
int ret = 0;
{
"grub_reiserfs_iterate_dir called on a non-directory item");
goto fail;
}
if (! block_header)
goto fail;
do
{
& (GRUB_DISK_SECTOR_SIZE - 1)),
block_size, (char *) block_header);
if (grub_errno)
goto fail;
#if 0
{
"reiserfs: block %d is not a leaf block",
goto fail;
}
#endif
= ((struct grub_reiserfs_directory_header *)
((char *) block_header
{
{
char *entry_name;
entry_name = (((char *) directory_headers)
2);
if (! entry_item)
goto fail;
!= GRUB_ERR_NONE)
{
goto fail;
}
else
{
/* Order is very important here.
First set the offset to 0 using current key version.
Then change the key type, which affects key version
detection. */
2);
!= GRUB_ERR_NONE)
{
goto fail;
}
if (entry_item->block_number != 0)
{
#if 0
grub_dprintf ("reiserfs",
"version %04x block %08x (%08x) position %08x\n",
#endif
if (entry_version == 0) /* Version 1 stat item. */
{
sizeof (entry_v1_stat),
(char *) &entry_v1_stat);
if (grub_errno)
goto fail;
#if 0
grub_dprintf ("reiserfs",
"%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n",
grub_dprintf ("reiserfs",
"%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n",
#endif
== S_IFLNK)
else
}
else
{
sizeof (entry_v2_stat),
(char *) &entry_v2_stat);
if (grub_errno)
goto fail;
#if 0
grub_dprintf ("reiserfs",
"%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n",
grub_dprintf ("reiserfs",
"%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n",
#endif
== S_IFLNK)
else
}
}
else
{
/* Pseudo file ".." never has stat block. */
grub_dprintf ("reiserfs",
"Warning : %s has no stat block !\n",
goto next;
}
}
{
ret = 1;
goto found;
}
next:
*entry_name = 0; /* Make sure next entry name (which is just
before this one in disk order) stops before
the current one. */
}
}
if (next_offset == 0)
break;
&directory_item) != GRUB_ERR_NONE)
goto fail;
}
while (block_number);
return ret;
fail:
return 0;
}
/****************************************************************************/
/* grub api functions */
/****************************************************************************/
/* Open a file named NAME and initialize FILE. */
static grub_err_t
{
if (! data)
goto fail;
goto fail;
if (root.block_number == 0)
{
goto fail; /* Should never happen since checked at mount. */
}
if (grub_errno)
goto fail;
grub_reiserfs_set_key_offset (&key, 0);
goto fail;
if (info.block_number == 0)
{
goto fail;
}
if (entry_version == 0) /* Version 1 stat item. */
{
& (GRUB_DISK_SECTOR_SIZE - 1)),
sizeof (entry_v1_stat), &entry_v1_stat);
if (grub_errno)
goto fail;
}
else
{
& (GRUB_DISK_SECTOR_SIZE - 1)),
sizeof (entry_v2_stat), &entry_v2_stat);
if (grub_errno)
goto fail;
}
return GRUB_ERR_NONE;
fail:
return grub_errno;
}
static grub_ssize_t
{
current_position = 0;
grub_dprintf ("reiserfs",
"Reading from %lld to %lld (%lld instead of requested %ld)\n",
(unsigned long long) initial_position,
(unsigned long long) final_position,
(unsigned long long) (final_position - initial_position),
(unsigned long) len);
while (current_position < final_position)
{
goto fail;
if (found.block_number == 0)
goto fail;
{
case GRUB_REISERFS_DIRECT:
{
- offset);
grub_dprintf ("reiserfs",
"Reading direct block %u from %u to %u...\n",
if (grub_errno)
goto fail;
}
else
break;
case GRUB_REISERFS_INDIRECT:
if (! indirect_block_ptr)
goto fail;
if (grub_errno)
goto fail;
for (indirect_block = 0;
{
{
0);
- offset);
grub_dprintf ("reiserfs",
"Reading indirect block %u from %u to %u...\n",
#if 0
grub_dprintf ("reiserfs",
"\nib=%04d/%04d, ip=%d, cp=%d, fp=%d, off=%d, l=%d, tl=%d\n",
#endif
if (grub_errno)
goto fail;
}
else
}
indirect_block_ptr = 0;
break;
default:
goto fail;
}
}
grub_dprintf ("reiserfs",
"Have successfully read %lld bytes (%ld requested)\n",
(unsigned long long) (current_position - initial_position),
(unsigned long) len);
return current_position - initial_position;
#if 0
{
case GRUB_REISERFS_DIRECT:
read_length, buf);
if (grub_errno)
goto fail;
break;
case GRUB_REISERFS_INDIRECT:
if (!indirect_block_ptr)
goto fail;
item_size, (char *) indirect_block_ptr);
if (grub_errno)
goto fail;
{
((void *) buf) + read_length);
if (grub_errno)
goto fail;
read_length += read;
}
break;
default:
goto fail;
}
return read_length;
#endif
fail:
return 0;
}
/* Close the file FILE. */
static grub_err_t
{
return GRUB_ERR_NONE;
}
/* Call HOOK with each file under DIR. */
static grub_err_t
const struct grub_dirhook_info *info))
{
enum grub_fshelp_filetype filetype,
enum grub_fshelp_filetype filetype,
{
}
if (! data)
goto fail;
goto fail;
if (root.block_number == 0)
{
goto fail;
}
if (grub_errno)
goto fail;
return GRUB_ERR_NONE;
fail:
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
{
if (data)
{
}
else
return grub_errno;
}
static grub_err_t
{
if (data)
{
unsigned i;
break;
}
return grub_errno;
}
{
.name = "reiserfs",
.dir = grub_reiserfs_dir,
.next = 0
};
{
}
{
}