btrfs-progs: mkfs, deprecate leafsize and clean up the code
[platform/upstream/btrfs-progs.git] / utils.c
diff --git a/utils.c b/utils.c
index 8885669..e89cb26 100644 (file)
--- a/utils.c
+++ b/utils.c
  * Boston, MA 021110-1307, USA.
  */
 
-#define _XOPEN_SOURCE 700
-#define __USE_XOPEN2K8
-#define __XOPEN2K8 /* due to an error in dirent.h, to get dirfd() */
-#define _GNU_SOURCE    /* O_NOATIME */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -175,7 +171,7 @@ int test_uuid_unique(char *fs_uuid)
 
 int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
               u64 blocks[7], u64 num_bytes, u32 nodesize,
-              u32 leafsize, u32 sectorsize, u32 stripesize, u64 features)
+              u32 sectorsize, u32 stripesize, u64 features)
 {
        struct btrfs_super_block super;
        struct extent_buffer *buf = NULL;
@@ -229,9 +225,9 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        btrfs_set_super_root(&super, blocks[1]);
        btrfs_set_super_chunk_root(&super, blocks[3]);
        btrfs_set_super_total_bytes(&super, num_bytes);
-       btrfs_set_super_bytes_used(&super, 6 * leafsize);
+       btrfs_set_super_bytes_used(&super, 6 * nodesize);
        btrfs_set_super_sectorsize(&super, sectorsize);
-       btrfs_set_super_leafsize(&super, leafsize);
+       btrfs_set_super_leafsize(&super, nodesize);
        btrfs_set_super_nodesize(&super, nodesize);
        btrfs_set_super_stripesize(&super, stripesize);
        btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
@@ -241,11 +237,11 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        if (label)
                strncpy(super.label, label, BTRFS_LABEL_SIZE - 1);
 
-       buf = malloc(sizeof(*buf) + max(sectorsize, leafsize));
+       buf = malloc(sizeof(*buf) + max(sectorsize, nodesize));
 
        /* create the tree of root objects */
-       memset(buf->data, 0, leafsize);
-       buf->len = leafsize;
+       memset(buf->data, 0, nodesize);
+       buf->len = nodesize;
        btrfs_set_header_bytenr(buf, blocks[1]);
        btrfs_set_header_nritems(buf, 4);
        btrfs_set_header_generation(buf, 1);
@@ -264,10 +260,10 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        btrfs_set_stack_inode_generation(inode_item, 1);
        btrfs_set_stack_inode_size(inode_item, 3);
        btrfs_set_stack_inode_nlink(inode_item, 1);
-       btrfs_set_stack_inode_nbytes(inode_item, leafsize);
+       btrfs_set_stack_inode_nbytes(inode_item, nodesize);
        btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
        btrfs_set_root_refs(&root_item, 1);
-       btrfs_set_root_used(&root_item, leafsize);
+       btrfs_set_root_used(&root_item, nodesize);
        btrfs_set_root_generation(&root_item, 1);
 
        memset(&disk_key, 0, sizeof(disk_key));
@@ -275,7 +271,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        btrfs_set_disk_key_offset(&disk_key, 0);
        nritems = 0;
 
-       itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - sizeof(root_item);
+       itemoff = __BTRFS_LEAF_DATA_SIZE(nodesize) - sizeof(root_item);
        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);
@@ -324,17 +320,17 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
 
 
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
-       ret = pwrite(fd, buf->data, leafsize, blocks[1]);
-       if (ret != leafsize) {
+       ret = pwrite(fd, buf->data, nodesize, blocks[1]);
+       if (ret != nodesize) {
                ret = (ret < 0 ? -errno : -EIO);
                goto out;
        }
 
        /* create the items for the extent tree */
-       memset(buf->data+sizeof(struct btrfs_header), 0,
-               leafsize-sizeof(struct btrfs_header));
+       memset(buf->data + sizeof(struct btrfs_header), 0,
+               nodesize - sizeof(struct btrfs_header));
        nritems = 0;
-       itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize);
+       itemoff = __BTRFS_LEAF_DATA_SIZE(nodesize);
        for (i = 1; i < 7; i++) {
                item_size = sizeof(struct btrfs_extent_item);
                if (!skinny_metadata)
@@ -353,7 +349,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
                } else {
                        btrfs_set_disk_key_type(&disk_key,
                                                BTRFS_EXTENT_ITEM_KEY);
-                       btrfs_set_disk_key_offset(&disk_key, leafsize);
+                       btrfs_set_disk_key_offset(&disk_key, nodesize);
                }
                btrfs_set_item_key(buf, &disk_key, nritems);
                btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
