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,
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;
if (max_block_count)
block_count = min(block_count, max_block_count);
- 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) {
/*
}
}
- 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;
}
/* scan other devices */
if (is_btrfs && total_devs > 1) {
- if ((ret = btrfs_scan_for_fsid(!BTRFS_UPDATE_KERNEL)))
+ ret = btrfs_scan_lblkid(!BTRFS_UPDATE_KERNEL);
+ if (ret)
return ret;
}
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",
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);
- 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;
- }
- 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, 0);
- 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;
-}
-
int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
int super_offset)
{
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]);
}
/*
{
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)
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
int i;
for (i = 0; i <64; i++)
- if (x << i & (1UL << 63))
+ if (x << i & (1ULL << 63))
return 64 - i;
return 64 - i;
}
if (!fi_args->num_devices)
goto out;
- di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args));
+ /*
+ * 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);
ret = get_device_info(fd, i, &di_args[ndevs]);
return 0;
}
-/*
- * scans devs for the btrfs
-*/
-int scan_for_btrfs(int where, int update_kernel)
-{
- int ret = 0;
-
- 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;
-}
-
int is_vol_small(char *file)
{
int fd = -1;
close(fd);
return -1;
}
- if (size < 1024 * 1024 * 1024) {
+ if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) {
close(fd);
return 1;
} else {
/*
* 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
*/
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;
struct mntent *ent;
int len;
int ret;
+ int not_btrfs = 1;
int longest_matchlen = 0;
char *longest_match = NULL;
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;
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;
+}