2007-06-12 13:07:11 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 Oracle. All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public
|
|
|
|
* License v2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 021110-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2007-02-02 14:18:22 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2015-01-29 08:32:39 +00:00
|
|
|
#include <uuid/uuid.h>
|
2007-02-02 14:18:22 +00:00
|
|
|
#include "kerncompat.h"
|
|
|
|
#include "radix-tree.h"
|
|
|
|
#include "ctree.h"
|
|
|
|
#include "disk-io.h"
|
2008-03-24 19:03:18 +00:00
|
|
|
#include "volumes.h"
|
2007-03-16 20:20:31 +00:00
|
|
|
#include "transaction.h"
|
2007-06-28 20:20:29 +00:00
|
|
|
#include "crc32c.h"
|
2008-03-24 19:05:44 +00:00
|
|
|
#include "utils.h"
|
2008-04-04 19:42:17 +00:00
|
|
|
#include "print-tree.h"
|
2014-10-10 20:57:08 +00:00
|
|
|
#include "rbtree-utils.h"
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2015-01-29 08:32:39 +00:00
|
|
|
/* specified errno for check_tree_block */
|
|
|
|
#define BTRFS_BAD_BYTENR (-1)
|
|
|
|
#define BTRFS_BAD_FSID (-2)
|
2015-05-22 01:01:23 +00:00
|
|
|
#define BTRFS_BAD_LEVEL (-3)
|
|
|
|
#define BTRFS_BAD_NRITEMS (-4)
|
|
|
|
|
|
|
|
/* Calculate max possible nritems for a leaf/node */
|
|
|
|
static u32 max_nritems(u8 level, u32 nodesize)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (level == 0)
|
|
|
|
return ((nodesize - sizeof(struct btrfs_header)) /
|
|
|
|
sizeof(struct btrfs_item));
|
|
|
|
return ((nodesize - sizeof(struct btrfs_header)) /
|
|
|
|
sizeof(struct btrfs_key_ptr));
|
|
|
|
}
|
2015-01-29 08:32:39 +00:00
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2008-11-18 15:40:06 +00:00
|
|
|
struct btrfs_fs_devices *fs_devices;
|
2015-01-29 08:32:39 +00:00
|
|
|
int ret = BTRFS_BAD_FSID;
|
2008-11-18 15:40:06 +00:00
|
|
|
|
2015-01-29 08:32:39 +00:00
|
|
|
if (buf->start != btrfs_header_bytenr(buf))
|
|
|
|
return BTRFS_BAD_BYTENR;
|
2015-05-22 01:01:23 +00:00
|
|
|
if (btrfs_header_level(buf) >= BTRFS_MAX_LEVEL)
|
|
|
|
return BTRFS_BAD_LEVEL;
|
|
|
|
if (btrfs_header_nritems(buf) > max_nritems(btrfs_header_level(buf),
|
|
|
|
root->nodesize))
|
|
|
|
return BTRFS_BAD_NRITEMS;
|
2008-11-18 15:40:06 +00:00
|
|
|
|
|
|
|
fs_devices = root->fs_info->fs_devices;
|
|
|
|
while (fs_devices) {
|
2015-05-11 08:08:45 +00:00
|
|
|
if (root->fs_info->ignore_fsid_mismatch ||
|
|
|
|
!memcmp_extent_buffer(buf, fs_devices->fsid,
|
2013-10-01 09:59:22 +00:00
|
|
|
btrfs_header_fsid(),
|
2008-11-18 15:40:06 +00:00
|
|
|
BTRFS_FSID_SIZE)) {
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fs_devices = fs_devices->seed;
|
|
|
|
}
|
|
|
|
return ret;
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
|
|
|
|
2015-01-16 03:04:09 +00:00
|
|
|
static void print_tree_block_error(struct btrfs_root *root,
|
2015-01-29 08:32:39 +00:00
|
|
|
struct extent_buffer *eb,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = {'\0'};
|
|
|
|
char found_uuid[BTRFS_UUID_UNPARSED_SIZE] = {'\0'};
|
|
|
|
u8 buf[BTRFS_UUID_SIZE];
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
case BTRFS_BAD_FSID:
|
|
|
|
read_extent_buffer(eb, buf, btrfs_header_fsid(),
|
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
uuid_unparse(buf, found_uuid);
|
|
|
|
uuid_unparse(root->fs_info->fsid, fs_uuid);
|
|
|
|
fprintf(stderr, "fsid mismatch, want=%s, have=%s\n",
|
|
|
|
fs_uuid, found_uuid);
|
|
|
|
break;
|
|
|
|
case BTRFS_BAD_BYTENR:
|
|
|
|
fprintf(stderr, "bytenr mismatch, want=%llu, have=%llu\n",
|
|
|
|
eb->start, btrfs_header_bytenr(eb));
|
|
|
|
break;
|
2015-05-22 01:01:23 +00:00
|
|
|
case BTRFS_BAD_LEVEL:
|
|
|
|
fprintf(stderr, "bad level, %u > %u\n",
|
|
|
|
btrfs_header_level(eb), BTRFS_MAX_LEVEL);
|
|
|
|
break;
|
|
|
|
case BTRFS_BAD_NRITEMS:
|
|
|
|
fprintf(stderr, "invalid nr_items: %u\n",
|
|
|
|
btrfs_header_nritems(eb));
|
|
|
|
break;
|
2015-01-29 08:32:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
|
2007-03-01 23:59:40 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
return crc32c(seed, data, len);
|
2007-03-01 23:59:40 +00:00
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
void btrfs_csum_final(u32 crc, char *result)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
*(__le32 *)result = ~cpu_to_le32(crc);
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:17 +00:00
|
|
|
static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
|
|
|
|
int verify, int silent)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
2008-12-02 14:58:23 +00:00
|
|
|
char *result;
|
2008-01-04 15:38:22 +00:00
|
|
|
u32 len;
|
|
|
|
u32 crc = ~(u32)0;
|
2007-02-02 14:18:22 +00:00
|
|
|
|
2008-12-02 14:58:23 +00:00
|
|
|
result = malloc(csum_size * sizeof(char));
|
|
|
|
if (!result)
|
|
|
|
return 1;
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
len = buf->len - BTRFS_CSUM_SIZE;
|
|
|
|
crc = crc32c(crc, buf->data + BTRFS_CSUM_SIZE, len);
|
|
|
|
btrfs_csum_final(crc, result);
|
2007-10-15 20:25:41 +00:00
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
if (verify) {
|
2008-12-02 14:58:23 +00:00
|
|
|
if (memcmp_extent_buffer(buf, result, 0, csum_size)) {
|
2013-07-03 13:25:17 +00:00
|
|
|
if (!silent)
|
|
|
|
printk("checksum verify failed on %llu found %08X wanted %08X\n",
|
|
|
|
(unsigned long long)buf->start,
|
|
|
|
*((u32 *)result),
|
|
|
|
*((u32*)(char *)buf->data));
|
2008-12-02 14:58:23 +00:00
|
|
|
free(result);
|
2008-01-04 15:38:22 +00:00
|
|
|
return 1;
|
2007-02-23 13:38:36 +00:00
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
} else {
|
2008-12-02 14:58:23 +00:00
|
|
|
write_extent_buffer(buf, result, 0, csum_size);
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
2008-12-02 14:58:23 +00:00
|
|
|
free(result);
|
2008-01-04 15:38:22 +00:00
|
|
|
return 0;
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:17 +00:00
|
|
|
int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size, int verify)
|
|
|
|
{
|
|
|
|
return __csum_tree_block_size(buf, csum_size, verify, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int verify_tree_block_csum_silent(struct extent_buffer *buf, u16 csum_size)
|
|
|
|
{
|
|
|
|
return __csum_tree_block_size(buf, csum_size, 1, 1);
|
|
|
|
}
|
|
|
|
|
2013-09-09 20:40:55 +00:00
|
|
|
int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
|
2013-08-07 01:03:33 +00:00
|
|
|
int verify)
|
2008-12-02 14:58:23 +00:00
|
|
|
{
|
|
|
|
u16 csum_size =
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_super_csum_size(root->fs_info->super_copy);
|
2015-01-16 03:04:09 +00:00
|
|
|
if (verify && root->fs_info->suppress_check_block_errors)
|
|
|
|
return verify_tree_block_csum_silent(buf, csum_size);
|
2008-12-02 14:58:23 +00:00
|
|
|
return csum_tree_block_size(buf, csum_size, verify);
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
|
|
|
|
u64 bytenr, u32 blocksize)
|
2007-03-01 23:59:40 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
return find_extent_buffer(&root->fs_info->extent_cache,
|
|
|
|
bytenr, blocksize);
|
2007-03-01 23:59:40 +00:00
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
|
|
|
|
u64 bytenr, u32 blocksize)
|
2007-03-01 23:59:40 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
return alloc_extent_buffer(&root->fs_info->extent_cache, bytenr,
|
|
|
|
blocksize);
|
2007-03-01 23:59:40 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 18:16:03 +00:00
|
|
|
void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
|
|
|
|
u64 parent_transid)
|
2007-06-28 20:20:29 +00:00
|
|
|
{
|
2008-04-03 20:35:48 +00:00
|
|
|
struct extent_buffer *eb;
|
|
|
|
u64 length;
|
2008-04-09 20:28:12 +00:00
|
|
|
struct btrfs_multi_bio *multi = NULL;
|
2008-04-03 20:35:48 +00:00
|
|
|
struct btrfs_device *device;
|
|
|
|
|
|
|
|
eb = btrfs_find_tree_block(root, bytenr, blocksize);
|
2014-08-27 18:16:03 +00:00
|
|
|
if (!(eb && btrfs_buffer_uptodate(eb, parent_transid)) &&
|
|
|
|
!btrfs_map_block(&root->fs_info->mapping_tree, READ,
|
|
|
|
bytenr, &length, &multi, 0, NULL)) {
|
|
|
|
device = multi->stripes[0].dev;
|
|
|
|
device->total_ios++;
|
|
|
|
blocksize = min(blocksize, (u32)(64 * 1024));
|
|
|
|
readahead(device->fd, multi->stripes[0].physical, blocksize);
|
2008-04-03 20:35:48 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 18:16:03 +00:00
|
|
|
free_extent_buffer(eb);
|
2008-04-09 20:28:12 +00:00
|
|
|
kfree(multi);
|
2007-06-28 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
2008-05-13 17:48:58 +00:00
|
|
|
static int verify_parent_transid(struct extent_io_tree *io_tree,
|
2011-08-26 13:51:36 +00:00
|
|
|
struct extent_buffer *eb, u64 parent_transid,
|
|
|
|
int ignore)
|
2008-05-13 17:48:58 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!parent_transid || btrfs_header_generation(eb) == parent_transid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (extent_buffer_uptodate(eb) &&
|
|
|
|
btrfs_header_generation(eb) == parent_transid) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
printk("parent transid verify failed on %llu wanted %llu found %llu\n",
|
|
|
|
(unsigned long long)eb->start,
|
|
|
|
(unsigned long long)parent_transid,
|
|
|
|
(unsigned long long)btrfs_header_generation(eb));
|
2011-08-26 13:51:36 +00:00
|
|
|
if (ignore) {
|
2013-10-01 13:00:19 +00:00
|
|
|
eb->flags |= EXTENT_BAD_TRANSID;
|
2011-08-26 13:51:36 +00:00
|
|
|
printk("Ignoring transid failure\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-05-13 17:48:58 +00:00
|
|
|
ret = 1;
|
|
|
|
out:
|
|
|
|
clear_extent_buffer_uptodate(io_tree, eb);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Btrfs-progs: enhance btrfs-image to restore image onto multiple disks
This adds a 'btrfs-image -m' option, which let us restore an image that
is built from a btrfs of multiple disks onto several disks altogether.
This aims to address the following case,
$ mkfs.btrfs -m raid0 sda sdb
$ btrfs-image sda image.file
$ btrfs-image -r image.file sdc
---------
so we can only restore metadata onto sdc, and another thing is we can
only mount sdc with degraded mode as we don't provide informations of
another disk. And, it's built as RAID0 and we have only one disk,
so after mount sdc we'll get into readonly mode.
This is just annoying for people(like me) who're trying to restore image
but turn to find they cannot make it work.
So this'll make your life easier, just tap
$ btrfs-image -m image.file sdc sdd
---------
then you get everything about metadata done, the same offset with that of
the originals(of course, you need offer enough disk size, at least the disk
size of the original disks).
Besides, this also works with raid5 and raid6 metadata image.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
2013-06-22 05:32:45 +00:00
|
|
|
int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror)
|
2009-07-11 17:12:37 +00:00
|
|
|
{
|
|
|
|
unsigned long offset = 0;
|
|
|
|
struct btrfs_multi_bio *multi = NULL;
|
|
|
|
struct btrfs_device *device;
|
|
|
|
int ret = 0;
|
|
|
|
u64 read_len;
|
|
|
|
unsigned long bytes_left = eb->len;
|
|
|
|
|
|
|
|
while (bytes_left) {
|
|
|
|
read_len = bytes_left;
|
Btrfs-progs: enhance btrfs-image to restore image onto multiple disks
This adds a 'btrfs-image -m' option, which let us restore an image that
is built from a btrfs of multiple disks onto several disks altogether.
This aims to address the following case,
$ mkfs.btrfs -m raid0 sda sdb
$ btrfs-image sda image.file
$ btrfs-image -r image.file sdc
---------
so we can only restore metadata onto sdc, and another thing is we can
only mount sdc with degraded mode as we don't provide informations of
another disk. And, it's built as RAID0 and we have only one disk,
so after mount sdc we'll get into readonly mode.
This is just annoying for people(like me) who're trying to restore image
but turn to find they cannot make it work.
So this'll make your life easier, just tap
$ btrfs-image -m image.file sdc sdd
---------
then you get everything about metadata done, the same offset with that of
the originals(of course, you need offer enough disk size, at least the disk
size of the original disks).
Besides, this also works with raid5 and raid6 metadata image.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
2013-06-22 05:32:45 +00:00
|
|
|
device = NULL;
|
|
|
|
|
2014-09-26 13:02:49 +00:00
|
|
|
if (!info->on_restoring &&
|
|
|
|
eb->start != BTRFS_SUPER_INFO_OFFSET) {
|
Btrfs-progs: enhance btrfs-image to restore image onto multiple disks
This adds a 'btrfs-image -m' option, which let us restore an image that
is built from a btrfs of multiple disks onto several disks altogether.
This aims to address the following case,
$ mkfs.btrfs -m raid0 sda sdb
$ btrfs-image sda image.file
$ btrfs-image -r image.file sdc
---------
so we can only restore metadata onto sdc, and another thing is we can
only mount sdc with degraded mode as we don't provide informations of
another disk. And, it's built as RAID0 and we have only one disk,
so after mount sdc we'll get into readonly mode.
This is just annoying for people(like me) who're trying to restore image
but turn to find they cannot make it work.
So this'll make your life easier, just tap
$ btrfs-image -m image.file sdc sdd
---------
then you get everything about metadata done, the same offset with that of
the originals(of course, you need offer enough disk size, at least the disk
size of the original disks).
Besides, this also works with raid5 and raid6 metadata image.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
2013-06-22 05:32:45 +00:00
|
|
|
ret = btrfs_map_block(&info->mapping_tree, READ,
|
|
|
|
eb->start + offset, &read_len, &multi,
|
|
|
|
mirror, NULL);
|
|
|
|
if (ret) {
|
|
|
|
printk("Couldn't map the block %Lu\n", eb->start + offset);
|
|
|
|
kfree(multi);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
device = multi->stripes[0].dev;
|
2009-07-11 17:12:37 +00:00
|
|
|
|
2015-08-21 03:21:26 +00:00
|
|
|
if (device->fd <= 0) {
|
Btrfs-progs: enhance btrfs-image to restore image onto multiple disks
This adds a 'btrfs-image -m' option, which let us restore an image that
is built from a btrfs of multiple disks onto several disks altogether.
This aims to address the following case,
$ mkfs.btrfs -m raid0 sda sdb
$ btrfs-image sda image.file
$ btrfs-image -r image.file sdc
---------
so we can only restore metadata onto sdc, and another thing is we can
only mount sdc with degraded mode as we don't provide informations of
another disk. And, it's built as RAID0 and we have only one disk,
so after mount sdc we'll get into readonly mode.
This is just annoying for people(like me) who're trying to restore image
but turn to find they cannot make it work.
So this'll make your life easier, just tap
$ btrfs-image -m image.file sdc sdd
---------
then you get everything about metadata done, the same offset with that of
the originals(of course, you need offer enough disk size, at least the disk
size of the original disks).
Besides, this also works with raid5 and raid6 metadata image.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
2013-06-22 05:32:45 +00:00
|
|
|
kfree(multi);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
eb->fd = device->fd;
|
|
|
|
device->total_ios++;
|
|
|
|
eb->dev_bytenr = multi->stripes[0].physical;
|
2013-02-25 22:54:42 +00:00
|
|
|
kfree(multi);
|
Btrfs-progs: enhance btrfs-image to restore image onto multiple disks
This adds a 'btrfs-image -m' option, which let us restore an image that
is built from a btrfs of multiple disks onto several disks altogether.
This aims to address the following case,
$ mkfs.btrfs -m raid0 sda sdb
$ btrfs-image sda image.file
$ btrfs-image -r image.file sdc
---------
so we can only restore metadata onto sdc, and another thing is we can
only mount sdc with degraded mode as we don't provide informations of
another disk. And, it's built as RAID0 and we have only one disk,
so after mount sdc we'll get into readonly mode.
This is just annoying for people(like me) who're trying to restore image
but turn to find they cannot make it work.
So this'll make your life easier, just tap
$ btrfs-image -m image.file sdc sdd
---------
then you get everything about metadata done, the same offset with that of
the originals(of course, you need offer enough disk size, at least the disk
size of the original disks).
Besides, this also works with raid5 and raid6 metadata image.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
2013-06-22 05:32:45 +00:00
|
|
|
multi = NULL;
|
|
|
|
} else {
|
|
|
|
/* special case for restore metadump */
|
|
|
|
list_for_each_entry(device, &info->fs_devices->devices, dev_list) {
|
|
|
|
if (device->devid == 1)
|
|
|
|
break;
|
|
|
|
}
|
2009-07-11 17:12:37 +00:00
|
|
|
|
Btrfs-progs: enhance btrfs-image to restore image onto multiple disks
This adds a 'btrfs-image -m' option, which let us restore an image that
is built from a btrfs of multiple disks onto several disks altogether.
This aims to address the following case,
$ mkfs.btrfs -m raid0 sda sdb
$ btrfs-image sda image.file
$ btrfs-image -r image.file sdc
---------
so we can only restore metadata onto sdc, and another thing is we can
only mount sdc with degraded mode as we don't provide informations of
another disk. And, it's built as RAID0 and we have only one disk,
so after mount sdc we'll get into readonly mode.
This is just annoying for people(like me) who're trying to restore image
but turn to find they cannot make it work.
So this'll make your life easier, just tap
$ btrfs-image -m image.file sdc sdd
---------
then you get everything about metadata done, the same offset with that of
the originals(of course, you need offer enough disk size, at least the disk
size of the original disks).
Besides, this also works with raid5 and raid6 metadata image.
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
2013-06-22 05:32:45 +00:00
|
|
|
eb->fd = device->fd;
|
|
|
|
eb->dev_bytenr = eb->start;
|
|
|
|
device->total_ios++;
|
|
|
|
}
|
2009-07-11 17:12:37 +00:00
|
|
|
|
|
|
|
if (read_len > bytes_left)
|
|
|
|
read_len = bytes_left;
|
|
|
|
|
|
|
|
ret = read_extent_from_disk(eb, offset, read_len);
|
|
|
|
if (ret)
|
|
|
|
return -EIO;
|
|
|
|
offset += read_len;
|
|
|
|
bytes_left -= read_len;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
|
2008-05-12 17:51:24 +00:00
|
|
|
u32 blocksize, u64 parent_transid)
|
2007-06-28 20:20:29 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
int ret;
|
|
|
|
struct extent_buffer *eb;
|
2011-08-26 13:51:36 +00:00
|
|
|
u64 best_transid = 0;
|
2008-04-09 20:28:12 +00:00
|
|
|
int mirror_num = 0;
|
2011-08-26 13:51:36 +00:00
|
|
|
int good_mirror = 0;
|
2008-04-09 20:28:12 +00:00
|
|
|
int num_copies;
|
2011-08-26 13:51:36 +00:00
|
|
|
int ignore = 0;
|
2007-06-28 20:20:29 +00:00
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
|
|
|
|
if (!eb)
|
2015-01-28 02:12:55 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-04-03 20:35:48 +00:00
|
|
|
|
2008-05-13 17:48:58 +00:00
|
|
|
if (btrfs_buffer_uptodate(eb, parent_transid))
|
2008-04-03 20:35:48 +00:00
|
|
|
return eb;
|
|
|
|
|
2008-04-09 20:28:12 +00:00
|
|
|
while (1) {
|
2009-07-11 17:12:37 +00:00
|
|
|
ret = read_whole_eb(root->fs_info, eb, mirror_num);
|
2015-01-16 03:04:09 +00:00
|
|
|
if (ret == 0 && csum_tree_block(root, eb, 1) == 0 &&
|
|
|
|
check_tree_block(root, eb) == 0 &&
|
2011-08-26 13:51:36 +00:00
|
|
|
verify_parent_transid(eb->tree, eb, parent_transid, ignore)
|
|
|
|
== 0) {
|
2013-10-01 13:00:19 +00:00
|
|
|
if (eb->flags & EXTENT_BAD_TRANSID &&
|
|
|
|
list_empty(&eb->recow)) {
|
|
|
|
list_add_tail(&eb->recow,
|
|
|
|
&root->fs_info->recow_ebs);
|
|
|
|
eb->refs++;
|
|
|
|
}
|
2008-04-09 20:28:12 +00:00
|
|
|
btrfs_set_buffer_uptodate(eb);
|
|
|
|
return eb;
|
|
|
|
}
|
2011-08-26 13:51:36 +00:00
|
|
|
if (ignore) {
|
2015-01-16 03:04:09 +00:00
|
|
|
if (check_tree_block(root, eb)) {
|
|
|
|
if (!root->fs_info->suppress_check_block_errors)
|
|
|
|
print_tree_block_error(root, eb,
|
2015-01-29 08:32:39 +00:00
|
|
|
check_tree_block(root, eb));
|
2015-01-16 03:04:09 +00:00
|
|
|
} else {
|
|
|
|
if (!root->fs_info->suppress_check_block_errors)
|
|
|
|
fprintf(stderr, "Csum didn't match\n");
|
|
|
|
}
|
2015-01-28 02:12:55 +00:00
|
|
|
ret = -EIO;
|
2011-08-26 13:51:36 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-04-09 20:28:12 +00:00
|
|
|
num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
|
|
|
|
eb->start, eb->len);
|
|
|
|
if (num_copies == 1) {
|
2011-08-26 13:51:36 +00:00
|
|
|
ignore = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2014-02-27 15:25:49 +00:00
|
|
|
if (btrfs_header_generation(eb) > best_transid && mirror_num) {
|
2011-08-26 13:51:36 +00:00
|
|
|
best_transid = btrfs_header_generation(eb);
|
|
|
|
good_mirror = mirror_num;
|
2008-04-09 20:28:12 +00:00
|
|
|
}
|
|
|
|
mirror_num++;
|
|
|
|
if (mirror_num > num_copies) {
|
2011-08-26 13:51:36 +00:00
|
|
|
mirror_num = good_mirror;
|
|
|
|
ignore = 1;
|
|
|
|
continue;
|
2008-04-09 20:28:12 +00:00
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
}
|
2008-04-09 20:28:12 +00:00
|
|
|
free_extent_buffer(eb);
|
2015-01-28 02:12:55 +00:00
|
|
|
return ERR_PTR(ret);
|
2007-06-28 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
2015-06-17 07:49:00 +00:00
|
|
|
int read_extent_data(struct btrfs_root *root, char *data,
|
|
|
|
u64 logical, u64 *len, int mirror)
|
|
|
|
{
|
|
|
|
u64 offset = 0;
|
|
|
|
struct btrfs_multi_bio *multi = NULL;
|
|
|
|
struct btrfs_fs_info *info = root->fs_info;
|
|
|
|
struct btrfs_device *device;
|
|
|
|
int ret = 0;
|
|
|
|
u64 max_len = *len;
|
|
|
|
|
|
|
|
ret = btrfs_map_block(&info->mapping_tree, READ, logical, len,
|
|
|
|
&multi, mirror, NULL);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "Couldn't map the block %llu\n",
|
|
|
|
logical + offset);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
device = multi->stripes[0].dev;
|
|
|
|
|
2015-08-21 03:21:26 +00:00
|
|
|
if (device->fd <= 0)
|
2015-06-17 07:49:00 +00:00
|
|
|
goto err;
|
|
|
|
if (*len > max_len)
|
|
|
|
*len = max_len;
|
|
|
|
|
|
|
|
ret = pread64(device->fd, data, *len, multi->stripes[0].physical);
|
|
|
|
if (ret != *len)
|
|
|
|
ret = -EIO;
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
kfree(multi);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-16 14:36:55 +00:00
|
|
|
int write_and_map_eb(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root,
|
|
|
|
struct extent_buffer *eb)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
2008-04-03 20:35:48 +00:00
|
|
|
int ret;
|
|
|
|
int dev_nr;
|
|
|
|
u64 length;
|
2009-07-11 17:12:37 +00:00
|
|
|
u64 *raid_map = NULL;
|
2008-04-09 20:28:12 +00:00
|
|
|
struct btrfs_multi_bio *multi = NULL;
|
2008-04-03 20:35:48 +00:00
|
|
|
|
|
|
|
dev_nr = 0;
|
2008-04-09 20:28:12 +00:00
|
|
|
length = eb->len;
|
|
|
|
ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE,
|
2009-07-11 17:12:37 +00:00
|
|
|
eb->start, &length, &multi, 0, &raid_map);
|
2008-04-10 20:22:00 +00:00
|
|
|
|
2009-07-11 17:12:37 +00:00
|
|
|
if (raid_map) {
|
|
|
|
ret = write_raid56_with_parity(root->fs_info, eb, multi,
|
|
|
|
length, raid_map);
|
|
|
|
BUG_ON(ret);
|
|
|
|
} else while (dev_nr < multi->num_stripes) {
|
2008-04-03 20:35:48 +00:00
|
|
|
BUG_ON(ret);
|
2008-04-09 20:28:12 +00:00
|
|
|
eb->fd = multi->stripes[dev_nr].dev->fd;
|
|
|
|
eb->dev_bytenr = multi->stripes[dev_nr].physical;
|
|
|
|
multi->stripes[dev_nr].dev->total_ios++;
|
2008-04-03 20:35:48 +00:00
|
|
|
dev_nr++;
|
|
|
|
ret = write_extent_to_disk(eb);
|
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
2015-08-25 06:11:07 +00:00
|
|
|
kfree(raid_map);
|
2008-04-09 20:28:12 +00:00
|
|
|
kfree(multi);
|
2008-04-03 20:35:48 +00:00
|
|
|
return 0;
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
|
|
|
|
2015-05-11 08:08:46 +00:00
|
|
|
int write_tree_block(struct btrfs_trans_handle *trans,
|
2013-10-16 14:36:55 +00:00
|
|
|
struct btrfs_root *root,
|
|
|
|
struct extent_buffer *eb)
|
|
|
|
{
|
2015-01-29 08:32:39 +00:00
|
|
|
if (check_tree_block(root, eb)) {
|
2015-01-16 03:04:09 +00:00
|
|
|
print_tree_block_error(root, eb, check_tree_block(root, eb));
|
2013-10-16 14:36:55 +00:00
|
|
|
BUG();
|
2015-01-29 08:32:39 +00:00
|
|
|
}
|
2013-10-16 14:36:55 +00:00
|
|
|
|
2015-05-11 08:08:46 +00:00
|
|
|
if (trans && !btrfs_buffer_uptodate(eb, trans->transid))
|
2013-10-16 14:36:55 +00:00
|
|
|
BUG();
|
|
|
|
|
|
|
|
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
|
|
|
|
csum_tree_block(root, eb, 0);
|
|
|
|
|
|
|
|
return write_and_map_eb(trans, root, eb);
|
|
|
|
}
|
|
|
|
|
2013-01-25 00:18:57 +00:00
|
|
|
int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
|
2008-01-04 15:38:22 +00:00
|
|
|
u32 stripesize, struct btrfs_root *root,
|
|
|
|
struct btrfs_fs_info *fs_info, u64 objectid)
|
2007-03-01 23:59:40 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
root->node = NULL;
|
|
|
|
root->commit_root = NULL;
|
|
|
|
root->sectorsize = sectorsize;
|
|
|
|
root->nodesize = nodesize;
|
|
|
|
root->leafsize = leafsize;
|
|
|
|
root->stripesize = stripesize;
|
|
|
|
root->ref_cows = 0;
|
2008-03-24 19:03:18 +00:00
|
|
|
root->track_dirty = 0;
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
root->fs_info = fs_info;
|
|
|
|
root->objectid = objectid;
|
|
|
|
root->last_trans = 0;
|
|
|
|
root->highest_inode = 0;
|
|
|
|
root->last_inode_alloc = 0;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&root->dirty_list);
|
2015-01-02 07:12:29 +00:00
|
|
|
INIT_LIST_HEAD(&root->orphan_data_extents);
|
2008-01-04 15:38:22 +00:00
|
|
|
memset(&root->root_key, 0, sizeof(root->root_key));
|
|
|
|
memset(&root->root_item, 0, sizeof(root->root_item));
|
|
|
|
root->root_key.objectid = objectid;
|
|
|
|
return 0;
|
2007-03-01 23:59:40 +00:00
|
|
|
}
|
|
|
|
|
2008-03-24 19:03:18 +00:00
|
|
|
static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root)
|
2007-03-13 20:47:54 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2008-03-24 19:03:18 +00:00
|
|
|
u64 old_root_bytenr;
|
|
|
|
struct btrfs_root *tree_root = root->fs_info->tree_root;
|
2007-03-20 18:38:32 +00:00
|
|
|
|
2008-03-24 19:03:18 +00:00
|
|
|
btrfs_write_dirty_block_groups(trans, root);
|
2007-03-13 20:47:54 +00:00
|
|
|
while(1) {
|
2008-03-24 19:03:18 +00:00
|
|
|
old_root_bytenr = btrfs_root_bytenr(&root->root_item);
|
|
|
|
if (old_root_bytenr == root->node->start)
|
2007-03-13 20:47:54 +00:00
|
|
|
break;
|
2008-03-24 19:03:18 +00:00
|
|
|
btrfs_set_root_bytenr(&root->root_item,
|
|
|
|
root->node->start);
|
2008-10-29 18:07:47 +00:00
|
|
|
btrfs_set_root_generation(&root->root_item,
|
|
|
|
trans->transid);
|
2008-03-24 19:03:18 +00:00
|
|
|
root->root_item.level = btrfs_header_level(root->node);
|
2007-03-16 20:20:31 +00:00
|
|
|
ret = btrfs_update_root(trans, tree_root,
|
2008-03-24 19:03:18 +00:00
|
|
|
&root->root_key,
|
|
|
|
&root->root_item);
|
2007-03-13 20:47:54 +00:00
|
|
|
BUG_ON(ret);
|
2008-03-24 19:03:18 +00:00
|
|
|
btrfs_write_dirty_block_groups(trans, root);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int commit_tree_roots(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
struct list_head *next;
|
2008-10-29 18:07:47 +00:00
|
|
|
struct extent_buffer *eb;
|
2013-01-17 20:06:29 +00:00
|
|
|
int ret;
|
2008-10-30 15:11:39 +00:00
|
|
|
|
|
|
|
if (fs_info->readonly)
|
|
|
|
return 0;
|
2008-10-29 18:07:47 +00:00
|
|
|
|
|
|
|
eb = fs_info->tree_root->node;
|
|
|
|
extent_buffer_get(eb);
|
2013-01-17 20:06:29 +00:00
|
|
|
ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb);
|
2008-10-29 18:07:47 +00:00
|
|
|
free_extent_buffer(eb);
|
2013-01-17 20:06:29 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
|
|
|
while(!list_empty(&fs_info->dirty_cowonly_roots)) {
|
|
|
|
next = fs_info->dirty_cowonly_roots.next;
|
|
|
|
list_del_init(next);
|
|
|
|
root = list_entry(next, struct btrfs_root, dirty_list);
|
|
|
|
update_cowonly_root(trans, root);
|
2013-05-14 18:44:22 +00:00
|
|
|
free_extent_buffer(root->commit_root);
|
|
|
|
root->commit_root = NULL;
|
2007-03-13 20:47:54 +00:00
|
|
|
}
|
2013-05-14 18:44:22 +00:00
|
|
|
|
2007-03-13 20:47:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
static int __commit_transaction(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root)
|
|
|
|
{
|
|
|
|
u64 start;
|
|
|
|
u64 end;
|
|
|
|
struct extent_buffer *eb;
|
2008-03-04 16:16:54 +00:00
|
|
|
struct extent_io_tree *tree = &root->fs_info->extent_cache;
|
2008-01-04 15:38:22 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
ret = find_first_extent_bit(tree, 0, &start, &end,
|
|
|
|
EXTENT_DIRTY);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
while(start <= end) {
|
|
|
|
eb = find_first_extent_buffer(tree, start);
|
|
|
|
BUG_ON(!eb || eb->start != start);
|
|
|
|
ret = write_tree_block(trans, root, eb);
|
|
|
|
BUG_ON(ret);
|
|
|
|
start += eb->len;
|
|
|
|
clear_extent_buffer_dirty(eb);
|
|
|
|
free_extent_buffer(eb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root)
|
2007-03-01 23:59:40 +00:00
|
|
|
{
|
2011-08-26 13:51:36 +00:00
|
|
|
u64 transid = trans->transid;
|
2007-03-07 01:08:01 +00:00
|
|
|
int ret = 0;
|
2008-01-04 15:38:22 +00:00
|
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
2007-03-07 01:08:01 +00:00
|
|
|
|
2007-03-13 20:47:54 +00:00
|
|
|
if (root->commit_root == root->node)
|
2008-01-04 15:38:22 +00:00
|
|
|
goto commit_tree;
|
Btrfs-progs: check, ability to detect and fix outdated snapshot root items
This change adds code to detect and fix the issue introduced in the kernel
release 3.17, where creation of read-only snapshots lead to a corrupted
filesystem if they were created at a moment when the source subvolume/snapshot
had orphan items. The issue was that the on-disk root items became incorrect,
referring to the pre orphan cleanup root node instead of the post orphan
cleanup root node.
A test filesystem can be generated with the test case recently submitted for
xfstests/fstests, which is essencially the following (bash script):
workout()
{
ops=$1
procs=$2
num_snapshots=$3
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap_\`date +'%H_%M_%S_%N'\`"
run_check $FSSTRESS_PROG -p $procs \
-x "$snapshot_cmd" -X $num_snapshots -d $SCRATCH_MNT -n $ops
}
ops=10000
procs=4
snapshots=500
workout $ops $procs $snapshots
Example of btrfsck's (btrfs check) behaviour against such filesystem:
$ btrfsck /dev/loop0
root item for root 311, current bytenr 44630016, current gen 60, current level 1, new bytenr 44957696, new gen 61, new level 1
root item for root 1480, current bytenr 1003569152, current gen 1271, current level 1, new bytenr 1004175360, new gen 1272, new level 1
root item for root 1509, current bytenr 1037434880, current gen 1300, current level 1, new bytenr 1038467072, new gen 1301, new level 1
root item for root 1562, current bytenr 33636352, current gen 1354, current level 1, new bytenr 34455552, new gen 1355, new level 1
root item for root 3094, current bytenr 1011712000, current gen 2935, current level 1, new bytenr 1008484352, new gen 2936, new level 1
root item for root 3716, current bytenr 80805888, current gen 3578, current level 1, new bytenr 73515008, new gen 3579, new level 1
root item for root 4085, current bytenr 714031104, current gen 3958, current level 1, new bytenr 716816384, new gen 3959, new level 1
Found 7 roots with an outdated root item.
Please run a filesystem check with the option --repair to fix them.
$ echo $?
1
$ btrfsck --repair /dev/loop0
enabling repair mode
fixing root item for root 311, current bytenr 44630016, current gen 60, current level 1, new bytenr 44957696, new gen 61, new level 1
fixing root item for root 1480, current bytenr 1003569152, current gen 1271, current level 1, new bytenr 1004175360, new gen 1272, new level 1
fixing root item for root 1509, current bytenr 1037434880, current gen 1300, current level 1, new bytenr 1038467072, new gen 1301, new level 1
fixing root item for root 1562, current bytenr 33636352, current gen 1354, current level 1, new bytenr 34455552, new gen 1355, new level 1
fixing root item for root 3094, current bytenr 1011712000, current gen 2935, current level 1, new bytenr 1008484352, new gen 2936, new level 1
fixing root item for root 3716, current bytenr 80805888, current gen 3578, current level 1, new bytenr 73515008, new gen 3579, new level 1
fixing root item for root 4085, current bytenr 714031104, current gen 3958, current level 1, new bytenr 716816384, new gen 3959, new level 1
Fixed 7 roots.
Checking filesystem on /dev/loop0
UUID: 2186e9b9-c977-4a35-9c7b-69c6609d4620
checking extents
checking free space cache
cache and super generation don't match, space cache will be invalidated
checking fs roots
checking csums
checking root refs
found 618537000 bytes used err is 0
total csum bytes: 130824
total tree bytes: 601620480
total fs tree bytes: 580288512
total extent tree bytes: 18464768
btree space waste bytes: 136939144
file data blocks allocated: 34150318080
referenced 27815415808
Btrfs v3.17-rc3-2-gbbe1dd8
$ echo $?
0
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
2014-10-17 17:20:08 +00:00
|
|
|
if (root == root->fs_info->tree_root)
|
|
|
|
goto commit_tree;
|
2015-07-07 08:15:22 +00:00
|
|
|
if (root == root->fs_info->chunk_root)
|
|
|
|
goto commit_tree;
|
2007-03-13 20:47:54 +00:00
|
|
|
|
2009-05-29 20:35:30 +00:00
|
|
|
free_extent_buffer(root->commit_root);
|
2008-01-04 15:38:22 +00:00
|
|
|
root->commit_root = NULL;
|
2007-03-13 20:47:54 +00:00
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_set_root_bytenr(&root->root_item, root->node->start);
|
2009-05-29 20:35:30 +00:00
|
|
|
btrfs_set_root_generation(&root->root_item, trans->transid);
|
2008-01-04 15:38:22 +00:00
|
|
|
root->root_item.level = btrfs_header_level(root->node);
|
|
|
|
ret = btrfs_update_root(trans, root->fs_info->tree_root,
|
2009-05-29 20:35:30 +00:00
|
|
|
&root->root_key, &root->root_item);
|
2008-01-04 15:38:22 +00:00
|
|
|
BUG_ON(ret);
|
|
|
|
commit_tree:
|
|
|
|
ret = commit_tree_roots(trans, fs_info);
|
2007-03-13 20:47:54 +00:00
|
|
|
BUG_ON(ret);
|
2008-01-04 15:38:22 +00:00
|
|
|
ret = __commit_transaction(trans, root);
|
2007-03-13 20:47:54 +00:00
|
|
|
BUG_ON(ret);
|
2008-01-04 15:38:22 +00:00
|
|
|
write_ctree_super(trans, root);
|
|
|
|
btrfs_finish_extent_commit(trans, fs_info->extent_root,
|
|
|
|
&fs_info->pinned_extents);
|
2007-12-05 15:41:38 +00:00
|
|
|
btrfs_free_transaction(root, trans);
|
2008-01-04 15:38:22 +00:00
|
|
|
free_extent_buffer(root->commit_root);
|
2007-03-07 01:08:01 +00:00
|
|
|
root->commit_root = NULL;
|
2008-01-04 15:38:22 +00:00
|
|
|
fs_info->running_transaction = NULL;
|
2011-08-26 13:51:36 +00:00
|
|
|
fs_info->last_trans_committed = transid;
|
2007-03-13 20:47:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
static int find_and_setup_root(struct btrfs_root *tree_root,
|
2007-03-20 18:38:32 +00:00
|
|
|
struct btrfs_fs_info *fs_info,
|
2008-01-04 15:38:22 +00:00
|
|
|
u64 objectid, struct btrfs_root *root)
|
2007-03-13 20:47:54 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2008-01-04 15:38:22 +00:00
|
|
|
u32 blocksize;
|
2008-10-29 18:07:47 +00:00
|
|
|
u64 generation;
|
2007-03-13 20:47:54 +00:00
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
__setup_root(tree_root->nodesize, tree_root->leafsize,
|
|
|
|
tree_root->sectorsize, tree_root->stripesize,
|
|
|
|
root, fs_info, objectid);
|
2007-03-13 20:47:54 +00:00
|
|
|
ret = btrfs_find_last_root(tree_root, objectid,
|
|
|
|
&root->root_item, &root->root_key);
|
2013-02-01 20:21:04 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2008-01-04 15:38:22 +00:00
|
|
|
|
|
|
|
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
|
2008-10-29 18:07:47 +00:00
|
|
|
generation = btrfs_root_generation(&root->root_item);
|
2008-01-04 15:38:22 +00:00
|
|
|
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
|
2008-10-29 18:07:47 +00:00
|
|
|
blocksize, generation);
|
2012-02-05 21:11:48 +00:00
|
|
|
if (!extent_buffer_uptodate(root->node))
|
|
|
|
return -EIO;
|
|
|
|
|
2007-02-20 21:40:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-15 18:30:14 +00:00
|
|
|
static int find_and_setup_log_root(struct btrfs_root *tree_root,
|
|
|
|
struct btrfs_fs_info *fs_info,
|
|
|
|
struct btrfs_super_block *disk_super)
|
|
|
|
{
|
|
|
|
u32 blocksize;
|
|
|
|
u64 blocknr = btrfs_super_log_root(disk_super);
|
|
|
|
struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root));
|
|
|
|
|
2012-11-15 04:47:51 +00:00
|
|
|
if (!log_root)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-11-15 04:47:50 +00:00
|
|
|
if (blocknr == 0) {
|
|
|
|
free(log_root);
|
2009-04-15 18:30:14 +00:00
|
|
|
return 0;
|
2012-11-15 04:47:50 +00:00
|
|
|
}
|
2009-04-15 18:30:14 +00:00
|
|
|
|
|
|
|
blocksize = btrfs_level_size(tree_root,
|
|
|
|
btrfs_super_log_root_level(disk_super));
|
|
|
|
|
|
|
|
__setup_root(tree_root->nodesize, tree_root->leafsize,
|
|
|
|
tree_root->sectorsize, tree_root->stripesize,
|
|
|
|
log_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
|
|
|
|
|
|
|
|
log_root->node = read_tree_block(tree_root, blocknr,
|
|
|
|
blocksize,
|
|
|
|
btrfs_super_generation(disk_super) + 1);
|
|
|
|
|
|
|
|
fs_info->log_root_tree = log_root;
|
2012-02-05 21:11:48 +00:00
|
|
|
|
2012-11-15 04:47:50 +00:00
|
|
|
if (!extent_buffer_uptodate(log_root->node)) {
|
2013-03-15 21:13:08 +00:00
|
|
|
free_extent_buffer(log_root->node);
|
2012-11-15 04:47:50 +00:00
|
|
|
free(log_root);
|
2013-03-15 21:13:08 +00:00
|
|
|
fs_info->log_root_tree = NULL;
|
2012-02-05 21:11:48 +00:00
|
|
|
return -EIO;
|
2012-11-15 04:47:50 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 18:30:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:13 +00:00
|
|
|
int btrfs_free_fs_root(struct btrfs_root *root)
|
2008-01-04 15:38:22 +00:00
|
|
|
{
|
|
|
|
if (root->node)
|
|
|
|
free_extent_buffer(root->node);
|
|
|
|
if (root->commit_root)
|
|
|
|
free_extent_buffer(root->commit_root);
|
2009-05-29 20:35:30 +00:00
|
|
|
kfree(root);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
static void __free_fs_root(struct rb_node *node)
|
2009-05-29 20:35:30 +00:00
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
root = container_of(node, struct btrfs_root, rb_node);
|
2013-07-03 13:25:13 +00:00
|
|
|
btrfs_free_fs_root(root);
|
2008-01-04 15:38:22 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
FREE_RB_BASED_TREE(fs_roots, __free_fs_root);
|
2013-07-03 13:25:13 +00:00
|
|
|
|
2009-05-29 20:35:30 +00:00
|
|
|
struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
|
|
|
|
struct btrfs_key *location)
|
2008-01-04 15:38:22 +00:00
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct extent_buffer *l;
|
2008-10-29 18:07:47 +00:00
|
|
|
u64 generation;
|
2008-01-04 15:38:22 +00:00
|
|
|
u32 blocksize;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
root = malloc(sizeof(*root));
|
|
|
|
if (!root)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
memset(root, 0, sizeof(*root));
|
|
|
|
if (location->offset == (u64)-1) {
|
|
|
|
ret = find_and_setup_root(tree_root, fs_info,
|
|
|
|
location->objectid, root);
|
|
|
|
if (ret) {
|
|
|
|
free(root);
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
|
|
|
goto insert;
|
|
|
|
}
|
|
|
|
|
|
|
|
__setup_root(tree_root->nodesize, tree_root->leafsize,
|
|
|
|
tree_root->sectorsize, tree_root->stripesize,
|
|
|
|
root, fs_info, location->objectid);
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
|
|
|
ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
|
|
|
|
if (ret != 0) {
|
|
|
|
if (ret > 0)
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
l = path->nodes[0];
|
|
|
|
read_extent_buffer(l, &root->root_item,
|
|
|
|
btrfs_item_ptr_offset(l, path->slots[0]),
|
|
|
|
sizeof(root->root_item));
|
|
|
|
memcpy(&root->root_key, location, sizeof(*location));
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
if (ret) {
|
|
|
|
free(root);
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
2008-10-29 18:07:47 +00:00
|
|
|
generation = btrfs_root_generation(&root->root_item);
|
2008-01-04 15:38:22 +00:00
|
|
|
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
|
|
|
|
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
|
2008-10-29 18:07:47 +00:00
|
|
|
blocksize, generation);
|
2015-01-28 02:12:55 +00:00
|
|
|
if (!extent_buffer_uptodate(root->node)) {
|
2014-01-10 14:50:02 +00:00
|
|
|
free(root);
|
|
|
|
return ERR_PTR(-EIO);
|
|
|
|
}
|
2008-01-04 15:38:22 +00:00
|
|
|
insert:
|
|
|
|
root->ref_cows = 1;
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
static int btrfs_fs_roots_compare_objectids(struct rb_node *node,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
u64 objectid = *((u64 *)data);
|
|
|
|
struct btrfs_root *root;
|
|
|
|
|
|
|
|
root = rb_entry(node, struct btrfs_root, rb_node);
|
|
|
|
if (objectid > root->objectid)
|
|
|
|
return 1;
|
|
|
|
else if (objectid < root->objectid)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_fs_roots_compare_roots(struct rb_node *node1,
|
|
|
|
struct rb_node *node2)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
|
|
|
|
root = rb_entry(node2, struct btrfs_root, rb_node);
|
|
|
|
return btrfs_fs_roots_compare_objectids(node1, (void *)&root->objectid);
|
|
|
|
}
|
|
|
|
|
2009-05-29 20:35:30 +00:00
|
|
|
struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
|
|
|
|
struct btrfs_key *location)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
2013-07-03 13:25:14 +00:00
|
|
|
struct rb_node *node;
|
2009-05-29 20:35:30 +00:00
|
|
|
int ret;
|
2014-01-17 14:00:43 +00:00
|
|
|
u64 objectid = location->objectid;
|
2009-05-29 20:35:30 +00:00
|
|
|
|
|
|
|
if (location->objectid == BTRFS_ROOT_TREE_OBJECTID)
|
|
|
|
return fs_info->tree_root;
|
|
|
|
if (location->objectid == BTRFS_EXTENT_TREE_OBJECTID)
|
|
|
|
return fs_info->extent_root;
|
|
|
|
if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID)
|
|
|
|
return fs_info->chunk_root;
|
|
|
|
if (location->objectid == BTRFS_DEV_TREE_OBJECTID)
|
|
|
|
return fs_info->dev_root;
|
|
|
|
if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
|
|
|
|
return fs_info->csum_root;
|
2014-05-07 20:07:17 +00:00
|
|
|
if (location->objectid == BTRFS_QUOTA_TREE_OBJECTID)
|
2015-01-07 09:23:43 +00:00
|
|
|
return fs_info->quota_root;
|
2012-02-05 21:11:48 +00:00
|
|
|
|
2009-05-29 20:35:30 +00:00
|
|
|
BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
|
|
|
|
location->offset != (u64)-1);
|
|
|
|
|
2014-01-17 14:00:43 +00:00
|
|
|
node = rb_search(&fs_info->fs_root_tree, (void *)&objectid,
|
2013-07-03 13:25:14 +00:00
|
|
|
btrfs_fs_roots_compare_objectids, NULL);
|
|
|
|
if (node)
|
|
|
|
return container_of(node, struct btrfs_root, rb_node);
|
2009-05-29 20:35:30 +00:00
|
|
|
|
|
|
|
root = btrfs_read_fs_root_no_cache(fs_info, location);
|
|
|
|
if (IS_ERR(root))
|
|
|
|
return root;
|
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
ret = rb_insert(&fs_info->fs_root_tree, &root->rb_node,
|
|
|
|
btrfs_fs_roots_compare_roots);
|
2009-05-29 20:35:30 +00:00
|
|
|
BUG_ON(ret);
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
2013-07-03 13:25:12 +00:00
|
|
|
free(fs_info->tree_root);
|
|
|
|
free(fs_info->extent_root);
|
|
|
|
free(fs_info->chunk_root);
|
|
|
|
free(fs_info->dev_root);
|
|
|
|
free(fs_info->csum_root);
|
2014-05-07 20:07:17 +00:00
|
|
|
free(fs_info->quota_root);
|
2013-07-03 13:25:12 +00:00
|
|
|
free(fs_info->super_copy);
|
|
|
|
free(fs_info->log_root_tree);
|
|
|
|
free(fs_info);
|
|
|
|
}
|
2007-02-02 14:18:22 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
|
|
|
|
{
|
|
|
|
struct btrfs_fs_info *fs_info;
|
2013-02-01 15:44:22 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info = malloc(sizeof(struct btrfs_fs_info));
|
|
|
|
if (!fs_info)
|
|
|
|
return NULL;
|
2008-03-24 19:05:44 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
memset(fs_info, 0, sizeof(struct btrfs_fs_info));
|
2008-03-24 19:05:44 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info->tree_root = malloc(sizeof(struct btrfs_root));
|
|
|
|
fs_info->extent_root = malloc(sizeof(struct btrfs_root));
|
|
|
|
fs_info->chunk_root = malloc(sizeof(struct btrfs_root));
|
|
|
|
fs_info->dev_root = malloc(sizeof(struct btrfs_root));
|
|
|
|
fs_info->csum_root = malloc(sizeof(struct btrfs_root));
|
2014-05-07 20:07:17 +00:00
|
|
|
fs_info->quota_root = malloc(sizeof(struct btrfs_root));
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info->super_copy = malloc(BTRFS_SUPER_INFO_SIZE);
|
2008-03-24 19:05:44 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
if (!fs_info->tree_root || !fs_info->extent_root ||
|
|
|
|
!fs_info->chunk_root || !fs_info->dev_root ||
|
2014-05-07 20:07:17 +00:00
|
|
|
!fs_info->csum_root || !fs_info->quota_root ||
|
|
|
|
!fs_info->super_copy)
|
2013-07-03 13:25:12 +00:00
|
|
|
goto free_all;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
memset(fs_info->super_copy, 0, BTRFS_SUPER_INFO_SIZE);
|
|
|
|
memset(fs_info->tree_root, 0, sizeof(struct btrfs_root));
|
|
|
|
memset(fs_info->extent_root, 0, sizeof(struct btrfs_root));
|
|
|
|
memset(fs_info->chunk_root, 0, sizeof(struct btrfs_root));
|
|
|
|
memset(fs_info->dev_root, 0, sizeof(struct btrfs_root));
|
|
|
|
memset(fs_info->csum_root, 0, sizeof(struct btrfs_root));
|
2014-05-07 20:07:17 +00:00
|
|
|
memset(fs_info->quota_root, 0, sizeof(struct btrfs_root));
|
2008-05-05 13:45:26 +00:00
|
|
|
|
2008-03-04 16:16:54 +00:00
|
|
|
extent_io_tree_init(&fs_info->extent_cache);
|
|
|
|
extent_io_tree_init(&fs_info->free_space_cache);
|
|
|
|
extent_io_tree_init(&fs_info->block_group_cache);
|
|
|
|
extent_io_tree_init(&fs_info->pinned_extents);
|
|
|
|
extent_io_tree_init(&fs_info->pending_del);
|
|
|
|
extent_io_tree_init(&fs_info->extent_ins);
|
2015-02-09 15:02:25 +00:00
|
|
|
fs_info->excluded_extents = NULL;
|
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
fs_info->fs_root_tree = RB_ROOT;
|
2008-03-24 19:03:18 +00:00
|
|
|
cache_tree_init(&fs_info->mapping_tree.cache_tree);
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
mutex_init(&fs_info->fs_mutex);
|
2008-03-24 19:03:18 +00:00
|
|
|
INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
|
2008-03-24 19:03:58 +00:00
|
|
|
INIT_LIST_HEAD(&fs_info->space_info);
|
2013-10-01 13:00:19 +00:00
|
|
|
INIT_LIST_HEAD(&fs_info->recow_ebs);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
if (!writable)
|
|
|
|
fs_info->readonly = 1;
|
2008-03-24 19:05:44 +00:00
|
|
|
|
2008-12-17 21:10:07 +00:00
|
|
|
fs_info->super_bytenr = sb_bytenr;
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info->data_alloc_profile = (u64)-1;
|
|
|
|
fs_info->metadata_alloc_profile = (u64)-1;
|
|
|
|
fs_info->system_alloc_profile = fs_info->metadata_alloc_profile;
|
|
|
|
return fs_info;
|
|
|
|
free_all:
|
|
|
|
btrfs_free_fs_info(fs_info);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-17 21:10:07 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable)
|
|
|
|
{
|
|
|
|
u64 features;
|
2009-05-29 20:35:30 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
features = btrfs_super_incompat_flags(sb) &
|
2009-05-29 20:35:30 +00:00
|
|
|
~BTRFS_FEATURE_INCOMPAT_SUPP;
|
|
|
|
if (features) {
|
|
|
|
printk("couldn't open because of unsupported "
|
2011-04-07 11:02:04 +00:00
|
|
|
"option features (%Lx).\n",
|
|
|
|
(unsigned long long)features);
|
2013-07-03 13:25:12 +00:00
|
|
|
return -ENOTSUP;
|
2009-05-29 20:35:30 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
features = btrfs_super_incompat_flags(sb);
|
2009-05-29 20:35:30 +00:00
|
|
|
if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
|
|
|
|
features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
|
2013-07-03 13:25:12 +00:00
|
|
|
btrfs_set_super_incompat_flags(sb, features);
|
2009-05-29 20:35:30 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
features = btrfs_super_compat_ro_flags(sb) &
|
2009-05-29 20:35:30 +00:00
|
|
|
~BTRFS_FEATURE_COMPAT_RO_SUPP;
|
2013-07-03 13:25:12 +00:00
|
|
|
if (writable && features) {
|
2009-05-29 20:35:30 +00:00
|
|
|
printk("couldn't open RDWR because of unsupported "
|
2011-04-07 11:02:04 +00:00
|
|
|
"option features (%Lx).\n",
|
|
|
|
(unsigned long long)features);
|
2013-07-03 13:25:12 +00:00
|
|
|
return -ENOTSUP;
|
2009-05-29 20:35:30 +00:00
|
|
|
}
|
2013-07-03 13:25:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-05-29 20:35:30 +00:00
|
|
|
|
2013-10-23 20:24:03 +00:00
|
|
|
static int find_best_backup_root(struct btrfs_super_block *super)
|
|
|
|
{
|
|
|
|
struct btrfs_root_backup *backup;
|
|
|
|
u64 orig_gen = btrfs_super_generation(super);
|
|
|
|
u64 gen = 0;
|
|
|
|
int best_index = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
|
|
|
|
backup = super->super_roots + i;
|
|
|
|
if (btrfs_backup_tree_root_gen(backup) != orig_gen &&
|
|
|
|
btrfs_backup_tree_root_gen(backup) > gen) {
|
|
|
|
best_index = i;
|
|
|
|
gen = btrfs_backup_tree_root_gen(backup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return best_index;
|
|
|
|
}
|
|
|
|
|
2014-10-15 23:14:21 +00:00
|
|
|
static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
|
|
|
|
enum btrfs_open_ctree_flags flags,
|
|
|
|
struct btrfs_root *info_root,
|
|
|
|
u64 objectid, char *str)
|
|
|
|
{
|
|
|
|
struct btrfs_super_block *sb = fs_info->super_copy;
|
|
|
|
struct btrfs_root *root = fs_info->tree_root;
|
|
|
|
u32 leafsize = btrfs_super_leafsize(sb);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = find_and_setup_root(root, fs_info, objectid, info_root);
|
|
|
|
if (ret) {
|
|
|
|
printk("Couldn't setup %s tree\n", str);
|
|
|
|
if (!(flags & OPEN_CTREE_PARTIAL))
|
|
|
|
return -EIO;
|
|
|
|
/*
|
|
|
|
* Need a blank node here just so we don't screw up in the
|
|
|
|
* million of places that assume a root has a valid ->node
|
|
|
|
*/
|
|
|
|
info_root->node =
|
|
|
|
btrfs_find_create_tree_block(info_root, 0, leafsize);
|
|
|
|
if (!info_root->node)
|
|
|
|
return -ENOMEM;
|
|
|
|
clear_extent_buffer_uptodate(NULL, info_root->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
|
|
|
|
enum btrfs_open_ctree_flags flags)
|
2013-07-03 13:25:12 +00:00
|
|
|
{
|
|
|
|
struct btrfs_super_block *sb = fs_info->super_copy;
|
|
|
|
struct btrfs_root *root;
|
|
|
|
struct btrfs_key key;
|
|
|
|
u32 sectorsize;
|
|
|
|
u32 nodesize;
|
|
|
|
u32 leafsize;
|
|
|
|
u32 stripesize;
|
|
|
|
u64 generation;
|
|
|
|
u32 blocksize;
|
|
|
|
int ret;
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
nodesize = btrfs_super_nodesize(sb);
|
|
|
|
leafsize = btrfs_super_leafsize(sb);
|
|
|
|
sectorsize = btrfs_super_sectorsize(sb);
|
|
|
|
stripesize = btrfs_super_stripesize(sb);
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
root = fs_info->tree_root;
|
2008-03-24 19:03:18 +00:00
|
|
|
__setup_root(nodesize, leafsize, sectorsize, stripesize,
|
2013-07-03 13:25:12 +00:00
|
|
|
root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
|
|
|
|
blocksize = btrfs_level_size(root, btrfs_super_root_level(sb));
|
|
|
|
generation = btrfs_super_generation(sb);
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
if (!root_tree_bytenr && !(flags & OPEN_CTREE_BACKUP_ROOT)) {
|
2013-07-03 13:25:12 +00:00
|
|
|
root_tree_bytenr = btrfs_super_root(sb);
|
2013-10-28 18:28:43 +00:00
|
|
|
} else if (flags & OPEN_CTREE_BACKUP_ROOT) {
|
2013-10-23 20:24:03 +00:00
|
|
|
struct btrfs_root_backup *backup;
|
|
|
|
int index = find_best_backup_root(sb);
|
|
|
|
if (index >= BTRFS_NUM_BACKUP_ROOTS) {
|
|
|
|
fprintf(stderr, "Invalid backup root number\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
backup = fs_info->super_copy->super_roots + index;
|
|
|
|
root_tree_bytenr = btrfs_backup_tree_root(backup);
|
|
|
|
generation = btrfs_backup_tree_root_gen(backup);
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
root->node = read_tree_block(root, root_tree_bytenr, blocksize,
|
|
|
|
generation);
|
|
|
|
if (!extent_buffer_uptodate(root->node)) {
|
|
|
|
fprintf(stderr, "Couldn't read tree root\n");
|
|
|
|
return -EIO;
|
2011-08-26 13:51:36 +00:00
|
|
|
}
|
2013-07-03 13:25:12 +00:00
|
|
|
|
2014-10-15 23:14:21 +00:00
|
|
|
ret = setup_root_or_create_block(fs_info, flags, fs_info->extent_root,
|
|
|
|
BTRFS_EXTENT_TREE_OBJECTID, "extent");
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info->extent_root->track_dirty = 1;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
ret = find_and_setup_root(root, fs_info, BTRFS_DEV_TREE_OBJECTID,
|
|
|
|
fs_info->dev_root);
|
2011-08-26 13:51:36 +00:00
|
|
|
if (ret) {
|
|
|
|
printk("Couldn't setup device tree\n");
|
2013-07-03 13:25:12 +00:00
|
|
|
return -EIO;
|
2011-08-26 13:51:36 +00:00
|
|
|
}
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info->dev_root->track_dirty = 1;
|
2008-03-24 19:03:18 +00:00
|
|
|
|
2014-10-15 23:14:21 +00:00
|
|
|
ret = setup_root_or_create_block(fs_info, flags, fs_info->csum_root,
|
|
|
|
BTRFS_CSUM_TREE_OBJECTID, "csum");
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-07-03 13:25:12 +00:00
|
|
|
fs_info->csum_root->track_dirty = 1;
|
2008-12-17 21:10:07 +00:00
|
|
|
|
2014-05-07 20:07:17 +00:00
|
|
|
ret = find_and_setup_root(root, fs_info, BTRFS_QUOTA_TREE_OBJECTID,
|
|
|
|
fs_info->quota_root);
|
|
|
|
if (ret == 0)
|
|
|
|
fs_info->quota_enabled = 1;
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
ret = find_and_setup_log_root(root, fs_info, sb);
|
|
|
|
if (ret) {
|
|
|
|
printk("Couldn't setup log root tree\n");
|
2014-10-31 18:01:22 +00:00
|
|
|
if (!(flags & OPEN_CTREE_PARTIAL))
|
|
|
|
return -EIO;
|
2013-07-03 13:25:12 +00:00
|
|
|
}
|
2009-06-03 15:59:47 +00:00
|
|
|
|
2011-08-26 13:51:36 +00:00
|
|
|
fs_info->generation = generation;
|
|
|
|
fs_info->last_trans_committed = generation;
|
2013-10-28 18:28:43 +00:00
|
|
|
if (extent_buffer_uptodate(fs_info->extent_root->node) &&
|
|
|
|
!(flags & OPEN_CTREE_NO_BLOCK_GROUPS))
|
|
|
|
btrfs_read_block_groups(fs_info->tree_root);
|
2009-05-29 20:35:30 +00:00
|
|
|
|
|
|
|
key.objectid = BTRFS_FS_TREE_OBJECTID;
|
|
|
|
key.type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
key.offset = (u64)-1;
|
|
|
|
fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
|
2008-04-04 19:42:17 +00:00
|
|
|
|
Btrfs-progs: fsck: fix wrong check for btrfs_read_fs_root()
When encountering a corrupted fs root node, fsck hit following message:
Check tree block failed, want=29360128, have=0
Check tree block failed, want=29360128, have=0
Check tree block failed, want=29360128, have=0
Check tree block failed, want=29360128, have=0
Check tree block failed, want=29360128, have=0
read block failed check_tree_block
Checking filesystem on /dev/sda9
UUID: 0d295d80-bae2-45f2-a106-120dbfd0e173
checking extents
Segmentation fault (core dumped)
This is because in btrfs_setup_all_roots(), we check
btrfs_read_fs_root() return value by verifing whether it is
NULL pointer, this is wrong since btrfs_read_fs_root() return
PTR_ERR(ret), fix it.
Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
2014-05-28 11:20:41 +00:00
|
|
|
if (IS_ERR(fs_info->fs_root))
|
2013-07-03 13:25:12 +00:00
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-02-05 21:11:48 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
void btrfs_release_all_roots(struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
2014-05-07 20:07:17 +00:00
|
|
|
if (fs_info->quota_root)
|
|
|
|
free_extent_buffer(fs_info->quota_root->node);
|
2012-02-05 21:11:48 +00:00
|
|
|
if (fs_info->csum_root)
|
|
|
|
free_extent_buffer(fs_info->csum_root->node);
|
|
|
|
if (fs_info->dev_root)
|
|
|
|
free_extent_buffer(fs_info->dev_root->node);
|
|
|
|
if (fs_info->extent_root)
|
|
|
|
free_extent_buffer(fs_info->extent_root->node);
|
|
|
|
if (fs_info->tree_root)
|
|
|
|
free_extent_buffer(fs_info->tree_root->node);
|
2013-07-03 13:25:12 +00:00
|
|
|
if (fs_info->log_root_tree)
|
|
|
|
free_extent_buffer(fs_info->log_root_tree->node);
|
2012-02-05 21:11:48 +00:00
|
|
|
if (fs_info->chunk_root)
|
|
|
|
free_extent_buffer(fs_info->chunk_root->node);
|
2013-07-03 13:25:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:13 +00:00
|
|
|
static void free_map_lookup(struct cache_extent *ce)
|
2013-07-03 13:25:12 +00:00
|
|
|
{
|
|
|
|
struct map_lookup *map;
|
|
|
|
|
2013-07-03 13:25:13 +00:00
|
|
|
map = container_of(ce, struct map_lookup, ce);
|
|
|
|
kfree(map);
|
2013-07-03 13:25:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:13 +00:00
|
|
|
FREE_EXTENT_CACHE_BASED_TREE(mapping_cache, free_map_lookup);
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
2013-10-01 13:00:19 +00:00
|
|
|
while (!list_empty(&fs_info->recow_ebs)) {
|
|
|
|
struct extent_buffer *eb;
|
|
|
|
eb = list_first_entry(&fs_info->recow_ebs,
|
|
|
|
struct extent_buffer, recow);
|
|
|
|
list_del_init(&eb->recow);
|
|
|
|
free_extent_buffer(eb);
|
|
|
|
}
|
2013-07-03 13:25:13 +00:00
|
|
|
free_mapping_cache_tree(&fs_info->mapping_tree.cache_tree);
|
2011-08-26 13:51:36 +00:00
|
|
|
extent_io_tree_cleanup(&fs_info->extent_cache);
|
|
|
|
extent_io_tree_cleanup(&fs_info->free_space_cache);
|
|
|
|
extent_io_tree_cleanup(&fs_info->block_group_cache);
|
|
|
|
extent_io_tree_cleanup(&fs_info->pinned_extents);
|
|
|
|
extent_io_tree_cleanup(&fs_info->pending_del);
|
|
|
|
extent_io_tree_cleanup(&fs_info->extent_ins);
|
2013-07-03 13:25:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_scan_fs_devices(int fd, const char *path,
|
2013-07-17 16:03:40 +00:00
|
|
|
struct btrfs_fs_devices **fs_devices,
|
2015-02-03 14:48:57 +00:00
|
|
|
u64 sb_bytenr, int super_recover,
|
|
|
|
int skip_devices)
|
2013-07-03 13:25:12 +00:00
|
|
|
{
|
|
|
|
u64 total_devs;
|
2014-11-12 05:52:11 +00:00
|
|
|
u64 dev_size;
|
2014-12-19 06:13:09 +00:00
|
|
|
off_t seek_ret;
|
2013-07-03 13:25:12 +00:00
|
|
|
int ret;
|
2013-07-17 16:03:40 +00:00
|
|
|
if (!sb_bytenr)
|
|
|
|
sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
|
2013-07-03 13:25:12 +00:00
|
|
|
|
2014-12-19 06:13:09 +00:00
|
|
|
seek_ret = lseek(fd, 0, SEEK_END);
|
|
|
|
if (seek_ret < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
dev_size = seek_ret;
|
2014-11-12 05:52:11 +00:00
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
if (sb_bytenr > dev_size) {
|
|
|
|
fprintf(stderr, "Superblock bytenr is larger than device size\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
ret = btrfs_scan_one_device(fd, path, fs_devices,
|
2014-07-03 09:36:36 +00:00
|
|
|
&total_devs, sb_bytenr, super_recover);
|
2013-07-03 13:25:12 +00:00
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "No valid Btrfs found on %s\n", path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-03 14:48:57 +00:00
|
|
|
if (!skip_devices && total_devs != 1) {
|
2014-10-31 04:11:20 +00:00
|
|
|
ret = btrfs_scan_lblkid();
|
2013-07-03 13:25:12 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
|
|
|
struct btrfs_super_block *sb = fs_info->super_copy;
|
|
|
|
u32 sectorsize;
|
|
|
|
u32 nodesize;
|
|
|
|
u32 leafsize;
|
|
|
|
u32 blocksize;
|
|
|
|
u32 stripesize;
|
|
|
|
u64 generation;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
nodesize = btrfs_super_nodesize(sb);
|
|
|
|
leafsize = btrfs_super_leafsize(sb);
|
|
|
|
sectorsize = btrfs_super_sectorsize(sb);
|
|
|
|
stripesize = btrfs_super_stripesize(sb);
|
|
|
|
|
|
|
|
__setup_root(nodesize, leafsize, sectorsize, stripesize,
|
|
|
|
fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
|
|
|
|
|
|
|
|
ret = btrfs_read_sys_array(fs_info->chunk_root);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
blocksize = btrfs_level_size(fs_info->chunk_root,
|
|
|
|
btrfs_super_chunk_root_level(sb));
|
|
|
|
generation = btrfs_super_chunk_root_generation(sb);
|
|
|
|
|
|
|
|
fs_info->chunk_root->node = read_tree_block(fs_info->chunk_root,
|
|
|
|
btrfs_super_chunk_root(sb),
|
|
|
|
blocksize, generation);
|
2015-01-28 02:12:55 +00:00
|
|
|
if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
|
2013-07-03 13:25:12 +00:00
|
|
|
fprintf(stderr, "Couldn't read chunk root\n");
|
2013-09-05 05:51:30 +00:00
|
|
|
return -EIO;
|
2013-07-03 13:25:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(btrfs_super_flags(sb) & BTRFS_SUPER_FLAG_METADUMP)) {
|
|
|
|
ret = btrfs_read_chunk_tree(fs_info->chunk_root);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "Couldn't read chunk tree\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
|
|
|
|
u64 sb_bytenr,
|
2013-10-28 18:28:43 +00:00
|
|
|
u64 root_tree_bytenr,
|
|
|
|
enum btrfs_open_ctree_flags flags)
|
2013-07-03 13:25:12 +00:00
|
|
|
{
|
|
|
|
struct btrfs_fs_info *fs_info;
|
|
|
|
struct btrfs_super_block *disk_super;
|
|
|
|
struct btrfs_fs_devices *fs_devices = NULL;
|
|
|
|
struct extent_buffer *eb;
|
|
|
|
int ret;
|
2014-01-13 13:14:55 +00:00
|
|
|
int oflags;
|
2013-07-03 13:25:12 +00:00
|
|
|
|
|
|
|
if (sb_bytenr == 0)
|
|
|
|
sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
|
|
|
|
|
|
|
|
/* try to drop all the caches */
|
|
|
|
if (posix_fadvise(fp, 0, 0, POSIX_FADV_DONTNEED))
|
|
|
|
fprintf(stderr, "Warning, could not drop caches\n");
|
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
fs_info = btrfs_new_fs_info(flags & OPEN_CTREE_WRITES, sb_bytenr);
|
2013-07-03 13:25:12 +00:00
|
|
|
if (!fs_info) {
|
|
|
|
fprintf(stderr, "Failed to allocate memory for fs_info\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-10-28 18:28:43 +00:00
|
|
|
if (flags & OPEN_CTREE_RESTORE)
|
2013-07-03 18:24:43 +00:00
|
|
|
fs_info->on_restoring = 1;
|
2015-01-16 03:04:09 +00:00
|
|
|
if (flags & OPEN_CTREE_SUPPRESS_CHECK_BLOCK_ERRORS)
|
|
|
|
fs_info->suppress_check_block_errors = 1;
|
2015-05-11 08:08:45 +00:00
|
|
|
if (flags & OPEN_CTREE_IGNORE_FSID_MISMATCH)
|
|
|
|
fs_info->ignore_fsid_mismatch = 1;
|
2013-07-03 13:25:12 +00:00
|
|
|
|
2013-09-21 08:34:18 +00:00
|
|
|
ret = btrfs_scan_fs_devices(fp, path, &fs_devices, sb_bytenr,
|
2015-02-03 14:48:57 +00:00
|
|
|
(flags & OPEN_CTREE_RECOVER_SUPER),
|
|
|
|
(flags & OPEN_CTREE_NO_DEVICES));
|
2013-07-03 13:25:12 +00:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
fs_info->fs_devices = fs_devices;
|
2013-10-28 18:28:43 +00:00
|
|
|
if (flags & OPEN_CTREE_WRITES)
|
2014-01-13 13:14:55 +00:00
|
|
|
oflags = O_RDWR;
|
2013-07-03 13:25:12 +00:00
|
|
|
else
|
2014-01-13 13:14:55 +00:00
|
|
|
oflags = O_RDONLY;
|
|
|
|
|
|
|
|
if (flags & OPEN_CTREE_EXCLUSIVE)
|
|
|
|
oflags |= O_EXCL;
|
|
|
|
|
|
|
|
ret = btrfs_open_devices(fs_devices, oflags);
|
2013-07-03 13:25:12 +00:00
|
|
|
if (ret)
|
2014-04-20 13:17:53 +00:00
|
|
|
goto out;
|
2013-07-03 13:25:12 +00:00
|
|
|
|
|
|
|
disk_super = fs_info->super_copy;
|
2013-10-28 18:28:43 +00:00
|
|
|
if (!(flags & OPEN_CTREE_RECOVER_SUPER))
|
2013-09-21 08:34:18 +00:00
|
|
|
ret = btrfs_read_dev_super(fs_devices->latest_bdev,
|
2014-07-03 09:36:36 +00:00
|
|
|
disk_super, sb_bytenr, 1);
|
2013-09-21 08:34:18 +00:00
|
|
|
else
|
2014-07-03 09:36:36 +00:00
|
|
|
ret = btrfs_read_dev_super(fp, disk_super, sb_bytenr, 0);
|
2013-07-03 13:25:12 +00:00
|
|
|
if (ret) {
|
|
|
|
printk("No valid btrfs found\n");
|
|
|
|
goto out_devices;
|
|
|
|
}
|
|
|
|
|
2015-05-11 08:08:45 +00:00
|
|
|
if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_CHANGING_FSID &&
|
|
|
|
!fs_info->ignore_fsid_mismatch) {
|
|
|
|
fprintf(stderr, "ERROR: Filesystem UUID change in progress\n");
|
|
|
|
goto out_devices;
|
|
|
|
}
|
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
|
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
ret = btrfs_check_fs_compatibility(fs_info->super_copy,
|
|
|
|
flags & OPEN_CTREE_WRITES);
|
2013-07-03 13:25:12 +00:00
|
|
|
if (ret)
|
|
|
|
goto out_devices;
|
|
|
|
|
|
|
|
ret = btrfs_setup_chunk_tree_and_device_map(fs_info);
|
|
|
|
if (ret)
|
|
|
|
goto out_chunk;
|
|
|
|
|
|
|
|
eb = fs_info->chunk_root->node;
|
|
|
|
read_extent_buffer(eb, fs_info->chunk_tree_uuid,
|
2013-10-02 12:28:27 +00:00
|
|
|
btrfs_header_chunk_tree_uuid(eb),
|
2013-07-03 13:25:12 +00:00
|
|
|
BTRFS_UUID_SIZE);
|
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, flags);
|
2015-02-12 12:41:00 +00:00
|
|
|
if (ret && !(flags & __OPEN_CTREE_RETURN_CHUNK_ROOT))
|
Btrfs-progs: fsck: disallow partial opening if critical roots corrupted
If btrfs tree root is corrupted, fsck will hit the following segmentation.
enabling repair mode
Check tree block failed, want=29376512, have=0
Check tree block failed, want=29376512, have=0
Check tree block failed, want=29376512, have=0
Check tree block failed, want=29376512, have=0
Check tree block failed, want=29376512, have=0
read block failed check_tree_block
Couldn't read tree root
Checking filesystem on /dev/sda9
UUID: 0e1a754d-04a5-4256-ae79-0f769751803e
Critical roots corrupted, unable to fsck the FS
Segmentation fault (core dumped)
In btrfs_setup_all_roots(), we could tolerate some trees(extent tree, csum tree)
corrupted, and we have did careful check inside that function, it will
return NULL if critial roots corrupt(for example tree root).
The problem is that we check @OPEN_CTREE_PARTIAL flag again after
calling btrfs_setup_all_roots() which will successfully return
@fs_info though critial roots corrupted.
Fix this problem by removing @OPEN_CTREE_PARTIAL flag check outsize
btrfs_setup_all_roots().
Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
2014-05-28 11:20:39 +00:00
|
|
|
goto out_chunk;
|
2013-07-03 13:25:12 +00:00
|
|
|
|
|
|
|
return fs_info;
|
|
|
|
|
|
|
|
out_chunk:
|
|
|
|
btrfs_release_all_roots(fs_info);
|
|
|
|
btrfs_cleanup_all_caches(fs_info);
|
|
|
|
out_devices:
|
|
|
|
btrfs_close_devices(fs_devices);
|
2011-08-26 13:51:36 +00:00
|
|
|
out:
|
2013-07-03 13:25:12 +00:00
|
|
|
btrfs_free_fs_info(fs_info);
|
2011-08-26 13:51:36 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-05 21:11:48 +00:00
|
|
|
struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
|
2013-04-16 17:13:38 +00:00
|
|
|
u64 sb_bytenr, u64 root_tree_bytenr,
|
2013-10-28 18:28:43 +00:00
|
|
|
enum btrfs_open_ctree_flags flags)
|
2011-08-26 13:51:36 +00:00
|
|
|
{
|
|
|
|
int fp;
|
2012-02-05 21:11:48 +00:00
|
|
|
struct btrfs_fs_info *info;
|
2013-10-28 18:28:43 +00:00
|
|
|
int oflags = O_CREAT | O_RDWR;
|
2011-08-26 13:51:36 +00:00
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
if (!(flags & OPEN_CTREE_WRITES))
|
|
|
|
oflags = O_RDONLY;
|
2011-08-26 13:51:36 +00:00
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
fp = open(filename, oflags, 0600);
|
2011-08-26 13:51:36 +00:00
|
|
|
if (fp < 0) {
|
|
|
|
fprintf (stderr, "Could not open %s\n", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-04-16 17:13:38 +00:00
|
|
|
info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
|
2013-10-28 18:28:43 +00:00
|
|
|
flags);
|
2011-08-26 13:51:36 +00:00
|
|
|
close(fp);
|
2012-02-05 21:11:48 +00:00
|
|
|
return info;
|
|
|
|
}
|
2011-08-26 13:51:36 +00:00
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr,
|
|
|
|
enum btrfs_open_ctree_flags flags)
|
2012-02-05 21:11:48 +00:00
|
|
|
{
|
|
|
|
struct btrfs_fs_info *info;
|
|
|
|
|
2013-10-28 18:28:43 +00:00
|
|
|
info = open_ctree_fs_info(filename, sb_bytenr, 0, flags);
|
2012-02-05 21:11:48 +00:00
|
|
|
if (!info)
|
|
|
|
return NULL;
|
2015-02-12 12:41:00 +00:00
|
|
|
if (flags & __OPEN_CTREE_RETURN_CHUNK_ROOT)
|
2015-01-16 03:22:28 +00:00
|
|
|
return info->chunk_root;
|
2012-02-05 21:11:48 +00:00
|
|
|
return info->fs_root;
|
2011-08-26 13:51:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
|
2013-10-28 18:28:43 +00:00
|
|
|
enum btrfs_open_ctree_flags flags)
|
2011-08-26 13:51:36 +00:00
|
|
|
{
|
2012-02-05 21:11:48 +00:00
|
|
|
struct btrfs_fs_info *info;
|
2013-10-28 18:28:43 +00:00
|
|
|
info = __open_ctree_fd(fp, path, sb_bytenr, 0, flags);
|
2012-02-05 21:11:48 +00:00
|
|
|
if (!info)
|
|
|
|
return NULL;
|
2015-02-12 12:41:00 +00:00
|
|
|
if (flags & __OPEN_CTREE_RETURN_CHUNK_ROOT)
|
2015-01-16 03:22:28 +00:00
|
|
|
return info->chunk_root;
|
2012-02-05 21:11:48 +00:00
|
|
|
return info->fs_root;
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
|
|
|
|
2014-07-03 09:36:36 +00:00
|
|
|
int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
|
|
|
|
int super_recover)
|
2008-12-17 21:10:07 +00:00
|
|
|
{
|
2009-06-03 15:59:47 +00:00
|
|
|
u8 fsid[BTRFS_FSID_SIZE];
|
2012-09-04 17:59:26 +00:00
|
|
|
int fsid_is_initialized = 0;
|
2008-12-17 21:10:07 +00:00
|
|
|
struct btrfs_super_block buf;
|
|
|
|
int i;
|
|
|
|
int ret;
|
2014-07-03 09:36:36 +00:00
|
|
|
int max_super = super_recover ? BTRFS_SUPER_MIRROR_MAX : 1;
|
2008-12-17 21:10:07 +00:00
|
|
|
u64 transid = 0;
|
|
|
|
u64 bytenr;
|
|
|
|
|
|
|
|
if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) {
|
|
|
|
ret = pread64(fd, &buf, sizeof(buf), sb_bytenr);
|
|
|
|
if (ret < sizeof(buf))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (btrfs_super_bytenr(&buf) != sb_bytenr ||
|
2013-06-26 05:27:08 +00:00
|
|
|
btrfs_super_magic(&buf) != BTRFS_MAGIC)
|
2008-12-17 21:10:07 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(sb, &buf, sizeof(*sb));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-17 11:59:01 +00:00
|
|
|
/*
|
|
|
|
* we would like to check all the supers, but that would make
|
|
|
|
* a btrfs mount succeed after a mkfs from a different FS.
|
|
|
|
* So, we need to add a special mount option to scan for
|
|
|
|
* later supers, using BTRFS_SUPER_MIRROR_MAX instead
|
|
|
|
*/
|
|
|
|
|
2014-07-03 09:36:36 +00:00
|
|
|
for (i = 0; i < max_super; i++) {
|
2008-12-17 21:10:07 +00:00
|
|
|
bytenr = btrfs_sb_offset(i);
|
|
|
|
ret = pread64(fd, &buf, sizeof(buf), bytenr);
|
|
|
|
if (ret < sizeof(buf))
|
|
|
|
break;
|
|
|
|
|
2012-09-04 17:59:26 +00:00
|
|
|
if (btrfs_super_bytenr(&buf) != bytenr )
|
|
|
|
continue;
|
|
|
|
/* if magic is NULL, the device was removed */
|
2013-06-26 05:27:08 +00:00
|
|
|
if (btrfs_super_magic(&buf) == 0 && i == 0)
|
2012-09-04 17:59:26 +00:00
|
|
|
return -1;
|
2013-06-26 05:27:08 +00:00
|
|
|
if (btrfs_super_magic(&buf) != BTRFS_MAGIC)
|
2008-12-17 21:10:07 +00:00
|
|
|
continue;
|
|
|
|
|
2012-09-04 17:59:26 +00:00
|
|
|
if (!fsid_is_initialized) {
|
2009-06-03 15:59:47 +00:00
|
|
|
memcpy(fsid, buf.fsid, sizeof(fsid));
|
2012-09-04 17:59:26 +00:00
|
|
|
fsid_is_initialized = 1;
|
|
|
|
} else if (memcmp(fsid, buf.fsid, sizeof(fsid))) {
|
|
|
|
/*
|
|
|
|
* the superblocks (the original one and
|
|
|
|
* its backups) contain data of different
|
|
|
|
* filesystems -> the super cannot be trusted
|
|
|
|
*/
|
2009-06-03 15:59:47 +00:00
|
|
|
continue;
|
2012-09-04 17:59:26 +00:00
|
|
|
}
|
2009-06-03 15:59:47 +00:00
|
|
|
|
2008-12-17 21:10:07 +00:00
|
|
|
if (btrfs_super_generation(&buf) > transid) {
|
|
|
|
memcpy(sb, &buf, sizeof(*sb));
|
|
|
|
transid = btrfs_super_generation(&buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return transid > 0 ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2013-08-07 01:03:33 +00:00
|
|
|
static int write_dev_supers(struct btrfs_root *root,
|
|
|
|
struct btrfs_super_block *sb,
|
|
|
|
struct btrfs_device *device)
|
2008-12-05 17:21:31 +00:00
|
|
|
{
|
|
|
|
u64 bytenr;
|
2008-12-17 21:10:07 +00:00
|
|
|
u32 crc;
|
2008-12-05 17:21:31 +00:00
|
|
|
int i, ret;
|
|
|
|
|
2008-12-17 21:10:07 +00:00
|
|
|
if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) {
|
|
|
|
btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr);
|
|
|
|
crc = ~(u32)0;
|
|
|
|
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
|
|
|
|
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
|
|
|
|
btrfs_csum_final(crc, (char *)&sb->csum[0]);
|
|
|
|
|
2013-03-06 16:32:51 +00:00
|
|
|
/*
|
|
|
|
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
|
|
|
|
* zero filled, we can use it directly
|
|
|
|
*/
|
|
|
|
ret = pwrite64(device->fd, root->fs_info->super_copy,
|
|
|
|
BTRFS_SUPER_INFO_SIZE,
|
|
|
|
root->fs_info->super_bytenr);
|
2008-12-17 21:10:07 +00:00
|
|
|
BUG_ON(ret != BTRFS_SUPER_INFO_SIZE);
|
2013-03-06 16:32:51 +00:00
|
|
|
return 0;
|
2008-12-17 21:10:07 +00:00
|
|
|
}
|
|
|
|
|
2008-12-05 17:21:31 +00:00
|
|
|
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
|
|
|
|
bytenr = btrfs_sb_offset(i);
|
2012-10-31 16:56:56 +00:00
|
|
|
if (bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
|
2008-12-05 17:21:31 +00:00
|
|
|
break;
|
|
|
|
|
2008-12-17 21:10:07 +00:00
|
|
|
btrfs_set_super_bytenr(sb, bytenr);
|
2008-12-05 17:21:31 +00:00
|
|
|
|
2008-12-17 21:10:07 +00:00
|
|
|
crc = ~(u32)0;
|
|
|
|
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
|
|
|
|
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
|
|
|
|
btrfs_csum_final(crc, (char *)&sb->csum[0]);
|
|
|
|
|
2013-03-06 16:32:51 +00:00
|
|
|
/*
|
|
|
|
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
|
|
|
|
* zero filled, we can use it directly
|
|
|
|
*/
|
|
|
|
ret = pwrite64(device->fd, root->fs_info->super_copy,
|
|
|
|
BTRFS_SUPER_INFO_SIZE, bytenr);
|
2008-12-17 21:10:07 +00:00
|
|
|
BUG_ON(ret != BTRFS_SUPER_INFO_SIZE);
|
2008-12-05 17:21:31 +00:00
|
|
|
}
|
2013-03-06 16:32:51 +00:00
|
|
|
|
2008-12-05 17:21:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-10 20:22:00 +00:00
|
|
|
int write_all_supers(struct btrfs_root *root)
|
|
|
|
{
|
|
|
|
struct list_head *cur;
|
|
|
|
struct list_head *head = &root->fs_info->fs_devices->devices;
|
|
|
|
struct btrfs_device *dev;
|
2008-12-17 21:10:07 +00:00
|
|
|
struct btrfs_super_block *sb;
|
2008-04-10 20:22:00 +00:00
|
|
|
struct btrfs_dev_item *dev_item;
|
|
|
|
int ret;
|
2008-12-17 21:10:07 +00:00
|
|
|
u64 flags;
|
2008-04-10 20:22:00 +00:00
|
|
|
|
2013-03-06 16:32:51 +00:00
|
|
|
sb = root->fs_info->super_copy;
|
2008-12-17 21:10:07 +00:00
|
|
|
dev_item = &sb->dev_item;
|
2008-04-10 20:22:00 +00:00
|
|
|
list_for_each(cur, head) {
|
|
|
|
dev = list_entry(cur, struct btrfs_device, dev_list);
|
2008-11-18 15:40:06 +00:00
|
|
|
if (!dev->writeable)
|
|
|
|
continue;
|
|
|
|
|
2008-12-17 21:10:07 +00:00
|
|
|
btrfs_set_stack_device_generation(dev_item, 0);
|
|
|
|
btrfs_set_stack_device_type(dev_item, dev->type);
|
|
|
|
btrfs_set_stack_device_id(dev_item, dev->devid);
|
|
|
|
btrfs_set_stack_device_total_bytes(dev_item, dev->total_bytes);
|
|
|
|
btrfs_set_stack_device_bytes_used(dev_item, dev->bytes_used);
|
|
|
|
btrfs_set_stack_device_io_align(dev_item, dev->io_align);
|
|
|
|
btrfs_set_stack_device_io_width(dev_item, dev->io_width);
|
|
|
|
btrfs_set_stack_device_sector_size(dev_item, dev->sector_size);
|
|
|
|
memcpy(dev_item->uuid, dev->uuid, BTRFS_UUID_SIZE);
|
|
|
|
memcpy(dev_item->fsid, dev->fs_devices->fsid, BTRFS_UUID_SIZE);
|
|
|
|
|
|
|
|
flags = btrfs_super_flags(sb);
|
|
|
|
btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN);
|
|
|
|
|
2008-12-05 17:21:31 +00:00
|
|
|
ret = write_dev_supers(root, sb, dev);
|
2008-04-10 20:22:00 +00:00
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
int write_ctree_super(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2008-01-04 15:38:22 +00:00
|
|
|
struct btrfs_root *tree_root = root->fs_info->tree_root;
|
2008-03-24 19:03:18 +00:00
|
|
|
struct btrfs_root *chunk_root = root->fs_info->chunk_root;
|
2008-05-05 13:45:26 +00:00
|
|
|
|
|
|
|
if (root->fs_info->readonly)
|
|
|
|
return 0;
|
|
|
|
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_generation(root->fs_info->super_copy,
|
2008-01-04 15:38:22 +00:00
|
|
|
trans->transid);
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_root(root->fs_info->super_copy,
|
2008-01-04 15:38:22 +00:00
|
|
|
tree_root->node->start);
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_root_level(root->fs_info->super_copy,
|
2008-01-04 15:38:22 +00:00
|
|
|
btrfs_header_level(tree_root->node));
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_chunk_root(root->fs_info->super_copy,
|
2008-03-24 19:03:18 +00:00
|
|
|
chunk_root->node->start);
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_chunk_root_level(root->fs_info->super_copy,
|
2008-03-24 19:03:18 +00:00
|
|
|
btrfs_header_level(chunk_root->node));
|
2013-03-06 16:32:51 +00:00
|
|
|
btrfs_set_super_chunk_root_generation(root->fs_info->super_copy,
|
2008-10-29 18:07:47 +00:00
|
|
|
btrfs_header_generation(chunk_root->node));
|
2008-12-17 21:10:07 +00:00
|
|
|
|
2008-04-10 20:22:00 +00:00
|
|
|
ret = write_all_supers(root);
|
2008-01-04 15:38:22 +00:00
|
|
|
if (ret)
|
2007-02-21 22:04:57 +00:00
|
|
|
fprintf(stderr, "failed to write new super block err %d\n", ret);
|
2008-01-04 15:38:22 +00:00
|
|
|
return ret;
|
2007-03-01 23:59:40 +00:00
|
|
|
}
|
2007-04-12 16:14:47 +00:00
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
int close_ctree(struct btrfs_root *root)
|
2007-02-21 22:04:57 +00:00
|
|
|
{
|
2007-03-13 20:47:54 +00:00
|
|
|
int ret;
|
2007-03-16 20:20:31 +00:00
|
|
|
struct btrfs_trans_handle *trans;
|
2008-01-04 15:38:22 +00:00
|
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
|
|
|
|
2011-08-26 13:51:36 +00:00
|
|
|
if (fs_info->last_trans_committed !=
|
|
|
|
fs_info->generation) {
|
|
|
|
trans = btrfs_start_transaction(root, 1);
|
|
|
|
btrfs_commit_transaction(trans, root);
|
|
|
|
trans = btrfs_start_transaction(root, 1);
|
|
|
|
ret = commit_tree_roots(trans, fs_info);
|
|
|
|
BUG_ON(ret);
|
|
|
|
ret = __commit_transaction(trans, root);
|
|
|
|
BUG_ON(ret);
|
|
|
|
write_ctree_super(trans, root);
|
|
|
|
btrfs_free_transaction(root, trans);
|
|
|
|
}
|
2009-05-29 20:35:30 +00:00
|
|
|
btrfs_free_block_groups(fs_info);
|
|
|
|
|
2013-07-03 13:25:14 +00:00
|
|
|
free_fs_roots_tree(&fs_info->fs_root_tree);
|
2008-01-04 15:38:22 +00:00
|
|
|
|
2013-07-03 13:25:12 +00:00
|
|
|
btrfs_release_all_roots(fs_info);
|
2013-07-03 13:25:11 +00:00
|
|
|
btrfs_close_devices(fs_info->fs_devices);
|
2013-07-03 13:25:12 +00:00
|
|
|
btrfs_cleanup_all_caches(fs_info);
|
|
|
|
btrfs_free_fs_info(fs_info);
|
2007-02-02 14:18:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:38:22 +00:00
|
|
|
int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|
|
|
struct extent_buffer *eb)
|
2007-02-02 14:18:22 +00:00
|
|
|
{
|
2008-01-04 15:38:22 +00:00
|
|
|
return clear_extent_buffer_dirty(eb);
|
|
|
|
}
|
|
|
|
|
|
|
|
int wait_on_tree_block_writeback(struct btrfs_root *root,
|
|
|
|
struct extent_buffer *eb)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void btrfs_mark_buffer_dirty(struct extent_buffer *eb)
|
|
|
|
{
|
|
|
|
set_extent_buffer_dirty(eb);
|
2007-02-02 14:18:22 +00:00
|
|
|
}
|
|
|
|
|
2008-05-13 17:48:58 +00:00
|
|
|
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
|
2008-01-04 15:38:22 +00:00
|
|
|
{
|
2008-05-13 17:48:58 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = extent_buffer_uptodate(buf);
|
|
|
|
if (!ret)
|
|
|
|
return ret;
|
|
|
|
|
2011-08-26 13:51:36 +00:00
|
|
|
ret = verify_parent_transid(buf->tree, buf, parent_transid, 1);
|
2008-05-13 17:48:58 +00:00
|
|
|
return !ret;
|
2008-01-04 15:38:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_set_buffer_uptodate(struct extent_buffer *eb)
|
|
|
|
{
|
|
|
|
return set_extent_buffer_uptodate(eb);
|
|
|
|
}
|