On 24.04.19 г. 17:47 ч., Johannes Thumshirn wrote:
> Add a 'btrfs inspect-internal csum-dump' command to dump the on-disk
> checksums of a file.
> 
> The dump command first uses the FIEMAP ioctl() to get a map of the file's
> extents and then uses the BTRFS_TREE_SEARCH_V2 ioctl() to get the
> checksums for these extents.
> 
> Using FIEMAP instead of the BTRFS_TREE_SEARCH_V2 ioctl() to get the
> extents allows us to quickly filter out any holes in the file, as this is
> already done for us in the kernel.
> 
> Signed-off-by: Johannes Thumshirn <jthumsh...@suse.de>
> ---
>  Makefile                 |   3 +-
>  cmds-inspect-dump-csum.c | 253 
> +++++++++++++++++++++++++++++++++++++++++++++++
>  cmds-inspect.c           |   2 +
>  commands.h               |   2 +
>  4 files changed, 259 insertions(+), 1 deletion(-)
>  create mode 100644 cmds-inspect-dump-csum.c
> 
> diff --git a/Makefile b/Makefile
> index e25e256f96af..f5d0c0532faf 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -130,7 +130,8 @@ cmds_objects = cmds-subvolume.o cmds-filesystem.o 
> cmds-device.o cmds-scrub.o \
>              cmds-restore.o cmds-rescue.o chunk-recover.o super-recover.o \
>              cmds-property.o cmds-fi-usage.o cmds-inspect-dump-tree.o \
>              cmds-inspect-dump-super.o cmds-inspect-tree-stats.o cmds-fi-du.o 
> \
> -            mkfs/common.o check/mode-common.o check/mode-lowmem.o
> +            cmds-inspect-dump-csum.o mkfs/common.o check/mode-common.o \
> +            check/mode-lowmem.o
>  libbtrfs_objects = send-stream.o send-utils.o kernel-lib/rbtree.o 
> btrfs-list.o \
>                  kernel-lib/crc32c.o messages.o \
>                  uuid-tree.o utils-lib.o rbtree-utils.o
> diff --git a/cmds-inspect-dump-csum.c b/cmds-inspect-dump-csum.c
> new file mode 100644
> index 000000000000..67e14fde6ec7
> --- /dev/null
> +++ b/cmds-inspect-dump-csum.c
> @@ -0,0 +1,253 @@
> +/*
> + * Copyright (C) 2019 SUSE. 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.
> + */
> +
> +#include <linux/fiemap.h>
> +#include <linux/fs.h>
> +
> +#include <sys/types.h>
> +#include <sys/ioctl.h>
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include <getopt.h>
> +
> +#include "kerncompat.h"
> +#include "ctree.h"
> +#include "messages.h"
> +#include "help.h"
> +#include "ioctl.h"
> +#include "utils.h"
> +#include "disk-io.h"
> +
> +static bool debug = false;
> +
> +static int btrfs_lookup_csum_for_phys(int fd, struct btrfs_super_block *sb,
> +                                   u64 phys, u64 extent_csums)

nit: This function could be renamed to btrfs_lookup_csum_for_extent.
Then instead of passing 'phys' and the calculated 'extent_csum' values
you pass directly a struct fiemap_extent so all calculation involving
the extent will be encapsulated in a single function. It's my personal
feeling this makes it a bit more cohesivr.

