#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifndef __CHECKER__
#include <sys/ioctl.h>
#include <sys/mount.h>
-#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
-#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <mntent.h>
#include <linux/major.h>
#include <linux/kdev_t.h>
#include <limits.h>
+#include <blkid/blkid.h>
#include "kerncompat.h"
#include "radix-tree.h"
#include "ctree.h"
#include "volumes.h"
#include "ioctl.h"
-#ifdef __CHECKER__
-#define BLKGETSIZE64 0
-static inline int ioctl(int fd, int define, u64 *size) { return 0; }
-#endif
-
#ifndef BLKDISCARD
#define BLKDISCARD _IO(0x12,119)
#endif
-static int
-discard_blocks(int fd, u64 start, u64 len)
+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)
+{
+ sprintf(argv0_buf, "%s", argv[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
+ */
+static int discard_range(int fd, u64 start, u64 len)
{
u64 range[2] = { start, len };
return 0;
}
+/*
+ * Discard blocks in the given range in 1G chunks, the process is interruptible
+ */
+static int discard_blocks(int fd, u64 start, u64 len)
+{
+ while (len > 0) {
+ /* 1G granularity */
+ u64 chunk_size = min_t(u64, len, 1*1024*1024*1024);
+ int ret;
+
+ ret = discard_range(fd, start, chunk_size);
+ if (ret)
+ return ret;
+ len -= chunk_size;
+ start += chunk_size;
+ }
+
+ return 0;
+}
+
static u64 reference_root_table[] = {
[1] = BTRFS_ROOT_TREE_OBJECTID,
[2] = BTRFS_EXTENT_TREE_OBJECTID,
[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)
+ u32 leafsize, 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;
u64 ref_root;
u32 array_size;
u32 item_size;
+ int skinny_metadata = !!(features &
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1;
first_free &= ~((u64)sectorsize - 1);
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);
btrfs_set_super_bytenr(&super, blocks[0]);
btrfs_set_super_num_devices(&super, 1);
- super.magic = cpu_to_le64(BTRFS_MAGIC);
+ btrfs_set_super_magic(&super, BTRFS_MAGIC);
btrfs_set_super_generation(&super, 1);
btrfs_set_super_root(&super, blocks[1]);
btrfs_set_super_chunk_root(&super, blocks[3]);
btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
btrfs_set_super_chunk_root_generation(&super, 1);
btrfs_set_super_cache_generation(&super, -1);
+ btrfs_set_super_incompat_flags(&super, features);
if (label)
strncpy(super.label, label, BTRFS_LABEL_SIZE - 1);
btrfs_set_header_generation(buf, 1);
btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
- write_extent_buffer(buf, super.fsid, (unsigned long)
- btrfs_header_fsid(buf), BTRFS_FSID_SIZE);
+ write_extent_buffer(buf, super.fsid, btrfs_header_fsid(),
+ BTRFS_FSID_SIZE);
- write_extent_buffer(buf, chunk_tree_uuid, (unsigned long)
+ write_extent_buffer(buf, chunk_tree_uuid,
btrfs_header_chunk_tree_uuid(buf),
BTRFS_UUID_SIZE);
btrfs_set_root_bytenr(&root_item, blocks[2]);
btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
sizeof(root_item));
write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf,
nritems), sizeof(root_item));
btrfs_set_root_bytenr(&root_item, blocks[4]);
btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
sizeof(root_item));
write_extent_buffer(buf, &root_item,
btrfs_item_ptr_offset(buf, nritems),
btrfs_set_root_bytenr(&root_item, blocks[5]);
btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
sizeof(root_item));
write_extent_buffer(buf, &root_item,
btrfs_item_ptr_offset(buf, nritems),
btrfs_set_root_bytenr(&root_item, blocks[6]);
btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
sizeof(root_item));
write_extent_buffer(buf, &root_item,
btrfs_item_ptr_offset(buf, nritems),
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[1]);
- BUG_ON(ret != leafsize);
+ if (ret != leafsize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
/* create the items for the extent tree */
memset(buf->data+sizeof(struct btrfs_header), 0,
nritems = 0;
itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize);
for (i = 1; i < 7; i++) {
+ item_size = sizeof(struct btrfs_extent_item);
+ if (!skinny_metadata)
+ item_size += sizeof(struct btrfs_tree_block_info);
+
BUG_ON(blocks[i] < first_free);
BUG_ON(blocks[i] < blocks[i - 1]);
/* create extent item */
- itemoff -= sizeof(struct btrfs_extent_item) +
- sizeof(struct btrfs_tree_block_info);
+ itemoff -= item_size;
btrfs_set_disk_key_objectid(&disk_key, blocks[i]);
- btrfs_set_disk_key_offset(&disk_key, leafsize);
- btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY);
+ if (skinny_metadata) {
+ btrfs_set_disk_key_type(&disk_key,
+ BTRFS_METADATA_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ } else {
+ btrfs_set_disk_key_type(&disk_key,
+ BTRFS_EXTENT_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, leafsize);
+ }
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
- sizeof(struct btrfs_extent_item) +
- sizeof(struct btrfs_tree_block_info));
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
+ item_size);
extent_item = btrfs_item_ptr(buf, nritems,
struct btrfs_extent_item);
btrfs_set_extent_refs(buf, extent_item, 1);
btrfs_set_disk_key_offset(&disk_key, ref_root);
btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems),
itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), 0);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0);
nritems++;
}
btrfs_set_header_bytenr(buf, blocks[2]);
btrfs_set_header_nritems(buf, nritems);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[2]);
- BUG_ON(ret != leafsize);
+ if (ret != leafsize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
/* create the chunk tree */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_disk_key_offset(&disk_key, 1);
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
btrfs_set_device_id(buf, dev_item, 1);
btrfs_set_disk_key_offset(&disk_key, 0);
btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems), item_size);
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size);
chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk);
btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
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 = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
/* create the device tree */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_disk_key_offset(&disk_key, 0);
btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
btrfs_set_item_key(buf, &disk_key, nritems);
- btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
- btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+ btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(nritems),
sizeof(struct btrfs_dev_extent));
dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent);
btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
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 = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
/* create the FS root */
memset(buf->data+sizeof(struct btrfs_header), 0,
btrfs_set_header_nritems(buf, 0);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[5]);
- BUG_ON(ret != leafsize);
-
+ if (ret != leafsize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
/* finally create the csum root */
memset(buf->data+sizeof(struct btrfs_header), 0,
leafsize-sizeof(struct btrfs_header));
btrfs_set_header_nritems(buf, 0);
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, leafsize, blocks[6]);
- BUG_ON(ret != leafsize);
+ if (ret != leafsize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
/* and write out the super block */
BUG_ON(sizeof(super) > sectorsize);
buf->len = sectorsize;
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
ret = pwrite(fd, buf->data, sectorsize, blocks[0]);
- BUG_ON(ret != sectorsize);
+ if (ret != sectorsize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+ ret = 0;
+out:
free(buf);
- return 0;
+ return ret;
}
-static u64 device_size(int fd, struct stat *st)
+u64 btrfs_device_size(int fd, struct stat *st)
{
u64 size;
if (S_ISREG(st->st_mode)) {
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,
u64 num_devs;
int ret;
- device = kmalloc(sizeof(*device), GFP_NOFS);
+ device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
return -ENOMEM;
buf = kmalloc(sectorsize, GFP_NOFS);
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 nodiscard)
+ u64 max_block_count, int *mixed, int discard)
{
u64 block_count;
- u64 bytenr;
struct stat st;
int i, ret;
ret = fstat(fd, &st);
if (ret < 0) {
fprintf(stderr, "unable to stat %s\n", file);
- exit(1);
+ return 1;
}
- block_count = device_size(fd, &st);
+ block_count = btrfs_device_size(fd, &st);
if (block_count == 0) {
fprintf(stderr, "unable to find %s size\n", file);
- exit(1);
+ return 1;
}
if (max_block_count)
block_count = min(block_count, max_block_count);
- zero_end = 1;
- if (block_count < 1024 * 1024 * 1024 && !(*mixed)) {
- printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
+ if (block_count < BTRFS_MKFS_SMALL_VOLUME_SIZE && !(*mixed))
*mixed = 1;
- }
- if (!nodiscard) {
+ if (discard) {
/*
- * We intentionally ignore errors from the discard ioctl. It is
- * not necessary for the mkfs functionality but just an optimization.
+ * We intentionally ignore errors from the discard ioctl. It
+ * is not necessary for the mkfs functionality but just an
+ * optimization.
*/
- discard_blocks(fd, 0, block_count);
+ if (discard_range(fd, 0, 0) == 0) {
+ fprintf(stderr, "Performing full device TRIM (%s) ...\n",
+ pretty_size(block_count));
+ discard_blocks(fd, 0, block_count);
+ }
}
- ret = zero_dev_start(fd);
- if (ret) {
- fprintf(stderr, "failed to zero device start %d\n", ret);
- exit(1);
- }
+ 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);
- for (i = 0 ; i < BTRFS_SUPER_MIRROR_MAX; i++) {
- bytenr = btrfs_sb_offset(i);
- if (bytenr >= block_count)
- break;
- zero_blocks(fd, bytenr, BTRFS_SUPER_INFO_SIZE);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: failed to zero device '%s' - %s\n",
+ file, strerror(-ret));
+ return 1;
}
- if (zero_end) {
- ret = zero_dev_end(fd, block_count);
- if (ret) {
- fprintf(stderr, "failed to zero device end %d\n", ret);
- exit(1);
- }
- }
+ btrfs_wipe_existing_sb(fd);
+
*block_count_ret = block_count;
return 0;
}
* Returns negative errno on failure, otherwise
* returns 1 for blockdev, 0 for not-blockdev
*/
-int is_block_device(const char *path) {
+int is_block_device(const char *path)
+{
struct stat statbuf;
if (stat(path, &statbuf) < 0)
}
/*
+ * check if given path is a mount point
+ * return 1 if yes. 0 if no. -1 for error
+ */
+int is_mount_point(const char *path)
+{
+ FILE *f;
+ struct mntent *mnt;
+ int ret = 0;
+
+ f = setmntent("/proc/self/mounts", "r");
+ if (f == NULL)
+ return -1;
+
+ while ((mnt = getmntent(f)) != NULL) {
+ if (strcmp(mnt->mnt_dir, path))
+ continue;
+ ret = 1;
+ break;
+ }
+ endmntent(f);
+ return ret;
+}
+
+/*
* Find the mount point for a mounted device.
* On success, returns 0 with mountpoint in *mp.
* On failure, returns -errno (not mounted yields -EINVAL)
* Is noisy on failures, expects to be given a mounted device.
*/
-int get_btrfs_mount(const char *dev, char *mp, size_t mp_size) {
+int get_btrfs_mount(const char *dev, char *mp, size_t mp_size)
+{
int ret;
int fd = -1;
ret = check_mounted_where(fd, dev, mp, mp_size, NULL);
if (!ret) {
- fprintf(stderr, "%s is not a mounted btrfs device\n", dev);
ret = -EINVAL;
} else { /* mounted, all good */
ret = 0;
out:
if (fd != -1)
close(fd);
- if (ret)
- fprintf(stderr, "Could not get mountpoint for %s\n", dev);
return ret;
}
*
* On error, return -1, errno should be set.
*/
-int open_path_or_dev_mnt(const char *path)
+int open_path_or_dev_mnt(const char *path, DIR **dirstream)
{
char mp[BTRFS_PATH_NAME_MAX + 1];
int fdmnt;
errno = EINVAL;
return -1;
}
- fdmnt = open(mp, O_RDWR);
+ fdmnt = open_file_or_dir(mp, dirstream);
} else {
- fdmnt = open_file_or_dir(path);
+ fdmnt = open_file_or_dir(path, dirstream);
}
return fdmnt;
}
/* checks if a device is a loop device */
-int is_loop_device (const char* device) {
+static int is_loop_device (const char* device) {
struct stat statbuf;
if(stat(device, &statbuf) < 0)
/* Takes a loop device path (e.g. /dev/loop0) and returns
* the associated file (e.g. /images/my_btrfs.img) */
-int resolve_loop_device(const char* loop_dev, char* loop_file, int max_len)
+static int resolve_loop_device(const char* loop_dev, char* loop_file,
+ int max_len)
{
int ret;
FILE *f;
/* Checks whether a and b are identical or device
* files associated with the same block device
*/
-int is_same_blk_file(const char* a, const char* b)
+static int is_same_blk_file(const char* a, const char* b)
{
struct stat st_buf_a, st_buf_b;
char real_a[PATH_MAX];
char real_b[PATH_MAX];
- if(!realpath(a, real_a) ||
- !realpath(b, real_b))
- {
- return -errno;
- }
+ if(!realpath(a, real_a))
+ strcpy(real_a, a);
+
+ if (!realpath(b, real_b))
+ strcpy(real_b, b);
/* Identical path? */
if(strcmp(real_a, real_b) == 0)
* if one file is a loop device that uses the other
* file.
*/
-int is_same_loop_file(const char* a, const char* b)
+static int is_same_loop_file(const char* a, const char* b)
{
char res_a[PATH_MAX];
char res_b[PATH_MAX];
- const char* final_a;
- const char* final_b;
+ const char* final_a = NULL;
+ const char* final_b = NULL;
int ret;
/* Resolve a if it is a loop device */
return 0;
return ret;
} else if (ret) {
- if ((ret = resolve_loop_device(a, res_a, sizeof(res_a))) < 0)
- return ret;
-
- final_a = res_a;
+ ret = resolve_loop_device(a, res_a, sizeof(res_a));
+ if (ret < 0) {
+ if (errno != EPERM)
+ return ret;
+ } else {
+ final_a = res_a;
+ }
} else {
final_a = a;
}
return 0;
return ret;
} else if (ret) {
- if((ret = resolve_loop_device(b, res_b, sizeof(res_b))) < 0)
- return ret;
-
- final_b = res_b;
+ ret = resolve_loop_device(b, res_b, sizeof(res_b));
+ if (ret < 0) {
+ if (errno != EPERM)
+ return ret;
+ } else {
+ final_b = res_b;
+ }
} else {
final_b = b;
}
}
/* Checks if a file exists and is a block or regular file*/
-int is_existing_blk_or_reg_file(const char* filename)
+static int is_existing_blk_or_reg_file(const char* filename)
{
struct stat st_buf;
/* Checks if a file is used (directly or indirectly via a loop device)
* by a device in fs_devices
*/
-int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices, const char* file)
+static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
+ const char* file)
{
int ret;
struct list_head *head;
}
/*
+ * Resolve a pathname to a device mapper node to /dev/mapper/<name>
+ * 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 <name>\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.
*/
/* 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(fs_devices_mnt, total_devs, 1)))
+ ret = btrfs_scan_lblkid(!BTRFS_UPDATE_KERNEL);
+ if (ret)
return ret;
}
/* iterate over the list of currently mountes filesystems */
- if ((f = setmntent ("/proc/mounts", "r")) == NULL)
+ if ((f = setmntent ("/proc/self/mounts", "r")) == NULL)
return -errno;
while ((mnt = getmntent (f)) != NULL) {
return ret;
}
-/* Gets the mount point of btrfs filesystem that is using the specified device.
- * Returns 0 is everything is good, <0 if we have an error.
- * TODO: Fix this fucntion and check_mounted to work with multiple drive BTRFS
- * setups.
- */
-int get_mountpt(char *dev, char *mntpt, size_t size)
-{
- struct mntent *mnt;
- FILE *f;
- int ret = 0;
-
- f = setmntent("/proc/mounts", "r");
- if (f == NULL)
- return -errno;
-
- while ((mnt = getmntent(f)) != NULL )
- {
- if (strcmp(dev, mnt->mnt_fsname) == 0)
- {
- strncpy(mntpt, mnt->mnt_dir, size);
- if (size)
- mntpt[size-1] = 0;
- break;
- }
- }
-
- if (mnt == NULL)
- {
- /* We didn't find an entry so lets report an error */
- ret = -1;
- }
-
- return ret;
-}
-
struct pending_dir {
struct list_head list;
char name[PATH_MAX];
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\n");
+ "skipping device registration: %s\n",
+ strerror(errno));
return;
}
strncpy(args.name, fname, BTRFS_PATH_NAME_MAX);
close(fd);
}
-int btrfs_scan_one_dir(char *dirname, int run_ioctl)
-{
- 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);
- free(fullpath);
- return -ENOENT;
- }
- 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;
- }
- 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);
- if (dirp)
- closedir(dirp);
- return ret;
-}
-
-int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
- int run_ioctls)
-{
- int ret;
-
- ret = btrfs_scan_block_devices(run_ioctls);
- if (ret)
- ret = btrfs_scan_one_dir("/dev", run_ioctls);
- return ret;
-}
-
int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
int super_offset)
{
ret = 0;
disk_super = (struct btrfs_super_block *)buf;
- if (disk_super->magic != cpu_to_le64(BTRFS_MAGIC))
+ if (btrfs_super_magic(disk_super) != BTRFS_MAGIC)
goto brelse;
if (!memcmp(disk_super->fsid, root->fs_info->super_copy->fsid,
return ret;
}
-static char *size_strs[] = { "", "KB", "MB", "GB", "TB",
- "PB", "EB", "ZB", "YB"};
-char *pretty_sizes(u64 size)
+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 pretty_len = 16;
+ int num_divs;
float fraction;
- char *pretty;
+ u64 base = 0;
+ int mult = 0;
+ const char** suffix = NULL;
+ u64 last_size;
- if( size < 1024 ){
- fraction = size;
- num_divs = 0;
- } else {
- u64 last_size = size;
- num_divs = 0;
- while(size >= 1024){
+ if (str_size == 0)
+ return 0;
+
+ 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))
- return NULL;
- 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;
}
- pretty = malloc(pretty_len);
- snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs]);
- return pretty;
+ fraction = (float)last_size / base;
+
+ return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]);
}
/*
/* Open the super_block at the default location
* and as read-write.
*/
- root = open_ctree(dev, 0, 1);
+ root = open_ctree(dev, 0, OPEN_CTREE_WRITES);
if (!root) /* errors are printed by open_ctree() */
return -1;
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
- fprintf(stderr, "ERROR: unable access to '%s'\n", mount_path);
+ fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
return -1;
}
return 0;
}
-static int get_label_unmounted(const char *dev)
+static int get_label_unmounted(const char *dev, char *label)
{
struct btrfs_root *root;
int ret;
if(!root)
return -1;
- fprintf(stdout, "%s\n", root->fs_info->super_copy->label);
+ memcpy(label, root->fs_info->super_copy->label, BTRFS_LABEL_SIZE);
/* Now we close it since we are done. */
close_ctree(root);
* mounted path rather than device. Return the corresponding error
* the user specified the device path.
*/
-static int get_label_mounted(const char *mount_path)
+int get_label_mounted(const char *mount_path, char *labelp)
{
char label[BTRFS_LABEL_SIZE];
int fd;
fd = open(mount_path, O_RDONLY | O_NOATIME);
if (fd < 0) {
- fprintf(stderr, "ERROR: unable access to '%s'\n", mount_path);
+ fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path);
return -1;
}
return -1;
}
- fprintf(stdout, "%s\n", label);
+ strncpy(labelp, label, sizeof(label));
close(fd);
return 0;
}
-int get_label(const char *btrfs_dev)
+int get_label(const char *btrfs_dev, char *label)
{
- return is_existing_blk_or_reg_file(btrfs_dev) ?
- get_label_unmounted(btrfs_dev) :
- get_label_mounted(btrfs_dev);
+ int ret;
+
+ 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)
{
- if (check_label(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)
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
fd = open(fullpath, O_RDONLY);
if (fd < 0) {
- fprintf(stderr, "failed to open %s: %s\n",
- fullpath, strerror(errno));
+ if (errno != ENOMEDIUM)
+ fprintf(stderr, "failed to open %s: %s\n",
+ fullpath, strerror(errno));
continue;
}
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);
}
return 0;
}
-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;
+ /* fallthrough */
case 'p':
mult *= 1024;
+ /* fallthrough */
case 't':
mult *= 1024;
+ /* fallthrough */
case 'g':
mult *= 1024;
+ /* fallthrough */
case 'm':
mult *= 1024;
+ /* fallthrough */
case 'k':
mult *= 1024;
+ /* fallthrough */
case 'b':
break;
default:
- 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)
+int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags)
{
int ret;
struct stat st;
- DIR *dirstream;
int fd;
ret = stat(fname, &st);
return -1;
}
if (S_ISDIR(st.st_mode)) {
- dirstream = opendir(fname);
- if (!dirstream) {
- return -2;
- }
- fd = dirfd(dirstream);
+ *dirstream = opendir(fname);
+ if (!*dirstream)
+ return -1;
+ fd = dirfd(*dirstream);
+ } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+ fd = open(fname, open_flags);
} else {
- fd = open(fname, O_RDWR);
+ /*
+ * we set this on purpose, in case the caller output
+ * strerror(errno) as success
+ */
+ errno = EINVAL;
+ return -1;
}
if (fd < 0) {
- return -3;
+ fd = -1;
+ if (*dirstream)
+ closedir(*dirstream);
}
return fd;
}
+int open_file_or_dir(const char *fname, DIR **dirstream)
+{
+ return open_file_or_dir3(fname, dirstream, O_RDWR);
+}
+
+void close_file_or_dir(int fd, DIR *dirstream)
+{
+ if (dirstream)
+ closedir(dirstream);
+ else if (fd >= 0)
+ close(fd);
+}
+
int get_device_info(int fd, u64 devid,
- struct btrfs_ioctl_dev_info_args *di_args)
+ struct btrfs_ioctl_dev_info_args *di_args)
{
int ret;
return ret ? -errno : 0;
}
-int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args,
+/*
+ * For a given path, fill in the ioctl fs_ and info_ args.
+ * If the path is a btrfs mountpoint, fill info for all devices.
+ * If the path is a btrfs device, fill in only that device.
+ *
+ * The path provided must be either on a mounted btrfs fs,
+ * or be a mounted btrfs device.
+ *
+ * Returns 0 on success, or a negative errno.
+ */
+int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
struct btrfs_ioctl_dev_info_args **di_ret)
{
+ int fd = -1;
int ret = 0;
int ndevs = 0;
- int i = 1;
+ int i = 0;
struct btrfs_fs_devices *fs_devices_mnt = NULL;
struct btrfs_ioctl_dev_info_args *di_args;
char mp[BTRFS_PATH_NAME_MAX + 1];
+ DIR *dirstream = NULL;
memset(fi_args, 0, sizeof(*fi_args));
- ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
- if (ret && (errno == EINVAL || errno == ENOTTY)) {
- /* path is not a mounted btrfs. Try if it's a device */
+ if (is_block_device(path)) {
+ struct btrfs_super_block *disk_super;
+ char buf[BTRFS_SUPER_INFO_SIZE];
+ u64 devid;
+
+ /* Ensure it's mounted, then set path to the mountpoint */
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ fprintf(stderr, "Couldn't open %s: %s\n",
+ path, strerror(errno));
+ goto out;
+ }
ret = check_mounted_where(fd, path, mp, sizeof(mp),
&fs_devices_mnt);
- if (!ret)
- return -EINVAL;
+ if (!ret) {
+ ret = -EINVAL;
+ goto out;
+ }
if (ret < 0)
- return ret;
+ goto out;
+ path = mp;
+ /* Only fill in this one device */
fi_args->num_devices = 1;
- fi_args->max_id = fs_devices_mnt->latest_devid;
- i = fs_devices_mnt->latest_devid;
+
+ disk_super = (struct btrfs_super_block *)buf;
+ ret = btrfs_read_dev_super(fd, disk_super,
+ BTRFS_SUPER_INFO_OFFSET, 0);
+ if (ret < 0) {
+ ret = -EIO;
+ goto out;
+ }
+ devid = btrfs_stack_device_id(&disk_super->dev_item);
+
+ fi_args->max_id = devid;
+ i = devid;
+
memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE);
close(fd);
- fd = open_file_or_dir(mp);
- if (fd < 0)
- return -errno;
- } else if (ret) {
- return -errno;
+ }
+
+ /* at this point path must not be for a block device */
+ fd = open_file_or_dir(path, &dirstream);
+ if (fd < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ /* fill in fi_args if not just a single device */
+ if (fi_args->num_devices != 1) {
+ ret = ioctl(fd, BTRFS_IOC_FS_INFO, fi_args);
+ if (ret < 0) {
+ ret = -errno;
+ goto out;
+ }
}
if (!fi_args->num_devices)
- return 0;
+ goto out;
- di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
- if (!di_args)
- return -errno;
+ /*
+ * with kernel patch
+ * btrfs: ioctl BTRFS_IOC_FS_INFO and BTRFS_IOC_DEV_INFO miss-matched with slots
+ * the kernel now returns total_devices which does not include
+ * replacing device if running.
+ * As we need to get dev info of the replace device if it is running,
+ * so just add one to fi_args->num_devices.
+ */
+
+ di_args = *di_ret = malloc((fi_args->num_devices + 1) * sizeof(*di_args));
+ if (!di_args) {
+ ret = -errno;
+ goto out;
+ }
+
+ /* get the replace target device if it is there */
+ ret = get_device_info(fd, i, &di_args[ndevs]);
+ if (!ret) {
+ ndevs++;
+ fi_args->num_devices++;
+ }
+ i++;
for (; i <= fi_args->max_id; ++i) {
BUG_ON(ndevs >= fi_args->num_devices);
if (ret == -ENODEV)
continue;
if (ret)
- return ret;
+ goto out;
ndevs++;
}
- BUG_ON(ndevs == 0);
+ /*
+ * only when the only dev we wanted to find is not there then
+ * let any error be returned
+ */
+ if (fi_args->num_devices != 1) {
+ BUG_ON(ndevs == 0);
+ ret = 0;
+ }
- return 0;
+out:
+ close_file_or_dir(fd, dirstream);
+ return ret;
}
#define isoctal(c) (((c) & ~7) == '0')
* Checks if the swap device.
* Returns 1 if swap device, < 0 on error or 0 if not swap device.
*/
-int is_swap_device(const char *file)
+static int is_swap_device(const char *file)
{
FILE *f;
struct stat st_buf;
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(
+ char *device)
+{
+ const char *type;
+ blkid_probe pr = NULL;
+ int ret;
+ blkid_loff_t size;
+
+ if (!device || !*device)
+ return 0;
+
+ ret = -1; /* will reset on success of all setup calls */
+
+ pr = blkid_new_probe_from_filename(device);
+ if (!pr)
+ goto out;
+
+ size = blkid_probe_get_size(pr);
+ if (size < 0)
+ goto out;
+
+ /* nothing to overwrite on a 0-length device */
+ if (size == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = blkid_probe_enable_partitions(pr, 1);
+ if (ret < 0)
+ goto out;
+
+ ret = blkid_do_fullprobe(pr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * 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;
+ }
+
+ 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;
+
+out:
+ if (pr)
+ blkid_free_probe(pr);
+ if (ret == -1)
+ fprintf(stderr,
+ "probe of %s failed, cannot detect "
+ "existing filesystem.\n", device);
+ return ret;
+}
+
+int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
+ u64 dev_cnt, int mixed, char *estr)
+{
+ size_t sz = 100;
+ u64 allowed = 0;
+
+ switch (dev_cnt) {
+ default:
+ case 4:
+ allowed |= BTRFS_BLOCK_GROUP_RAID10;
+ case 3:
+ allowed |= BTRFS_BLOCK_GROUP_RAID6;
+ case 2:
+ allowed |= BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 |
+ BTRFS_BLOCK_GROUP_RAID5;
+ break;
+ case 1:
+ allowed |= BTRFS_BLOCK_GROUP_DUP;
+ }
+
+ if (metadata_profile & ~allowed) {
+ snprintf(estr, sz, "unable to create FS with metadata "
+ "profile %llu (have %llu devices)\n",
+ metadata_profile, dev_cnt);
+ 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);
+ return 1;
+ }
+
+ if (!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP)) {
+ snprintf(estr, sz,
+ "dup for data is allowed only in mixed mode");
+ return 1;
+ }
+ return 0;
+}
+
+/* Check if disk is suitable for btrfs
+ * returns:
+ * 1: something is wrong, estr provides the error
+ * 0: all is fine
+ */
+int test_dev_for_mkfs(char *file, int force_overwrite, char *estr)
+{
+ int ret, fd;
+ size_t sz = 100;
+ struct stat st;
+
+ ret = is_swap_device(file);
+ if (ret < 0) {
+ snprintf(estr, sz, "error checking %s status: %s\n", file,
+ strerror(-ret));
+ return 1;
+ }
+ if (ret == 1) {
+ snprintf(estr, sz, "%s is a swap device\n", file);
+ return 1;
+ }
+ if (!force_overwrite) {
+ if (check_overwrite(file)) {
+ snprintf(estr, sz, "Use the -f option to force overwrite.\n");
+ return 1;
+ }
+ }
+ ret = check_mounted(file);
+ if (ret < 0) {
+ snprintf(estr, sz, "error checking %s mount status\n",
+ file);
+ return 1;
+ }
+ if (ret == 1) {
+ snprintf(estr, sz, "%s is mounted\n", file);
+ return 1;
+ }
+ /* check if the device is busy */
+ fd = open(file, O_RDWR|O_EXCL);
+ if (fd < 0) {
+ snprintf(estr, sz, "unable to open %s: %s\n", file,
+ strerror(errno));
+ return 1;
+ }
+ if (fstat(fd, &st)) {
+ snprintf(estr, sz, "unable to stat %s: %s\n", file,
+ strerror(errno));
+ close(fd);
+ return 1;
+ }
+ if (!S_ISBLK(st.st_mode)) {
+ fprintf(stderr, "'%s' is not a block device\n", file);
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ return 0;
+}
+
+int btrfs_scan_lblkid(int update_kernel)
+{
+ int fd = -1;
+ int ret;
+ u64 num_devices;
+ struct btrfs_fs_devices *tmp_devices;
+ blkid_dev_iterate iter = NULL;
+ blkid_dev dev = NULL;
+ blkid_cache cache = NULL;
+ char path[PATH_MAX];
+
+ if (blkid_get_cache(&cache, 0) < 0) {
+ printf("ERROR: lblkid cache get failed\n");
+ return 1;
+ }
+ blkid_probe_all(cache);
+ iter = blkid_dev_iterate_begin(cache);
+ blkid_dev_set_search(iter, "TYPE", "btrfs");
+ while (blkid_dev_next(iter, &dev) == 0) {
+ dev = blkid_verify(cache, dev);
+ if (!dev)
+ continue;
+ /* if we are here its definitely a btrfs disk*/
+ strncpy(path, blkid_dev_devname(dev), PATH_MAX);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ printf("ERROR: could not open %s\n", path);
+ continue;
+ }
+ ret = btrfs_scan_one_device(fd, path, &tmp_devices,
+ &num_devices, BTRFS_SUPER_INFO_OFFSET, 0);
+ if (ret) {
+ printf("ERROR: could not scan %s\n", path);
+ close (fd);
+ continue;
+ }
+
+ close(fd);
+ if (update_kernel)
+ btrfs_register_one_device(path);
+ }
+ blkid_dev_iterate_end(iter);
+ blkid_put_cache(cache);
+ return 0;
+}
+
+int is_vol_small(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
+ * or y.
+ */
+int ask_user(char *question)
+{
+ char buf[30] = {0,};
+ char *saveptr = NULL;
+ char *answer;
+
+ printf("%s [y/N]: ", question);
+
+ return fgets(buf, sizeof(buf) - 1, stdin) &&
+ (answer = strtok_r(buf, " \t\n\r", &saveptr)) &&
+ (!strcasecmp(answer, "yes") || !strcasecmp(answer, "y"));
+}
+
+/*
+ * For a given:
+ * - file or directory return the containing tree root id
+ * - subvolume return its own tree id
+ * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is
+ * undefined and function returns -1
+ */
+int lookup_ino_rootid(int fd, u64 *rootid)
+{
+ struct btrfs_ioctl_ino_lookup_args args;
+ int ret;
+ int e;
+
+ memset(&args, 0, sizeof(args));
+ args.treeid = 0;
+ args.objectid = BTRFS_FIRST_FREE_OBJECTID;
+
+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+ e = errno;
+ if (ret) {
+ fprintf(stderr, "ERROR: Failed to lookup root id - %s\n",
+ strerror(e));
+ return ret;
+ }
+
+ *rootid = args.treeid;
+
+ return 0;
+}
+
+/*
+ * 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;
+ int fd;
+ struct mntent *ent;
+ int len;
+ int ret;
+ int not_btrfs = 1;
+ int longest_matchlen = 0;
+ char *longest_match = NULL;
+
+ fd = open(path, O_RDONLY | O_NOATIME);
+ if (fd < 0)
+ return -errno;
+ close(fd);
+
+ mnttab = setmntent("/proc/self/mounts", "r");
+ if (!mnttab)
+ return -errno;
+
+ while ((ent = getmntent(mnttab))) {
+ len = strlen(ent->mnt_dir);
+ if (strncmp(ent->mnt_dir, path, len) == 0) {
+ /* match found 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)
+ return -ENOENT;
+ if (not_btrfs) {
+ free(longest_match);
+ return 1;
+ }
+
+ ret = 0;
+ *mount_root = realpath(longest_match, NULL);
+ if (!*mount_root)
+ ret = -errno;
+
+ free(longest_match);
+ return ret;
+}
+
+int test_minimum_size(const char *file, u32 leafsize)
+{
+ 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(leafsize)) {
+ 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;
+}