From patchwork Sat Dec 11 22:47:02 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Goffredo Baroncelli X-Patchwork-Id: 400162 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oBBMgArK017359 for ; Sat, 11 Dec 2010 22:42:15 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751814Ab0LKWmG (ORCPT ); Sat, 11 Dec 2010 17:42:06 -0500 Received: from smtp204.alice.it ([82.57.200.100]:48375 "EHLO smtp204.alice.it" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751675Ab0LKWmF (ORCPT ); Sat, 11 Dec 2010 17:42:05 -0500 Received: from venice.localnet (87.6.217.145) by smtp204.alice.it (8.5.124.08) id 4C88E33B077875E2 for linux-btrfs@vger.kernel.org; Sat, 11 Dec 2010 23:42:00 +0100 To: linux-btrfs@vger.kernel.org Subject: [RFC] Improve btrfs subvolume find-new command From: Goffredo Baroncelli Reply-To: kreijack@libero.it Date: Sat, 11 Dec 2010 23:47:02 +0100 MIME-Version: 1.0 Message-Id: <201012112347.10004.kreijack@libero.it> Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Sat, 11 Dec 2010 22:42:16 +0000 (UTC) diff --git a/btrfs-list.c b/btrfs-list.c index 93766a8..3905436 100644 --- a/btrfs-list.c +++ b/btrfs-list.c @@ -310,7 +310,7 @@ static int lookup_ino_path(int fd, struct root_info *ri) * 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) +u64 find_root_gen(int fd) { struct btrfs_ioctl_ino_lookup_args ino_args; int ret; @@ -657,11 +657,43 @@ int list_subvols(int fd) return ret; } -static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, +static u64 cache_get_full_path_dirid = 0; +static u64 cache_get_full_path_ino = 0; +static char *cache_get_full_path_dir_name = NULL; +static char *cache_get_full_path_full_name = NULL; + +static void init_cache_get_full_path(void) +{ + cache_get_full_path_dirid = 0; + cache_get_full_path_ino = 0; + cache_get_full_path_dir_name = NULL; + cache_get_full_path_full_name = NULL; +} + +static char *get_full_path(int fd, struct btrfs_ioctl_search_header *sh) +{ + char *name = NULL; + + if (sh->objectid == cache_get_full_path_ino) { + name = cache_get_full_path_full_name; + } else if (cache_get_full_path_full_name) { + free(cache_get_full_path_full_name); + cache_get_full_path_full_name = NULL; + } + if (!name) { + name = ino_resolve(fd, sh->objectid, + &cache_get_full_path_dirid, + &cache_get_full_path_dir_name); + cache_get_full_path_full_name = name; + cache_get_full_path_ino = sh->objectid; + } + + return name; +} + +static int print_one_extent(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 found_gen) { u64 len = 0; u64 disk_start = 0; @@ -669,22 +701,6 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, 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); @@ -708,9 +724,8 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, return -EIO; } - printf("inode %llu file offset %llu len %llu disk start %llu " + printf("\tEXTENT: 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, @@ -732,29 +747,151 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh, if (!flags) printf("NONE"); - printf(" %s\n", name); + printf("\n"); return 0; } -int find_updated_files(int fd, u64 root_id, u64 oldest_gen) +BTRFS_SETGET_STACK_FUNCS(stack_inode_nbyte, + struct btrfs_inode_item, nbytes, 32); +int print_one_inode(struct btrfs_inode_item *item, + u64 found_gen) { - int ret; - struct btrfs_ioctl_search_args args; - struct btrfs_ioctl_search_key *sk = &args.key; + u32 mode; + + mode = btrfs_stack_inode_mode(item); + printf("\tINODE: mode 0x%08x gen %llu nbyte %llu nlink %llu uid %llu" + " gid %llu flags 0x%016llx\n", + mode, found_gen, + (unsigned long long)btrfs_stack_inode_nbyte(item), + (unsigned long long)btrfs_stack_inode_nlink(item), + (unsigned long long)btrfs_stack_inode_uid(item), + (unsigned long long)btrfs_stack_inode_gid(item), + (unsigned long long)btrfs_stack_inode_flags(item) + ); + + return 0; +} + + +BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, + struct btrfs_dir_item, name_len, 16); +BTRFS_SETGET_STACK_FUNCS(stack_dir_data_len, + struct btrfs_dir_item, data_len, 16); +static int print_one_xattr( struct btrfs_dir_item *item ) + +{ + u32 name_len; + u32 data_len; + + name_len = btrfs_stack_dir_name_len(item); + data_len = btrfs_stack_dir_data_len(item); + + printf("\tXATTR: namelen %llu datalen %llu name %.*s\n", + (unsigned long long)name_len, + (unsigned long long)data_len, + name_len, (char *)(item + 1)); + return 0; +} + + +static inline void print_filename_one_time( int fd, + struct btrfs_ioctl_search_header *sh, u64 *old_objectid, + int verbose) +{ + if ( sh->objectid != *old_objectid ){ + if(verbose >=50 ) + printf("inode %llu name ", + (unsigned long long)sh->objectid); + printf("%s\n", get_full_path(fd, sh)); + *old_objectid = sh->objectid; + } +} + + +BTRFS_SETGET_STACK_FUNCS(stack_inode_transid, + struct btrfs_inode_item, transid, 64); +static void _find_updated_files_2(int fd, + struct btrfs_ioctl_search_args *args, + u64 *old_objectid, + u64 oldest_gen, + int verbose ) +{ + 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)); + + /* + * for each item, pull the key out of the header and then + * read the root_ref item it contains + */ + for (off = 0, 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_filename_one_time(fd, sh, old_objectid, verbose); + if(verbose>=100) + print_one_extent(sh,item, found_gen); + } else if (sh->type == BTRFS_INODE_ITEM_KEY ){ + struct btrfs_inode_item *i = + (struct btrfs_inode_item*)(args->buf+off); + found_gen = btrfs_stack_inode_transid(i); + if( found_gen >= oldest_gen) { + print_filename_one_time(fd, sh, old_objectid, + verbose); + if(verbose>=100) + print_one_inode(i,found_gen); + + } + } else if (sh->type == BTRFS_XATTR_ITEM_KEY ){ + struct btrfs_dir_item *i = + (struct btrfs_dir_item*)(args->buf+off); + print_filename_one_time(fd, sh, old_objectid, verbose); + if(verbose>=100) + print_one_xattr(i); + } + + 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; + } + +} + +int find_updated_files(int fd, u64 root_id, u64 oldest_gen, int verbose) +{ + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + u64 old_objectid = -1; + memset(&args, 0, sizeof(args)); + init_cache_get_full_path(); sk->tree_id = root_id; @@ -770,7 +907,6 @@ int find_updated_files(int fd, u64 root_id, u64 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) { @@ -781,43 +917,9 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen) 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; + _find_updated_files_2( fd, &args, &old_objectid, oldest_gen, + verbose ); - /* - * 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++; @@ -828,8 +930,5 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen) } 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.c b/btrfs.c index 46314cf..1b5fe9f 100644 --- a/btrfs.c +++ b/btrfs.c @@ -61,9 +61,12 @@ static struct Command commands[] = { { do_subvol_list, 1, "subvolume list", "\n" "List the snapshot/subvolume of a filesystem." }, - { do_find_newer, 2, "subvolume find-new", " \n" + { do_find_newer, -2, "subvolume find-new", "[-v|--verbose][-s|-- subvol] \n" "List the recently modified files in a filesystem." }, + { do_get_latest_gen, 1, "subvolume last-gen", "\n" + "Return the latest generation of a filesystem." + }, { do_defrag, -1, "filesystem defragment", "[-vcf] [-s start] [-l len] [-t size] | [|...]\n" "Defragment a file or a directory." diff --git a/btrfs_cmds.c b/btrfs_cmds.c index 8031c58..9bcc280 100644 --- a/btrfs_cmds.c +++ b/btrfs_cmds.c @@ -247,16 +247,90 @@ int do_defrag(int ac, char **av) return errors + 20; } +static int _get_latest_gen(char *subvol, u64 *max_found) +{ + int fd; + int ret; + + 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; + } + *max_found = find_root_gen(fd); + return 0; +} + + +int do_get_latest_gen(int argc, char **argv) +{ + int ret; + u64 max_found = 0; + + ret = _get_latest_gen(argv[1], &max_found); + if(ret) + return ret; + printf("%llu\n", (unsigned long long)max_found); + return 0; +} + int do_find_newer(int argc, char **argv) { int fd; int ret; - char *subvol; - u64 last_gen; + char *subvol=0, *gen=0; + u64 last_gen = (u64)-1; + int i = 1; + int verbose=0; /* 0 print only file/dir name; 100 is verbose */ + int last_gen_as_subvol=0; - subvol = argv[1]; - last_gen = atoll(argv[2]); + for(i=1;i \fP .PP +\fBbtrfs\fP \fBsubvolume last-gen\fP\fI \fP +.PP +\fBbtrfs\fP \fBsubvolume find-new\fP\fI \fP +.PP \fBbtrfs\fP \fBfilesystem defrag\fP\fI | [|...]\fP .PP \fBbtrfs\fP \fBfilesystem sync\fP\fI \fP @@ -96,6 +100,21 @@ These may be used by the \fBsubvolume set-default\fR command, or at mount time via the \fIsubvol=\fR option. .TP +\fBsubvolume last-gen\fR\fI \fR +Return the most current generation id of \fI\fR. This number is +suitable for use with the \fBsubvolume find-new\fR command, for example. +A single number is sent to stdout, representing the most recent generation +within a subvolume/snapshot. + +\fBsubvolume find-new\fR\fI \fR +Display changes to the subvolume \fI\fR since the generation id +\fI\fR. The resulting information includes filenames, offset +within the file, length, and more. The last line output displays the most +recent generation id represented by the output. For example, one could +feed this id back in to get an ongoing report of changes to the +subvolume. +.TP + \fBsubvolume set-default\fR\fI \fR Set the subvolume of the filesystem \fI\fR which is mounted as