From 0b8f9cff0018e92f9cf3a110f1fda259d60d4606 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mar 24 2010 19:33:27 +0000 Subject: - bring btrfs-progs uptodate with upstream, add btrfs command and other features. --- diff --git a/btrfs-progs-build-everything.patch b/btrfs-progs-build-everything.patch index 522e6c8..b70a656 100644 --- a/btrfs-progs-build-everything.patch +++ b/btrfs-progs-build-everything.patch @@ -1,12 +1,12 @@ -diff -up btrfs-progs-0.19/Makefile.orig btrfs-progs-0.19/Makefile ---- btrfs-progs-0.19/Makefile.orig 2009-06-19 08:05:49.151573429 -0400 -+++ btrfs-progs-0.19/Makefile 2009-06-19 08:08:35.192509803 -0400 -@@ -16,7 +16,7 @@ prefix ?= /usr/local - bindir = $(prefix)/bin +--- btrfs-progs-0.19/Makefile.orig 2010-03-24 14:45:18.001490900 -0400 ++++ btrfs-progs-0.19/Makefile 2010-03-24 14:45:35.745491310 -0400 +@@ -17,8 +17,7 @@ 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-image btrfstune + progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ +- btrfs \ +- btrfs-map-logical ++ btrfs btrfs-map-logical btrfstune btrfs-image # make C=1 to enable sparse ifdef C diff --git a/btrfs-progs-upstream.patch b/btrfs-progs-upstream.patch index 3186e28..2f41e3d 100644 --- a/btrfs-progs-upstream.patch +++ b/btrfs-progs-upstream.patch @@ -1,3 +1,2524 @@ +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", "[|