X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=utils.c;h=b175b01135bf3891e94afd0ecda4aef943c7edf0;hb=9a86668071e77b2da0173b8921153de4d7ba99d8;hp=3e9c527a492c67f10e8751572914222722cbc88a;hpb=4724d7b07553ad27429bb2d61de1c5e8d4d7613d;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/utils.c b/utils.c index 3e9c527..b175b01 100644 --- a/utils.c +++ b/utils.c @@ -17,10 +17,6 @@ * 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 #include #include @@ -38,6 +34,8 @@ #include #include #include +#include + #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" @@ -52,6 +50,55 @@ #define BLKDISCARD _IO(0x12,119) #endif +static int btrfs_scan_done = 0; + +static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs"; + +void fixup_argv0(char **argv, const char *token) +{ + int len = strlen(argv0_buf); + + snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token); + argv[0] = argv0_buf; +} + +void set_argv0(char **argv) +{ + strncpy(argv0_buf, argv[0], sizeof(argv0_buf)); + argv0_buf[sizeof(argv0_buf) - 1] = 0; +} + +int check_argc_exact(int nargs, int expected) +{ + if (nargs < expected) + fprintf(stderr, "%s: too few arguments\n", argv0_buf); + if (nargs > expected) + fprintf(stderr, "%s: too many arguments\n", argv0_buf); + + return nargs != expected; +} + +int check_argc_min(int nargs, int expected) +{ + if (nargs < expected) { + fprintf(stderr, "%s: too few arguments\n", argv0_buf); + return 1; + } + + return 0; +} + +int check_argc_max(int nargs, int expected) +{ + if (nargs > expected) { + fprintf(stderr, "%s: too many arguments\n", argv0_buf); + return 1; + } + + return 0; +} + + /* * Discard the given range in one go */ @@ -93,12 +140,41 @@ static u64 reference_root_table[] = { [6] = BTRFS_CSUM_TREE_OBJECTID, }; -int make_btrfs(int fd, const char *device, const char *label, +int test_uuid_unique(char *fs_uuid) +{ + int unique = 1; + blkid_dev_iterate iter = NULL; + blkid_dev dev = NULL; + blkid_cache cache = NULL; + + 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, "UUID", fs_uuid); + + while (blkid_dev_next(iter, &dev) == 0) { + dev = blkid_verify(cache, dev); + if (dev) { + unique = 0; + break; + } + } + + blkid_dev_iterate_end(iter); + blkid_put_cache(cache); + + return unique; +} + +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; + struct extent_buffer *buf = NULL; struct btrfs_root_item root_item; struct btrfs_disk_key disk_key; struct btrfs_extent_item *extent_item; @@ -125,7 +201,20 @@ int make_btrfs(int fd, const char *device, const char *label, memset(&super, 0, sizeof(super)); num_bytes = (num_bytes / sectorsize) * sectorsize; - uuid_generate(super.fsid); + if (fs_uuid) { + if (uuid_parse(fs_uuid, super.fsid) != 0) { + fprintf(stderr, "could not parse UUID: %s\n", fs_uuid); + ret = -EINVAL; + goto out; + } + if (!test_uuid_unique(fs_uuid)) { + fprintf(stderr, "non-unique UUID: %s\n", fs_uuid); + ret = -EBUSY; + goto out; + } + } else { + uuid_generate(super.fsid); + } uuid_generate(super.dev_item.uuid); uuid_generate(chunk_tree_uuid); @@ -136,9 +225,9 @@ int make_btrfs(int fd, const char *device, const char *label, 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); @@ -148,11 +237,11 @@ int make_btrfs(int fd, const char *device, const char *label, 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); @@ -171,10 +260,10 @@ int make_btrfs(int fd, const char *device, const char *label, 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)); @@ -182,7 +271,7 @@ int make_btrfs(int fd, const char *device, const char *label, 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); @@ -231,17 +320,17 @@ 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 != 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) @@ -260,7 +349,7 @@ int make_btrfs(int fd, const char *device, const char *label, } 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), @@ -290,18 +379,18 @@ int make_btrfs(int fd, const char *device, const char *label, 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); @@ -377,17 +466,17 @@ int make_btrfs(int fd, const char *device, const char *label, 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); @@ -416,33 +505,33 @@ int make_btrfs(int fd, const char *device, const char *label, 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; } @@ -466,6 +555,96 @@ out: return ret; } +static const struct btrfs_fs_feature { + const char *name; + u64 flag; + const char *desc; +} mkfs_features[] = { + { "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS, + "mixed data and metadata block groups" }, + { "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF, + "increased hardlink limit per file to 65536" }, + { "raid56", BTRFS_FEATURE_INCOMPAT_RAID56, + "raid56 extended format" }, + { "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA, + "reduced-size metadata extent refs" }, + { "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES, + "no explicit hole extents for files" }, + /* Keep this one last */ + { "list-all", BTRFS_FEATURE_LIST_ALL, NULL } +}; + +static int parse_one_fs_feature(const char *name, u64 *flags) +{ + int i; + int found = 0; + + for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) { + if (name[0] == '^' && + !strcmp(mkfs_features[i].name, name + 1)) { + *flags &= ~ mkfs_features[i].flag; + found = 1; + } else if (!strcmp(mkfs_features[i].name, name)) { + *flags |= mkfs_features[i].flag; + found = 1; + } + } + + return !found; +} + +void btrfs_process_fs_features(u64 flags) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) { + if (flags & mkfs_features[i].flag) { + printf("Turning ON incompat feature '%s': %s\n", + mkfs_features[i].name, + mkfs_features[i].desc); + } + } +} + +void btrfs_list_all_fs_features(u64 mask_disallowed) +{ + int i; + + fprintf(stderr, "Filesystem features available:\n"); + for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) { + char *is_default = ""; + + if (mkfs_features[i].flag & mask_disallowed) + continue; + if (mkfs_features[i].flag & BTRFS_MKFS_DEFAULT_FEATURES) + is_default = ", default"; + fprintf(stderr, "%-20s- %s (0x%llx%s)\n", + mkfs_features[i].name, + mkfs_features[i].desc, + mkfs_features[i].flag, + is_default); + } +} + +/* + * Return NULL if all features were parsed fine, otherwise return the name of + * the first unparsed. + */ +char* btrfs_parse_fs_features(char *namelist, u64 *flags) +{ + char *this_char; + char *save_ptr = NULL; /* Satisfy static checkers */ + + for (this_char = strtok_r(namelist, ",", &save_ptr); + this_char != NULL; + this_char = strtok_r(NULL, ",", &save_ptr)) { + if (parse_one_fs_feature(this_char, flags)) + return this_char; + } + + return NULL; +} + u64 btrfs_device_size(int fd, struct stat *st) { u64 size; @@ -497,25 +676,23 @@ static int zero_blocks(int fd, off_t start, size_t len) return ret; } -static int zero_dev_start(int fd) +#define ZERO_DEV_BYTES (2 * 1024 * 1024) + +/* don't write outside the device by clamping the region to the device size */ +static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size) { - off_t start = 0; - size_t len = 2 * 1024 * 1024; + off_t end = max(start, start + len); #ifdef __sparc__ - /* don't overwrite the disk labels on sparc */ - start = 1024; - len -= 1024; + /* and don't overwrite the disk labels on sparc */ + start = max(start, 1024); + end = max(end, 1024); #endif - return zero_blocks(fd, start, len); -} -static int zero_dev_end(int fd, u64 dev_size) -{ - size_t len = 2 * 1024 * 1024; - off_t start = dev_size - len; + start = min_t(u64, start, dev_size); + end = min_t(u64, end, dev_size); - return zero_blocks(fd, start, len); + return zero_blocks(fd, start, end - start); } int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, @@ -592,11 +769,46 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, return 0; } +static void btrfs_wipe_existing_sb(int fd) +{ + const char *off = NULL; + size_t len = 0; + loff_t offset; + char buf[BUFSIZ]; + int rc = 0; + blkid_probe pr = NULL; + + pr = blkid_new_probe(); + if (!pr) + return; + + if (blkid_probe_set_device(pr, fd, 0, 0)) + goto out; + + rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); + if (!rc) + rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len); + + if (rc || len == 0 || off == NULL) + goto out; + + offset = strtoll(off, NULL, 10); + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, len); + rc = pwrite(fd, buf, len, offset); + fsync(fd); + +out: + blkid_free_probe(pr); + return; +} + int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, u64 max_block_count, int *mixed, int discard) { u64 block_count; - u64 bytenr; struct stat st; int i, ret; @@ -613,12 +825,9 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, } 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"); + if (block_count < BTRFS_MKFS_SMALL_VOLUME_SIZE && !(*mixed)) *mixed = 1; - } if (discard) { /* @@ -626,43 +835,30 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, * is not necessary for the mkfs functionality but just an * optimization. */ - if (discard_blocks(fd, 0, 0) == 0) { - fprintf(stderr, "Performing full device TRIM (%s) ...\n", + if (discard_range(fd, 0, 0) == 0) { + printf("Performing full device TRIM (%s) ...\n", pretty_size(block_count)); discard_blocks(fd, 0, block_count); } } - ret = zero_dev_start(fd); - 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; - 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) - goto zero_dev_error; - } - *block_count_ret = block_count; + ret = zero_dev_clamped(fd, 0, ZERO_DEV_BYTES, block_count); + for (i = 0 ; !ret && i < BTRFS_SUPER_MIRROR_MAX; i++) + ret = zero_dev_clamped(fd, btrfs_sb_offset(i), + BTRFS_SUPER_INFO_SIZE, block_count); + if (!ret && zero_end) + ret = zero_dev_clamped(fd, block_count - ZERO_DEV_BYTES, + ZERO_DEV_BYTES, 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; } + + btrfs_wipe_existing_sb(fd); + + *block_count_ret = block_count; return 0; } @@ -677,7 +873,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); @@ -744,6 +940,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. @@ -853,7 +1095,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) @@ -862,36 +1105,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; } @@ -986,6 +1224,64 @@ static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices, } /* + * Resolve a pathname to a device mapper node to /dev/mapper/ + * Returns NULL on invalid input or malloc failure; Other failures + * will be handled by the caller using the input pathame. + */ +char *canonicalize_dm_name(const char *ptname) +{ + FILE *f; + size_t sz; + char path[PATH_MAX], name[PATH_MAX], *res = NULL; + + if (!ptname || !*ptname) + return NULL; + + snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); + if (!(f = fopen(path, "r"))) + return NULL; + + /* read \n from sysfs */ + if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) { + name[sz - 1] = '\0'; + snprintf(path, sizeof(path), "/dev/mapper/%s", name); + + if (access(path, F_OK) == 0) + res = strdup(path); + } + fclose(f); + return res; +} + +/* + * Resolve a pathname to a canonical device node, e.g. /dev/sda1 or + * to a device mapper pathname. + * Returns NULL on invalid input or malloc failure; Other failures + * will be handled by the caller using the input pathame. + */ +char *canonicalize_path(const char *path) +{ + char *canonical, *p; + + if (!path || !*path) + return NULL; + + canonical = realpath(path, NULL); + if (!canonical) + return strdup(path); + p = strrchr(canonical, '/'); + if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { + char *dm = canonicalize_dm_name(p + 1); + + if (dm) { + free(canonical); + return dm; + } + } + return canonical; +} + +/* * returns 1 if the device was mounted, < 0 on error or 0 if everything * is safe to continue. */ @@ -1018,12 +1314,13 @@ int check_mounted_where(int fd, const char *file, char *where, int size, /* scan the initial device */ ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt, - &total_devs, BTRFS_SUPER_INFO_OFFSET); + &total_devs, BTRFS_SUPER_INFO_OFFSET, 0); is_btrfs = (ret >= 0); /* scan other devices */ if (is_btrfs && total_devs > 1) { - if ((ret = btrfs_scan_for_fsid(!BTRFS_UPDATE_KERNEL))) + ret = btrfs_scan_lblkid(); + if (ret) return ret; } @@ -1075,150 +1372,58 @@ struct pending_dir { char name[PATH_MAX]; }; -void btrfs_register_one_device(char *fname) +int btrfs_register_one_device(const char *fname) { struct btrfs_ioctl_vol_args args; int fd; int ret; int e; - fd = open("/dev/btrfs-control", O_RDONLY); + fd = open("/dev/btrfs-control", O_RDWR); if (fd < 0) { fprintf(stderr, "failed to open /dev/btrfs-control " "skipping device registration: %s\n", strerror(errno)); - return; + return -errno; } strncpy(args.name, fname, BTRFS_PATH_NAME_MAX); args.name[BTRFS_PATH_NAME_MAX-1] = 0; ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); e = errno; - if(ret<0){ + if (ret < 0) { fprintf(stderr, "ERROR: device scan failed '%s' - %s\n", fname, strerror(e)); + ret = -e; } close(fd); + return ret; } -int btrfs_scan_one_dir(char *dirname, int run_ioctl) +/* + * Register all devices in the fs_uuid list created in the user + * space. Ensure btrfs_scan_lblkid() is called before this func. + */ +int btrfs_register_all_devices(void) { - DIR *dirp = NULL; - struct dirent *dirent; - struct pending_dir *pending; - struct stat st; - int ret; - int fd; - int dirname_len; - char *fullpath; - struct list_head pending_list; - struct btrfs_fs_devices *tmp_devices; - u64 num_devices; - - INIT_LIST_HEAD(&pending_list); - - pending = malloc(sizeof(*pending)); - if (!pending) - return -ENOMEM; - strcpy(pending->name, dirname); - -again: - dirname_len = strlen(pending->name); - fullpath = malloc(PATH_MAX); - dirname = pending->name; - - if (!fullpath) { - ret = -ENOMEM; - goto fail; - } - dirp = opendir(dirname); - if (!dirp) { - fprintf(stderr, "Unable to open %s for scanning\n", dirname); - ret = -errno; - goto fail; - } - while(1) { - dirent = readdir(dirp); - if (!dirent) - break; - if (dirent->d_name[0] == '.') - continue; - if (dirname_len + strlen(dirent->d_name) + 2 > PATH_MAX) { - ret = -EFAULT; - goto fail; - } - snprintf(fullpath, PATH_MAX, "%s/%s", dirname, dirent->d_name); - ret = lstat(fullpath, &st); - if (ret < 0) { - fprintf(stderr, "failed to stat %s\n", fullpath); - continue; - } - if (S_ISLNK(st.st_mode)) - continue; - if (S_ISDIR(st.st_mode)) { - struct pending_dir *next = malloc(sizeof(*next)); - if (!next) { - ret = -ENOMEM; - goto fail; + int err; + struct btrfs_fs_devices *fs_devices; + struct btrfs_device *device; + struct list_head *all_uuids; + + all_uuids = btrfs_scanned_uuids(); + + list_for_each_entry(fs_devices, all_uuids, list) { + list_for_each_entry(device, &fs_devices->devices, dev_list) { + if (strlen(device->name) != 0) { + err = btrfs_register_one_device(device->name); + if (err < 0) + return err; + if (err > 0) + return -err; } - strcpy(next->name, fullpath); - list_add_tail(&next->list, &pending_list); - } - if (!S_ISBLK(st.st_mode)) { - continue; } - fd = open(fullpath, O_RDONLY); - if (fd < 0) { - /* ignore the following errors: - ENXIO (device don't exists) - ENOMEDIUM (No medium found -> - like a cd tray empty) - */ - if(errno != ENXIO && errno != ENOMEDIUM) - fprintf(stderr, "failed to read %s: %s\n", - fullpath, strerror(errno)); - continue; - } - ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices, - &num_devices, - BTRFS_SUPER_INFO_OFFSET); - if (ret == 0 && run_ioctl > 0) { - btrfs_register_one_device(fullpath); - } - close(fd); } - if (!list_empty(&pending_list)) { - free(pending); - pending = list_entry(pending_list.next, struct pending_dir, - list); - free(fullpath); - list_del(&pending->list); - closedir(dirp); - dirp = NULL; - goto again; - } - ret = 0; -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; -} - -int btrfs_scan_for_fsid(int run_ioctls) -{ - int ret; - - ret = scan_for_btrfs(BTRFS_SCAN_PROC, run_ioctls); - if (ret) - ret = scan_for_btrfs(BTRFS_SCAN_DEV, run_ioctls); - return ret; + return 0; } int btrfs_device_already_in_root(struct btrfs_root *root, int fd, @@ -1251,35 +1456,76 @@ out: return ret; } -static char *size_strs[] = { "", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; -int pretty_size_snprintf(u64 size, char *str, size_t str_bytes) +static const char* unit_suffix_binary[] = + { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; +static const char* unit_suffix_decimal[] = + { "B", "kB", "MB", "GB", "TB", "PB", "EB"}; + +int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mode) { - int num_divs = 0; + int num_divs; float fraction; + u64 base = 0; + int mult = 0; + const char** suffix = NULL; + u64 last_size; - if (str_bytes == 0) + if (str_size == 0) return 0; - if( size < 1024 ){ - fraction = size; - num_divs = 0; - } else { - u64 last_size = size; - num_divs = 0; - while(size >= 1024){ + if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) { + snprintf(str, str_size, "%llu", size); + return 0; + } + + if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_BINARY) { + base = 1024; + mult = 1024; + suffix = unit_suffix_binary; + } else if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_DECIMAL) { + base = 1000; + mult = 1000; + suffix = unit_suffix_decimal; + } + + /* Unknown mode */ + if (!base) { + fprintf(stderr, "INTERNAL ERROR: unknown unit base, mode %d\n", + unit_mode); + assert(0); + return -1; + } + + num_divs = 0; + last_size = size; + switch (unit_mode & UNITS_MODE_MASK) { + case UNITS_TBYTES: base *= mult; num_divs++; + case UNITS_GBYTES: base *= mult; num_divs++; + case UNITS_MBYTES: base *= mult; num_divs++; + case UNITS_KBYTES: num_divs++; + break; + case UNITS_BYTES: + base = 1; + num_divs = 0; + break; + default: + while (size >= mult) { last_size = size; - size /= 1024; - num_divs ++; + size /= mult; + num_divs++; } + } - if (num_divs >= ARRAY_SIZE(size_strs)) { - str[0] = '\0'; - return -1; - } - fraction = (float)last_size / 1024; + if (num_divs >= ARRAY_SIZE(unit_suffix_binary)) { + str[0] = '\0'; + printf("INTERNAL ERROR: unsupported unit suffix, index %d\n", + num_divs); + assert(0); + return -1; } - return snprintf(str, str_bytes, "%.2f%s", fraction, - size_strs[num_divs]); + fraction = (float)last_size / base; + + return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]); } /* @@ -1438,22 +1684,29 @@ int get_label(const char *btrfs_dev, char *label) { int ret; - if (is_existing_blk_or_reg_file(btrfs_dev)) - ret = get_label_unmounted(btrfs_dev, label); - else + ret = is_existing_blk_or_reg_file(btrfs_dev); + if (!ret) ret = get_label_mounted(btrfs_dev, label); + else if (ret > 0) + ret = get_label_unmounted(btrfs_dev, label); return ret; } int set_label(const char *btrfs_dev, const char *label) { + int ret; + if (check_label(label)) return -1; - return is_existing_blk_or_reg_file(btrfs_dev) ? - set_label_unmounted(btrfs_dev, label) : - set_label_mounted(btrfs_dev, label); + ret = is_existing_blk_or_reg_file(btrfs_dev); + if (!ret) + ret = set_label_mounted(btrfs_dev, label); + else if (ret > 0) + ret = set_label_unmounted(btrfs_dev, label); + + return ret; } int btrfs_scan_block_devices(int run_ioctl) @@ -1488,7 +1741,12 @@ scan_again: strcpy(fullpath,"/dev/"); while(fgets(buf, 1023, proc_partitions)) { - i = sscanf(buf," %*d %*d %*d %99s", fullpath+5); + ret = sscanf(buf," %*d %*d %*d %99s", fullpath + 5); + if (ret != 1) { + fprintf(stderr, + "failed to scan device name from /proc/partitions\n"); + break; + } /* * multipath and MD devices may register as a btrfs filesystem @@ -1523,7 +1781,7 @@ scan_again: } ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices, &num_devices, - BTRFS_SUPER_INFO_OFFSET); + BTRFS_SUPER_INFO_OFFSET, 0); if (ret == 0 && run_ioctl > 0) { btrfs_register_one_device(fullpath); } @@ -1540,52 +1798,75 @@ scan_again: } /* - * 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(). + * 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. */ -u64 arg_strtou64(const char *str) +static int __is_subvol(const char *path) { - u64 value; - char *ptr_parse_end = NULL; + struct stat st; + int ret; - 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; + ret = lstat(path, &st); + if (ret < 0) + return ret; + + return st.st_ino == BTRFS_FIRST_FREE_OBJECTID; } -u64 parse_size(char *s) +/* + * A not-so-good version fls64. No fascinating optimization since + * no one except parse_size use it + */ +static int fls64(u64 x) { int i; + + for (i = 0; i <64; i++) + if (x << i & (1ULL << 63)) + return 64 - i; + return 64 - i; +} + +u64 parse_size(char *s) +{ char c; + char *endptr; u64 mult = 1; + u64 ret; - for (i = 0; s && s[i] && isdigit(s[i]); i++) ; - if (!i) { - fprintf(stderr, "ERROR: size value is empty\n"); - exit(50); + if (!s) { + fprintf(stderr, "ERROR: Size value is empty\n"); + exit(1); } - - if (s[i]) { - c = tolower(s[i]); + if (s[0] == '-') { + fprintf(stderr, + "ERROR: Size value '%s' is less equal than 0\n", s); + exit(1); + } + ret = strtoull(s, &endptr, 10); + if (endptr == s) { + fprintf(stderr, "ERROR: Size value '%s' is invalid\n", s); + exit(1); + } + if (endptr[0] && endptr[1]) { + fprintf(stderr, "ERROR: Illegal suffix contains character '%c' in wrong position\n", + endptr[1]); + exit(1); + } + /* + * strtoll returns LLONG_MAX when overflow, if this happens, + * need to call strtoull to get the real size + */ + if (errno == ERANGE && ret == ULLONG_MAX) { + fprintf(stderr, + "ERROR: Size value '%s' is too large for u64\n", s); + exit(1); + } + if (endptr[0]) { + c = tolower(endptr[0]); switch (c) { case 'e': mult *= 1024; @@ -1608,21 +1889,71 @@ u64 parse_size(char *s) case 'b': break; default: - fprintf(stderr, "ERROR: Unknown size descriptor " - "'%c'\n", c); + fprintf(stderr, "ERROR: Unknown size descriptor '%c'\n", + c); exit(1); } } - if (s[i] && s[i+1]) { - fprintf(stderr, "ERROR: Illegal suffix contains " - "character '%c' in wrong position\n", - s[i+1]); - exit(51); + /* Check whether ret * mult overflow */ + if (fls64(ret) + fls64(mult) - 1 > 64) { + fprintf(stderr, + "ERROR: Size value '%s' is too large for u64\n", s); + exit(1); } - return strtoull(s, NULL, 10) * mult; + ret *= mult; + return ret; } -int open_file_or_dir(const char *fname, DIR **dirstream) +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; struct stat st; @@ -1638,7 +1969,7 @@ int open_file_or_dir(const char *fname, DIR **dirstream) return -1; fd = dirfd(*dirstream); } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - fd = open(fname, O_RDWR); + fd = open(fname, open_flags); } else { /* * we set this on purpose, in case the caller output @@ -1655,6 +1986,11 @@ int open_file_or_dir(const char *fname, DIR **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) @@ -1675,6 +2011,75 @@ int get_device_info(int fd, u64 devid, return ret ? -errno : 0; } +static u64 find_max_device_id(struct btrfs_ioctl_search_args *search_args, + int nr_items) +{ + struct btrfs_dev_item *dev_item; + char *buf = search_args->buf; + + buf += (nr_items - 1) * (sizeof(struct btrfs_ioctl_search_header) + + sizeof(struct btrfs_dev_item)); + buf += sizeof(struct btrfs_ioctl_search_header); + + dev_item = (struct btrfs_dev_item *)buf; + + return btrfs_stack_device_id(dev_item); +} + +static int search_chunk_tree_for_fs_info(int fd, + struct btrfs_ioctl_fs_info_args *fi_args) +{ + int ret; + int max_items; + u64 start_devid = 1; + struct btrfs_ioctl_search_args search_args; + struct btrfs_ioctl_search_key *search_key = &search_args.key; + + fi_args->num_devices = 0; + + max_items = BTRFS_SEARCH_ARGS_BUFSIZE + / (sizeof(struct btrfs_ioctl_search_header) + + sizeof(struct btrfs_dev_item)); + + search_key->tree_id = BTRFS_CHUNK_TREE_OBJECTID; + search_key->min_objectid = BTRFS_DEV_ITEMS_OBJECTID; + search_key->max_objectid = BTRFS_DEV_ITEMS_OBJECTID; + search_key->min_type = BTRFS_DEV_ITEM_KEY; + search_key->max_type = BTRFS_DEV_ITEM_KEY; + search_key->min_transid = 0; + search_key->max_transid = (u64)-1; + search_key->nr_items = max_items; + search_key->max_offset = (u64)-1; + +again: + search_key->min_offset = start_devid; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_args); + if (ret < 0) + return -errno; + + fi_args->num_devices += (u64)search_key->nr_items; + + if (search_key->nr_items == max_items) { + start_devid = find_max_device_id(&search_args, + search_key->nr_items) + 1; + goto again; + } + + /* get the lastest max_id to stay consistent with the num_devices */ + if (search_key->nr_items == 0) + /* + * last tree_search returns an empty buf, use the devid of + * the last dev_item of the previous tree_search + */ + fi_args->max_id = start_devid - 1; + else + fi_args->max_id = find_max_device_id(&search_args, + search_key->nr_items); + + return 0; +} + /* * For a given path, fill in the ioctl fs_ and info_ args. * If the path is a btrfs mountpoint, fill info for all devices. @@ -1692,8 +2097,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; @@ -1725,7 +2132,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, fi_args->num_devices = 1; disk_super = (struct btrfs_super_block *)buf; - ret = btrfs_read_dev_super(fd, disk_super, BTRFS_SUPER_INFO_OFFSET); + ret = btrfs_read_dev_super(fd, disk_super, + BTRFS_SUPER_INFO_OFFSET, 0); if (ret < 0) { ret = -EIO; goto out; @@ -1753,19 +2161,40 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, ret = -errno; goto out; } + + /* + * The fs_args->num_devices does not include seed devices + */ + 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) goto out; - di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args)); + di_args = *di_ret = malloc((fi_args->num_devices) * sizeof(*di_args)); if (!di_args) { ret = -errno; goto out; } + if (replacing) + memcpy(di_args, &tmp, sizeof(tmp)); for (; i <= fi_args->max_id; ++i) { - BUG_ON(ndevs >= fi_args->num_devices); ret = get_device_info(fd, i, &di_args[ndevs]); if (ret == -ENODEV) continue; @@ -1943,6 +2372,25 @@ out: return ret; } +static int group_profile_devs_min(u64 flag) +{ + switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: /* single */ + case BTRFS_BLOCK_GROUP_DUP: + return 1; + case BTRFS_BLOCK_GROUP_RAID0: + case BTRFS_BLOCK_GROUP_RAID1: + case BTRFS_BLOCK_GROUP_RAID5: + return 2; + case BTRFS_BLOCK_GROUP_RAID6: + return 3; + case BTRFS_BLOCK_GROUP_RAID10: + return 4; + default: + return -1; + } +} + int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, u64 dev_cnt, int mixed, char *estr) { @@ -1963,16 +2411,26 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, allowed |= BTRFS_BLOCK_GROUP_DUP; } + if (dev_cnt > 1 && + ((metadata_profile | data_profile) & BTRFS_BLOCK_GROUP_DUP)) { + snprintf(estr, sz, + "DUP is not allowed when FS has multiple devices\n"); + return 1; + } if (metadata_profile & ~allowed) { - snprintf(estr, sz, "unable to create FS with metadata " - "profile %llu (have %llu devices)\n", - metadata_profile, dev_cnt); + snprintf(estr, sz, + "unable to create FS with metadata profile %s " + "(have %llu devices but %d devices are required)\n", + btrfs_group_profile_str(metadata_profile), dev_cnt, + group_profile_devs_min(metadata_profile)); return 1; } if (data_profile & ~allowed) { - snprintf(estr, sz, "unable to create FS with data " - "profile %llu (have %llu devices)\n", - metadata_profile, dev_cnt); + snprintf(estr, sz, + "unable to create FS with data profile %s " + "(have %llu devices but %d devices are required)\n", + btrfs_group_profile_str(data_profile), dev_cnt, + group_profile_devs_min(data_profile)); return 1; } @@ -2043,7 +2501,7 @@ int test_dev_for_mkfs(char *file, int force_overwrite, char *estr) return 0; } -int btrfs_scan_lblkid(int update_kernel) +int btrfs_scan_lblkid() { int fd = -1; int ret; @@ -2054,6 +2512,9 @@ int btrfs_scan_lblkid(int update_kernel) blkid_cache cache = NULL; char path[PATH_MAX]; + if (btrfs_scan_done) + return 0; + if (blkid_get_cache(&cache, 0) < 0) { printf("ERROR: lblkid cache get failed\n"); return 1; @@ -2066,7 +2527,7 @@ int btrfs_scan_lblkid(int update_kernel) 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) { @@ -2074,7 +2535,7 @@ int btrfs_scan_lblkid(int update_kernel) continue; } ret = btrfs_scan_one_device(fd, path, &tmp_devices, - &num_devices, BTRFS_SUPER_INFO_OFFSET); + &num_devices, BTRFS_SUPER_INFO_OFFSET, 0); if (ret) { printf("ERROR: could not scan %s\n", path); close (fd); @@ -2082,33 +2543,13 @@ int btrfs_scan_lblkid(int update_kernel) } 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 -*/ -int scan_for_btrfs(int where, int update_kernel) -{ - int ret = 0; + btrfs_scan_done = 1; - switch (where) { - case BTRFS_SCAN_PROC: - ret = btrfs_scan_block_devices(update_kernel); - break; - 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; + return 0; } int is_vol_small(char *file) @@ -2131,7 +2572,7 @@ int is_vol_small(char *file) close(fd); return -1; } - if (size < 1024 * 1024 * 1024) { + if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) { close(fd); return 1; } else { @@ -2161,7 +2602,7 @@ int ask_user(char *question) /* * For a given: * - file or directory return the containing tree root id - * - subvolume return it's own tree id + * - subvolume return its own tree id * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is * undefined and function returns -1 */ @@ -2188,6 +2629,11 @@ int lookup_ino_rootid(int fd, u64 *rootid) return 0; } +/* + * return 0 if a btrfs mount point is found + * return 1 if a mount point is found but not btrfs + * return <0 if something goes wrong + */ int find_mount_root(const char *path, char **mount_root) { FILE *mnttab; @@ -2195,6 +2641,7 @@ int find_mount_root(const char *path, char **mount_root) struct mntent *ent; int len; int ret; + int not_btrfs = 1; int longest_matchlen = 0; char *longest_match = NULL; @@ -2210,21 +2657,22 @@ int find_mount_root(const char *path, char **mount_root) while ((ent = getmntent(mnttab))) { len = strlen(ent->mnt_dir); if (strncmp(ent->mnt_dir, path, len) == 0) { - /* match found */ - if (longest_matchlen < len) { + /* match found and use the latest match */ + if (longest_matchlen <= len) { free(longest_match); longest_matchlen = len; longest_match = strdup(ent->mnt_dir); + not_btrfs = strcmp(ent->mnt_type, "btrfs"); } } } endmntent(mnttab); - if (!longest_match) { - fprintf(stderr, - "ERROR: Failed to find mount root for path %s.\n", - path); + if (!longest_match) return -ENOENT; + if (not_btrfs) { + free(longest_match); + return 1; } ret = 0; @@ -2235,3 +2683,219 @@ int find_mount_root(const char *path, char **mount_root) free(longest_match); 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 name is a correct subvolume name + * this function return + * 0-> name is not a correct subvolume name + * 1-> name is a correct subvolume name + */ +int test_issubvolname(const char *name) +{ + return name[0] != '\0' && !strchr(name, '/') && + strcmp(name, ".") && strcmp(name, ".."); +} + +/* + * test if path is a directory + * this function return + * 0-> path exists but it is not a directory + * 1-> path exists and it is a directory + * -1 -> path is unaccessible + */ +int test_isdir(const char *path) +{ + struct stat st; + int ret; + + ret = stat(path, &st); + if(ret < 0 ) + return -1; + + return S_ISDIR(st.st_mode); +} + +void units_set_mode(unsigned *units, unsigned mode) +{ + unsigned base = *units & UNITS_MODE_MASK; + + *units = base | mode; +} + +void units_set_base(unsigned *units, unsigned base) +{ + unsigned mode = *units & ~UNITS_MODE_MASK; + + *units = base | mode; +} + +int find_next_key(struct btrfs_path *path, struct btrfs_key *key) +{ + int level; + + for (level = 0; level < BTRFS_MAX_LEVEL; level++) { + if (!path->nodes[level]) + break; + if (path->slots[level] + 1 >= + btrfs_header_nritems(path->nodes[level])) + continue; + if (level == 0) + btrfs_item_key_to_cpu(path->nodes[level], key, + path->slots[level] + 1); + else + btrfs_node_key_to_cpu(path->nodes[level], key, + path->slots[level] + 1); + return 0; + } + return 1; +} + +char* btrfs_group_type_str(u64 flag) +{ + u64 mask = BTRFS_BLOCK_GROUP_TYPE_MASK | + BTRFS_SPACE_INFO_GLOBAL_RSV; + + switch (flag & mask) { + case BTRFS_BLOCK_GROUP_DATA: + return "Data"; + case BTRFS_BLOCK_GROUP_SYSTEM: + return "System"; + case BTRFS_BLOCK_GROUP_METADATA: + return "Metadata"; + case BTRFS_BLOCK_GROUP_DATA|BTRFS_BLOCK_GROUP_METADATA: + return "Data+Metadata"; + case BTRFS_SPACE_INFO_GLOBAL_RSV: + return "GlobalReserve"; + default: + return "unknown"; + } +} + +char* btrfs_group_profile_str(u64 flag) +{ + switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: + return "single"; + case BTRFS_BLOCK_GROUP_RAID0: + return "RAID0"; + case BTRFS_BLOCK_GROUP_RAID1: + return "RAID1"; + case BTRFS_BLOCK_GROUP_RAID5: + return "RAID5"; + case BTRFS_BLOCK_GROUP_RAID6: + return "RAID6"; + case BTRFS_BLOCK_GROUP_DUP: + return "DUP"; + case BTRFS_BLOCK_GROUP_RAID10: + return "RAID10"; + default: + return "unknown"; + } +} + +u64 disk_size(char *path) +{ + struct statfs sfs; + + if (statfs(path, &sfs) < 0) + return 0; + else + return sfs.f_bsize * sfs.f_blocks; +} + +u64 get_partition_size(char *dev) +{ + u64 result; + int fd = open(dev, O_RDONLY); + + if (fd < 0) + return 0; + if (ioctl(fd, BLKGETSIZE64, &result) < 0) { + close(fd); + return 0; + } + close(fd); + + 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; +}