btrfs-progs: check/lowmem: Fix false alert of data extent backref lost for snapshot
[platform/upstream/btrfs-progs.git] / utils.c
diff --git a/utils.c b/utils.c
index 20cddd9..715bab0 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -24,6 +24,7 @@
 #include <sys/mount.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/sysinfo.h>
 #include <uuid/uuid.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -39,6 +40,8 @@
 #include <linux/magic.h>
 #include <getopt.h>
 
+#include <btrfsutil.h>
+
 #include "kerncompat.h"
 #include "radix-tree.h"
 #include "ctree.h"
@@ -179,7 +182,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
                      u32 sectorsize)
 {
        struct btrfs_super_block *disk_super;
-       struct btrfs_super_block *super = root->fs_info->super_copy;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct btrfs_super_block *super = fs_info->super_copy;
        struct btrfs_device *device;
        struct btrfs_dev_item *dev_item;
        char *buf = NULL;
@@ -214,7 +218,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        device->total_bytes = device_total_bytes;
        device->bytes_used = 0;
        device->total_ios = 0;
-       device->dev_root = root->fs_info->dev_root;
+       device->dev_root = fs_info->dev_root;
        device->name = strdup(path);
        if (!device->name) {
                ret = -ENOMEM;
@@ -222,7 +226,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        }
 
        INIT_LIST_HEAD(&device->dev_list);
-       ret = btrfs_add_device(trans, root, device);
+       ret = btrfs_add_device(trans, fs_info, device);
        if (ret)
                goto out;
 
@@ -248,8 +252,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        BUG_ON(ret != sectorsize);
 
        free(buf);
-       list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
-       device->fs_devices = root->fs_info->fs_devices;
+       list_add(&device->dev_list, &fs_info->fs_devices->devices);
+       device->fs_devices = fs_info->fs_devices;
        return 0;
 
 out:
@@ -296,7 +300,7 @@ static int btrfs_wipe_existing_sb(int fd)
        memset(buf, 0, len);
        ret = pwrite(fd, buf, len, offset);
        if (ret < 0) {
-               error("cannot wipe existing superblock: %s", strerror(errno));
+               error("cannot wipe existing superblock: %m");
                ret = -1;
        } else if (ret != len) {
                error("cannot wipe existing superblock: wrote %d of %zd", ret, len);
@@ -318,7 +322,7 @@ int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret,
 
        ret = fstat(fd, &st);
        if (ret < 0) {
-               error("unable to stat %s: %s", file, strerror(errno));
+               error("unable to stat %s: %m", file);
                return 1;
        }
 
@@ -378,7 +382,7 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
        btrfs_set_stack_inode_generation(&inode_item, trans->transid);
        btrfs_set_stack_inode_size(&inode_item, 0);
        btrfs_set_stack_inode_nlink(&inode_item, 1);
-       btrfs_set_stack_inode_nbytes(&inode_item, root->nodesize);
+       btrfs_set_stack_inode_nbytes(&inode_item, root->fs_info->nodesize);
        btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
        btrfs_set_stack_timespec_sec(&inode_item.atime, now);
        btrfs_set_stack_timespec_nsec(&inode_item.atime, 0);
@@ -445,7 +449,7 @@ int is_mount_point(const char *path)
        return ret;
 }
 
-static int is_reg_file(const char *path)
+int is_reg_file(const char *path)
 {
        struct stat statbuf;
 
@@ -454,6 +458,21 @@ static int is_reg_file(const char *path)
        return S_ISREG(statbuf.st_mode);
 }
 
+int is_path_exist(const char *path)
+{
+       struct stat statbuf;
+       int ret;
+
+       ret = stat(path, &statbuf);
+       if (ret < 0) {
+               if (errno == ENOENT)
+                       return 0;
+               else
+                       return -errno;
+       }
+       return 1;
+}
+
 /*
  * This function checks if the given input parameter is
  * an uuid or a path
@@ -517,7 +536,7 @@ int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
        fd = open(dev, O_RDONLY);
        if (fd < 0) {
                ret = -errno;
-               error("cannot open %s: %s", dev, strerror(errno));
+               error("cannot open %s: %m", dev);
                goto out;
        }
 
@@ -555,8 +574,8 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose)
                        return -1;
                }
                ret = open_file_or_dir(mp, dirstream);
-               error_on(verbose && ret < 0, "can't access '%s': %s",
-                        path, strerror(errno));
+               error_on(verbose && ret < 0, "can't access '%s': %m",
+                        path);
        } else {
                ret = btrfs_open_dir(path, dirstream, 1);
        }
@@ -567,17 +586,16 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose)
 /*
  * Do the following checks before calling open_file_or_dir():
  * 1: path is in a btrfs filesystem
- * 2: path is a directory
+ * 2: path is a directory if dir_only is 1
  */
-int btrfs_open_dir(const char *path, DIR **dirstream, int verbose)
+int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only)
 {
        struct statfs stfs;
        struct stat st;
        int ret;
 
        if (statfs(path, &stfs) != 0) {
-               error_on(verbose, "cannot access '%s': %s", path,
-                               strerror(errno));
+               error_on(verbose, "cannot access '%s': %m", path);
                return -1;
        }
 
@@ -587,25 +605,33 @@ int btrfs_open_dir(const char *path, DIR **dirstream, int verbose)
        }
 
        if (stat(path, &st) != 0) {
-               error_on(verbose, "cannot access '%s': %s", path,
-                               strerror(errno));
+               error_on(verbose, "cannot access '%s': %m", path);
                return -1;
        }
 