> +{
> +     struct btrfs_ioctl_search_args_v2 *search;
> +     struct btrfs_ioctl_search_key *sk;
> +     int bufsz = 1024;

nit: I'd rather have this value be a #defined CONSTANT 1024. At the very
least it should have const qualifier.

> +     char buf[bufsz], *bp;
> +     unsigned int off = 0;
> +     const int csum_size = btrfs_super_csum_size(sb);
> +     const int sector_size = btrfs_super_sectorsize(sb);
> +     int ret, i, j;
> +     u64 needle = phys;
> +     u64 pending_csum_count = extent_csums;
> +
> +     memset(buf, 0, sizeof(buf));
> +     search = (struct btrfs_ioctl_search_args_v2 *)buf;
> +     sk = &search->key;
> +
> +again:
> +     if (debug)
> +             printf(
> +"Looking up checksums for extent at physial offset: %llu (searching at 
> %llu), looking for %llu csums\n",
> +                    phys, needle, pending_csum_count);
> +
> +     sk->tree_id = BTRFS_CSUM_TREE_OBJECTID;
> +     sk->min_objectid = BTRFS_EXTENT_CSUM_OBJECTID;
> +     sk->max_objectid = BTRFS_EXTENT_CSUM_OBJECTID;
> +     sk->max_type = BTRFS_EXTENT_CSUM_KEY;
> +     sk->min_type = BTRFS_EXTENT_CSUM_KEY;
> +     sk->min_offset = needle;
> +     sk->max_offset = (u64)-1;
> +     sk->max_transid = (u64)-1;
> +     sk->nr_items = 1;
> +     search->buf_size = bufsz - sizeof(*search);
> +
> +     ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, search);
> +     if (ret < 0)
> +             return ret;
> +
> +     /*
> +      * If we don't find the csum item at @needle go back by @sector_size and
> +      * retry until we've found it.
> +      */
> +     if (sk->nr_items == 0) {
> +             needle -= sector_size;
> +             goto again;
> +     }
> +
> +
> +     bp = (char *) search->buf;
> +
> +     for (i = 0; i < sk->nr_items; i++) {
> +             struct btrfs_ioctl_search_header *sh;
> +             u64 csums_in_item;
> +
> +             sh = (struct btrfs_ioctl_search_header *) (bp + off);
> +             off += sizeof(*sh);
> +
> +             csums_in_item = btrfs_search_header_len(sh) / csum_size;
> +             csums_in_item = min(csums_in_item, pending_csum_count);
> +
> +             for (j = 0; j < csums_in_item; j++) {
> +                     struct btrfs_csum_item *csum_item;
> +
> +                     csum_item = (struct btrfs_csum_item *)
> +                                             (bp + off + j * csum_size);
> +
> +                     printf("Offset: %llu, checksum: 0x%08x\n",
> +                            phys + j * sector_size, *(u32 *)csum_item);
> +             }
> +
> +             off += btrfs_search_header_len(sh);
> +             pending_csum_count -= csums_in_item;
> +
> +     }
> +
> +     return ret;
> +}
> +
> +static int btrfs_get_extent_csum(int fd, struct btrfs_super_block *sb)
> +{
> +     struct fiemap *fiemap, *tmp;
> +     struct fiemap_extent *fe;
> +     size_t ext_size;
> +     int ret, i;
> +
> +     fiemap = calloc(1, sizeof(*fiemap));
> +     if (!fiemap)
> +             return -ENOMEM;
> +
> +     fiemap->fm_length = ~0;
> +
> +     ret = ioctl(fd, FS_IOC_FIEMAP, fiemap);
> +     if (ret)
> +             goto free_fiemap;
> +
> +     ext_size = fiemap->fm_mapped_extents * sizeof(struct fiemap_extent);
> +
> +     tmp = realloc(fiemap, sizeof(*fiemap) + ext_size);
> +     if (!tmp) {
> +             ret = -ENOMEM;
> +             goto free_fiemap;
> +     }
> +
> +     fiemap = tmp;
> +     fiemap->fm_extent_count = fiemap->fm_mapped_extents;
> +     fiemap->fm_mapped_extents = 0;
> +
> +     ret = ioctl(fd, FS_IOC_FIEMAP, fiemap);
> +     if (ret)
> +             goto free_fiemap;
> +
> +     for (i = 0; i < fiemap->fm_mapped_extents; i++) {
> +             u64 extent_csums;
> +
> +             fe = &fiemap->fm_extents[i];
> +             extent_csums = fe->fe_length / btrfs_super_sectorsize(sb);
> +
> +             if (debug)
> +                     printf(
> +"Found extent at physial offset: %llu, length %llu, looking for %llu 
> csums\n",
> +                            fe->fe_physical, fe->fe_length, extent_csums);
> +
> +             ret = btrfs_lookup_csum_for_phys(fd, sb, fe->fe_physical,
> +                                              extent_csums);
> +             if (ret)
> +                     break;
> +
> +             if(fe->fe_flags & FIEMAP_EXTENT_LAST)
> +                     break;

Does this add any value, given fm_mapped_extents contains the exact
number of extents so after the last one is processed i will be ==
fm_mapped_extents hence the loop will terminate as expected?

> +     }
> +
> +
> +free_fiemap:
> +     free(fiemap);
> +     return ret;
> +}
> +
> +const char * const cmd_inspect_dump_csum_usage[] = {
> +     "btrfs inspect-internal dump-csum <path> <device>",
> +     "Get Checksums for a given file",
> +     "-d|--debug    Be more verbose",
> +     NULL
> +};
> +
> +int cmd_inspect_dump_csum(int argc, char **argv)
> +{
> +     struct btrfs_super_block *sb;
> +     u8 super_block_data[BTRFS_SUPER_INFO_SIZE] = { 0 };

Any particular reason why you've defined the storage for sb this way?
Why not simply struct btrfs_super_block sb;

> +     char *filename;
> +     char *device;
> +     int fd;
> +     int devfd;
> +     int ret;
> +
> +     optind = 0;
> +
> +     sb = (struct btrfs_super_block *)super_block_data;

Then you could remove this.

> +
> +     while (1) {
> +             static const struct option longopts[] = {
> +                     { "debug", no_argument, NULL, 'd' },
> +                     { NULL, 0, NULL, 0 }
> +             };
> +
> +             int opt = getopt_long(argc, argv, "d", longopts, NULL);
> +             if (opt < 0)
> +                     break;
> +
> +             switch (opt) {
> +             case 'd':
> +                     debug = true;
> +                     break;
> +             default:
> +                     usage(cmd_inspect_dump_csum_usage);
> +             }
> +     }
> +
> +     if (check_argc_exact(argc - optind, 2))
> +             usage(cmd_inspect_dump_csum_usage);
> +
> +     filename = argv[optind];
> +     device = argv[optind + 1];
> +
> +     fd = open(filename, O_RDONLY);
> +     if (fd < 0) {
> +             error("cannot open file %s:%m", filename);
> +             return -errno;
> +     }
> +
> +     devfd = open(device, O_RDONLY);
> +     if (devfd < 0) {
> +             ret = -errno;
> +             goto out_close;
> +     }
> +     load_sb(devfd, btrfs_sb_offset(0), sb, BTRFS_SUPER_INFO_SIZE);

then just pass &sb here

> +     close(devfd);
> +
> +     if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
> +             ret = -EINVAL;
> +             error("bad magic on superblock on %s", device);
> +             goto out_close;
> +     }
> +
> +     ret = btrfs_get_extent_csum(fd,sb);