@@ -383,18 +379,18 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID);
        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 != leafsize) {
+       ret = pwrite(fd, buf->data, nodesize, blocks[2]);
+       if (ret != nodesize) {
                ret = (ret < 0 ? -errno : -EIO);
                goto out;
        }
 
        /* create the chunk tree */
-       memset(buf->data+sizeof(struct btrfs_header), 0,
-               leafsize-sizeof(struct btrfs_header));
+       memset(buf->data + sizeof(struct btrfs_header), 0,
+               nodesize - sizeof(struct btrfs_header));
        nritems = 0;
        item_size = sizeof(*dev_item);
-       itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - item_size;
+       itemoff = __BTRFS_LEAF_DATA_SIZE(nodesize) - item_size;
 
        /* first device 1 (there is no device 0) */
        btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
@@ -470,17 +466,17 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID);
        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 != leafsize) {
+       ret = pwrite(fd, buf->data, nodesize, blocks[3]);
+       if (ret != nodesize) {
                ret = (ret < 0 ? -errno : -EIO);
                goto out;
        }
 
        /* create the device tree */
-       memset(buf->data+sizeof(struct btrfs_header), 0,
-               leafsize-sizeof(struct btrfs_header));
+       memset(buf->data + sizeof(struct btrfs_header), 0,
+               nodesize - sizeof(struct btrfs_header));
        nritems = 0;
-       itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) -
+       itemoff = __BTRFS_LEAF_DATA_SIZE(nodesize) -
                sizeof(struct btrfs_dev_extent);
 
        btrfs_set_disk_key_objectid(&disk_key, 1);
@@ -509,33 +505,33 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid,
        btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID);
        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 != leafsize) {
+       ret = pwrite(fd, buf->data, nodesize, blocks[4]);
+       if (ret != nodesize) {
                ret = (ret < 0 ? -errno : -EIO);
                goto out;
        }
 
        /* create the FS root */
-       memset(buf->data+sizeof(struct btrfs_header), 0,
-               leafsize-sizeof(struct btrfs_header));
+       memset(buf->data + sizeof(struct btrfs_header), 0,
+               nodesize - sizeof(struct btrfs_header));
        btrfs_set_header_bytenr(buf, blocks[5]);
        btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID);
        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 != leafsize) {
+       ret = pwrite(fd, buf->data, nodesize, blocks[5]);
+       if (ret != nodesize) {
                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));
+       memset(buf->data + sizeof(struct btrfs_header), 0,
+               nodesize - sizeof(struct btrfs_header));
        btrfs_set_header_bytenr(buf, blocks[6]);
        btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID);
        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 != leafsize) {
+       ret = pwrite(fd, buf->data, nodesize, blocks[6]);
+       if (ret != nodesize) {
                ret = (ret < 0 ? -errno : -EIO);
                goto out;
        }
@@ -750,7 +746,7 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
                 * optimization.
                 */
                if (discard_range(fd, 0, 0) == 0) {
-                       fprintf(stderr, "Performing full device TRIM (%s) ...\n",
+                       printf("Performing full device TRIM (%s) ...\n",
                                pretty_size(block_count));
                        discard_blocks(fd, 0, block_count);
                }
@@ -787,7 +783,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->leafsize);
+       btrfs_set_stack_inode_nbytes(&inode_item, root->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);
@@ -854,6 +850,52 @@ int is_mount_point(const char *path)
        return ret;
 }
 
+static int is_reg_file(const char *path)
+{
+       struct stat statbuf;
+
+       if (stat(path, &statbuf) < 0)
+               return -errno;
+       return S_ISREG(statbuf.st_mode);
+}
+
+/*
+ * This function checks if the given input parameter is
+ * an uuid or a path
+ * return <0 : some error in the given input
+ * return BTRFS_ARG_UNKNOWN:   unknown input
+ * return BTRFS_ARG_UUID:      given input is uuid
+ * return BTRFS_ARG_MNTPOINT:  given input is path
+ * return BTRFS_ARG_REG:       given input is regular file
+ */
+int check_arg_type(const char *input)
+{
+       uuid_t uuid;
+       char path[PATH_MAX];
+
+       if (!input)
+               return -EINVAL;
+
+       if (realpath(input, path)) {
+               if (is_block_device(path) == 1)
+                       return BTRFS_ARG_BLKDEV;
+
+               if (is_mount_point(path) == 1)
+                       return BTRFS_ARG_MNTPOINT;
+
+               if (is_reg_file(path))
+                       return BTRFS_ARG_REG;
+
+               return BTRFS_ARG_UNKNOWN;
+       }
+
+       if (strlen(input) == (BTRFS_UUID_UNPARSED_SIZE - 1) &&
+               !uuid_parse(input, uuid))
+               return BTRFS_ARG_UUID;
+
+       return BTRFS_ARG_UNKNOWN;
+}
+
 /*
  * Find the mount point for a mounted device.
  * On success, returns 0 with mountpoint in *mp.
@@ -963,7 +1005,8 @@ static int resolve_loop_device(const char* loop_dev, char* loop_file,
        return 0;
 }
 
-/* Checks whether a and b are identical or device
+/*
+ * Checks whether a and b are identical or device
  * files associated with the same block device
  */
 static int is_same_blk_file(const char* a, const char* b)
