Add btrfs subvol find-new command

btrfs-subvol find-new <subvol> <id> will search through a given subvol
and print out all the files with extents newer than a given id.

Care must be taken to make sure any pending delalloc is on disk before
running this because that won't show up in the output.
master
Chris Mason 2010-03-18 12:32:32 -04:00
parent fb3a539c92
commit 8f55b769c7
6 changed files with 464 additions and 10 deletions

View File

@ -303,6 +303,238 @@ static int lookup_ino_path(int fd, struct root_info *ri)
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;
@ -363,14 +595,15 @@ int list_subvols(int fd)
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);
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);
add_root(&root_lookup, sh->objectid, sh->offset,
dir_id, name, name_len);
}
off += sh->len;
@ -386,9 +619,11 @@ int list_subvols(int fd)
/* this iteration is done, step forward one root for the next
* ioctl
*/
if (sk->min_objectid < (u64)-1)
if (sk->min_objectid < (u64)-1) {
sk->min_objectid++;
else
sk->min_type = BTRFS_ROOT_BACKREF_KEY;
sk->min_offset = 0;
} else
break;
}
/*
@ -420,3 +655,171 @@ int list_subvols(int fd)
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;
}

View File

@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include "kerncompat.h"
#include "btrfs_cmds.h"
#include "version.h"
@ -60,6 +61,9 @@ static struct Command commands[] = {
{ do_subvol_list, 1, "subvolume list", "<path>\n"
"List the snapshot/subvolume of a filesystem."
},
{ do_find_newer, 2, "subvolume find-new", "<path> <last_gen>\n"
"List the recently modified files in a filesystem."
},
{ do_defrag, -1,
"filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]\n"
"Defragment a file or a directory."

View File

@ -247,6 +247,37 @@ int do_defrag(int ac, char **av)
return errors + 20;
}
int do_find_newer(int argc, char **argv)
{
int fd;
int ret;
char *subvol;
u64 last_gen;
subvol = argv[1];
last_gen = atoll(argv[2]);
ret = test_issubvolume(subvol);
if (ret < 0) {
fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
return 12;
}
if (!ret) {
fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
return 13;
}
fd = open_file_or_dir(subvol);
if (fd < 0) {
fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
return 12;
}
ret = find_updated_files(fd, 0, last_gen);
if (ret)
return 19;
return 0;
}
int do_subvol_list(int argc, char **argv)
{
int fd;

View File

@ -30,3 +30,5 @@ int do_subvol_list(int nargs, char **argv);
int do_set_default_subvol(int nargs, char **argv);
int list_subvols(int fd);
int do_df_filesystem(int nargs, char **argv);
int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
int do_find_newer(int argc, char **argv);

14
ctree.h
View File

@ -1047,6 +1047,7 @@ BTRFS_SETGET_STACK_FUNCS(block_group_flags,
/* struct btrfs_inode_ref */
BTRFS_SETGET_FUNCS(inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
BTRFS_SETGET_STACK_FUNCS(stack_inode_ref_name_len, struct btrfs_inode_ref, name_len, 16);
BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
/* struct btrfs_inode_item */
@ -1576,6 +1577,7 @@ static inline unsigned long btrfs_leaf_data(struct extent_buffer *l)
/* struct btrfs_file_extent_item */
BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item, type, 8);
static inline unsigned long btrfs_file_extent_inline_start(struct
btrfs_file_extent_item *e)
@ -1592,18 +1594,30 @@ static inline u32 btrfs_file_extent_calc_inline_size(u32 datasize)
BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item,
disk_bytenr, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_bytenr, struct btrfs_file_extent_item,
disk_bytenr, 64);
BTRFS_SETGET_FUNCS(file_extent_generation, struct btrfs_file_extent_item,
generation, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_generation, struct btrfs_file_extent_item,
generation, 64);
BTRFS_SETGET_FUNCS(file_extent_disk_num_bytes, struct btrfs_file_extent_item,
disk_num_bytes, 64);
BTRFS_SETGET_FUNCS(file_extent_offset, struct btrfs_file_extent_item,
offset, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_offset, struct btrfs_file_extent_item,
offset, 64);
BTRFS_SETGET_FUNCS(file_extent_num_bytes, struct btrfs_file_extent_item,
num_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_num_bytes, struct btrfs_file_extent_item,
num_bytes, 64);
BTRFS_SETGET_FUNCS(file_extent_ram_bytes, struct btrfs_file_extent_item,
ram_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_ram_bytes, struct btrfs_file_extent_item,
ram_bytes, 64);
BTRFS_SETGET_FUNCS(file_extent_compression, struct btrfs_file_extent_item,
compression, 8);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression, struct btrfs_file_extent_item,
compression, 8);
BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
encryption, 8);
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,

View File

@ -72,7 +72,7 @@ struct btrfs_ioctl_search_header {
__u64 offset;
__u32 type;
__u32 len;
};
} __attribute__((may_alias));
#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
/*