btrfs-progs: add mount status check for btrfs-image
[platform/upstream/btrfs-progs.git] / cmds-restore.c
index ae0527b..e09acc4 100644 (file)
@@ -34,7 +34,7 @@
 #include <regex.h>
 #include <getopt.h>
 #include <sys/types.h>
-#include <attr/xattr.h>
+#include <sys/xattr.h>
 
 #include "ctree.h"
 #include "disk-io.h"
@@ -53,6 +53,7 @@ static int verbose = 0;
 static int ignore_errors = 0;
 static int overwrite = 0;
 static int get_xattrs = 0;
+static int dry_run = 0;
 
 #define LZO_LEN 4
 #define PAGE_CACHE_SIZE 4096
@@ -114,6 +115,13 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len,
 
        while (tot_in < tot_len) {
                in_len = read_compress_length(inbuf);
+
+               if ((tot_in + LZO_LEN + in_len) > tot_len) {
+                       fprintf(stderr, "bad compress length %lu\n",
+                               (unsigned long)in_len);
+                       return -1;
+               }
+
                inbuf += LZO_LEN;
                tot_in += LZO_LEN;
 
@@ -228,8 +236,7 @@ static int copy_one_inline(int fd, struct btrfs_path *path, u64 pos)
        fi = btrfs_item_ptr(leaf, path->slots[0],
                            struct btrfs_file_extent_item);
        ptr = btrfs_file_extent_inline_start(fi);
-       len = btrfs_file_extent_inline_item_len(leaf,
-                                       btrfs_item_nr(path->slots[0]));
+       len = btrfs_file_extent_inline_len(leaf, path->slots[0], fi);
        read_extent_buffer(leaf, buf, ptr, len);
 
        compress = btrfs_file_extent_compression(leaf, fi);
@@ -296,8 +303,9 @@ static int copy_one_extent(struct btrfs_root *root, int fd,
        ram_size = btrfs_file_extent_ram_bytes(leaf, fi);
        offset = btrfs_file_extent_offset(leaf, fi);
        num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
-       size_left = num_bytes;
-       bytenr += offset;
+       size_left = disk_size;
+       if (compress == BTRFS_COMPRESS_NONE)
+               bytenr += offset;
 
        if (offset)
                printf("offset is %Lu\n", offset);
@@ -374,7 +382,7 @@ again:
                goto out;
        }
 
-       ret = decompress(inbuf, outbuf, num_bytes, &ram_size, compress);
+       ret = decompress(inbuf, outbuf, disk_size, &ram_size, compress);
        if (ret) {
                num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
                                              bytenr, length);
@@ -387,8 +395,10 @@ again:
                goto again;
        }
 