-       if (!S_ISDIR(st.st_mode)) {
+       if (dir_only && !S_ISDIR(st.st_mode)) {
                error_on(verbose, "not a directory: %s", path);
                return -3;
        }
 
        ret = open_file_or_dir(path, dirstream);
        if (ret < 0) {
-               error_on(verbose, "cannot access '%s': %s", path,
-                               strerror(errno));
+               error_on(verbose, "cannot access '%s': %m", path);
        }
 
        return ret;
 }
 
+int btrfs_open_dir(const char *path, DIR **dirstream, int verbose)
+{
+       return btrfs_open(path, dirstream, verbose, 1);
+}
+
+int btrfs_open_file_or_dir(const char *path, DIR **dirstream, int verbose)
+{
+       return btrfs_open(path, dirstream, verbose, 0);
+}
+
 /* checks if a device is a loop device */
 static int is_loop_device (const char* device) {
        struct stat statbuf;
@@ -792,14 +818,9 @@ static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
                const char* file)
 {
        int ret;
-       struct list_head *head;
-       struct list_head *cur;
        struct btrfs_device *device;
 
-       head = &fs_devices->devices;
-       list_for_each(cur, head) {
-               device = list_entry(cur, struct btrfs_device, dev_list);
-
+       list_for_each_entry(device, &fs_devices->devices, dev_list) {
                if((ret = is_same_loop_file(device->name, file)))
                        return ret;
        }
@@ -876,8 +897,7 @@ int check_mounted(const char* file)
 
        fd = open(file, O_RDONLY);
        if (fd < 0) {
-               error("mount check: cannot open %s: %s", file,
-                               strerror(errno));
+               error("mount check: cannot open %s: %m", file);
                return -errno;
        }
 
@@ -966,16 +986,14 @@ int btrfs_register_one_device(const char *fname)
        fd = open("/dev/btrfs-control", O_RDWR);
        if (fd < 0) {
                warning(
-       "failed to open /dev/btrfs-control, skipping device registration: %s",
-                       strerror(errno));
+       "failed to open /dev/btrfs-control, skipping device registration: %m");
                return -errno;
        }
        memset(&args, 0, sizeof(args));
        strncpy_null(args.name, fname);
        ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
        if (ret < 0) {
-               error("device scan failed on '%s': %s", fname,
-                               strerror(errno));
+               error("device scan failed on '%s': %m", fname);
                ret = -errno;
        }
        close(fd);
@@ -1228,6 +1246,7 @@ static int set_label_unmounted(const char *dev, const char *label)
                return -1;
 
        trans = btrfs_start_transaction(root, 1);
+       BUG_ON(IS_ERR(trans));
        __strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1);
 
        btrfs_commit_transaction(trans, root);
