diff --git a/Makefile b/Makefile index 7c71aa36..e810383f 100644 --- a/Makefile +++ b/Makefile @@ -134,7 +134,8 @@ objects = ctree.o disk-io.o kernel-lib/radix-tree.o extent-tree.o print-tree.o \ common/task-utils.o \ inode.o file.o find-root.o free-space-tree.o common/help.o send-dump.o \ common/fsfeatures.o kernel-lib/tables.o kernel-lib/raid56.o transaction.o \ - delayed-ref.o common/format-output.o common/path-utils.o + delayed-ref.o common/format-output.o common/path-utils.o \ + common/device-utils.o cmds_objects = cmds/subvolume.o cmds/filesystem.o cmds/device.o cmds/scrub.o \ cmds/inspect.o cmds/balance.o cmds/send.o cmds/receive.o \ cmds/quota.o cmds/qgroup.o cmds/replace.o check/main.o \ diff --git a/cmds/device.c b/cmds/device.c index 54b0309a..b364ff80 100644 --- a/cmds/device.c +++ b/cmds/device.c @@ -34,6 +34,7 @@ #include "cmds/commands.h" #include "common/help.h" #include "common/path-utils.h" +#include "common/device-utils.h" #include "mkfs/common.h" static const char * const device_cmd_group_usage[] = { diff --git a/cmds/filesystem-usage.c b/cmds/filesystem-usage.c index d82177f2..21232218 100644 --- a/cmds/filesystem-usage.c +++ b/cmds/filesystem-usage.c @@ -34,6 +34,7 @@ #include "version.h" #include "common/help.h" +#include "common/device-utils.h" /* * Add the chunk info to the chunk_info list diff --git a/cmds/replace.c b/cmds/replace.c index 2f59af45..2321aa15 100644 --- a/cmds/replace.c +++ b/cmds/replace.c @@ -39,6 +39,7 @@ #include "cmds/commands.h" #include "common/help.h" #include "common/path-utils.h" +#include "common/device-utils.h" #include "mkfs/common.h" static int print_replace_status(int fd, const char *path, int once); diff --git a/common/device-utils.c b/common/device-utils.c new file mode 100644 index 00000000..b03d62fa --- /dev/null +++ b/common/device-utils.c @@ -0,0 +1,254 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kernel-lib/sizes.h" +#include "disk-io.h" +#include "common/device-utils.h" +#include "common/internal.h" +#include "common/messages.h" +#include "common/utils.h" + +#ifndef BLKDISCARD +#define BLKDISCARD _IO(0x12,119) +#endif + +/* + * Discard the given range in one go + */ +static int discard_range(int fd, u64 start, u64 len) +{ + u64 range[2] = { start, len }; + + if (ioctl(fd, BLKDISCARD, &range) < 0) + return errno; + 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, SZ_1G); + int ret; + + ret = discard_range(fd, start, chunk_size); + if (ret) + return ret; + len -= chunk_size; + start += chunk_size; + } + + return 0; +} + +static int zero_blocks(int fd, off_t start, size_t len) +{ + char *buf = malloc(len); + int ret = 0; + ssize_t written; + + if (!buf) + return -ENOMEM; + memset(buf, 0, len); + written = pwrite(fd, buf, len, start); + if (written != len) + ret = -EIO; + free(buf); + return ret; +} + +#define ZERO_DEV_BYTES SZ_2M + +/* 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 end = max(start, start + len); + +#ifdef __sparc__ + /* and don't overwrite the disk labels on sparc */ + start = max(start, 1024); + end = max(end, 1024); +#endif + + start = min_t(u64, start, dev_size); + end = min_t(u64, end, dev_size); + + return zero_blocks(fd, start, end - start); +} + +static int btrfs_wipe_existing_sb(int fd) +{ + const char *off = NULL; + size_t len = 0; + loff_t offset; + char buf[BUFSIZ]; + int ret = 0; + blkid_probe pr = NULL; + + pr = blkid_new_probe(); + if (!pr) + return -1; + + if (blkid_probe_set_device(pr, fd, 0, 0)) { + ret = -1; + goto out; + } + + ret = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); + if (!ret) + ret = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len); + + if (ret || len == 0 || off == NULL) { + /* + * If lookup fails, the probe did not find any values, eg. for + * a file image or a loop device. Soft error. + */ + ret = 1; + goto out; + } + + offset = strtoll(off, NULL, 10); + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, len); + ret = pwrite(fd, buf, len, offset); + if (ret < 0) { + error("cannot wipe existing superblock: %m"); + ret = -1; + } else if (ret != len) { + error("cannot wipe existing superblock: wrote %d of %zd", ret, len); + ret = -1; + } + fsync(fd); + +out: + blkid_free_probe(pr); + return ret; +} + +int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, + u64 max_block_count, unsigned opflags) +{ + u64 block_count; + struct stat st; + int i, ret; + + ret = fstat(fd, &st); + if (ret < 0) { + error("unable to stat %s: %m", file); + return 1; + } + + block_count = btrfs_device_size(fd, &st); + if (block_count == 0) { + error("unable to determine size of %s", file); + return 1; + } + if (max_block_count) + block_count = min(block_count, max_block_count); + + if (opflags & PREP_DEVICE_DISCARD) { + /* + * We intentionally ignore errors from the discard ioctl. It + * is not necessary for the mkfs functionality but just an + * optimization. + */ + if (discard_range(fd, 0, 0) == 0) { + if (opflags & PREP_DEVICE_VERBOSE) + printf("Performing full device TRIM %s (%s) ...\n", + file, pretty_size(block_count)); + discard_blocks(fd, 0, 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 && (opflags & PREP_DEVICE_ZERO_END)) + ret = zero_dev_clamped(fd, block_count - ZERO_DEV_BYTES, + ZERO_DEV_BYTES, block_count); + + if (ret < 0) { + errno = -ret; + error("failed to zero device '%s': %m", file); + return 1; + } + + ret = btrfs_wipe_existing_sb(fd); + if (ret < 0) { + error("cannot wipe superblocks on %s", file); + return 1; + } + + *block_count_ret = block_count; + return 0; +} + +u64 btrfs_device_size(int fd, struct stat *st) +{ + u64 size; + if (S_ISREG(st->st_mode)) { + return st->st_size; + } + if (!S_ISBLK(st->st_mode)) { + return 0; + } + if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { + return size; + } + return 0; +} + +u64 disk_size(const char *path) +{ + struct statfs sfs; + + if (statfs(path, &sfs) < 0) + return 0; + else + return sfs.f_bsize * sfs.f_blocks; +} + +u64 get_partition_size(const 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; +} + diff --git a/common/device-utils.h b/common/device-utils.h new file mode 100644 index 00000000..70d19cae --- /dev/null +++ b/common/device-utils.h @@ -0,0 +1,33 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef __DEVICE_UTILS_H__ +#define __DEVICE_UTILS_H__ + +#include "kerncompat.h" +#include "sys/stat.h" + +#define PREP_DEVICE_ZERO_END (1U << 0) +#define PREP_DEVICE_DISCARD (1U << 1) +#define PREP_DEVICE_VERBOSE (1U << 2) + +u64 get_partition_size(const char *dev); +u64 disk_size(const char *path); +u64 btrfs_device_size(int fd, struct stat *st); +int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, + u64 max_block_count, unsigned opflags); + +#endif diff --git a/common/utils.c b/common/utils.c index b0b4cc93..159689c1 100644 --- a/common/utils.c +++ b/common/utils.c @@ -55,10 +55,6 @@ #include "cmds/commands.h" #include "mkfs/common.h" -#ifndef BLKDISCARD -#define BLKDISCARD _IO(0x12,119) -#endif - static int btrfs_scan_done = 0; static int rand_seed_initialized = 0; @@ -66,38 +62,6 @@ static unsigned short rand_seed[3]; struct btrfs_config bconf; -/* - * Discard the given range in one go - */ -static int discard_range(int fd, u64 start, u64 len) -{ - u64 range[2] = { start, len }; - - if (ioctl(fd, BLKDISCARD, &range) < 0) - return errno; - 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, SZ_1G); - int ret; - - ret = discard_range(fd, start, chunk_size); - if (ret) - return ret; - len -= chunk_size; - start += chunk_size; - } - - return 0; -} - int test_uuid_unique(char *fs_uuid) { int unique = 1; @@ -127,56 +91,6 @@ int test_uuid_unique(char *fs_uuid) return unique; } -u64 btrfs_device_size(int fd, struct stat *st) -{ - u64 size; - if (S_ISREG(st->st_mode)) { - return st->st_size; - } - if (!S_ISBLK(st->st_mode)) { - return 0; - } - if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { - return size; - } - return 0; -} - -static int zero_blocks(int fd, off_t start, size_t len) -{ - char *buf = malloc(len); - int ret = 0; - ssize_t written; - - if (!buf) - return -ENOMEM; - memset(buf, 0, len); - written = pwrite(fd, buf, len, start); - if (written != len) - ret = -EIO; - free(buf); - return ret; -} - -#define ZERO_DEV_BYTES SZ_2M - -/* 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 end = max(start, start + len); - -#ifdef __sparc__ - /* and don't overwrite the disk labels on sparc */ - start = max(start, 1024); - end = max(end, 1024); -#endif - - start = min_t(u64, start, dev_size); - end = min_t(u64, end, dev_size); - - return zero_blocks(fd, start, end - start); -} - int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, struct btrfs_root *root, int fd, const char *path, u64 device_total_bytes, u32 io_width, u32 io_align, @@ -263,116 +177,6 @@ out: return ret; } -static int btrfs_wipe_existing_sb(int fd) -{ - const char *off = NULL; - size_t len = 0; - loff_t offset; - char buf[BUFSIZ]; - int ret = 0; - blkid_probe pr = NULL; - - pr = blkid_new_probe(); - if (!pr) - return -1; - - if (blkid_probe_set_device(pr, fd, 0, 0)) { - ret = -1; - goto out; - } - - ret = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); - if (!ret) - ret = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len); - - if (ret || len == 0 || off == NULL) { - /* - * If lookup fails, the probe did not find any values, eg. for - * a file image or a loop device. Soft error. - */ - ret = 1; - goto out; - } - - offset = strtoll(off, NULL, 10); - if (len > sizeof(buf)) - len = sizeof(buf); - - memset(buf, 0, len); - ret = pwrite(fd, buf, len, offset); - if (ret < 0) { - error("cannot wipe existing superblock: %m"); - ret = -1; - } else if (ret != len) { - error("cannot wipe existing superblock: wrote %d of %zd", ret, len); - ret = -1; - } - fsync(fd); - -out: - blkid_free_probe(pr); - return ret; -} - -int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, - u64 max_block_count, unsigned opflags) -{ - u64 block_count; - struct stat st; - int i, ret; - - ret = fstat(fd, &st); - if (ret < 0) { - error("unable to stat %s: %m", file); - return 1; - } - - block_count = btrfs_device_size(fd, &st); - if (block_count == 0) { - error("unable to determine size of %s", file); - return 1; - } - if (max_block_count) - block_count = min(block_count, max_block_count); - - if (opflags & PREP_DEVICE_DISCARD) { - /* - * We intentionally ignore errors from the discard ioctl. It - * is not necessary for the mkfs functionality but just an - * optimization. - */ - if (discard_range(fd, 0, 0) == 0) { - if (opflags & PREP_DEVICE_VERBOSE) - printf("Performing full device TRIM %s (%s) ...\n", - file, pretty_size(block_count)); - discard_blocks(fd, 0, 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 && (opflags & PREP_DEVICE_ZERO_END)) - ret = zero_dev_clamped(fd, block_count - ZERO_DEV_BYTES, - ZERO_DEV_BYTES, block_count); - - if (ret < 0) { - errno = -ret; - error("failed to zero device '%s': %m", file); - return 1; - } - - ret = btrfs_wipe_existing_sb(fd); - if (ret < 0) { - error("cannot wipe superblocks on %s", file); - return 1; - } - - *block_count_ret = block_count; - return 0; -} - int btrfs_make_root_dir(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid) { @@ -1918,33 +1722,6 @@ const char* btrfs_group_profile_str(u64 flag) return "unknown"; } } - -u64 disk_size(const char *path) -{ - struct statfs sfs; - - if (statfs(path, &sfs) < 0) - return 0; - else - return sfs.f_bsize * sfs.f_blocks; -} - -u64 get_partition_size(const 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; -} - /* * Check if the BTRFS_IOC_TREE_SEARCH_V2 ioctl is supported on a given * filesystem, opened at fd diff --git a/common/utils.h b/common/utils.h index 4e31a27c..8da5bbf4 100644 --- a/common/utils.h +++ b/common/utils.h @@ -66,10 +66,6 @@ void units_set_mode(unsigned *units, unsigned mode); void units_set_base(unsigned *units, unsigned base); -#define PREP_DEVICE_ZERO_END (1U << 0) -#define PREP_DEVICE_DISCARD (1U << 1) -#define PREP_DEVICE_VERBOSE (1U << 2) - #define SEEN_FSID_HASH_SIZE 256 struct seen_fsid { u8 fsid[BTRFS_FSID_SIZE]; @@ -80,8 +76,6 @@ struct seen_fsid { int btrfs_make_root_dir(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid); -int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, - u64 max_block_count, unsigned opflags); int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, struct btrfs_root *root, int fd, const char *path, u64 block_count, u32 io_width, u32 io_align, @@ -122,7 +116,6 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose); int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only); int btrfs_open_dir(const char *path, DIR **dirstream, int verbose); int btrfs_open_file_or_dir(const char *path, DIR **dirstream, int verbose); -u64 btrfs_device_size(int fd, struct stat *st); int get_label_mounted(const char *mount_path, char *labelp); int get_label_unmounted(const char *dev, char *label); int group_profile_max_safe_loss(u64 flags); @@ -137,8 +130,6 @@ int get_device_info(int fd, u64 devid, struct btrfs_ioctl_dev_info_args *di_args); int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret); int test_uuid_unique(char *fs_uuid); -u64 disk_size(const char *path); -u64 get_partition_size(const char *dev); const char *subvol_strip_mountpoint(const char *mnt, const char *full_path); int find_next_key(struct btrfs_path *path, struct btrfs_key *key); diff --git a/image/main.c b/image/main.c index cabae619..9c7adb9a 100644 --- a/image/main.c +++ b/image/main.c @@ -36,6 +36,7 @@ #include "volumes.h" #include "extent_io.h" #include "common/help.h" +#include "common/device-utils.h" #include "image/metadump.h" #include "image/sanitize.h" diff --git a/mkfs/common.c b/mkfs/common.c index b620f072..caca5e70 100644 --- a/mkfs/common.c +++ b/mkfs/common.c @@ -24,6 +24,7 @@ #include "volumes.h" #include "common/utils.h" #include "common/path-utils.h" +#include "common/device-utils.h" #include "mkfs/common.h" static u64 reference_root_table[] = { diff --git a/mkfs/main.c b/mkfs/main.c index dd907451..c50f7eb5 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -38,6 +38,7 @@ #include "transaction.h" #include "common/utils.h" #include "common/path-utils.h" +#include "common/device-utils.h" #include "kernel-lib/list_sort.h" #include "common/help.h" #include "common/rbtree-utils.h"