From patchwork Wed Jan 16 22:30:07 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Fasheh X-Patchwork-Id: 1993631 Return-Path: X-Original-To: patchwork-linux-btrfs@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id BFBE53FDD1 for ; Wed, 16 Jan 2013 22:37:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757859Ab3APWhK (ORCPT ); Wed, 16 Jan 2013 17:37:10 -0500 Received: from cantor2.suse.de ([195.135.220.15]:48112 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757814Ab3APWhE (ORCPT ); Wed, 16 Jan 2013 17:37:04 -0500 Received: from relay2.suse.de (unknown [195.135.220.254]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx2.suse.de (Postfix) with ESMTP id 85D8BA3B99; Wed, 16 Jan 2013 23:37:02 +0100 (CET) From: Mark Fasheh To: chris.mason@fusionio.com, linux-btrfs@vger.kernel.org Cc: ablock84@googlemail.com, aschnell@suse.de, Anand.Jain@oracle.com, Mark Fasheh Subject: [PATCH 3/4] btrfs-progs: add send-test Date: Wed, 16 Jan 2013 14:30:07 -0800 Message-Id: <1358375408-25285-4-git-send-email-mfasheh@suse.de> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1358375408-25285-1-git-send-email-mfasheh@suse.de> References: <1358375408-25285-1-git-send-email-mfasheh@suse.de> Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org send-test.c links against libbtrfs and uses the send functionality provided to decode and print a send stream to the console. Signed-off-by: Mark Fasheh --- Makefile | 5 +- send-test.c | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+), 1 deletion(-) create mode 100644 send-test.c diff --git a/Makefile b/Makefile index c5f5aa0..558cb3d 100644 --- a/Makefile +++ b/Makefile @@ -119,6 +119,9 @@ btrfs-convert: $(objects) $(libs) convert.o ioctl-test: $(objects) $(libs) 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) @@ -128,7 +131,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: $(libs) $(progs) install-man diff --git a/send-test.c b/send-test.c new file mode 100644 index 0000000..8c14718 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#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 \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; +}