@@ -1244,15 +1263,14 @@ static int set_label_mounted(const char *mount_path, const char *labelp)
 
        fd = open(mount_path, O_RDONLY | O_NOATIME);
        if (fd < 0) {
-               error("unable to access %s: %s", mount_path, strerror(errno));
+               error("unable to access %s: %m", mount_path);
                return -1;
        }
 
        memset(label, 0, sizeof(label));
        __strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1);
        if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) {
-               error("unable to set label of %s: %s", mount_path,
-                               strerror(errno));
+               error("unable to set label of %s: %m", mount_path);
                close(fd);
                return -1;
        }
@@ -1300,7 +1318,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
 
        fd = open(mount_path, O_RDONLY | O_NOATIME);
        if (fd < 0) {
-               error("unable to access %s: %s", mount_path, strerror(errno));
+               error("unable to access %s: %m", mount_path);
                return -1;
        }
 
@@ -1308,8 +1326,7 @@ int get_label_mounted(const char *mount_path, char *labelp)
        ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label);
        if (ret < 0) {
                if (errno != ENOTTY)
-                       error("unable to get label of %s: %s", mount_path,
-                                       strerror(errno));
+                       error("unable to get label of %s: %m", mount_path);
                ret = -errno;
                close(fd);
                return ret;
@@ -1438,6 +1455,7 @@ u64 parse_qgroupid(const char *p)
        char *s = strchr(p, '/');
        const char *ptr_src_end = p + strlen(p);
        char *ptr_parse_end = NULL;
+       enum btrfs_util_error err;
        u64 level;
        u64 id;
        int fd;
@@ -1465,8 +1483,8 @@ u64 parse_qgroupid(const char *p)
 
 path:
        /* Path format like subv at 'my_subvol' is the fallback case */
-       ret = test_issubvolume(p);
-       if (ret < 0 || !ret)
+       err = btrfs_util_is_subvolume(p);
+       if (err)
                goto err;
        fd = open(p, O_RDONLY);
        if (fd < 0)
@@ -1526,10 +1544,16 @@ int open_file_or_dir(const char *fname, DIR **dirstream)
 
 void close_file_or_dir(int fd, DIR *dirstream)
 {
-       if (dirstream)
+       int old_errno;
+
+       old_errno = errno;
+       if (dirstream) {
                closedir(dirstream);
-       else if (fd >= 0)
+       } else if (fd >= 0) {
                close(fd);
+       }
+
+       errno = old_errno;
 }
 
 int get_device_info(int fd, u64 devid,
@@ -1647,7 +1671,7 @@ int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                fd = open(path, O_RDONLY);
                if (fd < 0) {
                        ret = -errno;
-                       error("cannot open %s: %s", path, strerror(errno));
+                       error("cannot open %s: %m", path);
                        goto out;
                }
                ret = check_mounted_where(fd, path, mp, sizeof(mp),
@@ -1746,157 +1770,107 @@ out:
        return ret;
 }
 
-#define isoctal(c)     (((c) & ~7) == '0')
-
-static inline void translate(char *f, char *t)
+int get_fsid(const char *path, u8 *fsid, int silent)
 {
-       while (*f != '\0') {
-               if (*f == '\\' &&
-                   isoctal(f[1]) && isoctal(f[2]) && isoctal(f[3])) {
-                       *t++ = 64*(f[1] & 7) + 8*(f[2] & 7) + (f[3] & 7);
-                       f += 4;
-               } else
-                       *t++ = *f++;
-       }
-       *t = '\0';
-       return;
-}
-
-/*
- * Checks if the swap device.
- * Returns 1 if swap device, < 0 on error or 0 if not swap device.
- */
-static int is_swap_device(const char *file)
-{
-       FILE    *f;
-       struct stat     st_buf;
-       dev_t   dev;
-       ino_t   ino = 0;
-       char    tmp[PATH_MAX];
-       char    buf[PATH_MAX];
-       char    *cp;
-       int     ret = 0;
-
-       if (stat(file, &st_buf) < 0)
-               return -errno;
-       if (S_ISBLK(st_buf.st_mode))
-               dev = st_buf.st_rdev;
-       else if (S_ISREG(st_buf.st_mode)) {
-               dev = st_buf.st_dev;
-               ino = st_buf.st_ino;
-       } else
-               return 0;
-
-       if ((f = fopen("/proc/swaps", "r")) == NULL)
-               return 0;
+       int ret;
+       int fd;
+       struct btrfs_ioctl_fs_info_args args;
 
-       /* skip the first line */
-       if (fgets(tmp, sizeof(tmp), f) == NULL)
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               ret = -errno;
+               if (!silent)
+                       error("failed to open %s: %s", path,
+                               strerror(-ret));
                goto out;
+       }
 
-       while (fgets(tmp, sizeof(tmp), f) != NULL) {
-               if ((cp = strchr(tmp, ' ')) != NULL)
-                       *cp = '\0';
-               if ((cp = strchr(tmp, '\t')) != NULL)
-                       *cp = '\0';
-               translate(tmp, buf);
-               if (stat(buf, &st_buf) != 0)
-                       continue;
-               if (S_ISBLK(st_buf.st_mode)) {
-                       if (dev == st_buf.st_rdev) {
-                               ret = 1;
-                               break;
-                       }
-               } else if (S_ISREG(st_buf.st_mode)) {
-                       if (dev == st_buf.st_dev && ino == st_buf.st_ino) {
-                               ret = 1;
-                               break;
-                       }
-               }
+       ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
+       if (ret < 0) {
+               ret = -errno;
+               goto out;
        }
 
-out:
-       fclose(f);
+       memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
+       ret = 0;
 
+out:
+       if (fd != -1)
+               close(fd);
        return ret;
 }
 
-/*
- * Check for existing filesystem or partition table on device.
- * Returns:
- *      1 for existing fs or partition
- *      0 for nothing found
- *     -1 for internal error
- */
-static int check_overwrite(const char *device)
+int is_seen_fsid(u8 *fsid, struct seen_fsid *seen_fsid_hash[])
 {
-       const char      *type;
-       blkid_probe     pr = NULL;
-       int             ret;
-       blkid_loff_t    size;
+       u8 hash = fsid[0];
+       int slot = hash % SEEN_FSID_HASH_SIZE;
+       struct seen_fsid *seen = seen_fsid_hash[slot];
 
-       if (!device || !*device)
-               return 0;
+       while (seen) {
+               if (memcmp(seen->fsid, fsid, BTRFS_FSID_SIZE) == 0)
+                       return 1;
 
-       ret = -1; /* will reset on success of all setup calls */
+               seen = seen->next;
+       }
 
-       pr = blkid_new_probe_from_filename(device);
-       if (!pr)
-               goto out;
+       return 0;
+}
 
-       size = blkid_probe_get_size(pr);
-       if (size < 0)
-               goto out;
+int add_seen_fsid(u8 *fsid, struct seen_fsid *seen_fsid_hash[],
+               int fd, DIR *dirstream)
+{
+       u8 hash = fsid[0];
+       int slot = hash % SEEN_FSID_HASH_SIZE;
+       struct seen_fsid *seen = seen_fsid_hash[slot];
+       struct seen_fsid *alloc;
 
-       /* nothing to overwrite on a 0-length device */
-       if (size == 0) {
-               ret = 0;
-               goto out;
-       }
+       if (!seen)
+               goto insert;
 
-       ret = blkid_probe_enable_partitions(pr, 1);
-       if (ret < 0)
-               goto out;
+       while (1) {
+               if (memcmp(seen->fsid, fsid, BTRFS_FSID_SIZE) == 0)
+                       return -EEXIST;
 
-       ret = blkid_do_fullprobe(pr);
-       if (ret < 0)
-               goto out;
+               if (!seen->next)
+                       break;
 
-       /*
-        * Blkid returns 1 for nothing found and 0 when it finds a signature,
-        * but we want the exact opposite, so reverse the return value here.
-        *
-        * In addition print some useful diagnostics about what actually is
-        * on the device.
-        */
-       if (ret) {
-               ret = 0;
-               goto out;
+               seen = seen->next;
        }
 
-       if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
-               fprintf(stderr,
-                       "%s appears to contain an existing "
-                       "filesystem (%s).\n", device, type);
-       } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
-               fprintf(stderr,
-                       "%s appears to contain a partition "
-                       "table (%s).\n", device, type);
-       } else {
-               fprintf(stderr,
-                       "%s appears to contain something weird "
-                       "according to blkid\n", device);
-       }
-       ret = 1;
+insert:
+       alloc = malloc(sizeof(*alloc));
+       if (!alloc)
+               return -ENOMEM;
 
-out:
-       if (pr)
-               blkid_free_probe(pr);
-       if (ret == -1)
-               fprintf(stderr,
-                       "probe of %s failed, cannot detect "
-                         "existing filesystem.\n", device);
-       return ret;
+       alloc->next = NULL;
+       memcpy(alloc->fsid, fsid, BTRFS_FSID_SIZE);
+       alloc->fd = fd;
+       alloc->dirstream = dirstream;
+
+       if (seen)
+               seen->next = alloc;
+       else
+               seen_fsid_hash[slot] = alloc;
+
+       return 0;
+}
+
+void free_seen_fsid(struct seen_fsid *seen_fsid_hash[])
+{
+       int slot;
+       struct seen_fsid *seen;
+       struct seen_fsid *next;
+
+       for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
+               seen = seen_fsid_hash[slot];
+               while (seen) {
+                       next = seen->next;
+                       close_file_or_dir(seen->fd, seen->dirstream);
+                       free(seen);
+                       seen = next;
+               }
+               seen_fsid_hash[slot] = NULL;
+       }
 }
 
 static int group_profile_devs_min(u64 flag)
