From patchwork Tue Apr 21 05:48:24 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Merillat X-Patchwork-Id: 6245681 Return-Path: X-Original-To: patchwork-linux-btrfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id CCA849F1BE for ; Tue, 21 Apr 2015 05:48:56 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B48F12041F for ; Tue, 21 Apr 2015 05:48:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 969C02041E for ; Tue, 21 Apr 2015 05:48:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750936AbbDUFs2 (ORCPT ); Tue, 21 Apr 2015 01:48:28 -0400 Received: from mail-qc0-f169.google.com ([209.85.216.169]:34718 "EHLO mail-qc0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750867AbbDUFs1 (ORCPT ); Tue, 21 Apr 2015 01:48:27 -0400 Received: by qcyk17 with SMTP id k17so71065286qcy.1 for ; Mon, 20 Apr 2015 22:48:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:subject :content-type:content-transfer-encoding; bh=6retP5Aq02RVaxiiMezLSOo+RlCJ+GYRu0D9osAoqTc=; b=gQbl5eUr6NaWVgPgQFjgaPgJT690ynZvn2ENw/WJZt/P8fybafxnlCbwskb+sHWCm0 8rsCYq0RNvtNgos4fYDrluDJVFDWTVt7YmCnPMBd+fzwVKD5nAAAO3u/3tm8xJODd1Wu jXfui22lBc0cK7x6AuSlyzkyqS7kMf22qj5DkIJHrrg4KjzgqEcdcFa+SR0BfKXc4txF M+jWAMV85vsLC+9p+cLJqaTtPKa/N/Fwjau+WYZHJ/ouePWF3sRtsVrYYSYMJHBgVzgI pjP204pK4PgNsXa6jqQUrOxhDHvQhx8d/W7ZKAAQTKIKwZCiJXbpirMtagdYaRFqryIF 98QA== X-Received: by 10.140.104.1 with SMTP id z1mr21071888qge.76.1429595306595; Mon, 20 Apr 2015 22:48:26 -0700 (PDT) Received: from ?IPv6:2001:470:8:414::10? (wolf.welp.gs. [2001:470:8:414::10]) by mx.google.com with ESMTPSA id n83sm694770qkh.31.2015.04.20.22.48.25 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 20 Apr 2015 22:48:26 -0700 (PDT) Message-ID: <5535E4A8.8040600@gmail.com> Date: Tue, 21 Apr 2015 01:48:24 -0400 From: Dan Merillat User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:32.0) Gecko/20100101 Icedove/32.0 MIME-Version: 1.0 To: BTRFS Subject: [PATCH v2 1/1] btrfs-progs: optionally restore metadata Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP As long as the inode is intact, the file metadata can be restored. Directory data is restored at the end of search_dir. Errors are checked and returned, unless ignore_errors is requested. Signed-off-by: Dan Merillat --- Documentation/btrfs-restore.txt | 3 ++ cmds-restore.c | 114 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/Documentation/btrfs-restore.txt b/Documentation/btrfs-restore.txt index 20fc366..a4e4d37 100644 --- a/Documentation/btrfs-restore.txt +++ b/Documentation/btrfs-restore.txt @@ -29,6 +29,9 @@ get snapshots, btrfs restore skips snapshots in default. -x:: get extended attributes. +-m|--metadata:: +set owner, permissions, access time and modify time. + -v:: verbose. diff --git a/cmds-restore.c b/cmds-restore.c index d2fc951..e95018f 100644 --- a/cmds-restore.c +++ b/cmds-restore.c @@ -48,6 +48,7 @@ static char fs_name[4096]; static char path_name[4096]; static int get_snaps = 0; static int verbose = 0; +static int restore_metadata = 0; static int ignore_errors = 0; static int overwrite = 0; static int get_xattrs = 0; @@ -547,6 +548,57 @@ out: return ret; } +static int copy_metadata(struct btrfs_root *root, int fd, + struct btrfs_key *key) +{ + struct btrfs_path *path; + struct btrfs_inode_item *inode_item; + int ret; + + path = btrfs_alloc_path(); + if (!path) { + fprintf(stderr, "Ran out of memory\n"); + return -ENOMEM; + } + + ret = btrfs_lookup_inode(NULL, root, path, key, 0); + if (ret == 0) { + + inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + + ret=fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item), + btrfs_inode_gid(path->nodes[0], inode_item)); + if (ret) { + fprintf(stderr, "Failed to change owner: %s\n", strerror(errno)); + goto out; + } + ret=fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item)); + if (ret) { + fprintf(stderr, "Failed to change mode: %s\n", strerror(errno)); + goto out; + } + struct btrfs_timespec *bts; + struct timespec times[2]; + + bts = btrfs_inode_atime(inode_item); + times[0].tv_sec=btrfs_timespec_sec(path->nodes[0], bts); + times[0].tv_nsec=btrfs_timespec_nsec(path->nodes[0], bts); + + bts = btrfs_inode_mtime(inode_item); + times[1].tv_sec=btrfs_timespec_sec(path->nodes[0], bts); + times[1].tv_nsec=btrfs_timespec_nsec(path->nodes[0], bts); + + ret=futimens(fd, times); + if (ret) { + fprintf(stderr, "Failed to set times: %s\n", strerror(errno)); + goto out; + } + } +out: + btrfs_release_path(path); + return ret; +} static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, const char *file) @@ -555,6 +607,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, struct btrfs_path *path; struct btrfs_file_extent_item *fi; struct btrfs_inode_item *inode_item; + struct btrfs_timespec *bts; struct btrfs_key found_key; int ret; int extent_type; @@ -567,12 +620,41 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key, fprintf(stderr, "Ran out of memory\n"); return -ENOMEM; } + struct timespec times[2]; + int times_ok=0; ret = btrfs_lookup_inode(NULL, root, path, key, 0); if (ret == 0) { inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); found_size = btrfs_inode_size(path->nodes[0], inode_item); + + if (restore_metadata) { + /* change the ownership and mode now, set times when + * copyout is finished */ + + ret=fchown(fd, btrfs_inode_uid(path->nodes[0], inode_item), + btrfs_inode_gid(path->nodes[0], inode_item)); + if (ret && !ignore_errors) { + btrfs_release_path(path); + return ret; + } + + ret=fchmod(fd, btrfs_inode_mode(path->nodes[0], inode_item)); + if (ret && !ignore_errors) { + btrfs_release_path(path); + return ret; + } + + bts = btrfs_inode_atime(inode_item); + times[0].tv_sec=btrfs_timespec_sec(path->nodes[0], bts); + times[0].tv_nsec=btrfs_timespec_nsec(path->nodes[0], bts); + + bts = btrfs_inode_mtime(inode_item); + times[1].tv_sec=btrfs_timespec_sec(path->nodes[0], bts); + times[1].tv_nsec=btrfs_timespec_nsec(path->nodes[0], bts); + times_ok=1; + } } btrfs_release_path(path); @@ -680,6 +762,11 @@ set_size: if (ret) return ret; } + if (restore_metadata && times_ok) { + ret=futimens(fd, times); + if (ret) + return ret; + } return 0; } @@ -929,6 +1016,25 @@ next: path->slots[0]++; } + if (restore_metadata) { + snprintf(path_name, 4096, "%s%s", output_rootdir, in_dir); + fd = open(path_name, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to access %s to restore metadata\n", path_name); + if (!ignore_errors) + return -1; + } else { + // set owner/mode/time on the directory as well + key->type=BTRFS_INODE_ITEM_KEY; + ret=copy_metadata(root, fd, key); + close(fd); + if (ret && !ignore_errors) { + btrfs_free_path(path); + return ret; + } + } + } + if (verbose) printf("Done searching %s\n", in_dir); btrfs_free_path(path); @@ -1126,6 +1232,7 @@ const char * const cmd_restore_usage[] = { "", "-s get snapshots", "-x get extended attributes", + "-m|--metadata restore owner, mode and times", "-v verbose", "-i ignore errors", "-o overwrite", @@ -1168,10 +1275,12 @@ int cmd_restore(int argc, char **argv) static const struct option long_options[] = { { "path-regex", 1, NULL, 256}, { "dry-run", 0, NULL, 'D'}, + { "metadata", 0, NULL, 'm'}, + { "debug-regex", 0, NULL, 257}, { NULL, 0, NULL, 0} }; - opt = getopt_long(argc, argv, "sxviot:u:df:r:lDc", long_options, + opt = getopt_long(argc, argv, "sxviot:u:dmf:r:lDc", long_options, &option_index); if (opt < 0) break; @@ -1217,6 +1326,9 @@ int cmd_restore(int argc, char **argv) case 'l': list_roots = 1; break; + case 'm': + restore_metadata = 1; + break; case 'D': dry_run = 1; break;