-       while (total < ram_size) {
-               done = pwrite(fd, outbuf+total, ram_size-total, pos+total);
+       while (total < num_bytes) {
+               done = pwrite(fd, outbuf + offset + total,
+                             num_bytes - total,
+                             pos + total);
                if (done < 0) {
                        ret = -1;
                        goto out;
@@ -401,23 +411,31 @@ out:
        return ret;
 }
 
-static int ask_to_continue(const char *file)
+enum loop_response {
+       LOOP_STOP,
+       LOOP_CONTINUE,
+       LOOP_DONTASK
+};
+
+static enum loop_response ask_to_continue(const char *file)
 {
        char buf[2];
        char *ret;
 
        printf("We seem to be looping a lot on %s, do you want to keep going "
-              "on ? (y/N): ", file);
+              "on ? (y/N/a): ", file);
 again:
        ret = fgets(buf, 2, stdin);
        if (*ret == '\n' || tolower(*ret) == 'n')
-               return 1;
+               return LOOP_STOP;
+       if (tolower(*ret) == 'a')
+               return LOOP_DONTASK;
        if (tolower(*ret) != 'y') {
-               printf("Please enter either 'y' or 'n': ");
+               printf("Please enter one of 'y', 'n', or 'a': ");
                goto again;
        }
 
-       return 0;
+       return LOOP_CONTINUE;
 }
 
 
@@ -585,11 +603,16 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
        }
 
        while (1) {
-               if (loops++ >= 1024) {
-                       ret = ask_to_continue(file);
-                       if (ret)
+               if (loops >= 0 && loops++ >= 1024) {
+                       enum loop_response resp;
+
+                       resp = ask_to_continue(file);
+                       if (resp == LOOP_STOP)
                                break;
-                       loops = 0;
+                       else if (resp == LOOP_CONTINUE)
+                               loops = 0;
+                       else if (resp == LOOP_DONTASK)
+                               loops = -1;
                }
                if (path->slots[0] >= btrfs_header_nritems(leaf)) {
                        do {
@@ -801,6 +824,8 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
                        }
                        if (verbose)
                                printf("Restoring %s\n", path_name);
+                       if (dry_run)
+                               goto next;
                        fd = open(path_name, O_CREAT|O_WRONLY, 0644);
                        if (fd < 0) {
                                fprintf(stderr, "Error creating %s: %d\n",
@@ -873,7 +898,10 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
                                printf("Restoring %s\n", path_name);
 
                        errno = 0;
-                       ret = mkdir(path_name, 0755);
+                       if (dry_run)
+                               ret = 0;
+                       else
+                               ret = mkdir(path_name, 0755);
                        if (ret && errno != EEXIST) {
                                free(dir);
                                fprintf(stderr, "Error mkdiring %s: %d\n",
@@ -974,7 +1002,8 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
 
        for (i = super_mirror; i < BTRFS_SUPER_MIRROR_MAX; i++) {
                bytenr = btrfs_sb_offset(i);
-               fs_info = open_ctree_fs_info(dev, bytenr, root_location, 0, 1);
+               fs_info = open_ctree_fs_info(dev, bytenr, root_location,
+                                            OPEN_CTREE_PARTIAL);
                if (fs_info)
                        break;
                fprintf(stderr, "Could not open root, trying backup super\n");
@@ -1089,6 +1118,7 @@ out:
 
 static struct option long_options[] = {
        { "path-regex", 1, NULL, 256},
+       { "dry-run", 0, NULL, 'D'},
        { NULL, 0, NULL, 0}
 };
 
@@ -1101,16 +1131,18 @@ const char * const cmd_restore_usage[] = {
        "-v              verbose",
        "-i              ignore errors",
        "-o              overwrite",
-       "-t <location>   tree location",
-       "-f <offset>     filesystem location",
-       "-u <block>      super mirror",
-       "-r <rootid>     root objectid",
+       "-t <bytenr>     tree location",
+       "-f <bytenr>     filesystem location",
+       "-u <mirror>     super mirror",
+       "-r <rootid>     root objectid",
        "-d              find dir",
        "-l              list tree roots",
+       "-D|--dry-run    dry run (only list files that would be recovered)",
        "--path-regex <regex>",
        "                restore only filenames matching regex,",
        "                you have to use following syntax (possibly quoted):",
        "                ^/(|home(|/username(|/Desktop(|/.*))))$",
+       "-c              ignore case (--path-regrex only)",
        NULL
 };
 
@@ -1134,7 +1166,7 @@ int cmd_restore(int argc, char **argv)
        regex_t match_reg, *mreg = NULL;
        char reg_err[256];
 
-       while ((opt = getopt_long(argc, argv, "sxviot:u:df:r:lc", long_options,
+       while ((opt = getopt_long(argc, argv, "sxviot:u:df:r:lDc", long_options,
                                        &option_index)) != -1) {
 
                switch (opt) {
@@ -1151,26 +1183,14 @@ int cmd_restore(int argc, char **argv)
                                overwrite = 1;
                                break;
                        case 't':
-                               errno = 0;
-                               tree_location = (u64)strtoll(optarg, NULL, 10);
-                               if (errno != 0) {
-                                       fprintf(stderr, "Tree location not valid\n");
-                                       exit(1);
-                               }
+                               tree_location = arg_strtou64(optarg);
                                break;
                        case 'f':
-                               errno = 0;
-                               fs_location = (u64)strtoll(optarg, NULL, 10);
-                               if (errno != 0) {
-                                       fprintf(stderr, "Fs location not valid\n");
-                                       exit(1);
-                               }
+                               fs_location = arg_strtou64(optarg);
                                break;
                        case 'u':
-                               errno = 0;
-                               super_mirror = (int)strtol(optarg, NULL, 10);
-                               if (errno != 0 ||
-                                   super_mirror >= BTRFS_SUPER_MIRROR_MAX) {
+                               super_mirror = arg_strtou64(optarg);
+                               if (super_mirror >= BTRFS_SUPER_MIRROR_MAX) {
                                        fprintf(stderr, "Super mirror not "
                                                "valid\n");
                                        exit(1);
@@ -1180,16 +1200,19 @@ int cmd_restore(int argc, char **argv)
                                find_dir = 1;
                                break;
                        case 'r':
-                               errno = 0;
-                               root_objectid = (u64)strtoll(optarg, NULL, 10);
-                               if (errno != 0) {
-                                       fprintf(stderr, "Root objectid not valid\n");
+                               root_objectid = arg_strtou64(optarg);
+                               if (!is_fstree(root_objectid)) {
+                                       fprintf(stderr, "objectid %llu is not a valid fs/file tree\n",
+                                                       root_objectid);
                                        exit(1);
                                }
                                break;
                        case 'l':
                                list_roots = 1;
                                break;
+                       case 'D':
+                               dry_run = 1;
+                               break;
                        case 'c':
                                match_cflags |= REG_ICASE;
                                break;
@@ -1205,11 +1228,17 @@ int cmd_restore(int argc, char **argv)
                }
        }
 
-       if (!list_roots && optind + 1 >= argc)
+       set_argv0(argv);
+       if (!list_roots && check_argc_min(argc - optind, 2))
                usage(cmd_restore_usage);
-       else if (list_roots && optind >= argc)
+       else if (list_roots && check_argc_min(argc - optind, 1))
                usage(cmd_restore_usage);
 
+       if (fs_location && root_objectid) {
+               fprintf(stderr, "don't use -f and -r at the same time.\n");
+               return 1;
+       }
+
        if ((ret = check_mounted(argv[optind])) < 0) {
                fprintf(stderr, "Could not check mount status: %s\n",
                        strerror(-ret));
@@ -1231,6 +1260,7 @@ int cmd_restore(int argc, char **argv)
                root->node = read_tree_block(root, fs_location, root->leafsize, 0);
                if (!root->node) {
                        fprintf(stderr, "Failed to read fs location\n");
+                       ret = 1;
                        goto out;
                }
        }
@@ -1254,7 +1284,8 @@ int cmd_restore(int argc, char **argv)
                key.offset = (u64)-1;
                root = btrfs_read_fs_root(orig_root->fs_info, &key);
                if (IS_ERR(root)) {
-                       fprintf(stderr, "Error reading root\n");
+                       fprintf(stderr, "fail to read root %llu: %s\n",
+                                       root_objectid, strerror(-PTR_ERR(root)));
                        root = orig_root;
                        ret = 1;
                        goto out;
@@ -1281,6 +1312,9 @@ int cmd_restore(int argc, char **argv)
                mreg = &match_reg;
        }
 
+       if (dry_run)
+               printf("This is a dry-run, no files are going to be restored\n");
+
        ret = search_dir(root, &key, dir_name, "", mreg);
 
 out: