btrfs-progs/btrfs-list.c
Miao Xie 7cb1cf7542 Btrfs-progs: fix wrong way to check if the root item contains otime and uuid
Now we check if the root item contains otime and uuid or not by comparing
->generation_v2 and ->generation of the btrfs_root_item structure, it is
wrong because it is possbile that ->generation may equal to the first
variant of the next item. We fix this problem by check the size of btrfs_root_item,
if it is larger than the original one, the new btrfs_root_item contains otime
and uuid. we needn't worry the case that the new filesystem is mounted on the
old kernel. because the otime and uuid are not changed on the old kernel, we can
get the correct result even on the kernel.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-10-04 16:26:32 -04:00

1341 lines
31 KiB
C

/*
* 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.
*/
#define _GNU_SOURCE
#ifndef __CHECKER__
#include <sys/ioctl.h>
#include <sys/mount.h>
#include "ioctl.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <libgen.h>
#include "kerncompat.h"
#include "ctree.h"
#include "transaction.h"
#include "utils.h"
#include <uuid/uuid.h>
#include "btrfs-list.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;
/* generation when the root is created or last updated */
u64 gen;
/* creation time of this root in sec*/
time_t otime;
u8 uuid[BTRFS_UUID_SIZE];
/* 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;
}
static int comp_entry_with_gen(struct root_info *entry, u64 root_id,
u64 ref_tree, u64 gen)
{
if (entry->gen < gen)
return 1;
if (entry->gen > gen)
return -1;
return comp_entry(entry, root_id, ref_tree);
}
/*
* 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, u64 *gen, 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);
if (!gen)
comp = comp_entry(entry, root_id, ref_tree);
else
comp = comp_entry_with_gen(entry, root_id, ref_tree,
*gen);
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, u64 *gen, time_t ot, void *uuid)
{
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;
if (name)
strncpy(ri->name, name, name_len);
if (name_len > 0)
ri->name[name_len] = 0;
if (gen)
ri->gen = *gen;
ri->otime = ot;
if (uuid)
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
else
memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
ret = tree_insert(&root_lookup->root, root_id, ref_tree, gen,
&ri->rb_node);
if (ret) {
printf("failed to insert tree %llu\n", (unsigned long long)root_id);
exit(1);
}
return 0;
}
static int update_root(struct root_lookup *root_lookup, u64 root_id, u64 gen,
time_t ot, void *uuid)
{
struct root_info *ri;
ri = tree_search(&root_lookup->root, root_id);
if (!ri || ri->root_id != root_id) {
fprintf(stderr, "could not find subvol %llu\n", root_id);
return -ENOENT;
}
ri->gen = gen;
ri->otime = ot;
if (uuid)
memcpy(&ri->uuid, uuid, BTRFS_UUID_SIZE);
else
memset(&ri->uuid, 0, BTRFS_UUID_SIZE);
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 *parent_id, u64 *top_id, char **path)
{
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.
*/
*parent_id = 0;
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;
/* record the first parent */
if (*parent_id == 0)
*parent_id = next;
/* 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;
}
}
*path = 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, e;
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);
e = errno;
if (ret) {
fprintf(stderr, "ERROR: Failed to lookup path for root %llu - %s\n",
(unsigned long long)ri->ref_tree,
strerror(e));
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;
int e;
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);
e = errno;
if (ret) {
fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
(unsigned long long)BTRFS_FIRST_FREE_OBJECTID,
strerror(e));
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);
e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(e));
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;
int e;
memset(&args, 0, sizeof(args));
args.objectid = dirid;
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
e = errno;
if (ret) {
fprintf(stderr, "ERROR: Failed to lookup path for dirid %llu - %s\n",
(unsigned long long)dirid, strerror(e) );
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;
int e;
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);
e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(e));
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;
}
static int get_default_subvolid(int fd, u64 *default_id)
{
struct btrfs_ioctl_search_args args;
struct btrfs_ioctl_search_key *sk = &args.key;
struct btrfs_ioctl_search_header *sh;
u64 found = 0;
int ret;
memset(&args, 0, sizeof(args));
/*
* search for a dir item with a name 'default' in the tree of
* tree roots, it should point us to a default root
*/
sk->tree_id = 1;
/* don't worry about ancient format and request only one item */
sk->nr_items = 1;
sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
sk->max_type = BTRFS_DIR_ITEM_KEY;
sk->min_type = BTRFS_DIR_ITEM_KEY;
sk->max_offset = (u64)-1;
sk->max_transid = (u64)-1;
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
if (ret < 0)
return ret;
/* the ioctl returns the number of items it found in nr_items */
if (sk->nr_items == 0)
goto out;
sh = (struct btrfs_ioctl_search_header *)args.buf;
if (sh->type == BTRFS_DIR_ITEM_KEY) {
struct btrfs_dir_item *di;
int name_len;
char *name;
di = (struct btrfs_dir_item *)(sh + 1);
name_len = btrfs_stack_dir_name_len(di);
name = (char *)(di + 1);
if (!strncmp("default", name, name_len))
found = btrfs_disk_key_objectid(&di->location);
}
out:
*default_id = found;
return 0;
}
static int __list_subvol_search(int fd, struct root_lookup *root_lookup)
{
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;
struct btrfs_root_item *ri;
unsigned long off = 0;
int name_len;
char *name;
u64 dir_id;
u8 type;
u64 gen = 0;
int i;
int get_gen = 0;
time_t t;
u8 uuid[BTRFS_UUID_SIZE];
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;
sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
/*
* set all the other params to the max, we'll take any objectid
* and any trans
*/
sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
sk->max_offset = (u64)-1;
sk->max_transid = (u64)-1;
again:
/* 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)
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 (!get_gen && 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, NULL, 0, NULL);
} else if (get_gen && sh->type == BTRFS_ROOT_ITEM_KEY) {
ri = (struct btrfs_root_item *)(args.buf + off);
gen = btrfs_root_generation(ri);
if(sh->len >
sizeof(struct btrfs_root_item_v0)) {
t = ri->otime.sec;
memcpy(uuid, ri->uuid, BTRFS_UUID_SIZE);
} else {
t = 0;
memset(uuid, 0, BTRFS_UUID_SIZE);
}
update_root(root_lookup, sh->objectid, gen, t,
uuid);
}
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 (get_gen)
type = BTRFS_ROOT_ITEM_KEY;
else
type = BTRFS_ROOT_BACKREF_KEY;
if (sk->min_type < type) {
sk->min_type = type;
sk->min_offset = 0;
} else if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
sk->min_objectid++;
sk->min_type = type;
sk->min_offset = 0;
} else
break;
}
if (!get_gen) {
memset(&args, 0, sizeof(args));
sk->tree_id = 1;
sk->max_type = BTRFS_ROOT_ITEM_KEY;
sk->min_type = BTRFS_ROOT_ITEM_KEY;
sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
sk->max_offset = (u64)-1;
sk->max_transid = (u64)-1;
get_gen = 1;
goto again;
}
return 0;
}
static int __list_snapshot_search(int fd, struct root_lookup *root_lookup)
{
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 gen = 0;
int i;
root_lookup_init(root_lookup);
memset(&args, 0, sizeof(args));
sk->tree_id = 1;
sk->max_type = BTRFS_ROOT_ITEM_KEY;
sk->min_type = BTRFS_ROOT_ITEM_KEY;
sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
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)
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++) {
struct btrfs_root_item *item;
time_t t;
u8 uuid[BTRFS_UUID_SIZE];
sh = (struct btrfs_ioctl_search_header *)(args.buf +
off);
off += sizeof(*sh);
if (sh->type == BTRFS_ROOT_ITEM_KEY && sh->offset) {
item = (struct btrfs_root_item *)(args.buf + off);
if(sh->len >
sizeof(struct btrfs_root_item_v0)) {
t = item->otime.sec;
memcpy(uuid, item->uuid,
BTRFS_UUID_SIZE);
} else {
t = 0;
memset(uuid, 0, BTRFS_UUID_SIZE);
}
gen = sh->offset;
add_root(root_lookup, sh->objectid, 0,
0, NULL, 0, &gen, t, uuid);
}
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_type < BTRFS_ROOT_ITEM_KEY) {
sk->min_type = BTRFS_ROOT_ITEM_KEY;
sk->min_offset = 0;
} else if (sk->min_objectid < BTRFS_LAST_FREE_OBJECTID) {
sk->min_objectid++;
sk->min_type = BTRFS_ROOT_ITEM_KEY;
sk->min_offset = 0;
} else
break;
}
return 0;
}
static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
{
struct rb_node *n;
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);
}
return 0;
}
int list_subvols(int fd, int print_parent, int get_default, int print_uuid)
{
struct root_lookup root_lookup;
struct rb_node *n;
u64 default_id;
int ret;
char uuidparse[37];
if (get_default) {
ret = get_default_subvolid(fd, &default_id);
if (ret) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(errno));
return ret;
}
if (default_id == 0) {
fprintf(stderr, "ERROR: 'default' dir item not found\n");
return ret;
}
/* no need to resolve roots if FS_TREE is default */
if (default_id == BTRFS_FS_TREE_OBJECTID) {
printf("ID 5 (FS_TREE)\n");
return ret;
}
}
ret = __list_subvol_search(fd, &root_lookup);
if (ret) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(errno));
return ret;
}
/*
* 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.
*/
ret = __list_subvol_fill_paths(fd, &root_lookup);
if (ret < 0)
return ret;
/* 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;
u64 level;
u64 parent_id;
char *path;
entry = rb_entry(n, struct root_info, rb_node);
if (get_default && entry->root_id != default_id) {
n = rb_prev(n);
continue;
}
resolve_root(&root_lookup, entry, &parent_id, &level, &path);
if (print_parent) {
if (print_uuid) {
if (uuid_is_null(entry->uuid))
strcpy(uuidparse, "-");
else
uuid_unparse(entry->uuid, uuidparse);
printf("ID %llu gen %llu parent %llu top level %llu"
" uuid %s path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)parent_id,
(unsigned long long)level,
uuidparse, path);
} else {
printf("ID %llu gen %llu parent %llu top level"
" %llu path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)parent_id,
(unsigned long long)level, path);
}
} else {
if (print_uuid) {
if (uuid_is_null(entry->uuid))
strcpy(uuidparse, "-");
else
uuid_unparse(entry->uuid, uuidparse);
printf("ID %llu gen %llu top level %llu"
" uuid %s path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)level,
uuidparse, path);
} else {
printf("ID %llu gen %llu top level %llu path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)level, path);
}
}
free(path);
n = rb_prev(n);
}
return ret;
}
int list_snapshots(int fd, int print_parent, int order, int print_uuid)
{
struct root_lookup root_lookup;
struct root_lookup root_lookup_snap;
struct rb_node *n;
int ret;
ret = __list_snapshot_search(fd, &root_lookup_snap);
if (ret) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(errno));
return ret;
}
ret = __list_subvol_search(fd, &root_lookup);
if (ret) {
fprintf(stderr, "ERROR: can't perform the search - %s\n",
strerror(errno));
return ret;
}
/*
* 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.
*/
ret = __list_subvol_fill_paths(fd, &root_lookup);
if (ret < 0)
return ret;
/* 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
*/
if (!order)
n = rb_last(&root_lookup_snap.root);
else
n = rb_first(&root_lookup_snap.root);
while (n) {
struct root_info *entry_snap;
struct root_info *entry;
u64 level;
u64 parent_id;
char *path;
time_t t;
char tstr[256];
char uuidparse[37];
entry_snap = rb_entry(n, struct root_info, rb_node);
entry = tree_search(&root_lookup.root, entry_snap->root_id);
resolve_root(&root_lookup, entry, &parent_id, &level, &path);
t = entry->otime;
if(t)
strftime(tstr,256,"%Y-%m-%d %X",localtime(&t));
else
strcpy(tstr,"-");
if (print_parent) {
if (print_uuid) {
if (uuid_is_null(entry->uuid))
strcpy(uuidparse, "-");
else
uuid_unparse(entry->uuid, uuidparse);
printf("ID %llu gen %llu cgen %llu parent %llu"
" top level %llu otime %s uuid %s path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)entry_snap->gen,
(unsigned long long)parent_id,
(unsigned long long)level,
tstr, uuidparse, path);
} else {
printf("ID %llu gen %llu cgen %llu parent %llu"
" top level %llu otime %s path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)entry_snap->gen,
(unsigned long long)parent_id,
(unsigned long long)level, tstr, path);
}
} else {
if (print_uuid) {
if (uuid_is_null(entry->uuid))
strcpy(uuidparse, "-");
else
uuid_unparse(entry->uuid, uuidparse);
printf("ID %llu gen %llu cgen %llu top level %llu "
"otime %s uuid %s path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)entry_snap->gen,
(unsigned long long)level,
tstr, uuidparse, path);
} else {
printf("ID %llu gen %llu cgen %llu top level %llu "
"otime %s path %s\n",
(unsigned long long)entry->root_id,
(unsigned long long)entry->gen,
(unsigned long long)entry_snap->gen,
(unsigned long long)level, tstr, path);
}
}
free(path);
if (!order)
n = rb_prev(n);
else
n = rb_next(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 = 0;
u64 disk_start = 0;
u64 disk_offset = 0;
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);
} else {
printf("unhandled extent type %d for inode %llu "
"file offset %llu gen %llu\n",
type,
(unsigned long long)sh->objectid,
(unsigned long long)sh->offset,
(unsigned long long)found_gen);
return -EIO;
}
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;
int e;
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);
e = errno;
if (ret < 0) {
fprintf(stderr, "ERROR: can't perform the search- %s\n",
strerror(e));
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;
}
char *path_for_root(int fd, u64 root)
{
struct root_lookup root_lookup;
struct rb_node *n;
char *ret_path = NULL;
int ret;
ret = __list_subvol_search(fd, &root_lookup);
if (ret < 0)
return ERR_PTR(ret);
ret = __list_subvol_fill_paths(fd, &root_lookup);
if (ret < 0)
return ERR_PTR(ret);
n = rb_last(&root_lookup.root);
while (n) {
struct root_info *entry;
u64 parent_id;
u64 level;
char *path;
entry = rb_entry(n, struct root_info, rb_node);
resolve_root(&root_lookup, entry, &parent_id, &level, &path);
if (entry->root_id == root)
ret_path = path;
else
free(path);
n = rb_prev(n);
}
return ret_path;
}