Message ID | 1357681304-2978-4-git-send-email-mfasheh@suse.de (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Thanks for libbtrfs. comments below. On 01/09/2013 05:41 AM, Mark Fasheh wrote: > send-test.c links against libbtrfs and uses the send functionality provided > to decode and print a send stream to the console. We use the > BTRFS_SEND_FLAG_NO_FILE_DATA flag as actual file data is never needed for > this. > > Signed-off-by: Mark Fasheh <mfasheh@suse.de> > --- > Makefile | 5 +- > send-test.c | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 462 insertions(+), 1 deletions(-) > create mode 100644 send-test.c > > diff --git a/Makefile b/Makefile > index 29c63b1..b3a994b 100644 > --- a/Makefile > +++ b/Makefile > @@ -115,6 +115,9 @@ btrfs-convert: $(objects) convert.o > ioctl-test: $(objects) ioctl-test.o > $(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS) > > +send-test: $(objects) send-test.o > + $(CC) $(CFLAGS) -o send-test send-test.o $(LDFLAGS) $(LIBS) -lpthread > + > manpages: > cd man; $(MAKE) > > @@ -124,7 +127,7 @@ install-man: > clean : > rm -f $(progs) $(libs) cscope.out *.o .*.d btrfs-convert btrfs-image \ > btrfs-select-super btrfs-zero-log btrfstune dir-test ioctl-test \ > - quick-test version.h > + quick-test send-test version.h > cd man; $(MAKE) clean > > install: $(progs) install-man > diff --git a/send-test.c b/send-test.c > new file mode 100644 > index 0000000..2eeaf79 > --- /dev/null > +++ b/send-test.c > @@ -0,0 +1,458 @@ > +/* > + * Copyright (C) 2013 SUSE. All rights reserved. > + * > + * This code is adapted from cmds-send.c and cmds-receive.c, minor: git am reports white space error at this line. > + * Both of which are: > + * > + * Copyright (C) 2012 Alexander Block. 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 > + > +#include <unistd.h> > +#include <stdint.h> > +#include <dirent.h> > +#include <pthread.h> > +#include <math.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <sys/ioctl.h> > +#include <libgen.h> > +#include <mntent.h> > +#include <limits.h> > +#include <stdlib.h> > +#include <asm/types.h> > +#include <uuid/uuid.h> > + > +/* > + * This should be compilable without the rest of the btrfs-progs > + * source distribution. > + */ > +#if BTRFS_FLAT_INCLUDES > +#include "send-utils.h" > +#include "send-stream.h" > +#else > +#include <btrfs/send-utils.h> > +#include <btrfs/send-stream.h> > +#endif /* BTRFS_FLAT_INCLUDES */ > + > +static int pipefd[2]; > +struct btrfs_ioctl_send_args io_send = {0, }; > +static char *subvol_path; > +static char *root_path; > + > +struct recv_args { > + char *full_subvol_path; > + char *root_path; > +}; > + > +void usage(int error) > +{ > + printf("send-test <btrfs root> <subvol>\n"); > + if (error) > + exit(error); > +} > + > +static int print_subvol(const char *path, const u8 *uuid, u64 ctransid, > + void *user) > +{ > + struct recv_args *r = user; > + char uuid_str[128]; > + > + r->full_subvol_path = path_cat(r->root_path, path); > + uuid_unparse(uuid, uuid_str); > + > + printf("subvol\t%s\t%llu\t%s\n", uuid_str, > + (unsigned long long)ctransid, r->full_subvol_path); > + > + return 0; > +} > + > +static int print_snapshot(const char *path, const u8 *uuid, u64 ctransid, > + const u8 *parent_uuid, u64 parent_ctransid, > + void *user) > +{ > + struct recv_args *r = user; > + char uuid_str[128]; > + char parent_uuid_str[128]; > + > + r->full_subvol_path = path_cat(r->root_path, path); > + uuid_unparse(uuid, uuid_str); > + uuid_unparse(parent_uuid, parent_uuid_str); > + > + printf("snapshot\t%s\t%llu\t%s\t%llu\t%s\n", uuid_str, > + (unsigned long long)ctransid, parent_uuid_str, > + (unsigned long long)parent_ctransid, r->full_subvol_path); > + > + return 0; > +} > + > +static int print_mkfile(const char *path, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("mkfile\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_mkdir(const char *path, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("mkdir\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_mknod(const char *path, u64 mode, u64 dev, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("mknod\t%llo\t0x%llx\t%s\n", (unsigned long long)mode, > + (unsigned long long)dev, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_mkfifo(const char *path, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("mkfifo\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_mksock(const char *path, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("mksock\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_symlink(const char *path, const char *lnk, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("symlink\t%s\t%s\n", lnk, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_rename(const char *from, const char *to, void *user) > +{ > + struct recv_args *r = user; > + char *full_from = path_cat(r->full_subvol_path, from); > + char *full_to = path_cat(r->full_subvol_path, to); > + > + printf("rename\t%s\t%s\n", from, to); > + > + free(full_from); > + free(full_to); > + return 0; > +} > + > +static int print_link(const char *path, const char *lnk, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("link\t%s\t%s\n", lnk, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_unlink(const char *path, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("unlink\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_rmdir(const char *path, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("rmdir\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_write(const char *path, const void *data, u64 offset, > + u64 len, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("write\t%llu\t%llu\t%s\n", (unsigned long long)offset, > + (unsigned long long)len, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_clone(const char *path, u64 offset, u64 len, > + const u8 *clone_uuid, u64 clone_ctransid, > + const char *clone_path, u64 clone_offset, > + void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("clone\t%s\t%s\n", full_path, clone_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_set_xattr(const char *path, const char *name, > + const void *data, int len, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("set_xattr\t%s\t%s\t%d\n", full_path, > + name, len); > + > + free(full_path); > + return 0; > +} > + > +static int print_remove_xattr(const char *path, const char *name, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("remove_xattr\t%s\t%s\n", full_path, name); > + > + free(full_path); > + return 0; > +} > + > +static int print_truncate(const char *path, u64 size, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("truncate\t%llu\t%s\n", (unsigned long long)size, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_chmod(const char *path, u64 mode, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("chmod\t%llo\t%s\n", (unsigned long long)mode, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_chown(const char *path, u64 uid, u64 gid, void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("chown\t%llu\t%llu\t%s\n", (unsigned long long)uid, > + (unsigned long long)gid, full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_utimes(const char *path, struct timespec *at, > + struct timespec *mt, struct timespec *ct, > + void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("utimes\t%s\n", full_path); > + > + free(full_path); > + return 0; > +} > + > +static int print_update_extent(const char *path, u64 offset, u64 len, > + void *user) > +{ > + struct recv_args *r = user; > + char *full_path = path_cat(r->full_subvol_path, path); > + > + printf("update_extent\t%s\t%llu\t%llu\n", full_path, offset, len); > + > + free(full_path); > + return 0; > +} > + > +struct btrfs_send_ops send_ops_print = { > + .subvol = print_subvol, > + .snapshot = print_snapshot, > + .mkfile = print_mkfile, > + .mkdir = print_mkdir, > + .mknod = print_mknod, > + .mkfifo = print_mkfifo, > + .mksock = print_mksock, > + .symlink = print_symlink, > + .rename = print_rename, > + .link = print_link, > + .unlink = print_unlink, > + .rmdir = print_rmdir, > + .write = print_write, > + .clone = print_clone, > + .set_xattr = print_set_xattr, > + .remove_xattr = print_remove_xattr, > + .truncate = print_truncate, > + .chmod = print_chmod, > + .chown = print_chown, > + .utimes = print_utimes, > + .update_extent = print_update_extent, > +}; > + > +static void *process_thread(void *arg_) > +{ > + int ret; > + > + while (1) { > + ret = btrfs_read_and_process_send_stream(pipefd[0], > + &send_ops_print, arg_); > + if (ret) > + break; > + } > + > + if (ret > 0) > + ret = 0; > + > + return ERR_PTR(ret); > +} > + > +int main(int argc, char **argv) > +{ > + int ret = 0; > + int subvol_fd; > + pthread_t t_read; > + pthread_attr_t t_attr; > + void *t_err = NULL; > + struct recv_args r; > + > + if (argc != 3) > + usage(EINVAL); > + > + root_path = realpath(argv[1], NULL); > + if (!root_path) { > + ret = errno; > + usage(ret); > + } > + > + subvol_path = realpath(argv[2], NULL); > + if (!subvol_path) { > + ret = errno; > + usage(ret); > + } > + > + r.full_subvol_path = subvol_path; > + r.root_path = root_path; > + > + subvol_fd = open(subvol_path, O_RDONLY|O_NOATIME); > + if (subvol_fd < 0) { > + ret = errno; > + fprintf(stderr, "ERROR: Subvolume open failed. %s\n", > + strerror(ret)); > + goto out; > + } > + > + ret = pthread_attr_init(&t_attr); > + if (ret < 0) { > + fprintf(stderr, "ERROR: pthread init failed. %s\n", > + strerror(ret)); > + goto out; > + } > + > + ret = pipe(pipefd); > + if (ret < 0) { > + ret = errno; > + fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(ret)); > + goto out; > + } > + > + ret = pthread_create(&t_read, &t_attr, process_thread, &r); > + if (ret < 0) { > + ret = errno; > + fprintf(stderr, "ERROR: pthread create failed. %s\n", > + strerror(ret)); > + goto out; > + } > + > + io_send.send_fd = pipefd[1]; > + io_send.clone_sources_count = 0; > + io_send.clone_sources = NULL; > + io_send.parent_root = 0; > + io_send.flags = BTRFS_SEND_FLAG_NO_FILE_DATA; > + > + ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send); > + if (ret) { > + ret = errno; > + fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret, > + strerror(ret)); > + goto out; > + } > + > + close(pipefd[1]); > + > + ret = pthread_join(t_read, &t_err); > + if (ret) { > + fprintf(stderr, "ERROR: pthread_join failed: %s\n", > + strerror(ret)); > + goto out; > + } > + if (t_err) { > + ret = (long int)t_err; > + fprintf(stderr, "ERROR: failed to process send stream, ret=%ld " > + "(%s)\n", (long int)t_err, strerror(ret)); > + goto out; > + } > + > + pthread_attr_destroy(&t_attr); > +out: > + return ret; > +} > -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Jan 11, 2013 at 05:28:22PM +0800, Anand Jain wrote:
> Thanks for libbtrfs. comments below.
No problem, thanks for looking over and testing this out. I'll correct the
whitespace error below for my next send of the patches.
--Mark
--
Mark Fasheh
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Makefile b/Makefile index 29c63b1..b3a994b 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,9 @@ btrfs-convert: $(objects) convert.o ioctl-test: $(objects) ioctl-test.o $(CC) $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS) +send-test: $(objects) send-test.o + $(CC) $(CFLAGS) -o send-test send-test.o $(LDFLAGS) $(LIBS) -lpthread + manpages: cd man; $(MAKE) @@ -124,7 +127,7 @@ install-man: clean : rm -f $(progs) $(libs) cscope.out *.o .*.d btrfs-convert btrfs-image \ btrfs-select-super btrfs-zero-log btrfstune dir-test ioctl-test \ - quick-test version.h + quick-test send-test version.h cd man; $(MAKE) clean install: $(progs) install-man diff --git a/send-test.c b/send-test.c new file mode 100644 index 0000000..2eeaf79 --- /dev/null +++ b/send-test.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2013 SUSE. All rights reserved. + * + * This code is adapted from cmds-send.c and cmds-receive.c, + * Both of which are: + * + * Copyright (C) 2012 Alexander Block. 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 + +#include <unistd.h> +#include <stdint.h> +#include <dirent.h> +#include <pthread.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <libgen.h> +#include <mntent.h> +#include <limits.h> +#include <stdlib.h> +#include <asm/types.h> +#include <uuid/uuid.h> + +/* + * This should be compilable without the rest of the btrfs-progs + * source distribution. + */ +#if BTRFS_FLAT_INCLUDES +#include "send-utils.h" +#include "send-stream.h" +#else +#include <btrfs/send-utils.h> +#include <btrfs/send-stream.h> +#endif /* BTRFS_FLAT_INCLUDES */ + +static int pipefd[2]; +struct btrfs_ioctl_send_args io_send = {0, }; +static char *subvol_path; +static char *root_path; + +struct recv_args { + char *full_subvol_path; + char *root_path; +}; + +void usage(int error) +{ + printf("send-test <btrfs root> <subvol>\n"); + if (error) + exit(error); +} + +static int print_subvol(const char *path, const u8 *uuid, u64 ctransid, + void *user) +{ + struct recv_args *r = user; + char uuid_str[128]; + + r->full_subvol_path = path_cat(r->root_path, path); + uuid_unparse(uuid, uuid_str); + + printf("subvol\t%s\t%llu\t%s\n", uuid_str, + (unsigned long long)ctransid, r->full_subvol_path); + + return 0; +} + +static int print_snapshot(const char *path, const u8 *uuid, u64 ctransid, + const u8 *parent_uuid, u64 parent_ctransid, + void *user) +{ + struct recv_args *r = user; + char uuid_str[128]; + char parent_uuid_str[128]; + + r->full_subvol_path = path_cat(r->root_path, path); + uuid_unparse(uuid, uuid_str); + uuid_unparse(parent_uuid, parent_uuid_str); + + printf("snapshot\t%s\t%llu\t%s\t%llu\t%s\n", uuid_str, + (unsigned long long)ctransid, parent_uuid_str, + (unsigned long long)parent_ctransid, r->full_subvol_path); + + return 0; +} + +static int print_mkfile(const char *path, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("mkfile\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_mkdir(const char *path, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("mkdir\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_mknod(const char *path, u64 mode, u64 dev, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("mknod\t%llo\t0x%llx\t%s\n", (unsigned long long)mode, + (unsigned long long)dev, full_path); + + free(full_path); + return 0; +} + +static int print_mkfifo(const char *path, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("mkfifo\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_mksock(const char *path, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("mksock\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_symlink(const char *path, const char *lnk, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("symlink\t%s\t%s\n", lnk, full_path); + + free(full_path); + return 0; +} + +static int print_rename(const char *from, const char *to, void *user) +{ + struct recv_args *r = user; + char *full_from = path_cat(r->full_subvol_path, from); + char *full_to = path_cat(r->full_subvol_path, to); + + printf("rename\t%s\t%s\n", from, to); + + free(full_from); + free(full_to); + return 0; +} + +static int print_link(const char *path, const char *lnk, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("link\t%s\t%s\n", lnk, full_path); + + free(full_path); + return 0; +} + +static int print_unlink(const char *path, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("unlink\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_rmdir(const char *path, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("rmdir\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_write(const char *path, const void *data, u64 offset, + u64 len, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("write\t%llu\t%llu\t%s\n", (unsigned long long)offset, + (unsigned long long)len, full_path); + + free(full_path); + return 0; +} + +static int print_clone(const char *path, u64 offset, u64 len, + const u8 *clone_uuid, u64 clone_ctransid, + const char *clone_path, u64 clone_offset, + void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("clone\t%s\t%s\n", full_path, clone_path); + + free(full_path); + return 0; +} + +static int print_set_xattr(const char *path, const char *name, + const void *data, int len, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("set_xattr\t%s\t%s\t%d\n", full_path, + name, len); + + free(full_path); + return 0; +} + +static int print_remove_xattr(const char *path, const char *name, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("remove_xattr\t%s\t%s\n", full_path, name); + + free(full_path); + return 0; +} + +static int print_truncate(const char *path, u64 size, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("truncate\t%llu\t%s\n", (unsigned long long)size, full_path); + + free(full_path); + return 0; +} + +static int print_chmod(const char *path, u64 mode, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("chmod\t%llo\t%s\n", (unsigned long long)mode, full_path); + + free(full_path); + return 0; +} + +static int print_chown(const char *path, u64 uid, u64 gid, void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("chown\t%llu\t%llu\t%s\n", (unsigned long long)uid, + (unsigned long long)gid, full_path); + + free(full_path); + return 0; +} + +static int print_utimes(const char *path, struct timespec *at, + struct timespec *mt, struct timespec *ct, + void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("utimes\t%s\n", full_path); + + free(full_path); + return 0; +} + +static int print_update_extent(const char *path, u64 offset, u64 len, + void *user) +{ + struct recv_args *r = user; + char *full_path = path_cat(r->full_subvol_path, path); + + printf("update_extent\t%s\t%llu\t%llu\n", full_path, offset, len); + + free(full_path); + return 0; +} + +struct btrfs_send_ops send_ops_print = { + .subvol = print_subvol, + .snapshot = print_snapshot, + .mkfile = print_mkfile, + .mkdir = print_mkdir, + .mknod = print_mknod, + .mkfifo = print_mkfifo, + .mksock = print_mksock, + .symlink = print_symlink, + .rename = print_rename, + .link = print_link, + .unlink = print_unlink, + .rmdir = print_rmdir, + .write = print_write, + .clone = print_clone, + .set_xattr = print_set_xattr, + .remove_xattr = print_remove_xattr, + .truncate = print_truncate, + .chmod = print_chmod, + .chown = print_chown, + .utimes = print_utimes, + .update_extent = print_update_extent, +}; + +static void *process_thread(void *arg_) +{ + int ret; + + while (1) { + ret = btrfs_read_and_process_send_stream(pipefd[0], + &send_ops_print, arg_); + if (ret) + break; + } + + if (ret > 0) + ret = 0; + + return ERR_PTR(ret); +} + +int main(int argc, char **argv) +{ + int ret = 0; + int subvol_fd; + pthread_t t_read; + pthread_attr_t t_attr; + void *t_err = NULL; + struct recv_args r; + + if (argc != 3) + usage(EINVAL); + + root_path = realpath(argv[1], NULL); + if (!root_path) { + ret = errno; + usage(ret); + } + + subvol_path = realpath(argv[2], NULL); + if (!subvol_path) { + ret = errno; + usage(ret); + } + + r.full_subvol_path = subvol_path; + r.root_path = root_path; + + subvol_fd = open(subvol_path, O_RDONLY|O_NOATIME); + if (subvol_fd < 0) { + ret = errno; + fprintf(stderr, "ERROR: Subvolume open failed. %s\n", + strerror(ret)); + goto out; + } + + ret = pthread_attr_init(&t_attr); + if (ret < 0) { + fprintf(stderr, "ERROR: pthread init failed. %s\n", + strerror(ret)); + goto out; + } + + ret = pipe(pipefd); + if (ret < 0) { + ret = errno; + fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(ret)); + goto out; + } + + ret = pthread_create(&t_read, &t_attr, process_thread, &r); + if (ret < 0) { + ret = errno; + fprintf(stderr, "ERROR: pthread create failed. %s\n", + strerror(ret)); + goto out; + } + + io_send.send_fd = pipefd[1]; + io_send.clone_sources_count = 0; + io_send.clone_sources = NULL; + io_send.parent_root = 0; + io_send.flags = BTRFS_SEND_FLAG_NO_FILE_DATA; + + ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send); + if (ret) { + ret = errno; + fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret, + strerror(ret)); + goto out; + } + + close(pipefd[1]); + + ret = pthread_join(t_read, &t_err); + if (ret) { + fprintf(stderr, "ERROR: pthread_join failed: %s\n", + strerror(ret)); + goto out; + } + if (t_err) { + ret = (long int)t_err; + fprintf(stderr, "ERROR: failed to process send stream, ret=%ld " + "(%s)\n", (long int)t_err, strerror(ret)); + goto out; + } + + pthread_attr_destroy(&t_attr); +out: + return ret; +}
send-test.c links against libbtrfs and uses the send functionality provided to decode and print a send stream to the console. We use the BTRFS_SEND_FLAG_NO_FILE_DATA flag as actual file data is never needed for this. Signed-off-by: Mark Fasheh <mfasheh@suse.de> --- Makefile | 5 +- send-test.c | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+), 1 deletions(-) create mode 100644 send-test.c