btrfs-progs: fix wrong max system array size check in user space
[platform/upstream/btrfs-progs.git] / utils.c
diff --git a/utils.c b/utils.c
index d022d58..29953d9 100644 (file)
--- a/utils.c
+++ b/utils.c
 #define BLKDISCARD     _IO(0x12,119)
 #endif
 
-static int
-discard_blocks(int fd, u64 start, u64 len)
+/*
+ * Discard the given range in one go
+ */
+static int discard_range(int fd, u64 start, u64 len)
 {
        u64 range[2] = { start, len };
 
@@ -62,6 +64,26 @@ discard_blocks(int fd, u64 start, u64 len)
        return 0;
 }
 
+/*
+ * Discard blocks in the given range in 1G chunks, the process is interruptible
+ */
+static int discard_blocks(int fd, u64 start, u64 len)
+{
+       while (len > 0) {
+               /* 1G granularity */
+               u64 chunk_size = min_t(u64, len, 1*1024*1024*1024);
+               int ret;
+
+               ret = discard_range(fd, start, chunk_size);
+               if (ret)
+                       return ret;
+               len -= chunk_size;
+               start += chunk_size;
+       }
+
+       return 0;
+}
+
 static u64 reference_root_table[] = {
        [1] =   BTRFS_ROOT_TREE_OBJECTID,
        [2] =   BTRFS_EXTENT_TREE_OBJECTID,
@@ -136,10 +158,10 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_header_generation(buf, 1);
        btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
        btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
-       write_extent_buffer(buf, super.fsid, (unsigned long)
-                           btrfs_header_fsid(buf), BTRFS_FSID_SIZE);
+       write_extent_buffer(buf, super.fsid, btrfs_header_fsid(),
+                           BTRFS_FSID_SIZE);
 
-       write_extent_buffer(buf, chunk_tree_uuid, (unsigned long)
+       write_extent_buffer(buf, chunk_tree_uuid,
                            btrfs_header_chunk_tree_uuid(buf),
                            BTRFS_UUID_SIZE);
 
@@ -164,8 +186,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_root_bytenr(&root_item, blocks[2]);
        btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems),
                            sizeof(root_item));
        write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf,
                            nritems), sizeof(root_item));
@@ -175,8 +197,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_root_bytenr(&root_item, blocks[4]);
        btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems),
                            sizeof(root_item));
        write_extent_buffer(buf, &root_item,
                            btrfs_item_ptr_offset(buf, nritems),
@@ -187,8 +209,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_root_bytenr(&root_item, blocks[5]);
        btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems),
                            sizeof(root_item));
        write_extent_buffer(buf, &root_item,
                            btrfs_item_ptr_offset(buf, nritems),
@@ -199,8 +221,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_root_bytenr(&root_item, blocks[6]);
        btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems),
                            sizeof(root_item));
        write_extent_buffer(buf, &root_item,
                            btrfs_item_ptr_offset(buf, nritems),
@@ -210,10 +232,10 @@ int make_btrfs(int fd, const char *device, const char *label,
 
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, leafsize, blocks[1]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != leafsize)
-               return -EIO;
+       if (ret != leafsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
 
        /* create the items for the extent tree */
        memset(buf->data+sizeof(struct btrfs_header), 0,
@@ -241,9 +263,9 @@ int make_btrfs(int fd, const char *device, const char *label,
                        btrfs_set_disk_key_offset(&disk_key, leafsize);
                }
                btrfs_set_item_key(buf, &disk_key, nritems);
-               btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems),
+               btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
                                      itemoff);
-               btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+               btrfs_set_item_size(buf, btrfs_item_nr(nritems),
                                    item_size);
                extent_item = btrfs_item_ptr(buf, nritems,
                                             struct btrfs_extent_item);
@@ -259,9 +281,9 @@ int make_btrfs(int fd, const char *device, const char *label,
                btrfs_set_disk_key_offset(&disk_key, ref_root);
                btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY);
                btrfs_set_item_key(buf, &disk_key, nritems);
-               btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems),
+               btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
                                      itemoff);
-               btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), 0);
+               btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0);
                nritems++;
        }
        btrfs_set_header_bytenr(buf, blocks[2]);
