diff --git a/Makefile b/Makefile index 8097b5a..525676e 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CFLAGS = -g -Werror -Os objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ - volumes.o utils.o + volumes.o utils.o btrfs-list.o # CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ @@ -16,7 +16,9 @@ prefix ?= /usr/local bindir = $(prefix)/bin LIBS=-luuid -progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck +progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ + btrfs \ + btrfs-map-logical # make C=1 to enable sparse ifdef C @@ -35,6 +37,10 @@ all: version $(progs) manpages version: bash version.sh +btrfs: $(objects) btrfs.o btrfs_cmds.o + gcc $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o \ + $(objects) $(LDFLAGS) $(LIBS) + btrfsctl: $(objects) btrfsctl.o gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) @@ -56,6 +62,9 @@ btrfs-debug-tree: $(objects) debug-tree.o btrfstune: $(objects) btrfstune.o gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS) +btrfs-map-logical: $(objects) btrfs-map-logical.o + gcc $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) + btrfs-image: $(objects) btrfs-image.o gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) @@ -68,6 +77,9 @@ quick-test: $(objects) quick-test.o convert: $(objects) convert.o gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) $(LIBS) +ioctl-test: $(objects) ioctl-test.o + gcc $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS) + manpages: cd man; make diff --git a/btrfs-defrag.c b/btrfs-defrag.c new file mode 100644 index 0000000..8f1525a --- /dev/null +++ b/btrfs-defrag.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef __CHECKER__ +#include +#include +#include "ioctl.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" + diff --git a/btrfs-list.c b/btrfs-list.c new file mode 100644 index 0000000..7741705 --- /dev/null +++ b/btrfs-list.c @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef __CHECKER__ +#include +#include +#include "ioctl.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" + +/* we store all the roots we find in an rbtree so that we can + * search for them later. + */ +struct root_lookup { + struct rb_root root; +}; + +/* + * one of these for each root we find. + */ +struct root_info { + struct rb_node rb_node; + + /* this root's id */ + u64 root_id; + + /* the id of the root that references this one */ + u64 ref_tree; + + /* the dir id we're in from ref_tree */ + u64 dir_id; + + /* path from the subvol we live in to this root, including the + * root's name. This is null until we do the extra lookup ioctl. + */ + char *path; + + /* the name of this root in the directory it lives in */ + char name[]; +}; + +static void root_lookup_init(struct root_lookup *tree) +{ + tree->root.rb_node = NULL; +} + +static int comp_entry(struct root_info *entry, u64 root_id, u64 ref_tree) +{ + if (entry->root_id > root_id) + return 1; + if (entry->root_id < root_id) + return -1; + if (entry->ref_tree > ref_tree) + return 1; + if (entry->ref_tree < ref_tree) + return -1; + return 0; +} + +/* + * insert a new root into the tree. returns the existing root entry + * if one is already there. Both root_id and ref_tree are used + * as the key + */ +static struct rb_node *tree_insert(struct rb_root *root, u64 root_id, + u64 ref_tree, struct rb_node *node) +{ + struct rb_node ** p = &root->rb_node; + struct rb_node * parent = NULL; + struct root_info *entry; + int comp; + + while(*p) { + parent = *p; + entry = rb_entry(parent, struct root_info, rb_node); + + comp = comp_entry(entry, root_id, ref_tree); + + if (comp < 0) + p = &(*p)->rb_left; + else if (comp > 0) + p = &(*p)->rb_right; + else + return parent; + } + + entry = rb_entry(parent, struct root_info, rb_node); + rb_link_node(node, parent, p); + rb_insert_color(node, root); + return NULL; +} + +/* + * find a given root id in the tree. We return the smallest one, + * rb_next can be used to move forward looking for more if required + */ +static struct root_info *tree_search(struct rb_root *root, u64 root_id) +{ + struct rb_node * n = root->rb_node; + struct root_info *entry; + + while(n) { + entry = rb_entry(n, struct root_info, rb_node); + + if (entry->root_id < root_id) + n = n->rb_left; + else if (entry->root_id > root_id) + n = n->rb_right; + else { + struct root_info *prev; + struct rb_node *prev_n; + while (1) { + prev_n = rb_prev(n); + if (!prev_n) + break; + prev = rb_entry(prev_n, struct root_info, + rb_node); + if (prev->root_id != root_id) + break; + entry = prev; + n = prev_n; + } + return entry; + } + } + return NULL; +} + +/* + * this allocates a new root in the lookup tree. + * + * root_id should be the object id of the root + * + * ref_tree is the objectid of the referring root. + * + * dir_id is the directory in ref_tree where this root_id can be found. + * + * name is the name of root_id in that directory + * + * name_len is the length of name + */ +static int add_root(struct root_lookup *root_lookup, + u64 root_id, u64 ref_tree, u64 dir_id, char *name, + int name_len) +{ + struct root_info *ri; + struct rb_node *ret; + ri = malloc(sizeof(*ri) + name_len + 1); + if (!ri) { + printf("memory allocation failed\n"); + exit(1); + } + memset(ri, 0, sizeof(*ri) + name_len + 1); + ri->path = NULL; + ri->dir_id = dir_id; + ri->root_id = root_id; + ri->ref_tree = ref_tree; + strncpy(ri->name, name, name_len); + + ret = tree_insert(&root_lookup->root, root_id, ref_tree, &ri->rb_node); + if (ret) { + printf("failed to insert tree %llu\n", (unsigned long long)root_id); + exit(1); + } + return 0; +} + +/* + * for a given root_info, search through the root_lookup tree to construct + * the full path name to it. + * + * This can't be called until all the root_info->path fields are filled + * in by lookup_ino_path + */ +static int resolve_root(struct root_lookup *rl, struct root_info *ri) +{ + u64 top_id; + char *full_path = NULL; + int len = 0; + struct root_info *found; + + /* + * we go backwards from the root_info object and add pathnames + * from parent directories as we go. + */ + found = ri; + while (1) { + char *tmp; + u64 next; + int add_len = strlen(found->path); + + /* room for / and for null */ + tmp = malloc(add_len + 2 + len); + if (full_path) { + memcpy(tmp + add_len + 1, full_path, len); + tmp[add_len] = '/'; + memcpy(tmp, found->path, add_len); + tmp [add_len + len + 1] = '\0'; + free(full_path); + full_path = tmp; + len += add_len + 1; + } else { + full_path = strdup(found->path); + len = add_len; + } + + next = found->ref_tree; + /* if the ref_tree refers to ourselves, we're at the top */ + if (next == found->root_id) { + top_id = next; + break; + } + + /* + * if the ref_tree wasn't in our tree of roots, we're + * at the top + */ + found = tree_search(&rl->root, next); + if (!found) { + top_id = next; + break; + } + } + printf("ID %llu top level %llu path %s\n", ri->root_id, top_id, + full_path); + free(full_path); + return 0; +} + +/* + * for a single root_info, ask the kernel to give us a path name + * inside it's ref_root for the dir_id where it lives. + * + * This fills in root_info->path with the path to the directory and and + * appends this root's name. + */ +static int lookup_ino_path(int fd, struct root_info *ri) +{ + struct btrfs_ioctl_ino_lookup_args args; + int ret; + + if (ri->path) + return 0; + + memset(&args, 0, sizeof(args)); + args.treeid = ri->ref_tree; + args.objectid = ri->dir_id; + + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); + if (ret) { + fprintf(stderr, "ERROR: Failed to lookup path for root %llu\n", + (unsigned long long)ri->ref_tree); + return ret; + } + + if (args.name[0]) { + /* + * we're in a subdirectory of ref_tree, the kernel ioctl + * puts a / in there for us + */ + ri->path = malloc(strlen(ri->name) + strlen(args.name) + 1); + if (!ri->path) { + perror("malloc failed"); + exit(1); + } + strcpy(ri->path, args.name); + strcat(ri->path, ri->name); + } else { + /* we're at the root of ref_tree */ + ri->path = strdup(ri->name); + if (!ri->path) { + perror("strdup failed"); + exit(1); + } + } + return 0; +} + +/* finding the generation for a given path is a two step process. + * First we use the inode loookup routine to find out the root id + * + * Then we use the tree search ioctl to scan all the root items for a + * given root id and spit out the latest generation we can find + */ +static u64 find_root_gen(int fd) +{ + struct btrfs_ioctl_ino_lookup_args ino_args; + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + unsigned long off = 0; + u64 max_found = 0; + int i; + + memset(&ino_args, 0, sizeof(ino_args)); + ino_args.objectid = BTRFS_FIRST_FREE_OBJECTID; + + /* this ioctl fills in ino_args->treeid */ + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args); + if (ret) { + fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n", + (unsigned long long)BTRFS_FIRST_FREE_OBJECTID); + return 0; + } + + memset(&args, 0, sizeof(args)); + + sk->tree_id = 1; + + /* + * there may be more than one ROOT_ITEM key if there are + * snapshots pending deletion, we have to loop through + * them. + */ + sk->min_objectid = ino_args.treeid; + sk->max_objectid = ino_args.treeid; + sk->max_type = BTRFS_ROOT_ITEM_KEY; + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; + sk->nr_items = 4096; + + while (1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search\n"); + return 0; + } + /* the ioctl returns the number of item it found in nr_items */ + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + struct btrfs_root_item *item; + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + + off += sizeof(*sh); + item = (struct btrfs_root_item *)(args.buf + off); + off += sh->len; + + sk->min_objectid = sh->objectid; + sk->min_type = sh->type; + sk->min_offset = sh->offset; + + if (sh->objectid > ino_args.treeid) + break; + + if (sh->objectid == ino_args.treeid && + sh->type == BTRFS_ROOT_ITEM_KEY) { + max_found = max(max_found, + btrfs_root_generation(item)); + } + } + if (sk->min_offset < (u64)-1) + sk->min_offset++; + else + break; + + if (sk->min_type != BTRFS_ROOT_ITEM_KEY) + break; + if (sk->min_objectid != BTRFS_ROOT_ITEM_KEY) + break; + } + return max_found; +} + +/* pass in a directory id and this will return + * the full path of the parent directory inside its + * subvolume root. + * + * It may return NULL if it is in the root, or an ERR_PTR if things + * go badly. + */ +static char *__ino_resolve(int fd, u64 dirid) +{ + struct btrfs_ioctl_ino_lookup_args args; + int ret; + char *full; + + memset(&args, 0, sizeof(args)); + args.objectid = dirid; + + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); + if (ret) { + fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu\n", + (unsigned long long)dirid); + return ERR_PTR(ret); + } + + if (args.name[0]) { + /* + * we're in a subdirectory of ref_tree, the kernel ioctl + * puts a / in there for us + */ + full = strdup(args.name); + if (!full) { + perror("malloc failed"); + return ERR_PTR(-ENOMEM); + } + } else { + /* we're at the root of ref_tree */ + full = NULL; + } + return full; +} + +/* + * simple string builder, returning a new string with both + * dirid and name + */ +char *build_name(char *dirid, char *name) +{ + char *full; + if (!dirid) + return strdup(name); + + full = malloc(strlen(dirid) + strlen(name) + 1); + if (!full) + return NULL; + strcpy(full, dirid); + strcat(full, name); + return full; +} + +/* + * given an inode number, this returns the full path name inside the subvolume + * to that file/directory. cache_dirid and cache_name are used to + * cache the results so we can avoid tree searches if a later call goes + * to the same directory or file name + */ +static char *ino_resolve(int fd, u64 ino, u64 *cache_dirid, char **cache_name) + +{ + u64 dirid; + char *dirname; + char *name; + char *full; + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + unsigned long off = 0; + int namelen; + + memset(&args, 0, sizeof(args)); + + sk->tree_id = 0; + + /* + * step one, we search for the inode back ref. We just use the first + * one + */ + sk->min_objectid = ino; + sk->max_objectid = ino; + sk->max_type = BTRFS_INODE_REF_KEY; + sk->max_offset = (u64)-1; + sk->min_type = BTRFS_INODE_REF_KEY; + sk->max_transid = (u64)-1; + sk->nr_items = 1; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search\n"); + return NULL; + } + /* the ioctl returns the number of item it found in nr_items */ + if (sk->nr_items == 0) + return NULL; + + off = 0; + sh = (struct btrfs_ioctl_search_header *)(args.buf + off); + + if (sh->type == BTRFS_INODE_REF_KEY) { + struct btrfs_inode_ref *ref; + dirid = sh->offset; + + ref = (struct btrfs_inode_ref *)(sh + 1); + namelen = btrfs_stack_inode_ref_name_len(ref); + + name = (char *)(ref + 1); + name = strndup(name, namelen); + + /* use our cached value */ + if (dirid == *cache_dirid && *cache_name) { + dirname = *cache_name; + goto build; + } + } else { + return NULL; + } + /* + * the inode backref gives us the file name and the parent directory id. + * From here we use __ino_resolve to get the path to the parent + */ + dirname = __ino_resolve(fd, dirid); +build: + full = build_name(dirname, name); + if (*cache_name && dirname != *cache_name) + free(*cache_name); + + *cache_name = dirname; + *cache_dirid = dirid; + free(name); + + return full; +} + +int list_subvols(int fd) +{ + struct root_lookup root_lookup; + struct rb_node *n; + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + struct btrfs_root_ref *ref; + unsigned long off = 0; + int name_len; + char *name; + u64 dir_id; + int i; + + root_lookup_init(&root_lookup); + + memset(&args, 0, sizeof(args)); + + /* search in the tree of tree roots */ + sk->tree_id = 1; + + /* + * set the min and max to backref keys. The search will + * only send back this type of key now. + */ + sk->max_type = BTRFS_ROOT_BACKREF_KEY; + sk->min_type = BTRFS_ROOT_BACKREF_KEY; + + /* + * set all the other params to the max, we'll take any objectid + * and any trans + */ + sk->max_objectid = (u64)-1; + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; + + /* just a big number, doesn't matter much */ + sk->nr_items = 4096; + + while(1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search\n"); + return ret; + } + /* the ioctl returns the number of item it found in nr_items */ + if (sk->nr_items == 0) + break; + + off = 0; + + /* + * for each item, pull the key out of the header and then + * read the root_ref item it contains + */ + for (i = 0; i < sk->nr_items; i++) { + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + off += sizeof(*sh); + if (sh->type == BTRFS_ROOT_BACKREF_KEY) { + ref = (struct btrfs_root_ref *)(args.buf + off); + name_len = btrfs_stack_root_ref_name_len(ref); + name = (char *)(ref + 1); + dir_id = btrfs_stack_root_ref_dirid(ref); + + add_root(&root_lookup, sh->objectid, sh->offset, + dir_id, name, name_len); + } + + off += sh->len; + + /* + * record the mins in sk so we can make sure the + * next search doesn't repeat this root + */ + sk->min_objectid = sh->objectid; + sk->min_type = sh->type; + sk->min_offset = sh->offset; + } + sk->nr_items = 4096; + /* this iteration is done, step forward one root for the next + * ioctl + */ + if (sk->min_objectid < (u64)-1) { + sk->min_objectid++; + sk->min_type = BTRFS_ROOT_BACKREF_KEY; + sk->min_offset = 0; + } else + break; + } + /* + * now we have an rbtree full of root_info objects, but we need to fill + * in their path names within the subvol that is referencing each one. + */ + n = rb_first(&root_lookup.root); + while (n) { + struct root_info *entry; + int ret; + entry = rb_entry(n, struct root_info, rb_node); + ret = lookup_ino_path(fd, entry); + if(ret < 0) + return ret; + n = rb_next(n); + } + + /* now that we have all the subvol-relative paths filled in, + * we have to string the subvols together so that we can get + * a path all the way back to the FS root + */ + n = rb_last(&root_lookup.root); + while (n) { + struct root_info *entry; + entry = rb_entry(n, struct root_info, rb_node); + resolve_root(&root_lookup, entry); + n = rb_prev(n); + } + + return ret; +} + +static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, + struct btrfs_file_extent_item *item, + u64 found_gen, u64 *cache_dirid, + char **cache_dir_name, u64 *cache_ino, + char **cache_full_name) +{ + u64 len; + u64 disk_start; + u64 disk_offset; + u8 type; + int compressed = 0; + int flags = 0; + char *name = NULL; + + if (sh->objectid == *cache_ino) { + name = *cache_full_name; + } else if (*cache_full_name) { + free(*cache_full_name); + *cache_full_name = NULL; + } + if (!name) { + name = ino_resolve(fd, sh->objectid, cache_dirid, + cache_dir_name); + *cache_full_name = name; + *cache_ino = sh->objectid; + } + if (!name) + return -EIO; + + type = btrfs_stack_file_extent_type(item); + compressed = btrfs_stack_file_extent_compression(item); + + if (type == BTRFS_FILE_EXTENT_REG || + type == BTRFS_FILE_EXTENT_PREALLOC) { + disk_start = btrfs_stack_file_extent_disk_bytenr(item); + disk_offset = btrfs_stack_file_extent_offset(item); + len = btrfs_stack_file_extent_num_bytes(item); + } else if (type == BTRFS_FILE_EXTENT_INLINE) { + disk_start = 0; + disk_offset = 0; + len = btrfs_stack_file_extent_ram_bytes(item); + } + printf("inode %llu file offset %llu len %llu disk start %llu " + "offset %llu gen %llu flags ", + (unsigned long long)sh->objectid, + (unsigned long long)sh->offset, + (unsigned long long)len, + (unsigned long long)disk_start, + (unsigned long long)disk_offset, + (unsigned long long)found_gen); + + if (compressed) { + printf("COMPRESS"); + flags++; + } + if (type == BTRFS_FILE_EXTENT_PREALLOC) { + printf("%sPREALLOC", flags ? "|" : ""); + flags++; + } + if (type == BTRFS_FILE_EXTENT_INLINE) { + printf("%sINLINE", flags ? "|" : ""); + flags++; + } + if (!flags) + printf("NONE"); + + printf(" %s\n", name); + return 0; +} + +int find_updated_files(int fd, u64 root_id, u64 oldest_gen) +{ + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + struct btrfs_file_extent_item *item; + unsigned long off = 0; + u64 found_gen; + u64 max_found = 0; + int i; + u64 cache_dirid = 0; + u64 cache_ino = 0; + char *cache_dir_name = NULL; + char *cache_full_name = NULL; + struct btrfs_file_extent_item backup; + + memset(&backup, 0, sizeof(backup)); + memset(&args, 0, sizeof(args)); + + sk->tree_id = root_id; + + /* + * set all the other params to the max, we'll take any objectid + * and any trans + */ + sk->max_objectid = (u64)-1; + sk->max_offset = (u64)-1; + sk->max_transid = (u64)-1; + sk->max_type = BTRFS_EXTENT_DATA_KEY; + sk->min_transid = oldest_gen; + /* just a big number, doesn't matter much */ + sk->nr_items = 4096; + + max_found = find_root_gen(fd); + while(1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search\n"); + return ret; + } + /* the ioctl returns the number of item it found in nr_items */ + if (sk->nr_items == 0) + break; + + off = 0; + + /* + * for each item, pull the key out of the header and then + * read the root_ref item it contains + */ + for (i = 0; i < sk->nr_items; i++) { + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + off += sizeof(*sh); + + /* + * just in case the item was too big, pass something other + * than garbage + */ + if (sh->len == 0) + item = &backup; + else + item = (struct btrfs_file_extent_item *)(args.buf + + off); + found_gen = btrfs_stack_file_extent_generation(item); + if (sh->type == BTRFS_EXTENT_DATA_KEY && + found_gen >= oldest_gen) { + print_one_extent(fd, sh, item, found_gen, + &cache_dirid, &cache_dir_name, + &cache_ino, &cache_full_name); + } + off += sh->len; + + /* + * record the mins in sk so we can make sure the + * next search doesn't repeat this root + */ + sk->min_objectid = sh->objectid; + sk->min_offset = sh->offset; + sk->min_type = sh->type; + } + sk->nr_items = 4096; + if (sk->min_offset < (u64)-1) + sk->min_offset++; + else if (sk->min_objectid < (u64)-1) { + sk->min_objectid++; + sk->min_offset = 0; + sk->min_type = 0; + } else + break; + } + free(cache_dir_name); + free(cache_full_name); + printf("transid marker was %llu\n", (unsigned long long)max_found); + return ret; +} diff --git a/btrfs-map-logical.c b/btrfs-map-logical.c new file mode 100644 index 0000000..a109c6a --- /dev/null +++ b/btrfs-map-logical.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009 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. + */ + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include "kerncompat.h" +#include "ctree.h" +#include "volumes.h" +#include "disk-io.h" +#include "print-tree.h" +#include "transaction.h" +#include "list.h" +#include "version.h" + +/* we write the mirror info to stdout unless they are dumping the data + * to stdout + * */ +static FILE *info_file; + +struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr, + u32 blocksize, int copy) +{ + int ret; + int dev_nr; + struct extent_buffer *eb; + u64 length; + struct btrfs_multi_bio *multi = NULL; + struct btrfs_device *device; + int num_copies; + int mirror_num = 1; + + eb = btrfs_find_create_tree_block(root, bytenr, blocksize); + if (!eb) + return NULL; + + dev_nr = 0; + length = blocksize; + while (1) { + ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, + eb->start, &length, &multi, mirror_num); + BUG_ON(ret); + device = multi->stripes[0].dev; + eb->fd = device->fd; + device->total_ios++; + eb->dev_bytenr = multi->stripes[0].physical; + + fprintf(info_file, "mirror %d logical %Lu physical %Lu " + "device %s\n", mirror_num, bytenr, eb->dev_bytenr, + device->name); + kfree(multi); + + if (!copy || mirror_num == copy) + ret = read_extent_from_disk(eb); + + num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, + eb->start, eb->len); + if (num_copies == 1) + break; + + mirror_num++; + if (mirror_num > num_copies) + break; + } + return eb; +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); + fprintf(stderr, "\t-l Logical extent to map\n"); + fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); + fprintf(stderr, "\t-o Output file to hold the extent\n"); + fprintf(stderr, "\t-s Number of bytes to read\n"); + exit(1); +} + +static struct option long_options[] = { + /* { "byte-count", 1, NULL, 'b' }, */ + { "logical", 1, NULL, 'l' }, + { "copy", 1, NULL, 'c' }, + { "output", 1, NULL, 'c' }, + { "bytes", 1, NULL, 'b' }, + { 0, 0, 0, 0} +}; + +int main(int ac, char **av) +{ + struct cache_tree root_cache; + struct btrfs_root *root; + struct extent_buffer *eb; + char *dev; + char *output_file = NULL; + u64 logical = 0; + int ret = 0; + int option_index = 0; + int copy = 0; + u64 bytes = 0; + int out_fd = 0; + int err; + + while(1) { + int c; + c = getopt_long(ac, av, "l:c:o:b:", long_options, + &option_index); + if (c < 0) + break; + switch(c) { + case 'l': + logical = atoll(optarg); + if (logical == 0) { + fprintf(stderr, + "invalid extent number\n"); + print_usage(); + } + break; + case 'c': + copy = atoi(optarg); + if (copy == 0) { + fprintf(stderr, + "invalid copy number\n"); + print_usage(); + } + break; + case 'b': + bytes = atoll(optarg); + if (bytes == 0) { + fprintf(stderr, + "invalid byte count\n"); + print_usage(); + } + break; + case 'o': + output_file = strdup(optarg); + break; + default: + print_usage(); + } + } + ac = ac - optind; + if (ac == 0) + print_usage(); + if (logical == 0) + print_usage(); + if (copy < 0) + print_usage(); + + dev = av[optind]; + + radix_tree_init(); + cache_tree_init(&root_cache); + + root = open_ctree(dev, 0, 0); + if (!root) { + fprintf(stderr, "Open ctree failed\n"); + exit(1); + } + + if (output_file) { + if (strcmp(output_file, "-") == 0) { + out_fd = 1; + info_file = stderr; + } else { + out_fd = open(output_file, O_RDWR | O_CREAT, 0600); + if (out_fd < 0) + goto close; + err = ftruncate(out_fd, 0); + if (err) { + close(out_fd); + goto close; + } + info_file = stdout; + } + } + + if (bytes == 0) + bytes = root->sectorsize; + + bytes = (bytes + root->sectorsize - 1) / root->sectorsize; + bytes *= root->sectorsize; + + while (bytes > 0) { + eb = debug_read_block(root, logical, root->sectorsize, copy); + if (eb && output_file) { + err = write(out_fd, eb->data, eb->len); + if (err < 0 || err != eb->len) { + fprintf(stderr, "output file write failed\n"); + goto out_close_fd; + } + } + free_extent_buffer(eb); + logical += root->sectorsize; + bytes -= root->sectorsize; + } + +out_close_fd: + if (output_file && out_fd != 1) + close(out_fd); +close: + close_ctree(root); + return ret; +} diff --git a/btrfs.c b/btrfs.c new file mode 100644 index 0000000..ab5e57f --- /dev/null +++ b/btrfs.c @@ -0,0 +1,387 @@ +/* + * 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. + */ + + +#include +#include +#include + +#include "kerncompat.h" +#include "btrfs_cmds.h" +#include "version.h" + +typedef int (*CommandFunction)(int argc, char **argv); + +struct Command { + CommandFunction func; /* function which implements the command */ + int nargs; /* if == 999, any number of arguments + if >= 0, number of arguments, + if < 0, _minimum_ number of arguments */ + char *verb; /* verb */ + char *help; /* help lines; form the 2nd onward they are + indented */ + + /* the following fields are run-time filled by the program */ + char **cmds; /* array of subcommands */ + int ncmds; /* number of subcommand */ +}; + +static struct Command commands[] = { + + /* + avoid short commands different for the case only + */ + { do_clone, 2, + "subvolume snapshot", " [/]\n" + "Create a writable snapshot of the subvolume with\n" + "the name in the directory." + }, + { do_delete_subvolume, 1, + "subvolume delete", "\n" + "Delete the subvolume ." + }, + { do_create_subvol, 1, + "subvolume create", "[/]\n" + "Create a subvolume in (or the current directory if\n" + "not passed)." + }, + { do_subvol_list, 1, "subvolume list", "\n" + "List the snapshot/subvolume of a filesystem." + }, + { do_find_newer, 2, "subvolume find-new", " \n" + "List the recently modified files in a filesystem." + }, + { do_defrag, -1, + "filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] | [|...]\n" + "Defragment a file or a directory." + }, + { do_set_default_subvol, 2, + "subvolume set-default", " \n" + "Set the subvolume of the filesystem which will be mounted\n" + "as default." + }, + { do_fssync, 1, + "filesystem sync", "\n" + "Force a sync on the filesystem ." + }, + { do_resize, 2, + "filesystem resize", "[+/-][gkm]|max \n" + "Resize the file system. If 'max' is passed, the filesystem\n" + "will occupe all available space on the device." + }, + { do_show_filesystem, 999, + "filesystem show", "[|