@@ -1987,84 +1961,6 @@ int group_profile_max_safe_loss(u64 flags)
        }
 }
 
-u64 btrfs_min_dev_size(u32 nodesize)
-{
-       return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE +
-                   btrfs_min_global_blk_rsv_size(nodesize));
-}
-
-/*
- * Btrfs minimum size calculation is complicated, it should include at least:
- * 1. system group size
- * 2. minimum global block reserve
- * 3. metadata used at mkfs
- * 4. space reservation to create uuid for first mount.
- * Also, raid factor should also be taken into consideration.
- * To avoid the overkill calculation, (system group + global block rsv) * 2
- * for *EACH* device should be good enough.
- */
-u64 btrfs_min_global_blk_rsv_size(u32 nodesize)
-{
-       return (u64)nodesize << 10;
-}
-
-/*
- * Check if a device is suitable for btrfs
- * returns:
- *  1: something is wrong, an error is printed
- *  0: all is fine
- */
-int test_dev_for_mkfs(const char *file, int force_overwrite)
-{
-       int ret, fd;
-       struct stat st;
-
-       ret = is_swap_device(file);
-       if (ret < 0) {
-               error("checking status of %s: %s", file, strerror(-ret));
-               return 1;
-       }
-       if (ret == 1) {
-               error("%s is a swap device", file);
-               return 1;
-       }
-       if (!force_overwrite) {
-               if (check_overwrite(file)) {
-                       error("use the -f option to force overwrite of %s",
-                                       file);
-                       return 1;
-               }
-       }
-       ret = check_mounted(file);
-       if (ret < 0) {
-               error("cannot check mount status of %s: %s", file,
-                               strerror(-ret));
-               return 1;
-       }
-       if (ret == 1) {
-               error("%s is mounted", file);
-               return 1;
-       }
-       /* check if the device is busy */
-       fd = open(file, O_RDWR|O_EXCL);
-       if (fd < 0) {
-               error("unable to open %s: %s", file, strerror(errno));
-               return 1;
-       }
-       if (fstat(fd, &st)) {
-               error("unable to stat %s: %s", file, strerror(errno));
-               close(fd);
-               return 1;
-       }
-       if (!S_ISBLK(st.st_mode)) {
-               error("%s is not a block device", file);
-               close(fd);
-               return 1;
-       }
-       close(fd);
-       return 0;
-}
-
 int btrfs_scan_devices(void)
 {
        int fd = -1;
@@ -2095,7 +1991,7 @@ int btrfs_scan_devices(void)
 
                fd = open(path, O_RDONLY);
                if (fd < 0) {
-                       error("cannot open %s: %s", path, strerror(errno));
+                       error("cannot open %s: %m", path);
                        continue;
                }
                ret = btrfs_scan_one_device(fd, path, &tmp_devices,
@@ -2117,35 +2013,6 @@ int btrfs_scan_devices(void)
        return 0;
 }
 
-int is_vol_small(const char *file)
-{
-       int fd = -1;
-       int e;
-       struct stat st;
-       u64 size;
-
-       fd = open(file, O_RDONLY);
-       if (fd < 0)
-               return -errno;
-       if (fstat(fd, &st) < 0) {
-               e = -errno;
-               close(fd);
-               return e;
-       }
-       size = btrfs_device_size(fd, &st);
-       if (size == 0) {
-               close(fd);
-               return -1;
-       }
-       if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) {
-               close(fd);
-               return 1;
-       } else {
-               close(fd);
-               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
@@ -2219,27 +2086,6 @@ int find_mount_root(const char *path, char **mount_root)
        return ret;
 }
 
-int test_minimum_size(const char *file, u32 nodesize)
-{
-       int fd;
-       struct stat statbuf;
-
-       fd = open(file, O_RDONLY);
-       if (fd < 0)
-               return -errno;
-       if (stat(file, &statbuf) < 0) {
-               close(fd);
-               return -errno;
-       }
-       if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) {
-               close(fd);
-               return 1;
-       }
-       close(fd);
-       return 0;
-}
-
-
 /*
  * Test if path is a directory
  * Returns:
@@ -2547,6 +2393,32 @@ unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode)
        return unit_mode;
 }
 
+u64 div_factor(u64 num, int factor)
+{
+       if (factor == 10)
+               return num;
+       num *= factor;
+       num /= 10;
+       return num;
+}
+/*
+ * Get the length of the string converted from a u64 number.
+ *
+ * Result is equal to log10(num) + 1, but without the use of math library.
+ */
+int count_digits(u64 num)
+{
+       int ret = 0;
+
+       if (num == 0)
+               return 1;
+       while (num > 0) {
+               ret++;
+               num /= 10;
+       }
+       return ret;
+}
+
 int string_is_numerical(const char *str)
 {
        if (!str)
@@ -2560,6 +2432,15 @@ int string_is_numerical(const char *str)
        return 1;
 }
 