@@ -972,36 +1015,31 @@ 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))
-               strcpy(real_a, a);
+       if (!realpath(a, real_a))
+               strncpy_null(real_a, a);
 
        if (!realpath(b, real_b))
-               strcpy(real_b, b);
+               strncpy_null(real_b, b);
 
        /* Identical path? */
-       if(strcmp(real_a, real_b) == 0)
+       if (strcmp(real_a, real_b) == 0)
                return 1;
 
-       if(stat(a, &st_buf_a) < 0 ||
-          stat(b, &st_buf_b) < 0)
-       {
+       if (stat(a, &st_buf_a) < 0 || stat(b, &st_buf_b) < 0) {
                if (errno == ENOENT)
                        return 0;
                return -errno;
        }
 
        /* Same blockdevice? */
-       if(S_ISBLK(st_buf_a.st_mode) &&
-          S_ISBLK(st_buf_b.st_mode) &&
-          st_buf_a.st_rdev == st_buf_b.st_rdev)
-       {
+       if (S_ISBLK(st_buf_a.st_mode) && S_ISBLK(st_buf_b.st_mode) &&
+           st_buf_a.st_rdev == st_buf_b.st_rdev) {
                return 1;
        }
 
        /* Hardlink? */
        if (st_buf_a.st_dev == st_buf_b.st_dev &&
-           st_buf_a.st_ino == st_buf_b.st_ino)
-       {
+           st_buf_a.st_ino == st_buf_b.st_ino) {
                return 1;
        }
 
@@ -1670,6 +1708,25 @@ scan_again:
 }
 
 /*
+ * Unsafe subvolume check.
+ *
+ * This only checks ino == BTRFS_FIRST_FREE_OBJECTID, even it is not in a
+ * btrfs mount point.
+ * Must use together with other reliable method like btrfs ioctl.
+ */
+static int __is_subvol(const char *path)
+{
+       struct stat st;
+       int ret;
+
+       ret = lstat(path, &st);
+       if (ret < 0)
+               return ret;
+
+       return st.st_ino == BTRFS_FIRST_FREE_OBJECTID;
+}
+
+/*
  * A not-so-good version fls64. No fascinating optimization since
  * no one except parse_size use it
  */
@@ -1757,6 +1814,55 @@ u64 parse_size(char *s)
        return ret;
 }
 
+u64 parse_qgroupid(const char *p)
+{
+       char *s = strchr(p, '/');
+       const char *ptr_src_end = p + strlen(p);
+       char *ptr_parse_end = NULL;
+       u64 level;
+       u64 id;
+       int fd;
+       int ret = 0;
+
+       if (p[0] == '/')
+               goto path;
+
+       /* Numeric format like '0/257' is the primary case */
+       if (!s) {
+               id = strtoull(p, &ptr_parse_end, 10);
+               if (ptr_parse_end != ptr_src_end)
+                       goto path;
+               return id;
+       }
+       level = strtoull(p, &ptr_parse_end, 10);
+       if (ptr_parse_end != s)
+               goto path;
+
+       id = strtoull(s + 1, &ptr_parse_end, 10);
+       if (ptr_parse_end != ptr_src_end)
+               goto  path;
+
+       return (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
+
+path:
+       /* Path format like subv at 'my_subvol' is the fallback case */
+       ret = __is_subvol(p);
+       if (ret < 0 || !ret)
+               goto err;
+       fd = open(p, O_RDONLY);
+       if (fd < 0)
+               goto err;
+       ret = lookup_ino_rootid(fd, &id);
+       close(fd);
+       if (ret < 0)
+               goto err;
+       return id;
+
+err:
+       fprintf(stderr, "ERROR: invalid qgroupid or subvolume path: %s\n", p);
+       exit(-1);
+}
+
 int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags)
 {
        int ret;
@@ -1901,8 +2007,10 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
        int ret = 0;
        int ndevs = 0;
        int i = 0;
+       int replacing = 0;
        struct btrfs_fs_devices *fs_devices_mnt = NULL;
        struct btrfs_ioctl_dev_info_args *di_args;
+       struct btrfs_ioctl_dev_info_args tmp;
        char mp[BTRFS_PATH_NAME_MAX + 1];
        DIR *dirstream = NULL;
 
@@ -1970,6 +2078,19 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                ret = search_chunk_tree_for_fs_info(fd, fi_args);
                if (ret)
                        goto out;
+
+               /*
+                * search_chunk_tree_for_fs_info() will lacks the devid 0
+                * so manual probe for it here.
+                */
+               ret = get_device_info(fd, 0, &tmp);
+               if (!ret) {
+                       fi_args->num_devices++;
+                       ndevs++;
+                       replacing = 1;
+                       if (i == 0)
+                               i++;
+               }
        }
 
        if (!fi_args->num_devices)
