#include "volumes.h"
#include "extent_io.h"
#include "help.h"
-
-#define HEADER_MAGIC 0xbd5c25e27295668bULL
-#define MAX_PENDING_SIZE (256 * 1024)
-#define BLOCK_SIZE 1024
-#define BLOCK_MASK (BLOCK_SIZE - 1)
-
-#define COMPRESS_NONE 0
-#define COMPRESS_ZLIB 1
+#include "image/metadump.h"
+#include "image/sanitize.h"
#define MAX_WORKER_THREADS (32)
-struct meta_cluster_item {
- __le64 bytenr;
- __le32 size;
-} __attribute__ ((__packed__));
-
-struct meta_cluster_header {
- __le64 magic;
- __le64 bytenr;
- __le32 nritems;
- u8 compress;
-} __attribute__ ((__packed__));
-
-/* cluster header + index items + buffers */
-struct meta_cluster {
- struct meta_cluster_header header;
- struct meta_cluster_item items[];
-} __attribute__ ((__packed__));
-
-#define ITEMS_PER_CLUSTER ((BLOCK_SIZE - sizeof(struct meta_cluster)) / \
- sizeof(struct meta_cluster_item))
-
-struct fs_chunk {
- u64 logical;
- u64 physical;
- /*
- * physical_dup only store additonal physical for BTRFS_BLOCK_GROUP_DUP
- * currently restore only support single and DUP
- * TODO: modify this structure and the function related to this
- * structure for support RAID*
- */
- u64 physical_dup;
- u64 bytes;
- struct rb_node l;
- struct rb_node p;
- struct list_head list;
-};
-
struct async_work {
struct list_head list;
struct list_head ordered;
int compress_level;
int done;
int data;
- int sanitize_names;
+ enum sanitize_mode sanitize_names;
int error;
};
-struct name {
- struct rb_node n;
- char *val;
- char *sub;
- u32 len;
-};
-
struct mdrestore_struct {
FILE *in;
FILE *out;
0x588982AFL,0x5D65F45EL,0x53516F4DL,0x56BD19BCL
};
-static int find_collision_brute_force(struct name *val, u32 name_len)
+/*
+ * Calculate a 4-byte suffix to match desired CRC32C
+ *
+ * @current_crc: CRC32C checksum of all bytes before the suffix
+ * @desired_crc: the checksum that we want to get after adding the suffix
+ *
+ * Outputs: @suffix: pointer to where the suffix will be written (4-bytes)
+ */
+static void find_collision_calc_suffix(unsigned long current_crc,
+ unsigned long desired_crc,
+ char *suffix)
+{
+ int i;
+
+ for(i = 3; i >= 0; i--) {
+ desired_crc = (desired_crc << 8)
+ ^ crc32c_rev_table[desired_crc >> 24 & 0xFF]
+ ^ ((current_crc >> i * 8) & 0xFF);
+ }
+ for (i = 0; i < 4; i++)
+ suffix[i] = (desired_crc >> i * 8) & 0xFF;
+}
+
+/*
+ * Check if suffix is valid according to our file name conventions
+ */
+static int find_collision_is_suffix_valid(const char *suffix)
+{
+ int i;
+ char c;
+
+ for (i = 0; i < 4; i++) {
+ c = suffix[i];
+ if (c < ' ' || c > 126 || c == '/')
+ return 0;
+ }
+ return 1;
+}
+
+static int find_collision_reverse_crc32c(struct name *val, u32 name_len)
{
unsigned long checksum;
+ unsigned long current_checksum;
int found = 0;
int i;
+ /* There are no same length collisions of 4 or less bytes */
+ if (name_len <= 4)
+ return 0;
checksum = crc32c(~1, val->val, name_len);
+ name_len -= 4;
memset(val->sub, ' ', name_len);
i = 0;
while (1) {
- if (crc32c(~1, val->sub, name_len) == checksum &&
+ current_checksum = crc32c(~1, val->sub, name_len);
+ find_collision_calc_suffix(current_checksum,
+ checksum,
+ val->sub + name_len);
+ if (find_collision_is_suffix_valid(val->sub + name_len) &&
memcmp(val->sub, val->val, val->len)) {
found = 1;
break;
return found;
}
-static char *find_collision(struct metadump_struct *md, char *name,
+static char *find_collision(struct rb_root *name_tree, char *name,
u32 name_len)
{
struct name *val;
tmp.val = name;
tmp.len = name_len;
- entry = tree_search(&md->name_tree, &tmp.n, name_cmp, 0);
+ entry = tree_search(name_tree, &tmp.n, name_cmp, 0);
if (entry) {
val = rb_entry(entry, struct name, n);
free(name);
return NULL;
}
- found = find_collision_brute_force(val, name_len);
+ found = find_collision_reverse_crc32c(val, name_len);
if (!found) {
warning(
}
}
- tree_insert(&md->name_tree, &val->n, name_cmp);
+ tree_insert(name_tree, &val->n, name_cmp);
return val->sub;
}
u32 cur = 0;
u32 this_len;
u32 name_len;
- int free_garbage = (md->sanitize_names == 1);
+ int free_garbage = (md->sanitize_names == SANITIZE_NAMES);
dir_item = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
total_len = btrfs_item_size_nr(eb, slot);
name_ptr = (unsigned long)(dir_item + 1);
name_len = btrfs_dir_name_len(eb, dir_item);
- if (md->sanitize_names > 1) {
+ if (md->sanitize_names == SANITIZE_COLLISIONS) {
buf = malloc(name_len);
if (!buf) {
error("cannot sanitize name, not enough memory");
return;
}
read_extent_buffer(eb, buf, name_ptr, name_len);
- garbage = find_collision(md, buf, name_len);
+ garbage = find_collision(&md->name_tree, buf, name_len);
} else {
garbage = generate_garbage(name_len);
}
u32 item_size;
u32 cur_offset = 0;
int len;
- int free_garbage = (md->sanitize_names == 1);
+ int free_garbage = (md->sanitize_names == SANITIZE_NAMES);
item_size = btrfs_item_size_nr(eb, slot);
ptr = btrfs_item_ptr_offset(eb, slot);
}
cur_offset += len;
- if (md->sanitize_names > 1) {
+ if (md->sanitize_names == SANITIZE_COLLISIONS) {
buf = malloc(len);
if (!buf) {
error("cannot sanitize name, not enough memory");
return;
}
read_extent_buffer(eb, buf, name_ptr, len);
- garbage = find_collision(md, buf, len);
+ garbage = find_collision(&md->name_tree, buf, len);
} else {
garbage = generate_garbage(len);
}
}
}
-static void sanitize_xattr(struct metadump_struct *md,
- struct extent_buffer *eb, int slot)
+static void sanitize_xattr(struct extent_buffer *eb, int slot)
{
struct btrfs_dir_item *dir_item;
unsigned long data_ptr;
sanitize_inode_ref(md, eb, slot, 1);
break;
case BTRFS_XATTR_ITEM_KEY:
- sanitize_xattr(md, eb, slot);
+ sanitize_xattr(eb, slot);
break;
default:
break;
static int metadump_init(struct metadump_struct *md, struct btrfs_root *root,
FILE *out, int num_threads, int compress_level,
- int sanitize_names)
+ enum sanitize_mode sanitize_names)
{
int i, ret = 0;
md->pending_start = (u64)-1;
md->compress_level = compress_level;
md->sanitize_names = sanitize_names;
- if (sanitize_names > 1)
+ if (sanitize_names == SANITIZE_COLLISIONS)
crc32c_optimization_init();
md->name_tree.rb_node = NULL;
}
static int create_metadump(const char *input, FILE *out, int num_threads,
- int compress_level, int sanitize, int walk_trees)
+ int compress_level, enum sanitize_mode sanitize,
+ int walk_trees)
{
struct btrfs_root *root;
struct btrfs_path path;
int walk_trees = 0;
int multi_devices = 0;
int ret;
- int sanitize = 0;
+ enum sanitize_mode sanitize = SANITIZE_NONE;
int dev_cnt = 0;
int usage_error = 0;
FILE *out;
old_restore = 1;
break;
case 's':
- sanitize++;
+ if (sanitize == SANITIZE_NONE)
+ sanitize = SANITIZE_NAMES;
+ else if (sanitize == SANITIZE_NAMES)
+ sanitize = SANITIZE_COLLISIONS;
break;
case 'w':
walk_trees = 1;
usage_error++;
}
} else {
- if (walk_trees || sanitize || compress_level) {
+ if (walk_trees || sanitize != SANITIZE_NONE || compress_level) {
error(
"useing -w, -s, -c options for restore makes no sense");
usage_error++;