+int prefixcmp(const char *str, const char *prefix)
+{
+       for (; ; str++, prefix++)
+               if (!*prefix)
+                       return 0;
+               else if (*str != *prefix)
+                       return (unsigned char)*prefix - (unsigned char)*str;
+}
+
 /* Subvolume helper functions */
 /*
  * test if name is a correct subvolume name
@@ -2573,111 +2454,24 @@ int test_issubvolname(const char *name)
                strcmp(name, ".") && strcmp(name, "..");
 }
 
-/*
- * Test if path is a subvolume
- * Returns:
- *   0 - path exists but it is not a subvolume
- *   1 - path exists and it is  a subvolume
- * < 0 - error
- */
-int test_issubvolume(const char *path)
-{
-       struct stat     st;
-       struct statfs stfs;
-       int             res;
-
-       res = stat(path, &st);
-       if (res < 0)
-               return -errno;
-
-       if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
-               return 0;
-
-       res = statfs(path, &stfs);
-       if (res < 0)
-               return -errno;
-
-       return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
-}
-
 const char *subvol_strip_mountpoint(const char *mnt, const char *full_path)
 {
        int len = strlen(mnt);
        if (!len)
                return full_path;
 
+       if ((strncmp(mnt, full_path, len) != 0) || (full_path[len] != '/')) {
+               error("not on mount point: %s", mnt);
+               exit(1);
+       }
+
        if (mnt[len - 1] != '/')
                len += 1;
 
        return full_path + len;
 }
 