And here: &sb

> +     if (ret)
> +             error("checsum lookup for file %s failed", filename);
> +out_close:
> +     close(fd);
> +     return ret;
> +}
> diff --git a/cmds-inspect.c b/cmds-inspect.c
> index efea0331b7aa..c20decbf6fac 100644
> --- a/cmds-inspect.c
> +++ b/cmds-inspect.c
> @@ -654,6 +654,8 @@ const struct cmd_group inspect_cmd_group = {
>                               cmd_inspect_dump_super_usage, NULL, 0 },
>               { "tree-stats", cmd_inspect_tree_stats,
>                               cmd_inspect_tree_stats_usage, NULL, 0 },
> +             { "dump-csum",  cmd_inspect_dump_csum,
> +                             cmd_inspect_dump_csum_usage, NULL, 0 },
>               NULL_CMD_STRUCT
>       }
>  };
> diff --git a/commands.h b/commands.h
> index 76991f2b28d5..698ae532b2b8 100644
> --- a/commands.h
> +++ b/commands.h
> @@ -92,6 +92,7 @@ extern const char * const cmd_rescue_usage[];
>  extern const char * const cmd_inspect_dump_super_usage[];
>  extern const char * const cmd_inspect_dump_tree_usage[];
>  extern const char * const cmd_inspect_tree_stats_usage[];
> +extern const char * const cmd_inspect_dump_csum_usage[];
>  extern const char * const cmd_filesystem_du_usage[];
>  extern const char * const cmd_filesystem_usage_usage[];
>  
> @@ -108,6 +109,7 @@ int cmd_super_recover(int argc, char **argv);
>  int cmd_inspect(int argc, char **argv);
>  int cmd_inspect_dump_super(int argc, char **argv);
>  int cmd_inspect_dump_tree(int argc, char **argv);
> +int cmd_inspect_dump_csum(int argc, char **argv);
>  int cmd_inspect_tree_stats(int argc, char **argv);
>  int cmd_property(int argc, char **argv);
>  int cmd_send(int argc, char **argv);
> 

Reply via email to