/* btrfs.c - B-tree file system. */
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2010,2011,2012,2013 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <grub/deflate.h>
#include <minilzo.h>
#include <grub/i18n.h>
+#include <grub/btrfs.h>
GRUB_MOD_LICENSE ("GPLv3+");
grub_uint64_t device_id;
grub_uint64_t size;
grub_uint8_t dummy[0x62 - 0x10];
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_superblock
{
char label[0x100];
grub_uint8_t dummy4[0x100];
grub_uint8_t bootstrap_mapping[0x800];
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct btrfs_header
{
grub_uint8_t dummy[0x30];
grub_uint32_t nitems;
grub_uint8_t level;
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_device_desc
{
struct grub_btrfs_extent_data *extent;
};
-enum
- {
- GRUB_BTRFS_ITEM_TYPE_INODE_ITEM = 0x01,
- GRUB_BTRFS_ITEM_TYPE_INODE_REF = 0x0c,
- GRUB_BTRFS_ITEM_TYPE_DIR_ITEM = 0x54,
- GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM = 0x6c,
- GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84,
- GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8,
- GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4
- };
-
-struct grub_btrfs_key
-{
- grub_uint64_t object_id;
- grub_uint8_t type;
- grub_uint64_t offset;
-} __attribute__ ((packed));
-
struct grub_btrfs_chunk_item
{
grub_uint64_t size;
grub_uint8_t dummy2[0xc];
grub_uint16_t nstripes;
grub_uint16_t nsubstripes;
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_chunk_stripe
{
grub_uint64_t device_id;
grub_uint64_t offset;
grub_btrfs_uuid_t device_uuid;
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_leaf_node
{
struct grub_btrfs_key key;
grub_uint32_t offset;
grub_uint32_t size;
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_internal_node
{
struct grub_btrfs_key key;
grub_uint64_t addr;
grub_uint64_t dummy;
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_dir_item
{
#define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7
grub_uint8_t type;
char name[0];
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_leaf_descriptor
{
} *data;
};
-struct grub_btrfs_root_item
-{
- grub_uint8_t dummy[0xb0];
- grub_uint64_t tree;
- grub_uint64_t inode;
-};
-
struct grub_btrfs_time
{
grub_int64_t sec;
grub_uint64_t size;
grub_uint8_t dummy2[0x70];
struct grub_btrfs_time mtime;
-} __attribute__ ((packed));
+} GRUB_PACKED;
struct grub_btrfs_extent_data
{
grub_uint64_t filled;
};
};
-} __attribute__ ((packed));
+} GRUB_PACKED;
#define GRUB_BTRFS_EXTENT_INLINE 0
#define GRUB_BTRFS_EXTENT_REGULAR 1
{
grub_uint32_t total_size, cblock_size;
grub_size_t ret = 0;
- unsigned char buf[GRUB_BTRFS_LZO_BLOCK_SIZE];
char *ibuf0 = ibuf;
total_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
if (off > 0 || osize < GRUB_BTRFS_LZO_BLOCK_SIZE)
{
grub_size_t to_copy = GRUB_BTRFS_LZO_BLOCK_SIZE - off;
+ grub_uint8_t *buf;
if (to_copy > osize)
to_copy = osize;
+ buf = grub_malloc (GRUB_BTRFS_LZO_BLOCK_SIZE);
+ if (!buf)
+ return -1;
+
if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, buf, &usize,
NULL) != LZO_E_OK)
- return -1;
+ {
+ grub_free (buf);
+ return -1;
+ }
if (to_copy > usize)
to_copy = usize;
obuf += to_copy;
ibuf += cblock_size;
off = 0;
+
+ grub_free (buf);
continue;
}
- (grub_uint8_t *) data->extent),
extoff, buf, csize)
!= (grub_ssize_t) csize)
- return -1;
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
+ "premature end of compressed");
+ return -1;
+ }
}
else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO)
{
grub_free (tmp);
if (ret != (grub_ssize_t) csize)
- return -1;
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
+ "premature end of compressed");
+ return -1;
+ }
break;
}
return pos - pos0;
}
+static grub_err_t
+get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key,
+ grub_uint64_t *tree, grub_uint8_t *type)
+{
+ grub_err_t err;
+ grub_disk_addr_t elemaddr;
+ grub_size_t elemsize;
+ struct grub_btrfs_key key_out, key_in;
+ struct grub_btrfs_root_item ri;
+
+ key_in.object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_ROOT_VOL_OBJECTID);
+ key_in.offset = 0;
+ key_in.type = GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM;
+ err = lower_bound (data, &key_in, &key_out,
+ data->sblock.root_tree,
+ &elemaddr, &elemsize, NULL, 0);
+ if (err)
+ return err;
+ if (key_in.object_id != key_out.object_id
+ || key_in.type != key_out.type
+ || key_in.offset != key_out.offset)
+ return grub_error (GRUB_ERR_BAD_FS, "no root");
+ err = grub_btrfs_read_logical (data, elemaddr, &ri,
+ sizeof (ri), 0);
+ if (err)
+ return err;
+ key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
+ key->offset = 0;
+ key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
+ *tree = ri.tree;
+ *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+ return GRUB_ERR_NONE;
+}
+
static grub_err_t
find_path (struct grub_btrfs_data *data,
const char *path, struct grub_btrfs_key *key,
grub_size_t allocated = 0;
struct grub_btrfs_dir_item *direl = NULL;
struct grub_btrfs_key key_out;
- int skip_default;
const char *ctoken;
grub_size_t ctokenlen;
char *path_alloc = NULL;
char *origpath = NULL;
unsigned symlinks_max = 32;
- *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
- *tree = data->sblock.root_tree;
- key->object_id = data->sblock.root_dir_objectid;
- key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
- key->offset = 0;
- skip_default = 1;
+ err = get_root (data, key, tree, type);
+ if (err)
+ return err;
+
origpath = grub_strdup (path);
if (!origpath)
return grub_errno;
while (1)
{
- if (!skip_default)
- {
- while (path[0] == '/')
- path++;
- if (!path[0])
- break;
- slash = grub_strchr (path, '/');
- if (!slash)
- slash = path + grub_strlen (path);
- ctoken = path;
- ctokenlen = slash - path;
- }
- else
- {
- ctoken = "default";
- ctokenlen = sizeof ("default") - 1;
- }
+ while (path[0] == '/')
+ path++;
+ if (!path[0])
+ break;
+ slash = grub_strchr (path, '/');
+ if (!slash)
+ slash = path + grub_strlen (path);
+ ctoken = path;
+ ctokenlen = slash - path;
if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
{
if (ctokenlen == 1 && ctoken[0] == '.')
{
- if (!skip_default)
- path = slash;
- skip_default = 0;
- continue;
+ path = slash;
+ continue;
}
if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.')
{
*type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
key->object_id = key_out.offset;
- if (!skip_default)
- path = slash;
- skip_default = 0;
+ path = slash;
continue;
}
return err;
}
- if (!skip_default)
- path = slash;
- skip_default = 0;
+ path = slash;
if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
{
struct grub_btrfs_inode inode;
path = path_alloc = tmp;
if (path[0] == '/')
{
- *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
- *tree = data->sblock.root_tree;
- key->object_id = data->sblock.root_dir_objectid;
- key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
- key->offset = 0;
- skip_default = 1;
+ err = get_root (data, key, tree, type);
+ if (err)
+ return err;
}
continue;
}
static grub_err_t
grub_btrfs_dir (grub_device_t device, const char *path,
- int (*hook) (const char *filename,
- const struct grub_dirhook_info *info))
+ grub_fs_dir_hook_t hook, void *hook_data)
{
struct grub_btrfs_data *data = grub_btrfs_mount (device);
struct grub_btrfs_key key_in, key_out;
c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
- if (hook (cdirel->name, &info))
+ if (hook (cdirel->name, &info, hook_data))
goto out;
cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
}