-/*
- * Returns
- * <0: Std error
- * 0: All fine
- * 1: Error; and error info printed to the terminal. Fixme.
- * 2: If the fullpath is root tree instead of subvol tree
- */
-int get_subvol_info(const char *fullpath, struct root_info *get_ri)
-{
-       u64 sv_id;
-       int ret = 1;
-       int fd = -1;
-       int mntfd = -1;
-       char *mnt = NULL;
-       const char *svpath = NULL;
-       DIR *dirstream1 = NULL;
-       DIR *dirstream2 = NULL;
-
-       ret = test_issubvolume(fullpath);
-       if (ret < 0)
-               return ret;
-       if (!ret) {
-               error("not a subvolume: %s", fullpath);
-               return 1;
-       }
-
-       ret = find_mount_root(fullpath, &mnt);
-       if (ret < 0)
-               return ret;
-       if (ret > 0) {
-               error("%s doesn't belong to btrfs mount point", fullpath);
-               return 1;
-       }
-       ret = 1;
-       svpath = subvol_strip_mountpoint(mnt, fullpath);
-
-       fd = btrfs_open_dir(fullpath, &dirstream1, 1);
-       if (fd < 0)
-               goto out;
-
-       ret = btrfs_list_get_path_rootid(fd, &sv_id);
-       if (ret)
-               goto out;
-
-       mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
-       if (mntfd < 0)
-               goto out;
-
-       memset(get_ri, 0, sizeof(*get_ri));
-       get_ri->root_id = sv_id;
-
-       if (sv_id == BTRFS_FS_TREE_OBJECTID)
-               ret = btrfs_get_toplevel_subvol(mntfd, get_ri);
-       else
-               ret = btrfs_get_subvol(mntfd, get_ri);
-       if (ret)
-               error("can't find '%s': %d", svpath, ret);
-
-out:
-       close_file_or_dir(mntfd, dirstream2);
-       close_file_or_dir(fd, dirstream1);
-       free(mnt);
-
-       return ret;
-}
-
+/* Set the seed manually */
 void init_rand_seed(u64 seed)
 {
        int i;
@@ -2727,6 +2521,7 @@ u32 rand_u32(void)
        return (u32)jrand48(rand_seed);
 }
 