@@ -1981,6 +2102,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                goto out;
        }
 
+       if (replacing)
+               memcpy(di_args, &tmp, sizeof(tmp));
        for (; i <= fi_args->max_id; ++i) {
                ret = get_device_info(fd, i, &di_args[ndevs]);
                if (ret == -ENODEV)
@@ -2314,7 +2437,7 @@ int btrfs_scan_lblkid()
                if (!dev)
                        continue;
                /* if we are here its definitely a btrfs disk*/
-               strncpy(path, blkid_dev_devname(dev), PATH_MAX);
+               strncpy_null(path, blkid_dev_devname(dev));
 
                fd = open(path, O_RDONLY);
                if (fd < 0) {
@@ -2471,7 +2594,7 @@ int find_mount_root(const char *path, char **mount_root)
        return ret;
 }
 
-int test_minimum_size(const char *file, u32 leafsize)
+int test_minimum_size(const char *file, u32 nodesize)
 {
        int fd;
        struct stat statbuf;
@@ -2483,7 +2606,7 @@ int test_minimum_size(const char *file, u32 leafsize)
                close(fd);
                return -errno;
        }
-       if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(leafsize)) {
+       if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) {
                close(fd);
                return 1;
        }
@@ -2625,3 +2748,64 @@ u64 get_partition_size(char *dev)
 
        return result;
 }
+
+int btrfs_tree_search2_ioctl_supported(int fd)
+{
+       struct btrfs_ioctl_search_args_v2 *args2;
+       struct btrfs_ioctl_search_key *sk;
+       int args2_size = 1024;
+       char args2_buf[args2_size];
+       int ret;
+       static int v2_supported = -1;
+
+       if (v2_supported != -1)
+               return v2_supported;
+
+       args2 = (struct btrfs_ioctl_search_args_v2 *)args2_buf;
+       sk = &(args2->key);
+
+       /*
+        * Search for the extent tree item in the root tree.
+        */
+       sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
+       sk->min_objectid = BTRFS_EXTENT_TREE_OBJECTID;
+       sk->max_objectid = BTRFS_EXTENT_TREE_OBJECTID;
+       sk->min_type = BTRFS_ROOT_ITEM_KEY;
+       sk->max_type = BTRFS_ROOT_ITEM_KEY;
+       sk->min_offset = 0;
+       sk->max_offset = (u64)-1;
+       sk->min_transid = 0;
+       sk->max_transid = (u64)-1;
+       sk->nr_items = 1;
+       args2->buf_size = args2_size - sizeof(struct btrfs_ioctl_search_args_v2);
+       ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, args2);
+       if (ret == -EOPNOTSUPP)
+               v2_supported = 0;
+       else if (ret == 0)
+               v2_supported = 1;
+       else
+               return ret;
+
+       return v2_supported;
+}
+
+int btrfs_check_nodesize(u32 nodesize, u32 sectorsize)
+{
+       if (nodesize < sectorsize) {
+               fprintf(stderr,
+                       "ERROR: Illegal nodesize %u (smaller than %u)\n",
+                       nodesize, sectorsize);
+               return -1;
+       } else if (nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) {
+               fprintf(stderr,
+                       "ERROR: Illegal nodesize %u (larger than %u)\n",
+                       nodesize, BTRFS_MAX_METADATA_BLOCKSIZE);
+               return -1;
+       } else if (nodesize & (sectorsize - 1)) {
+               fprintf(stderr,
+                       "ERROR: Illegal nodesize %u (not aligned to %u)\n",
+                       nodesize, sectorsize);
+               return -1;
+       }
+       return 0;
+}