* [patch 2/2] grub-0.97: btrfs multidevice configuration support
@ 2009-09-24 22:06 Edward Shishkin
2009-11-02 22:16 ` Johannes Hirte
0 siblings, 1 reply; 5+ messages in thread
From: Edward Shishkin @ 2009-09-24 22:06 UTC (permalink / raw)
To: The development of BTRFS
[-- Attachment #1: Type: text/plain, Size: 1 bytes --]
[-- Attachment #2: grub-btrfs-multidev.patch --]
[-- Type: text/plain, Size: 32964 bytes --]
Signed-off-by: Edward Shishkin <edward@redhat.com>
---
grub/Makefile.am | 2
stage2/btrfs.h | 55 ++--
stage2/builtins.c | 10
stage2/disk_io.c | 2
stage2/filesys.h | 4
stage2/fsys_btrfs.c | 693 +++++++++++++++++++++++++++++++++++++++++-----------
6 files changed, 595 insertions(+), 171 deletions(-)
--- grub-0.97.orig/stage2/btrfs.h
+++ grub-0.97/stage2/btrfs.h
@@ -124,6 +124,7 @@ static int btrfs_csum_sizes[] = { 4, 0 }
#define BTRFS_DEFAULT_NUM_DEVICES 1
#define BTRFS_DEFAULT_NODE_SIZE 4096
#define BTRFS_DEFAULT_LEAF_SIZE 4096
+#define BTRFS_NUM_CACHED_DEVICES 128
#define WARN_ON(c)
#define cassert(cond) ({ switch (-1) { case (cond): case 0: break; } })
@@ -315,13 +316,22 @@ struct btrfs_node {
struct btrfs_key_ptr ptrs[];
} __attribute__ ((__packed__));
+struct btrfs_device {
+ /* the internal btrfs device id */
+ u64 devid;
+ /* the internal grub device representation */
+ unsigned long drive;
+ unsigned long part;
+ unsigned long length;
+};
+
struct extent_buffer {
/* metadata */
+ struct btrfs_device dev;
u64 start;
u64 dev_bytenr;
u32 len;
- int refs;
- int flags;
+ /* data */
char *data;
};
@@ -555,12 +565,8 @@ struct btrfs_block_group_item {
struct btrfs_root {
struct extent_buffer node;
char data[4096];
- struct extent_buffer *commit_root;
struct btrfs_root_item root_item;
- struct btrfs_key root_key;
- struct btrfs_fs_info *fs_info;
u64 objectid;
- u64 last_trans;
/* data allocations are done in sectorsize units */
u32 sectorsize;
@@ -573,42 +579,31 @@ struct btrfs_root {
/* leaf allocations are done in leafsize units */
u32 stripesize;
+};
- int ref_cows;
- int track_dirty;
-
-
- u32 type;
- u64 highest_inode;
- u64 last_inode_alloc;
+struct btrfs_file_info {
+ struct btrfs_key key;
};
struct btrfs_root;
struct btrfs_fs_devices;
struct btrfs_fs_info {
u8 fsid[BTRFS_FSID_SIZE];
- u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
struct btrfs_root fs_root;
struct btrfs_root tree_root;
struct btrfs_root chunk_root;
- struct btrfs_key file_info; /* currently opened file */
+ struct btrfs_file_info file_info; /* currently opened file */
struct btrfs_path paths [LAST_LOOKUP_POOL];
- u64 generation;
- u64 last_trans_committed;
+ char mbr[SECTOR_SIZE];
- u64 system_alloc_profile;
- u64 alloc_start;
+ int sb_mirror;
+ u64 sb_transid;
+ struct btrfs_device sb_dev;
+ struct btrfs_super_block sb_copy;
- struct btrfs_super_block super_temp;
- struct btrfs_super_block super_copy;
-
- u64 super_bytenr;
- u64 total_pinned;
-
- int system_allocs;
- int readonly;
+ struct btrfs_device devices[BTRFS_NUM_CACHED_DEVICES + 1];
};
/*
@@ -1129,6 +1124,11 @@ static inline void btrfs_set_key_type(st
key->type = val;
}
+static inline u64 btrfs_super_devid(struct btrfs_super_block *disk_super)
+{
+ return le64_to_cpu(disk_super->dev_item.devid);
+}
+
/* struct btrfs_header */
BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64);
BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header,
@@ -1317,6 +1317,7 @@ struct btrfs_fs_devices {
};
struct btrfs_bio_stripe {
+ struct btrfs_device dev;
u64 physical;
};
--- grub-0.97.orig/stage2/fsys_btrfs.c
+++ grub-0.97/stage2/fsys_btrfs.c
@@ -31,15 +31,21 @@
#define BTRFS_FS_INFO \
((struct btrfs_fs_info *)((unsigned long)FSYS_BUF + \
LOOKUP_CACHE_SIZE))
-#define BTRFS_CACHE_SIZE (sizeof(struct btrfs_fs_info) + \
- LOOKUP_CACHE_SIZE)
-#define BTRFS_FILE_INFO (&BTRFS_FS_INFO->file_info)
-#define BTRFS_TREE_ROOT (&BTRFS_FS_INFO->tree_root)
-#define BTRFS_CHUNK_ROOT (&BTRFS_FS_INFO->chunk_root)
-#define BTRFS_FS_ROOT (&BTRFS_FS_INFO->fs_root)
-#define BTRFS_SUPER (&BTRFS_FS_INFO->super_copy)
-#define LOOKUP_CACHE_BUF(id) ((char *)((unsigned long)FSYS_BUF + \
- id * LOOKUP_CACHE_BUF_SIZE))
+#define BTRFS_CACHE_SIZE (sizeof(struct btrfs_fs_info) + \
+ LOOKUP_CACHE_SIZE)
+#define BTRFS_TREE_ROOT (&BTRFS_FS_INFO->tree_root)
+#define BTRFS_CHUNK_ROOT (&BTRFS_FS_INFO->chunk_root)
+#define BTRFS_FS_ROOT (&BTRFS_FS_INFO->fs_root)
+#define BTRFS_SUPER (&BTRFS_FS_INFO->sb_copy)
+#define BTRFS_DEVICES (&BTRFS_FS_INFO->devices[0])
+#define BTRFS_FILE_INFO (&BTRFS_FS_INFO->file_info)
+#define BTRFS_FILE_INFO_KEY (&BTRFS_FILE_INFO->key)
+
+#define BTRFS_VOLATILE_DEV_CACHE \
+ (&BTRFS_FS_INFO->devices[BTRFS_NUM_CACHED_DEVICES])
+
+#define LOOKUP_CACHE_BUF(id) ((char *)((unsigned long)FSYS_BUF + \
+ id * LOOKUP_CACHE_BUF_SIZE))
#define noop do {; } while (0)
@@ -76,13 +82,19 @@ static inline struct btrfs_path *btrfs_g
return &BTRFS_FS_INFO->paths[lpid];
}
-static inline void btrfs_update_file_info(struct btrfs_path *path)
+static inline void btrfs_set_path_key(struct btrfs_path *path,
+ struct btrfs_key *key)
{
btrfs_item_key_to_cpu(&path->nodes[0],
- BTRFS_FILE_INFO,
+ key,
path->slots[0]);
}
+static inline void btrfs_update_file_info(struct btrfs_path *path)
+{
+ btrfs_set_path_key(path, BTRFS_FILE_INFO_KEY);
+}
+
static inline void btrfs_set_root_dir_key(struct btrfs_key *key)
{
key->objectid = BTRFS_FIRST_FREE_OBJECTID;
@@ -120,14 +132,13 @@ static inline void init_btrfs_path(looku
static inline void init_btrfs_info(void)
{
int i;
- struct btrfs_fs_info *fs = BTRFS_FS_INFO;
- memset(fs, 0, sizeof (*fs));
+ memset(BTRFS_FS_INFO, 0, sizeof(struct btrfs_fs_info));
for(i = 0; i < LAST_LOOKUP_POOL; i++)
init_btrfs_path(i);
- init_btrfs_root(&fs->tree_root);
- init_btrfs_root(&fs->chunk_root);
- init_btrfs_root(&fs->fs_root);
+ init_btrfs_root(BTRFS_TREE_ROOT);
+ init_btrfs_root(BTRFS_CHUNK_ROOT);
+ init_btrfs_root(BTRFS_FS_ROOT);
}
static void setup_root(struct btrfs_root *root,
@@ -137,7 +148,6 @@ static void setup_root(struct btrfs_root
u32 stripesize,
u64 objectid)
{
- root->fs_info = BTRFS_FS_INFO;
root->nodesize = nodesize;
root->leafsize = leafsize;
root->sectorsize = sectorsize;
@@ -152,7 +162,6 @@ static void setup_root(struct btrfs_root
static int btrfs_find_last_root(struct btrfs_root *tree_root,
u64 objectid,
struct btrfs_root_item *item,
- struct btrfs_key *key,
lookup_pool_id lpid)
{
int ret;
@@ -175,10 +184,10 @@ static int btrfs_find_last_root(struct b
btrfs_item_key_to_cpu(&path->nodes[0], &found_key, slot);
if (found_key.objectid != objectid)
return 1;
+
read_extent_buffer(&path->nodes[0], item,
btrfs_item_ptr_offset(&path->nodes[0], slot),
sizeof(*item));
- memcpy(key, &found_key, sizeof(found_key));
return 0;
}
@@ -210,14 +219,13 @@ static int find_setup_root(struct btrfs_
*/
ret = btrfs_find_last_root(tree_root, objectid,
&dest_root->root_item,
- &dest_root->root_key,
lpid);
if (ret)
return ret;
bytenr = btrfs_root_bytenr(&dest_root->root_item);
blocksize = btrfs_level_size(dest_root,
btrfs_root_level(&dest_root->root_item));
- generation = btrfs_root_generation(&tree_root->root_item);
+ generation = btrfs_root_generation(&dest_root->root_item);
}
ret = read_tree_block(dest_root,
&eb,
@@ -243,12 +251,34 @@ static inline int btrfs_strncmp(const ch
return __res;
}
-static int btrfs_check_super_block(struct btrfs_super_block *sb)
-{
- if (sb->num_devices != BTRFS_DEFAULT_NUM_DEVICES) {
- btrfs_msg("Btrfs multi-device configuration unsupported\n");
- goto error;
+/*
+ * the same as devread, but accepts
+ * device number, start and length.
+ */
+static int btrfs_devread(unsigned long drive, unsigned long part,
+ unsigned long dev_len, int sector,
+ int byte_offset, int byte_len, char *buf)
+{
+ if (sector < 0
+ || ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS))
+ >= dev_len)) {
+ errnum = ERR_OUTSIDE_PART;
+ return 0;
}
+ sector += byte_offset >> SECTOR_BITS;
+ byte_offset &= SECTOR_SIZE - 1;
+#if !defined(STAGE1_5)
+ if (disk_read_hook && debug)
+ printf ("<%d, %d, %d>", sector, byte_offset, byte_len);
+#endif /* !STAGE1_5 */
+ return rawread(drive, part + sector, byte_offset,
+ byte_len, buf);
+}
+
+static int btrfs_check_super(void)
+{
+ struct btrfs_super_block *sb = BTRFS_SUPER;
+
if (sb->nodesize != BTRFS_DEFAULT_NODE_SIZE) {
btrfs_msg("Btrfs node size (%d) != %d unsupported\n",
sb->nodesize, BTRFS_DEFAULT_NODE_SIZE);
@@ -259,103 +289,412 @@ static int btrfs_check_super_block(struc
sb->leafsize, BTRFS_DEFAULT_LEAF_SIZE);
goto error;
}
- return 1;
- error:
- errnum = ERR_FSYS_MOUNT;
return 0;
+ error:
+ return 1;
}
-int btrfs_mount(void)
+/* lift the super block */
+static int btrfs_uptodate_super_copy(struct btrfs_fs_info *fs)
{
- int i;
- int ret;
- u64 transid = 0;
- u64 bytenr;
-
- struct btrfs_fs_info *fs = BTRFS_FS_INFO;
- struct btrfs_super_block *sb_tmp; /* current */
- struct btrfs_super_block *sb; /* latest */
-
- struct btrfs_root *tree_root = &fs->tree_root;
- struct btrfs_root *chunk_root = &fs->chunk_root;
- struct btrfs_root *fs_root = &fs->fs_root;
-
- check_btrfs_cache_size();
- init_btrfs_info();
+ errnum = ERR_NONE;
+ btrfs_devread(BTRFS_FS_INFO->sb_dev.drive,
+ BTRFS_FS_INFO->sb_dev.part,
+ BTRFS_FS_INFO->sb_dev.length,
+ btrfs_sb_offset(BTRFS_FS_INFO->sb_mirror) >> SECTOR_BITS,
+ 0,
+ sizeof(struct btrfs_super_block),
+ (char *)BTRFS_SUPER);
+ return btrfs_check_super();
+}
- sb_tmp = &fs->super_temp;
- sb = &fs->super_copy;
+/*
+ * Looking for a btrfs super block by magic, @fsid and @devid
+ * (the last two ones are optional). Update latest transid (if
+ * any). Return 0, if such super block was found. Otherwise,
+ * return 1.
+ *
+ * NOTE:
+ * After calling this function the sb_copy of global btrfs_fs_info
+ * can contain garbage, so the caller is responsible for this to be
+ * uptodate (see the function btrfs_uptodate_super_copy()).
+ */
+static int btrfs_find_super(struct btrfs_device *dev, char *fsid, u64 *devid)
+{
+ int i, ret;
+ int found = 0;
- /* pick up the latest version of superblock */
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- bytenr = btrfs_sb_offset(i);
- ret = devread(bytenr >> SECTOR_BITS,
- 0,
- sizeof(*sb_tmp),
- (char *)sb_tmp);
- if (!ret)
- continue;
-
- if (btrfs_super_bytenr(sb_tmp) != bytenr ||
- btrfs_strncmp((char *)(&sb_tmp->magic),
+ ret = btrfs_devread(dev->drive,
+ dev->part,
+ dev->length,
+ btrfs_sb_offset(i) >> SECTOR_BITS,
+ 0,
+ sizeof(struct btrfs_super_block),
+ (char *)BTRFS_SUPER);
+ if (!ret) {
+ if (errnum == ERR_OUTSIDE_PART) {
+ errnum = ERR_NONE;
+ break;
+ } else {
+ errnum = ERR_NONE;
+ continue;
+ }
+ }
+ if (btrfs_super_bytenr(BTRFS_SUPER) != btrfs_sb_offset(i) ||
+ btrfs_strncmp((char *)(&BTRFS_SUPER->magic),
BTRFS_MAGIC,
- sizeof(sb_tmp->magic)))
+ sizeof(BTRFS_SUPER->magic)))
continue;
- if (btrfs_super_generation(sb_tmp) > transid) {
- memcpy(sb, sb_tmp, sizeof(*sb_tmp));
- transid = btrfs_super_generation(sb);
+ if (fsid &&
+ btrfs_strncmp(fsid,
+ (char *)BTRFS_SUPER->fsid,
+ BTRFS_FSID_SIZE))
+ return 1;
+ if (devid &&
+ *devid != btrfs_super_devid(BTRFS_SUPER))
+ return 1;
+ found = 1;
+ dev->devid = btrfs_super_devid(BTRFS_SUPER);
+
+ if (btrfs_super_generation(BTRFS_SUPER) >
+ BTRFS_FS_INFO->sb_transid) {
+ BTRFS_FS_INFO->sb_transid =
+ btrfs_super_generation(BTRFS_SUPER);
+ BTRFS_FS_INFO->sb_mirror = i;
+ BTRFS_FS_INFO->sb_dev.devid =
+ btrfs_super_devid(BTRFS_SUPER);
+ BTRFS_FS_INFO->sb_dev.drive = dev->drive;
+ BTRFS_FS_INFO->sb_dev.part = dev->part;
+ BTRFS_FS_INFO->sb_dev.length = dev->length;
+ }
+ }
+ return !found;
+}
+
+/*
+ * "Discern" a btrfs device by fsid and
+ * optionaly by devid (if lookup is set).
+ * Populate persistent device cache (if
+ * there are free slots).
+ */
+static int btrfs_discerner(struct btrfs_device **dev, int lookup)
+{
+ if (btrfs_find_super(*dev,
+ (char *)BTRFS_FS_INFO->fsid,
+ (lookup ? &(*dev)->devid : 0)))
+ /* not found */
+ return 0;
+ if (*dev < BTRFS_VOLATILE_DEV_CACHE) {
+ /* populate persistent device cache */
+ memcpy(*dev + 1, *dev, sizeof(struct btrfs_device));
+ (*dev)++;
+ }
+ return 1;
+}
+
+/*
+ * Scan available grub devices and call discerner
+ * for them. Return a number of discerned devices
+ * The scanner was stolen from print_completions().
+ *
+ * Preconditions:
+ * The global structure btrfs_fs_info contains
+ * the latest valid version of btrfs superblock
+ * (the field @sb_copy)
+ */
+static u64 scan_grub_devices(struct btrfs_device *dev,
+ int (*discerner)(struct btrfs_device **, int),
+ int lookup)
+{
+ int i, j;
+ u64 count = 0;
+ struct geometry geom;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 8; j++) {
+ unsigned long part = 0xFFFFFF;
+ int type, entry, gpt_count, gpt_size;
+ unsigned long offset, ext_offset, gpt_offset;
+
+ dev->drive = (i * 0x80) + j;
+ if (get_diskinfo(dev->drive, &geom))
+ continue;
+ while (1) {
+ int ret;
+ buf_drive = -1;
+ errnum = ERR_NONE;
+ ret = next_partition(dev->drive, 0xFFFFFF,
+ &part, &type, &dev->part,
+ &dev->length, &offset,
+ &entry, &ext_offset,
+ &gpt_offset, &gpt_count,
+ &gpt_size,
+ BTRFS_FS_INFO->mbr);
+ if (!ret)
+ break;
+ if (discerner(&dev, lookup)) {
+ count++;
+ if (lookup)
+ goto exit;
+ }
+ }
+ }
+ errnum = ERR_NONE;
+ if (cdrom_drive != GRUB_INVALID_DRIVE &&
+ !get_diskinfo(cdrom_drive, &geom)) {
+ dev->drive = cdrom_drive;
+ dev->part = 0;
+ dev->length = geom.total_sectors;
+ if (discerner(&dev, lookup)) {
+ count++;
+ if (lookup)
+ goto exit;
+ }
+ }
+#ifdef SUPPORT_NETBOOT
+ errnum = ERR_NONE;
+ if (network_ready &&
+ !get_diskinfo(NETWORK_DRIVE, &geom)) {
+ dev->drive = NETWORK_DRIVE;
+ dev->part = 0;
+ dev->length = geom.total_sectors;
+ if (discerner(&dev, lookup)) {
+ count++;
+ if (lookup)
+ goto exit;
+ }
+ }
+#endif /* SUPPORT_NETBOOT */
+ exit:
+ return count;
+}
+
+#if 0
+static int btrfs_next_item(struct btrfs_root *root,
+ struct btrfs_path *path);
+
+/*
+ * Scan the chunk tree for dev items
+ * and call a seeker for all of them.
+ * Preconditions: chunk root is installed
+ * to the global btrfs_fs_info.
+ */
+static int scan_dev_tree(struct btrfs_device* (*seeker)(u64))
+{
+ int ret;
+ u64 num_devices = 0;
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct btrfs_path *path;
+ struct btrfs_root *root;
+
+ root = BTRFS_CHUNK_ROOT;
+ path = btrfs_grab_path(FIRST_EXTERNAL_LOOKUP_POOL);
+ key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+ key.type = 0;
+ key.offset = 0;
+
+ ret = aux_tree_lookup(root, &key, path);
+ if (ret == -1)
+ goto corrupted;
+ while (1) {
+ struct btrfs_device *result;
+ struct btrfs_dev_item *dev_item;
+
+ btrfs_item_key_to_cpu(&path->nodes[0],
+ &found_key,
+ path->slots[0]);
+ if (found_key.objectid != BTRFS_DEV_ITEMS_OBJECTID)
+ break;
+ dev_item = btrfs_item_ptr(&path->nodes[0],
+ path->slots[0],
+ struct btrfs_dev_item);
+ result = seeker(btrfs_device_id(&path->nodes[0], dev_item));
+ if (result == NULL) {
+ btrfs_msg("Btrfs device %llu is not available\n",
+ btrfs_device_id(&path->nodes[0], dev_item));
+ goto missed_dev;
}
+ num_devices++;
+ ret = btrfs_next_item(root, path);
+ if (ret)
+ break;
}
- /* there might be errors when reading super mirrors */
- if (errnum == ERR_OUTSIDE_PART)
- errnum = ERR_NONE;
- if (transid <= 0) {
- btrfs_msg("No valid Btrfs superblock found\n");
+ if (num_devices == btrfs_super_num_devices(BTRFS_SUPER))
+ return 0;
+ corrupted:
+ errnum = ERR_FSYS_CORRUPT;
+ return 1;
+ missed_dev:
+ errnum = ERR_FSYS_MOUNT;
+ return 1;
+}
+#endif /* 0 */
+
+/*
+ * Find a grub btrfs device by devid.
+ * Preconditions: global btrfs_fs_info
+ * contains a copy of btrfs super block.
+ *
+ * Return pointer to the cached device on success.
+ * Otherwise return NULL.
+ */
+static struct btrfs_device *btrfs_lookup_device(u64 devid)
+{
+ int i, result;
+ struct btrfs_device *cdev;
+
+ for (i = 0; i < BTRFS_NUM_CACHED_DEVICES; i++) {
+ cdev = &BTRFS_DEVICES[i];
+ if (cdev->devid == devid)
+ goto found_in_cache;
+ if (cdev->devid == 0)
+ goto not_found_in_cache;
+ }
+ not_found_in_cache:
+ cdev = BTRFS_VOLATILE_DEV_CACHE;
+ cdev->devid = devid;
+ result = scan_grub_devices(cdev,
+ btrfs_discerner,
+ 1);
+ if (result == 0)
+ /*
+ * At mount time we have figured out that
+ * number of available devices is not less
+ * then number of devices recorded in the
+ * super block. Hence we treat this case as
+ * file system corruption.
+ */
+ goto corrupt;
+ result = btrfs_uptodate_super_copy(BTRFS_FS_INFO);
+ if (result)
+ goto corrupt;
+ found_in_cache:
+ return cdev;
+ corrupt:
+ errnum = ERR_FSYS_CORRUPT;
+ return NULL;
+}
+
+static int btrfs_find_device(struct btrfs_device *dev)
+{
+ struct btrfs_device *cdev;
+
+ if (btrfs_super_num_devices(BTRFS_SUPER) == 1) {
+ dev->drive = current_drive;
+ dev->part = part_start;
+ dev->length = part_length;
return 0;
}
- if (!btrfs_check_super_block(sb))
+ cdev = btrfs_lookup_device(dev->devid);
+ if (cdev == NULL)
+ return 1;
+ dev->drive = cdev->drive;
+ dev->part = cdev->part;
+ dev->length = cdev->length;
+ return 0;
+}
+
+static inline void init_btrfs_volatile_dev_cache(void)
+{
+ BTRFS_VOLATILE_DEV_CACHE->devid = 0;
+ BTRFS_VOLATILE_DEV_CACHE->drive = current_drive;
+ BTRFS_VOLATILE_DEV_CACHE->part = part_start;
+ BTRFS_VOLATILE_DEV_CACHE->length = part_length;
+}
+
+/*
+ * check availability of btrfs devices
+ * and populate the persistent device cache
+ */
+static int btrfs_check_devices(void)
+{
+ u64 num_dev;
+
+ if (btrfs_super_num_devices(BTRFS_SUPER) == 1)
return 0;
+ num_dev = scan_grub_devices(BTRFS_DEVICES,
+ btrfs_discerner, 0);
+ if (btrfs_uptodate_super_copy(BTRFS_FS_INFO))
+ return 1;
+ if (num_dev < btrfs_super_num_devices(BTRFS_SUPER)) {
+ btrfs_msg("Some (%llu) Btrfs devices is not available\n",
+ btrfs_super_num_devices(BTRFS_SUPER) - num_dev);
+ return 1;
+ }
+ return 0;
+}
+
+int btrfs_mount(void)
+{
+ int ret;
+
+ check_btrfs_cache_size();
+ init_btrfs_info();
+ init_btrfs_volatile_dev_cache();
+
+ ret = btrfs_find_super(BTRFS_VOLATILE_DEV_CACHE, NULL, NULL);
+ if (ret) {
+ btrfs_msg("Drive %lu, partition %lu: no Btrfs metadata\n",
+ current_drive, part_start);
+ goto error;
+ }
+ ret = btrfs_uptodate_super_copy(BTRFS_FS_INFO);
+ if (ret)
+ goto error;
+ BTRFS_FS_INFO->sb_transid =
+ btrfs_super_generation(BTRFS_SUPER);
+ memcpy(BTRFS_FS_INFO->fsid,
+ BTRFS_SUPER->fsid,
+ BTRFS_FSID_SIZE);
+ ret = btrfs_check_devices();
+ if (ret)
+ goto error;
/* setup chunk root */
ret = find_setup_root(NULL,
- btrfs_super_nodesize(sb),
- btrfs_super_leafsize(sb),
- btrfs_super_sectorsize(sb),
- btrfs_super_stripesize(sb),
+ btrfs_super_nodesize(BTRFS_SUPER),
+ btrfs_super_leafsize(BTRFS_SUPER),
+ btrfs_super_sectorsize(BTRFS_SUPER),
+ btrfs_super_stripesize(BTRFS_SUPER),
BTRFS_CHUNK_TREE_OBJECTID,
- chunk_root,
- btrfs_super_chunk_root(sb),
- btrfs_chunk_root_level_size(sb),
- btrfs_super_chunk_root_generation(sb),
+ BTRFS_CHUNK_ROOT,
+ btrfs_super_chunk_root(BTRFS_SUPER),
+ btrfs_chunk_root_level_size(BTRFS_SUPER),
+ btrfs_super_chunk_root_generation(BTRFS_SUPER),
FIRST_EXTERNAL_LOOKUP_POOL);
if (ret)
return 0;
/* setup tree root */
ret = find_setup_root(NULL,
- btrfs_super_nodesize(sb),
- btrfs_super_leafsize(sb),
- btrfs_super_sectorsize(sb),
- btrfs_super_stripesize(sb),
+ btrfs_super_nodesize(BTRFS_SUPER),
+ btrfs_super_leafsize(BTRFS_SUPER),
+ btrfs_super_sectorsize(BTRFS_SUPER),
+ btrfs_super_stripesize(BTRFS_SUPER),
BTRFS_ROOT_TREE_OBJECTID,
- tree_root,
- btrfs_super_root(sb),
- btrfs_root_level_size(sb),
- btrfs_super_generation(sb),
+ BTRFS_TREE_ROOT,
+ btrfs_super_root(BTRFS_SUPER),
+ btrfs_root_level_size(BTRFS_SUPER),
+ btrfs_super_generation(BTRFS_SUPER),
FIRST_EXTERNAL_LOOKUP_POOL);
if (ret)
return 0;
/* setup fs_root */
- ret = find_setup_root(tree_root,
- btrfs_super_nodesize(sb),
- btrfs_super_leafsize(sb),
- btrfs_super_sectorsize(sb),
- btrfs_super_stripesize(sb),
+ ret = find_setup_root(BTRFS_TREE_ROOT,
+ btrfs_super_nodesize(BTRFS_SUPER),
+ btrfs_super_leafsize(BTRFS_SUPER),
+ btrfs_super_sectorsize(BTRFS_SUPER),
+ btrfs_super_stripesize(BTRFS_SUPER),
BTRFS_FS_TREE_OBJECTID,
- fs_root,
+ BTRFS_FS_ROOT,
0,
0,
0,
FIRST_EXTERNAL_LOOKUP_POOL);
return !ret;
+ error:
+ errnum = ERR_FSYS_MOUNT;
+ return 0;
}
/*
@@ -371,7 +710,7 @@ int check_read_chunk(struct btrfs_key *k
struct map_lookup *map,
u64 logical)
{
- int i;
+ int i, ret;
u64 chunk_start;
u64 chunk_size;
int num_stripes;
@@ -397,20 +736,26 @@ int check_read_chunk(struct btrfs_key *k
for (i = 0; i < num_stripes; i++) {
map->stripes[i].physical =
btrfs_stripe_offset_nr(leaf, chunk, i);
+ map->stripes[i].dev.devid =
+ btrfs_stripe_devid_nr(leaf, chunk, i);
+ ret = btrfs_find_device(&map->stripes[i].dev);
+ if (ret)
+ return 0;
}
return 1;
}
static void init_extent_buffer(struct extent_buffer *eb,
+ struct btrfs_device *dev,
u64 logical,
u32 blocksize,
u64 physical,
lookup_pool_id lpid)
{
+ if (dev)
+ memcpy(&eb->dev, dev, sizeof(*dev));
eb->start = logical;
eb->len = blocksize;
- eb->refs = 2;
- eb->flags = 0;
eb->dev_bytenr = physical;
eb->data = grab_lookup_cache(lpid);
}
@@ -516,8 +861,9 @@ static int chunk_tree_lookup(struct map_
* Look for an appropriate map-extent and
* perform a translation. Return 1 on errors.
*/
-int __btrfs_map_block(u64 logical, u64 *length, struct btrfs_multi_bio *multi,
- int mirror_num)
+static int btrfs_map_block(u64 logical, u64 *length,
+ struct btrfs_multi_bio *multi,
+ int mirror_num)
{
struct map_lookup map;
u64 offset;
@@ -592,29 +938,57 @@ int __btrfs_map_block(u64 logical, u64 *
multi->stripes[i].physical =
map.stripes[stripe_index].physical + stripe_offset +
stripe_nr * map.stripe_len;
+ memcpy(&multi->stripes[i].dev,
+ &map.stripes[stripe_index].dev,
+ sizeof(struct btrfs_device));
stripe_index++;
}
return 0;
}
-static u64 btrfs_map_block(u64 logical)
+static u64 read_data_extent(u64 logical_start, u64 to_read, char *pos)
{
int ret;
u64 length;
struct btrfs_multi_bio multi;
- ret = __btrfs_map_block(logical, &length, &multi, 0);
- if (ret) {
- errnum = ERR_FSYS_CORRUPT;
- return 0;
+ while (to_read) {
+ ret = btrfs_map_block(logical_start, &length, &multi, 0);
+ if (ret) {
+ errnum = ERR_FSYS_CORRUPT;
+ return ret;
+ }
+ if (length > to_read)
+ length = to_read;
+ disk_read_func = disk_read_hook;
+ ret = btrfs_devread(multi.stripes[0].dev.drive,
+ multi.stripes[0].dev.part,
+ multi.stripes[0].dev.length,
+ multi.stripes[0].physical >> SECTOR_BITS,
+ logical_start & ((u64)SECTOR_SIZE - 1),
+ length,
+ pos);
+ disk_read_func = NULL;
+ if (!ret)
+ return 1;
+ btrfs_msg("BTRFS data extent: read %llu bytes\n", length);
+ to_read -= length;
+ pos += length;
+ logical_start += length;
}
- return multi.stripes[0].physical;
+ return 0;
}
static int read_extent_from_disk(struct extent_buffer *eb)
{
WARN_ON(eb->dev_bytenr % SECTOR_BITS);
- return devread(eb->dev_bytenr >> SECTOR_BITS, 0, eb->len, eb->data);
+ return btrfs_devread(eb->dev.drive,
+ eb->dev.part,
+ eb->dev.length,
+ eb->dev_bytenr >> SECTOR_BITS,
+ 0,
+ eb->len,
+ eb->data);
}
static int verify_parent_transid(struct extent_buffer *eb, u64 parent_transid)
@@ -660,13 +1034,14 @@ int read_tree_block(struct btrfs_root *r
dev_nr = 0;
length = blocksize;
while (1) {
- ret = __btrfs_map_block(bytenr,
- &length, &multi, mirror_num);
+ ret = btrfs_map_block(bytenr,
+ &length, &multi, mirror_num);
if (ret) {
errnum = ERR_FSYS_CORRUPT;
return 0;
}
init_extent_buffer(eb,
+ &multi.stripes[0].dev,
bytenr,
blocksize,
multi.stripes[0].physical,
@@ -814,6 +1189,7 @@ int aux_tree_lookup(struct btrfs_root *r
int level;
struct extent_buffer node;
init_extent_buffer(&node,
+ NULL,
0,
0,
0,
@@ -939,13 +1315,18 @@ static int btrfs_next_item(struct btrfs_
* search for read operation
*/
static int path_is_valid(struct btrfs_path *path,
- struct btrfs_key *key)
+ struct btrfs_key *key, u64 offset)
{
btrfs_item_key_to_cpu(&path->nodes[0],
key,
path->slots[0]);
- return (key->objectid == BTRFS_FILE_INFO->objectid) &&
- (btrfs_key_type(key) == BTRFS_EXTENT_DATA_KEY);
+ if (BTRFS_FILE_INFO_KEY->objectid != key->objectid)
+ return 0;
+ if (btrfs_key_type(key) == BTRFS_INODE_ITEM_KEY)
+ return 1;
+ if (btrfs_key_type(key) != BTRFS_EXTENT_DATA_KEY)
+ return 0;
+ return BTRFS_FILE_INFO_KEY->offset <= offset;
}
/* ->read_func() */
@@ -954,31 +1335,35 @@ int btrfs_read(char *buf, int len)
int ret;
struct btrfs_root *fs_root;
struct btrfs_path *path;
- struct btrfs_key *info_key;
struct btrfs_key path_key;
- u64 off;
+ u64 ioff;
u64 bytes;
- unsigned int to_read;
+ int to_read;
char *pos = buf;
fs_root = BTRFS_FS_ROOT;
- info_key = BTRFS_FILE_INFO;
path = btrfs_grab_path(FIRST_EXTERNAL_LOOKUP_POOL);
- if (!path_is_valid(path, &path_key)) {
- btrfs_set_key_type(info_key, BTRFS_EXTENT_DATA_KEY);
- info_key->offset = filepos;
- ret = aux_tree_lookup(fs_root, info_key, path);
+ if (!path_is_valid(path, &path_key, filepos)) {
+ ret = aux_tree_lookup(fs_root, BTRFS_FILE_INFO_KEY, path);
if (ret < 0)
errnum = ERR_FSYS_CORRUPT;
}
while (!errnum) {
struct btrfs_item *item;
struct btrfs_file_extent_item *fi;
- unsigned int from;
+ u64 from;
- if (!path_is_valid(path, &path_key))
+ btrfs_item_key_to_cpu(&path->nodes[0],
+ &path_key,
+ path->slots[0]);
+ if (BTRFS_FILE_INFO_KEY->objectid != path_key.objectid)
break;
+ if (btrfs_key_type(&path_key) != BTRFS_EXTENT_DATA_KEY)
+ goto next;
+ /*
+ * current position is extent item
+ */
item = btrfs_item_nr(&path->nodes[0], path->slots[0]);
fi = btrfs_item_ptr(&path->nodes[0],
path->slots[0],
@@ -988,44 +1373,54 @@ int btrfs_read(char *buf, int len)
errnum = ERR_BAD_FILETYPE;
goto exit;
}
- off = filepos - path_key.offset;
+ ioff = filepos - path_key.offset;
switch (btrfs_file_extent_type(&path->nodes[0], fi)) {
case BTRFS_FILE_EXTENT_INLINE:
bytes = btrfs_file_extent_inline_item_len(&path->
nodes[0],
item);
- to_read = bytes - off;
+ if (path_key.offset + bytes < filepos)
+ goto next;
+ to_read = bytes - ioff;
if (to_read > len)
to_read = len;
- from = off + btrfs_file_extent_inline_start(fi);
+ from = ioff + btrfs_file_extent_inline_start(fi);
if (disk_read_hook != NULL) {
disk_read_func = disk_read_hook;
- ret = devread(path->nodes[0].dev_bytenr >>
- SECTOR_BITS, from, to_read, pos);
+ ret = btrfs_devread(path->nodes[0].dev.drive,
+ path->nodes[0].dev.part,
+ path->nodes[0].dev.length,
+ path->nodes[0].dev_bytenr >>
+ SECTOR_BITS,
+ from,
+ to_read,
+ pos);
disk_read_func = NULL;
- } else {
+ if (ret)
+ goto exit;
+ } else
memcpy(pos,
path->nodes[0].data + from,
to_read);
- }
+ btrfs_msg("BTRFS inline extent: read %d bytes pos %d\n",
+ to_read, filepos);
break;
case BTRFS_FILE_EXTENT_REG:
bytes = btrfs_file_extent_num_bytes(&path->nodes[0],
fi);
- to_read = bytes - off;
+ if (path_key.offset + bytes < filepos)
+ goto next;
+ to_read = bytes - ioff;
if (to_read > len)
to_read = len;
- from = off +
+ from = ioff +
btrfs_file_extent_disk_bytenr(&path->nodes[0],
- fi);
- disk_read_func = disk_read_hook;
- ret = devread(btrfs_map_block(from) >> SECTOR_BITS,
- from & ((u64)SECTOR_SIZE - 1),
- to_read,
- pos);
- disk_read_func = NULL;
- if (!ret)
+ fi) +
+ btrfs_file_extent_offset(&path->nodes[0],
+ fi);
+ ret = read_data_extent(from, to_read, pos);
+ if (ret)
goto exit;
break;
case BTRFS_FILE_EXTENT_PREALLOC:
@@ -1042,12 +1437,14 @@ int btrfs_read(char *buf, int len)
if (len == 0)
break;
/* not everything was read */
+ next:
ret = btrfs_next_item(fs_root, path);
- if (ret) {
- /* something should be found */
+ if (ret < 0) {
errnum = ERR_FSYS_CORRUPT;
break;
}
+ btrfs_update_file_info(path);
+ continue;
}
exit:
return errnum ? 0 : pos - buf;
@@ -1082,14 +1479,14 @@ static int btrfs_follow_link(struct btrf
filepos = 0;
/* extract symlink content */
while (1) {
- u64 oid = BTRFS_FILE_INFO->objectid;
+ u64 oid = BTRFS_FILE_INFO_KEY->objectid;
ret = btrfs_next_item(root, path);
if (ret)
break;
btrfs_update_file_info(path);
- if (oid != BTRFS_FILE_INFO->objectid)
+ if (oid != BTRFS_FILE_INFO_KEY->objectid)
break;
- if (btrfs_key_type(BTRFS_FILE_INFO) ==
+ if (btrfs_key_type(BTRFS_FILE_INFO_KEY) ==
BTRFS_EXTENT_DATA_KEY)
goto found;
}
@@ -1114,7 +1511,7 @@ static int update_fs_root(struct btrfs_r
if (location->offset != (u64)-1)
return 0;
- tree_root = &fs_root->fs_info->tree_root;
+ tree_root = &BTRFS_FS_INFO->tree_root;
ret = find_setup_root(tree_root,
tree_root->nodesize,
tree_root->leafsize,
@@ -1388,6 +1785,22 @@ int btrfs_dir(char *dirname)
}
}
+int btrfs_embed(int *start_sector, int needed_sectors)
+{
+ int ret;
+ init_btrfs_info();
+ init_btrfs_volatile_dev_cache();
+
+ ret = btrfs_find_super(BTRFS_VOLATILE_DEV_CACHE, NULL, NULL);
+ if (ret)
+ return 0;
+ ret = btrfs_uptodate_super_copy(BTRFS_FS_INFO);
+ if (ret)
+ return 0;
+ *start_sector = 1; /* reserve first sector for stage1 */
+ return needed_sectors <=
+ ((BTRFS_SUPER_INFO_OFFSET >> SECTOR_BITS) - 1);
+}
#endif /* FSYS_BTRFS */
/*
--- grub-0.97.orig/stage2/filesys.h
+++ grub-0.97/stage2/filesys.h
@@ -137,8 +137,8 @@ int iso9660_dir (char *dirname);
#ifndef NUM_FSYS
#define NUM_FSYS \
(FSYS_FFS_NUM + FSYS_FAT_NUM + FSYS_EXT2FS_NUM + FSYS_MINIX_NUM \
- + FSYS_REISERFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM + FSYS_XFS_NUM \
- + FSYS_TFTP_NUM + FSYS_ISO9660_NUM + FSYS_UFS2_NUM)
+ + FSYS_REISERFS_NUM + FSYS_BTRFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM \
+ + FSYS_XFS_NUM + FSYS_TFTP_NUM + FSYS_ISO9660_NUM + FSYS_UFS2_NUM)
#endif
/* defines for the block filesystem info area */
--- grub-0.97.orig/grub/Makefile.am
+++ grub-0.97/grub/Makefile.am
@@ -8,7 +8,7 @@ endif
AM_CPPFLAGS = -DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \
-DFSYS_ISO9660=1 -DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 \
- -DFSYS_UFS2=1 -DFSYS_VSTAFS=1 -DFSYS_XFS=1 \
+ -DFSYS_BTRFS=1 -DFSYS_UFS2=1 -DFSYS_VSTAFS=1 -DFSYS_XFS=1 \
-DUSE_MD5_PASSWORDS=1 -DSUPPORT_HERCULES=1 \
$(SERIAL_FLAGS) -I$(top_srcdir)/stage2 \
-I$(top_srcdir)/stage1 -I$(top_srcdir)/lib
--- grub-0.97.orig/stage2/disk_io.c
+++ grub-0.97/stage2/disk_io.c
@@ -65,7 +65,7 @@ struct fsys_entry fsys_table[NUM_FSYS +
{"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, reiserfs_embed},
# endif
# ifdef FSYS_BTRFS
- {"btrfs", btrfs_mount, btrfs_read, btrfs_dir, 0, 0},
+ {"btrfs", btrfs_mount, btrfs_read, btrfs_dir, 0, btrfs_embed},
# endif
# ifdef FSYS_VSTAFS
{"vstafs", vstafs_mount, vstafs_read, vstafs_dir, 0, 0},
--- grub-0.97.orig/stage2/builtins.c
+++ grub-0.97/stage2/builtins.c
@@ -2423,6 +2423,16 @@ install_func (char *arg, int flags)
else
#endif /* GRUB_UTIL */
{
+ /*
+ * FIXME: Ugly hack.
+ * Do not write to btrfs partition
+ * without a help of the file system!
+ */
+ if (!strcmp(fsys_table[fsys_type].name, "btrfs"))
+ {
+ errnum = ERR_BAD_ARGUMENT;
+ goto fail;
+ }
if (! devwrite (*saved_sector - part_start, 1, stage2_buffer))
goto fail;
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [patch 2/2] grub-0.97: btrfs multidevice configuration support
2009-09-24 22:06 [patch 2/2] grub-0.97: btrfs multidevice configuration support Edward Shishkin
@ 2009-11-02 22:16 ` Johannes Hirte
2009-11-03 0:59 ` Edward Shishkin
0 siblings, 1 reply; 5+ messages in thread
From: Johannes Hirte @ 2009-11-02 22:16 UTC (permalink / raw)
To: Edward Shishkin; +Cc: The development of BTRFS
Am Freitag 25 September 2009 00:06:40 schrieb Edward Shishkin:
>
Hi Edward,
I was pointed to a problem with this patch.
+static u64 scan_grub_devices(struct btrfs_device *dev,
+ int (*discerner)(struct btrfs_device **, int),
+ int lookup)
+{
...
+#ifdef SUPPORT_NETBOOT
+ errnum = ERR_NONE;
+ if (network_ready &&
+ !get_diskinfo(NETWORK_DRIVE, &geom)) {
+ dev->drive = NETWORK_DRIVE;
+ dev->part = 0;
+ dev->length = geom.total_sectors;
+ if (discerner(&dev, lookup)) {
+ count++;
+ if (lookup)
+ goto exit;
+ }
+ }
+#endif /* SUPPORT_NETBOOT */
+ exit:
+ return count;
+}
This won't compile since network_ready is undeclared. Why is the btrfs code
dealing with network devices at all?
regards
Johannes
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [patch 2/2] grub-0.97: btrfs multidevice configuration support
2009-11-02 22:16 ` Johannes Hirte
@ 2009-11-03 0:59 ` Edward Shishkin
2009-12-10 23:21 ` Johannes Hirte
0 siblings, 1 reply; 5+ messages in thread
From: Edward Shishkin @ 2009-11-03 0:59 UTC (permalink / raw)
To: Johannes Hirte; +Cc: The development of BTRFS
[-- Attachment #1: Type: text/plain, Size: 1129 bytes --]
Johannes Hirte wrote:
> Am Freitag 25 September 2009 00:06:40 schrieb Edward Shishkin:
>
>
> Hi Edward,
>
> I was pointed to a problem with this patch.
>
> +static u64 scan_grub_devices(struct btrfs_device *dev,
> + int (*discerner)(struct btrfs_device **, int),
> + int lookup)
> +{
> ...
> +#ifdef SUPPORT_NETBOOT
> + errnum = ERR_NONE;
> + if (network_ready &&
> + !get_diskinfo(NETWORK_DRIVE, &geom)) {
> + dev->drive = NETWORK_DRIVE;
> + dev->part = 0;
> + dev->length = geom.total_sectors;
> + if (discerner(&dev, lookup)) {
> + count++;
> + if (lookup)
> + goto exit;
> + }
> + }
> +#endif /* SUPPORT_NETBOOT */
> + exit:
> + return count;
> +}
>
> This won't compile since network_ready is undeclared.
Yup, indeed..
> Why is the btrfs code
> dealing with network devices at all?
>
Why not? :)
Well, would you please disable it for now with the attached patch?
Thanks,
Edward.
[-- Attachment #2: grub-btrfs-multidev-fixup.patch --]
[-- Type: text/plain, Size: 615 bytes --]
Disable booting from network devices.
Signed-off-by: Edward Shishkin <edward@redhat.com>
---
stage2/fsys_btrfs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- grub-0.97.orig/stage2/fsys_btrfs.c
+++ grub-0.97/stage2/fsys_btrfs.c
@@ -452,7 +452,7 @@ static u64 scan_grub_devices(struct btrf
goto exit;
}
}
-#ifdef SUPPORT_NETBOOT
+#if 0
errnum = ERR_NONE;
if (network_ready &&
!get_diskinfo(NETWORK_DRIVE, &geom)) {
@@ -465,7 +465,7 @@ static u64 scan_grub_devices(struct btrf
goto exit;
}
}
-#endif /* SUPPORT_NETBOOT */
+#endif /* 0 */
exit:
return count;
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [patch 2/2] grub-0.97: btrfs multidevice configuration support
2009-11-03 0:59 ` Edward Shishkin
@ 2009-12-10 23:21 ` Johannes Hirte
2009-12-11 11:26 ` Edward Shishkin
0 siblings, 1 reply; 5+ messages in thread
From: Johannes Hirte @ 2009-12-10 23:21 UTC (permalink / raw)
To: Edward Shishkin; +Cc: The development of BTRFS
Am Dienstag 03 November 2009 01:59:39 schrieb Edward Shishkin:
> Johannes Hirte wrote:
> > Why is the btrfs code
> > dealing with network devices at all?
>
> Why not? :)
I don't see the possiblity to get a btrfs filesystem this way. So as far as I
understand this, it's complete useless. The CD support doesn't look very
usefull too to me. It's possible to put a btrfs filesystem on a CD or DVD. But
that seems rather theoretical.
regards,
Johannes
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [patch 2/2] grub-0.97: btrfs multidevice configuration support
2009-12-10 23:21 ` Johannes Hirte
@ 2009-12-11 11:26 ` Edward Shishkin
0 siblings, 0 replies; 5+ messages in thread
From: Edward Shishkin @ 2009-12-11 11:26 UTC (permalink / raw)
To: Johannes Hirte; +Cc: The development of BTRFS
Johannes Hirte wrote:
> Am Dienstag 03 November 2009 01:59:39 schrieb Edward Shishkin:
>
>> Johannes Hirte wrote:
>>
>>> Why is the btrfs code
>>> dealing with network devices at all?
>>>
>> Why not? :)
>>
>
> I don't see the possiblity to get a btrfs filesystem this way. So as far as I
> understand this, it's complete useless. The CD support doesn't look very
> usefull too to me. It's possible to put a btrfs filesystem on a CD or DVD. But
> that seems rather theoretical.
>
Ok, let's keep this theoretical possibility,
I don't see wrong things here..
Thanks,
Edward.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2009-12-11 11:26 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-24 22:06 [patch 2/2] grub-0.97: btrfs multidevice configuration support Edward Shishkin
2009-11-02 22:16 ` Johannes Hirte
2009-11-03 0:59 ` Edward Shishkin
2009-12-10 23:21 ` Johannes Hirte
2009-12-11 11:26 ` Edward Shishkin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.