+/* Return random number in range [0, upper) */
 unsigned int rand_range(unsigned int upper)
 {
        __init_seed();
@@ -2737,6 +2532,61 @@ unsigned int rand_range(unsigned int upper)
        return (unsigned int)(jrand48(rand_seed) % upper);
 }
 
+int rand_int(void)
+{
+       return (int)(rand_u32());
+}
+
+u64 rand_u64(void)
+{
+       u64 ret = 0;
+
+       ret += rand_u32();
+       ret <<= 32;
+       ret += rand_u32();
+       return ret;
+}
+
+u16 rand_u16(void)
+{
+       return (u16)(rand_u32());
+}
+
+u8 rand_u8(void)
+{
+       return (u8)(rand_u32());
+}
+
 void btrfs_config_init(void)
 {
 }
+
+/* Returns total size of main memory in bytes, -1UL if error. */
+unsigned long total_memory(void)
+{
+        struct sysinfo si;
+
+        if (sysinfo(&si) < 0) {
+                error("can't determine memory size");
+                return -1UL;
+        }
+        return si.totalram * si.mem_unit;       /* bytes */
+}
+
+void print_device_info(struct btrfs_device *device, char *prefix)
+{
+       if (prefix)
+               printf("%s", prefix);
+       printf("Device: id = %llu, name = %s\n",
+              device->devid, device->name);
+}
+
+void print_all_devices(struct list_head *devices)
+{
+       struct btrfs_device *dev;
+
+       printf("All Devices:\n");
+       list_for_each_entry(dev, devices, dev_list)
+               print_device_info(dev, "\t");
+       printf("\n");
+}