@@ -269,10 +291,10 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_header_nritems(buf, nritems);
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, leafsize, blocks[2]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != leafsize)
-               return -EIO;
+       if (ret != leafsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
 
        /* create the chunk tree */
        memset(buf->data+sizeof(struct btrfs_header), 0,
@@ -286,8 +308,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_disk_key_offset(&disk_key, 1);
        btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size);
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
 
        dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
        btrfs_set_device_id(buf, dev_item, 1);
@@ -318,8 +340,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_disk_key_offset(&disk_key, 0);
        btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf,  nritems), item_size);
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
 
        chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk);
        btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
@@ -356,10 +378,10 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_header_nritems(buf, nritems);
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, leafsize, blocks[3]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != leafsize)
-               return -EIO;
+       if (ret != leafsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
 
        /* create the device tree */
        memset(buf->data+sizeof(struct btrfs_header), 0,
@@ -372,8 +394,8 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_disk_key_offset(&disk_key, 0);
        btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
        btrfs_set_item_key(buf, &disk_key, nritems);
-       btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
-       btrfs_set_item_size(buf, btrfs_item_nr(buf,  nritems),
+       btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+       btrfs_set_item_size(buf, btrfs_item_nr(nritems),
                            sizeof(struct btrfs_dev_extent));
        dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent);
        btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
@@ -395,10 +417,10 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_header_nritems(buf, nritems);
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, leafsize, blocks[4]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != leafsize)
-               return -EIO;
+       if (ret != leafsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
 
        /* create the FS root */
        memset(buf->data+sizeof(struct btrfs_header), 0,
@@ -408,11 +430,10 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_header_nritems(buf, 0);
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, leafsize, blocks[5]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != leafsize)
-               return -EIO;
-
+       if (ret != leafsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
        /* finally create the csum root */
        memset(buf->data+sizeof(struct btrfs_header), 0,
                leafsize-sizeof(struct btrfs_header));
@@ -421,10 +442,10 @@ int make_btrfs(int fd, const char *device, const char *label,
        btrfs_set_header_nritems(buf, 0);
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, leafsize, blocks[6]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != leafsize)
-               return -EIO;
+       if (ret != leafsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
 
        /* and write out the super block */
        BUG_ON(sizeof(super) > sectorsize);
@@ -433,13 +454,16 @@ int make_btrfs(int fd, const char *device, const char *label,
        buf->len = sectorsize;
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
        ret = pwrite(fd, buf->data, sectorsize, blocks[0]);
-       if (ret < 0)
-               return -errno;
-       else if (ret != sectorsize)
-               return -EIO;
+       if (ret != sectorsize) {
+               ret = (ret < 0 ? -errno : -EIO);
+               goto out;
+       }
+
+       ret = 0;
 
+out:
        free(buf);
-       return 0;
+       return ret;
 }
 
 u64 btrfs_device_size(int fd, struct stat *st)
@@ -569,7 +593,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 }
 
 int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
-                          u64 max_block_count, int *mixed, int nodiscard)
+                          u64 max_block_count, int *mixed, int discard)
 {
        u64 block_count;
        u64 bytenr;
@@ -579,52 +603,65 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
        ret = fstat(fd, &st);
        if (ret < 0) {
                fprintf(stderr, "unable to stat %s\n", file);
-               exit(1);
+               return 1;
        }
 
        block_count = btrfs_device_size(fd, &st);
        if (block_count == 0) {
                fprintf(stderr, "unable to find %s size\n", file);
-               exit(1);
+               return 1;
        }
        if (max_block_count)
                block_count = min(block_count, max_block_count);
-       zero_end = 1;
 
        if (block_count < 1024 * 1024 * 1024 && !(*mixed)) {
                printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
                *mixed = 1;
        }
 
-       if (!nodiscard) {
+       if (discard) {
                /*
-                * We intentionally ignore errors from the discard ioctl.  It is
-                * not necessary for the mkfs functionality but just an optimization.
+                * We intentionally ignore errors from the discard ioctl.  It
+                * is not necessary for the mkfs functionality but just an
+                * optimization.
                 */
-               discard_blocks(fd, 0, block_count);
+               if (discard_range(fd, 0, 0) == 0) {
+                       fprintf(stderr, "Performing full device TRIM (%s) ...\n",
+                               pretty_size(block_count));
+                       discard_blocks(fd, 0, block_count);
+               }
        }
 
        ret = zero_dev_start(fd);
-       if (ret) {
-               fprintf(stderr, "failed to zero device start %d\n", ret);
-               exit(1);
-       }
+       if (ret)
+               goto zero_dev_error;
 
        for (i = 0 ; i < BTRFS_SUPER_MIRROR_MAX; i++) {
                bytenr = btrfs_sb_offset(i);
                if (bytenr >= block_count)
                        break;
-               zero_blocks(fd, bytenr, BTRFS_SUPER_INFO_SIZE);
+               ret = zero_blocks(fd, bytenr, BTRFS_SUPER_INFO_SIZE);
+               if (ret)
+                       goto zero_dev_error;
        }
 
        if (zero_end) {
                ret = zero_dev_end(fd, block_count);
-               if (ret) {
-                       fprintf(stderr, "failed to zero device end %d\n", ret);
-                       exit(1);
-               }
+               if (ret)
+                       goto zero_dev_error;
        }
        *block_count_ret = block_count;
+
+zero_dev_error:
+       if (ret < 0) {
+               fprintf(stderr, "ERROR: failed to zero device '%s' - %s\n",
+                       file, strerror(-ret));
+               return 1;
+       } else if (ret > 0) {
+               fprintf(stderr, "ERROR: failed to zero device '%s' - %d\n",
+                       file, ret);
+               return 1;
+       }
        return 0;
 }
 
@@ -672,7 +709,8 @@ error:
  * Returns negative errno on failure, otherwise
  * returns 1 for blockdev, 0 for not-blockdev
  */
-int is_block_device(const char *path) {
+int is_block_device(const char *path)
+{
        struct stat statbuf;
 
        if (stat(path, &statbuf) < 0)
@@ -682,12 +720,37 @@ int is_block_device(const char *path) {
 }
 
 /*
+ * check if given path is a mount point
+ * return 1 if yes. 0 if no. -1 for error
+ */
+int is_mount_point(const char *path)
+{
+       FILE *f;
+       struct mntent *mnt;
+       int ret = 0;
+
+       f = setmntent("/proc/self/mounts", "r");
+       if (f == NULL)
+               return -1;
+
+       while ((mnt = getmntent(f)) != NULL) {
+               if (strcmp(mnt->mnt_dir, path))
+                       continue;
+               ret = 1;
+               break;
+       }
+       endmntent(f);
+       return ret;
+}
+
+/*
  * Find the mount point for a mounted device.
  * On success, returns 0 with mountpoint in *mp.
  * On failure, returns -errno (not mounted yields -EINVAL)
  * Is noisy on failures, expects to be given a mounted device.
  */
-static int get_btrfs_mount(const char *dev, char *mp, size_t mp_size) {
+int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
+{
        int ret;
        int fd = -1;
 
@@ -712,7 +775,6 @@ static int get_btrfs_mount(const char *dev, char *mp, size_t mp_size) {
 
        ret = check_mounted_where(fd, dev, mp, mp_size, NULL);
        if (!ret) {
-               fprintf(stderr, "%s is not a mounted btrfs device\n", dev);
                ret = -EINVAL;
        } else { /* mounted, all good */
                ret = 0;
@@ -720,8 +782,6 @@ static int get_btrfs_mount(const char *dev, char *mp, size_t mp_size) {
 out:
        if (fd != -1)
                close(fd);
-       if (ret)
-               fprintf(stderr, "Could not get mountpoint for %s\n", dev);
        return ret;
 }
 
@@ -801,11 +861,11 @@ static int is_same_blk_file(const char* a, const char* b)
        char real_a[PATH_MAX];
        char real_b[PATH_MAX];
 
-       if(!realpath(a, real_a) ||
-          !realpath(b, real_b))
-       {
-               return -errno;
-       }
+       if(!realpath(a, real_a))
+               strcpy(real_a, a);
+
+       if (!realpath(b, real_b))
+               strcpy(real_b, b);
 
        /* Identical path? */
        if(strcmp(real_a, real_b) == 0)
@@ -846,8 +906,8 @@ static int is_same_loop_file(const char* a, const char* b)
 {
        char res_a[PATH_MAX];
        char res_b[PATH_MAX];
-       const char* final_a;
-       const char* final_b;
+       const char* final_a = NULL;
+       const char* final_b = NULL;
        int ret;
 
        /* Resolve a if it is a loop device */
@@ -856,10 +916,13 @@ static int is_same_loop_file(const char* a, const char* b)
                        return 0;
                return ret;
        } else if (ret) {
-               if ((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0)
-                       return ret;
-
-               final_a = res_a;
+               ret = resolve_loop_device(a, res_a, sizeof(res_a));
+               if (ret < 0) {
+                       if (errno != EPERM)
+                               return ret;
+               } else {
+                       final_a = res_a;
+               }
        } else {
                final_a = a;
        }
@@ -870,10 +933,13 @@ static int is_same_loop_file(const char* a, const char* b)
                        return 0;
                return ret;
        } else if (ret) {
-               if((ret = resolve_loop_device(b, res_b, sizeof(res_b))) < 0)
-                       return ret;
-
-               final_b = res_b;
+               ret = resolve_loop_device(b, res_b, sizeof(res_b));
+               if (ret < 0) {
+                       if (errno != EPERM)
+                               return ret;
+               } else {
+                       final_b = res_b;
+               }
        } else {
                final_b = b;
        }
@@ -956,12 +1022,12 @@ int check_mounted_where(int fd, const char *file, char *where, int size,
 
        /* scan other devices */
        if (is_btrfs && total_devs > 1) {
-               if((ret = btrfs_scan_for_fsid(1)))
+               if ((ret = btrfs_scan_for_fsid(!BTRFS_UPDATE_KERNEL)))
                        return ret;
        }
 
        /* iterate over the list of currently mountes filesystems */
-       if ((f = setmntent ("/proc/mounts", "r")) == NULL)
+       if ((f = setmntent ("/proc/self/mounts", "r")) == NULL)
                return -errno;
 
        while ((mnt = getmntent (f)) != NULL) {
@@ -1066,8 +1132,8 @@ again:
        dirp = opendir(dirname);
        if (!dirp) {
                fprintf(stderr, "Unable to open %s for scanning\n", dirname);
-               free(fullpath);
-               return -ENOENT;
+               ret = -errno;
+               goto fail;
        }
        while(1) {
                dirent = readdir(dirp);
@@ -1133,6 +1199,12 @@ again:
 fail:
        free(pending);
        free(fullpath);
+       while (!list_empty(&pending_list)) {
+               pending = list_entry(pending_list.next, struct pending_dir,
+                                    list);
+               list_del(&pending->list);
+               free(pending);
+       }
        if (dirp)
                closedir(dirp);
        return ret;
@@ -1179,13 +1251,13 @@ out:
 }
 
 static char *size_strs[] = { "", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
-void pretty_size_snprintf(u64 size, char *str, size_t str_bytes)
+int pretty_size_snprintf(u64 size, char *str, size_t str_bytes)
 {
        int num_divs = 0;
        float fraction;
 
        if (str_bytes == 0)
-               return;
+               return 0;
 
        if( size < 1024 ){
                fraction = size;
@@ -1201,11 +1273,12 @@ void pretty_size_snprintf(u64 size, char *str, size_t str_bytes)
 
                if (num_divs >= ARRAY_SIZE(size_strs)) {
                        str[0] = '\0';
-                       return;
+                       return -1;
                }
                fraction = (float)last_size / 1024;
        }
-       snprintf(str, str_bytes, "%.2f%s", fraction, size_strs[num_divs]);
+       return snprintf(str, str_bytes, "%.2f%s", fraction,
+                       size_strs[num_divs]);
 }
 
 /*
@@ -1267,7 +1340,7 @@ static int set_label_unmounted(const char *dev, const char *label)
        /* Open the super_block at the default location
         * and as read-write.
         */
-       root = open_ctree(dev, 0, 1);
+       root = open_ctree(dev, 0, OPEN_CTREE_WRITES);
        if (!root) /* errors are printed by open_ctree() */
                return -1;
 
@@ -1287,7 +1360,7 @@ static int set_label_mounted(const char *mount_path, const char *label)
 
        fd = open(mount_path, O_RDONLY | O_NOATIME);
        if (fd < 0) {
-               fprintf(stderr, "ERROR: unable access to '%s'\n", mount_path);
+               fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
                return -1;
        }
 
@@ -1302,7 +1375,7 @@ static int set_label_mounted(const char *mount_path, const char *label)
        return 0;
 }
 
-static int get_label_unmounted(const char *dev)
+static int get_label_unmounted(const char *dev, char *label)
 {
        struct btrfs_root *root;
        int ret;
@@ -1325,7 +1398,7 @@ static int get_label_unmounted(const char *dev)
        if(!root)
                return -1;
 
-       fprintf(stdout, "%s\n", root->fs_info->super_copy->label);
+       memcpy(label, root->fs_info->super_copy->label, BTRFS_LABEL_SIZE);
 
        /* Now we close it since we are done. */
        close_ctree(root);
@@ -1344,7 +1417,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
 
        fd = open(mount_path, O_RDONLY | O_NOATIME);
        if (fd < 0) {
-               fprintf(stderr, "ERROR: unable access to '%s'\n", mount_path);
+               fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
                return -1;
        }
 
@@ -1360,18 +1433,15 @@ int get_label_mounted(const char *mount_path, char *labelp)
        return 0;
 }
 
-int get_label(const char *btrfs_dev)
+int get_label(const char *btrfs_dev, char *label)
 {
        int ret;
-       char label[BTRFS_LABEL_SIZE];
 
        if (is_existing_blk_or_reg_file(btrfs_dev))
-               ret = get_label_unmounted(btrfs_dev);
-       else {
+               ret = get_label_unmounted(btrfs_dev, label);
+       else
                ret = get_label_mounted(btrfs_dev, label);
-               if (!ret)
-                       fprintf(stdout, "%s\n", label);
-       }
+
        return ret;
 }
 
@@ -1468,6 +1538,39 @@ scan_again:
        return 0;
 }
 
+/*
+ * This function should be only used when parsing
+ * command arg, it won't return error to it's
+ * caller and rather exit directly just like usage().
+ */
+u64 arg_strtou64(const char *str)
+{
+       u64 value;
+       char *ptr_parse_end = NULL;
+
+       value = strtoull(str, &ptr_parse_end, 0);
+       if (ptr_parse_end && *ptr_parse_end != '\0') {
+               fprintf(stderr, "ERROR: %s is not a valid numeric value.\n",
+                       str);
+               exit(1);
+       }
+       /*
+        * if we pass a negative number to strtoull,
+        * it will return an unexpected number to us,
+        * so let's do the check ourselves.
+        */
+       if (str[0] == '-') {
+               fprintf(stderr, "ERROR: %s: negative value is invalid.\n",
+                       str);
+               exit(1);
+       }
+       if (value == ULLONG_MAX) {
+               fprintf(stderr, "ERROR: %s is too large.\n", str);
+               exit(1);
+       }
+       return value;
+}
+
 u64 parse_size(char *s)
 {
        int i;
@@ -1485,16 +1588,22 @@ u64 parse_size(char *s)
                switch (c) {
                case 'e':
                        mult *= 1024;
+                       /* fallthrough */
                case 'p':
                        mult *= 1024;
+                       /* fallthrough */
                case 't':
                        mult *= 1024;
+                       /* fallthrough */
                case 'g':
                        mult *= 1024;
+                       /* fallthrough */
                case 'm':
                        mult *= 1024;
+                       /* fallthrough */
                case 'k':
                        mult *= 1024;
+                       /* fallthrough */
                case 'b':
                        break;
                default:
@@ -1512,7 +1621,7 @@ u64 parse_size(char *s)
        return strtoull(s, NULL, 10) * mult;
 }
 
-int open_file_or_dir(const char *fname, DIR **dirstream)
+int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags)
 {
        int ret;
        struct stat st;
@@ -1525,19 +1634,31 @@ int open_file_or_dir(const char *fname, DIR **dirstream)
        if (S_ISDIR(st.st_mode)) {
                *dirstream = opendir(fname);
                if (!*dirstream)
-                       return -2;
+                       return -1;
                fd = dirfd(*dirstream);
+       } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+               fd = open(fname, open_flags);
        } else {
-               fd = open(fname, O_RDWR);
+               /*
+                * we set this on purpose, in case the caller output
+                * strerror(errno) as success
+                */
+               errno = EINVAL;
+               return -1;
        }
        if (fd < 0) {
-               fd = -3;
+               fd = -1;
                if (*dirstream)
                        closedir(*dirstream);
        }
        return fd;
 }
 
+int open_file_or_dir(const char *fname, DIR **dirstream)
+{
+       return open_file_or_dir3(fname, dirstream, O_RDWR);
+}
+
 void close_file_or_dir(int fd, DIR *dirstream)
 {
        if (dirstream)
@@ -1546,7 +1667,7 @@ void close_file_or_dir(int fd, DIR *dirstream)
                close(fd);
 }
 
-static int get_device_info(int fd, u64 devid,
+int get_device_info(int fd, u64 devid,
                struct btrfs_ioctl_dev_info_args *di_args)
 {
        int ret;
@@ -1574,7 +1695,7 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
        int fd = -1;
        int ret = 0;
        int ndevs = 0;
-       int i = 1;
+       int i = 0;
        struct btrfs_fs_devices *fs_devices_mnt = NULL;
        struct btrfs_ioctl_dev_info_args *di_args;
        char mp[BTRFS_PATH_NAME_MAX + 1];
@@ -1583,6 +1704,10 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
        memset(fi_args, 0, sizeof(*fi_args));
 
        if (is_block_device(path)) {
+               struct btrfs_super_block *disk_super;
+               char buf[BTRFS_SUPER_INFO_SIZE];
+               u64 devid;
+
                /* Ensure it's mounted, then set path to the mountpoint */
                fd = open(path, O_RDONLY);
                if (fd < 0) {
@@ -1602,8 +1727,18 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                path = mp;
                /* Only fill in this one device */
                fi_args->num_devices = 1;
-               fi_args->max_id = fs_devices_mnt->latest_devid;
-               i = fs_devices_mnt->latest_devid;
+
+               disk_super = (struct btrfs_super_block *)buf;
+               ret = btrfs_read_dev_super(fd, disk_super, BTRFS_SUPER_INFO_OFFSET);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto out;
+               }
+               devid = btrfs_stack_device_id(&disk_super->dev_item);
+
+               fi_args->max_id = devid;
+               i = devid;
+
                memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
                close(fd);
        }
@@ -1643,8 +1778,15 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                ndevs++;
        }
 
-       BUG_ON(ndevs == 0);
-       ret = 0;
+       /*
+       * only when the only dev we wanted to find is not there then
+       * let any error be returned
+       */
+       if (fi_args->num_devices != 1) {
+               BUG_ON(ndevs == 0);
+               ret = 0;
+       }
+
 out:
        close_file_or_dir(fd, dirstream);
        return ret;
@@ -1893,16 +2035,65 @@ int test_dev_for_mkfs(char *file, int force_overwrite, char *estr)
        if (fstat(fd, &st)) {
                snprintf(estr, sz, "unable to stat %s: %s\n", file,
                        strerror(errno));
+               close(fd);
                return 1;
        }
        if (!S_ISBLK(st.st_mode)) {
                fprintf(stderr, "'%s' is not a block device\n", file);
+               close(fd);
                return 1;
        }
        close(fd);
        return 0;
 }
 
+int btrfs_scan_lblkid(int update_kernel)
+{
+       int fd = -1;
+       int ret;
+       u64 num_devices;
+       struct btrfs_fs_devices *tmp_devices;
+       blkid_dev_iterate iter = NULL;
+       blkid_dev dev = NULL;
+       blkid_cache cache = NULL;
+       char path[PATH_MAX];
+
+       if (blkid_get_cache(&cache, 0) < 0) {
+               printf("ERROR: lblkid cache get failed\n");
+               return 1;
+       }
+       blkid_probe_all(cache);
+       iter = blkid_dev_iterate_begin(cache);
+       blkid_dev_set_search(iter, "TYPE", "btrfs");
+       while (blkid_dev_next(iter, &dev) == 0) {
+               dev = blkid_verify(cache, dev);
+               if (!dev)
+                       continue;
+               /* if we are here its definitely a btrfs disk*/
+               strncpy(path, blkid_dev_devname(dev), PATH_MAX);
+
+               fd = open(path, O_RDONLY);
+               if (fd < 0) {
+                       printf("ERROR: could not open %s\n", path);
+                       continue;
+               }
+               ret = btrfs_scan_one_device(fd, path, &tmp_devices,
+                               &num_devices, BTRFS_SUPER_INFO_OFFSET);
+               if (ret) {
+                       printf("ERROR: could not scan %s\n", path);
+                       close (fd);
+                       continue;
+               }
+
+               close(fd);
+               if (update_kernel)
+                       btrfs_register_one_device(path);
+       }
+       blkid_dev_iterate_end(iter);
+       blkid_put_cache(cache);
+       return 0;
+}
+
 /*
  * scans devs for the btrfs
 */
@@ -1917,6 +2108,9 @@ int scan_for_btrfs(int where, int update_kernel)
        case BTRFS_SCAN_DEV:
                ret = btrfs_scan_one_dir("/dev", update_kernel);
                break;
+       case BTRFS_SCAN_LBLKID:
+               ret = btrfs_scan_lblkid(update_kernel);
+               break;
        }
        return ret;
 }
@@ -1949,3 +2143,99 @@ int is_vol_small(char *file)
                return 0;
        }
 }
+
+/*
+ * This reads a line from the stdin and only returns non-zero if the
+ * first whitespace delimited token is a case insensitive match with yes
+ * or y.
+ */
+int ask_user(char *question)
+{
+       char buf[30] = {0,};
+       char *saveptr = NULL;
+       char *answer;
+
+       printf("%s [y/N]: ", question);
+
+       return fgets(buf, sizeof(buf) - 1, stdin) &&
+              (answer = strtok_r(buf, " \t\n\r", &saveptr)) &&
+              (!strcasecmp(answer, "yes") || !strcasecmp(answer, "y"));
+}
+
+/*
+ * For a given:
+ * - file or directory return the containing tree root id
+ * - subvolume return it's own tree id
+ * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is
+ *   undefined and function returns -1
+ */
+int lookup_ino_rootid(int fd, u64 *rootid)
+{
+       struct btrfs_ioctl_ino_lookup_args args;
+       int ret;
+       int e;
+
+       memset(&args, 0, sizeof(args));
+       args.treeid = 0;
+       args.objectid = BTRFS_FIRST_FREE_OBJECTID;
+
+       ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+       e = errno;
+       if (ret) {
+               fprintf(stderr, "ERROR: Failed to lookup root id - %s\n",
+                       strerror(e));
+               return ret;
+       }
+
+       *rootid = args.treeid;
+
+       return 0;
+}
+
+int find_mount_root(const char *path, char **mount_root)
+{
+       FILE *mnttab;
+       int fd;
+       struct mntent *ent;
+       int len;
+       int ret;
+       int longest_matchlen = 0;
+       char *longest_match = NULL;
+
+       fd = open(path, O_RDONLY | O_NOATIME);
+       if (fd < 0)
+               return -errno;
+       close(fd);
+
+       mnttab = setmntent("/proc/self/mounts", "r");
+       if (!mnttab)
+               return -errno;
+
+       while ((ent = getmntent(mnttab))) {
+               len = strlen(ent->mnt_dir);
+               if (strncmp(ent->mnt_dir, path, len) == 0) {
+                       /* match found */
+                       if (longest_matchlen < len) {
+                               free(longest_match);
+                               longest_matchlen = len;
+                               longest_match = strdup(ent->mnt_dir);
+                       }
+               }
+       }
+       endmntent(mnttab);
+
+       if (!longest_match) {
+               fprintf(stderr,
+                       "ERROR: Failed to find mount root for path %s.\n",
+                       path);
+               return -ENOENT;
+       }
+
+       ret = 0;
+       *mount_root = realpath(longest_match, NULL);
+       if (!*mount_root)
+               ret = -errno;
+
+       free(longest_match);
+       return ret;
+}