#include <errno.h>
#include <stdarg.h>
#include <getopt.h>
+#include <fcntl.h>
#include "utils.h"
#include "kerncompat.h"
#include "string-table.h"
#include "cmds-fi-usage.h"
#include "commands.h"
+#include "disk-io.h"
#include "version.h"
+#include "help.h"
/*
* Add the chunk info to the chunk_info list
for (j = 0 ; j < num_stripes ; j++) {
int i;
- struct chunk_info *p = 0;
+ struct chunk_info *p = NULL;
struct btrfs_stripe *stripe;
u64 devid;
}
if (!p) {
- int size = sizeof(struct btrfs_chunk) * (*info_count+1);
- struct chunk_info *res = realloc(*info_ptr, size);
+ int tmp = sizeof(struct btrfs_chunk) * (*info_count + 1);
+ struct chunk_info *res = realloc(*info_ptr, tmp);
if (!res) {
free(*info_ptr);
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return -ENOMEM;
}
return -e;
if (ret < 0) {
- fprintf(stderr,
- "ERROR: can't perform the search - %s\n",
- strerror(e));
+ error("cannot look up chunk tree info: %m");
return 1;
}
/* the ioctl returns the number of item it found in nr_items */
ret = add_info_to_list(info_ptr, info_count, item);
if (ret) {
- *info_ptr = 0;
+ *info_ptr = NULL;
return 1;
}
- off += sh->len;
+ off += btrfs_search_header_len(sh);
- sk->min_objectid = sh->objectid;
- sk->min_type = sh->type;
- sk->min_offset = sh->offset+1;
+ sk->min_objectid = btrfs_search_header_objectid(sh);
+ sk->min_type = btrfs_search_header_type(sh);
+ sk->min_offset = btrfs_search_header_offset(sh)+1;
}
if (!sk->min_offset) /* overflow */
*/
static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
{
- struct btrfs_ioctl_space_args *sargs = 0, *sargs_orig = 0;
- int e, ret, count;
+ struct btrfs_ioctl_space_args *sargs = NULL, *sargs_orig = NULL;
+ int ret, count;
sargs_orig = sargs = calloc(1, sizeof(struct btrfs_ioctl_space_args));
if (!sargs) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return NULL;
}
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
- e = errno;
- if (ret) {
- fprintf(stderr,
- "ERROR: couldn't get space info on '%s' - %s\n",
- path, strerror(e));
+ if (ret < 0) {
+ error("cannot get space info on '%s': %m", path);
free(sargs);
return NULL;
}
(count * sizeof(struct btrfs_ioctl_space_info)));
if (!sargs) {
free(sargs_orig);
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return NULL;
}
sargs->total_spaces = 0;
ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
- e = errno;
-
- if (ret) {
- fprintf(stderr,
- "ERROR: couldn't get space info on '%s' - %s\n",
- path, strerror(e));
+ if (ret < 0) {
+ error("cannot get space info with %u slots: %m",
+ count);
free(sargs);
return NULL;
}
}
/*
- * This function computes the space occuped by a *single* RAID5/RAID6 chunk.
+ * This function computes the space occupied by a *single* RAID5/RAID6 chunk.
* The computation is performed on the basis of the number of stripes
* which compose the chunk, which could be different from the number of devices
* if a disk is added later.
*/
-static void get_raid56_used(int fd, struct chunk_info *chunks, int chunkcount,
+static void get_raid56_used(struct chunk_info *chunks, int chunkcount,
u64 *raid5_used, u64 *raid6_used)
{
struct chunk_info *info_ptr = chunks;
}
}
-#define MIN_UNALOCATED_THRESH (16 * 1024 * 1024)
+#define MIN_UNALOCATED_THRESH SZ_16M
static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
int chunkcount, struct device_info *devinfo, int devcount,
char *path, unsigned unit_mode)
{
- struct btrfs_ioctl_space_args *sargs = 0;
+ struct btrfs_ioctl_space_args *sargs = NULL;
int i;
int ret = 0;
int width = 10; /* default 10 for human units */
u64 free_estimated = 0;
u64 free_min = 0;
int max_data_ratio = 1;
+ int mixed = 0;
sargs = load_space_info(fd, path);
if (!sargs) {
}
if (r_total_size == 0) {
- fprintf(stderr,
- "ERROR: couldn't get space info on '%s' - %s\n",
- path, strerror(errno));
+ error("cannot get space info on '%s': %m", path);
ret = 1;
goto exit;
}
- get_raid56_used(fd, chunkinfo, chunkcount, &raid5_used, &raid6_used);
+ get_raid56_used(chunkinfo, chunkcount, &raid5_used, &raid6_used);
for (i = 0; i < sargs->total_spaces; i++) {
int ratio;
ratio = 1;
if (!ratio)
- fprintf(stderr, "WARNING: RAID56 detected, not implemented\n");
+ warning("RAID56 detected, not implemented");
if (ratio > max_data_ratio)
max_data_ratio = ratio;
l_global_reserve_used = sargs->spaces[i].used_bytes;
}
if ((flags & (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA))
- == (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA)) {
- fprintf(stderr, "WARNING: MIXED blockgroups not handled\n");
+ == (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA)) {
+ mixed = 1;
}
-
if (flags & BTRFS_BLOCK_GROUP_DATA) {
r_data_used += sargs->spaces[i].used_bytes * ratio;
r_data_chunks += sargs->spaces[i].total_bytes * ratio;
}
}
- r_total_chunks = r_data_chunks + r_metadata_chunks + r_system_chunks;
- r_total_used = r_data_used + r_metadata_used + r_system_used;
+ r_total_chunks = r_data_chunks + r_system_chunks;
+ r_total_used = r_data_used + r_system_used;
+ if (!mixed) {
+ r_total_chunks += r_metadata_chunks;
+ r_total_used += r_metadata_used;
+ }
r_total_unused = r_total_size - r_total_chunks;
/* Raw / Logical = raid factor, >= 1 */
data_ratio = (double)r_data_chunks / l_data_chunks;
- metadata_ratio = (double)r_metadata_chunks / l_metadata_chunks;
+ if (mixed)
+ metadata_ratio = data_ratio;
+ else
+ metadata_ratio = (double)r_metadata_chunks / l_metadata_chunks;
#if 0
/* add the raid5/6 allocated space */
* In non-mixed case there's no difference.
*/
free_estimated = (r_data_chunks - r_data_used) / data_ratio;
+ /*
+ * For mixed-bg the metadata are left out in calculations thus global
+ * reserve would be lost. Part of it could be permanently allocated,
+ * we have to subtract the used bytes so we don't go under zero free.
+ */
+ if (mixed)
+ free_estimated -= l_global_reserve - l_global_reserve_used;
free_min = free_estimated;
/* Chop unallocatable space */
printf(" Device allocated:\t\t%*s\n", width,
pretty_size_mode(r_total_chunks, unit_mode));
printf(" Device unallocated:\t\t%*s\n", width,
- pretty_size_mode(r_total_unused, unit_mode));
+ pretty_size_mode(r_total_unused, unit_mode | UNITS_NEGATIVE));
printf(" Device missing:\t\t%*s\n", width,
pretty_size_mode(r_total_missing, unit_mode));
printf(" Used:\t\t\t%*s\n", width,
((struct device_info *)b)->path);
}
+int dev_to_fsid(const char *dev, u8 *fsid)
+{
+ struct btrfs_super_block *disk_super;
+ char buf[BTRFS_SUPER_INFO_SIZE];
+ int ret;
+ int fd;
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ return ret;
+ }
+
+ disk_super = (struct btrfs_super_block *)buf;
+ ret = btrfs_read_dev_super(fd, disk_super,
+ BTRFS_SUPER_INFO_OFFSET, SBREAD_DEFAULT);
+ if (ret)
+ goto out;
+
+ memcpy(fsid, disk_super->fsid, BTRFS_FSID_SIZE);
+ ret = 0;
+
+out:
+ close(fd);
+ return ret;
+}
+
/*
* This function loads the device_info structure and put them in an array
*/
struct btrfs_ioctl_fs_info_args fi_args;
struct btrfs_ioctl_dev_info_args dev_info;
struct device_info *info;
+ u8 fsid[BTRFS_UUID_SIZE];
*device_info_count = 0;
- *device_info_ptr = 0;
+ *device_info_ptr = NULL;
ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
if (ret < 0) {
if (errno == EPERM)
return -errno;
- fprintf(stderr, "ERROR: cannot get filesystem info - %s\n",
- strerror(errno));
+ error("cannot get filesystem info: %m");
return 1;
}
info = calloc(fi_args.num_devices, sizeof(struct device_info));
if (!info) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return 1;
}
for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) {
- BUG_ON(ndevs >= fi_args.num_devices);
+ if (ndevs >= fi_args.num_devices) {
+ error("unexpected number of devices: %d >= %llu", ndevs,
+ (unsigned long long)fi_args.num_devices);
+ error(
+ "if seed device is used, try running this command as root");
+ goto out;
+ }
memset(&dev_info, 0, sizeof(dev_info));
ret = get_device_info(fd, i, &dev_info);
if (ret == -ENODEV)
continue;
if (ret) {
- fprintf(stderr,
- "ERROR: cannot get info about device devid=%d\n",
- i);
- free(info);
- return ret;
+ error("cannot get info about device devid=%d", i);
+ goto out;
}
+ /*
+ * Skip seed device by checking device's fsid (requires root).
+ * And we will skip only if dev_to_fsid is successful and dev
+ * is a seed device.
+ * Ignore any other error including -EACCES, which is seen when
+ * a non-root process calls dev_to_fsid(path)->open(path).
+ */
+ ret = dev_to_fsid((const char *)dev_info.path, fsid);
+ if (!ret && memcmp(fi_args.fsid, fsid, BTRFS_FSID_SIZE) != 0)
+ continue;
+
info[ndevs].devid = dev_info.devid;
if (!dev_info.path[0]) {
strcpy(info[ndevs].path, "missing");
++ndevs;
}
- BUG_ON(ndevs != fi_args.num_devices);
+ if (ndevs != fi_args.num_devices) {
+ error("unexpected number of devices: %d != %llu", ndevs,
+ (unsigned long long)fi_args.num_devices);
+ goto out;
+ }
+
qsort(info, fi_args.num_devices,
sizeof(struct device_info), cmp_device_info);
*device_info_ptr = info;
return 0;
+
+out:
+ free(info);
+ return ret;
}
int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo,
ret = load_chunk_info(fd, chunkinfo, chunkcount);
if (ret == -EPERM) {
- fprintf(stderr,
- "WARNING: can't read detailed chunk info, RAID5/6 numbers will be incorrect, run as root\n");
+ warning(
+"cannot read detailed chunk info, RAID5/6 numbers will be incorrect, run as root");
} else if (ret) {
return ret;
}
ret = load_device_info(fd, devinfo, devcount);
if (ret == -EPERM) {
- fprintf(stderr,
- "WARNING: can't get filesystem info from ioctl(FS_INFO), run as root\n");
+ warning(
+ "cannot get filesystem info from ioctl(FS_INFO), run as root");
ret = 0;
}
{
int i;
u64 total_unused = 0;
- struct string_table *matrix = 0;
+ struct string_table *matrix = NULL;
int ncols, nrows;
int col;
int unallocated_col;
matrix = table_create(ncols, nrows);
if (!matrix) {
- fprintf(stderr, "ERROR: not enough memory\n");
+ error("not enough memory");
return;
}
unused = get_partition_size(device_info_ptr[i].path)
- total_allocated;
- table_printf(matrix, unallocated_col, vhdr_skip + i,
- ">%s", pretty_size_mode(unused, unit_mode));
+ table_printf(matrix, unallocated_col, vhdr_skip + i, ">%s",
+ pretty_size_mode(unused, unit_mode | UNITS_NEGATIVE));
total_unused += unused;
}
+ for (i = 0; i < spaceinfos_col; i++) {
+ table_printf(matrix, i, vhdr_skip - 1, "*-");
+ table_printf(matrix, i, vhdr_skip + device_info_count, "*-");
+ }
+
for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
continue;
- table_printf(matrix, col++, vhdr_skip + device_info_count, "=");
+ table_printf(matrix, col, vhdr_skip - 1, "*-");
+ table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
+ col++;
}
/* One for Unallocated */
- table_printf(matrix, col, vhdr_skip + device_info_count, "=");
+ table_printf(matrix, col, vhdr_skip - 1, "*-");
+ table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
/* footer */
table_printf(matrix, 1, vhdr_skip + device_info_count + 1, "<Total");
}
table_printf(matrix, unallocated_col, vhdr_skip + device_info_count + 1,
- ">%s", pretty_size_mode(total_unused, unit_mode));
+ ">%s",
+ pretty_size_mode(total_unused, unit_mode | UNITS_NEGATIVE));
table_printf(matrix, 1, vhdr_skip + device_info_count + 2, "<Used");
for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
printf("Unallocated:\n");
print_unused(info_ptr, info_count, device_info_ptr, device_info_count,
- unit_mode);
+ unit_mode | UNITS_NEGATIVE);
}
static int print_filesystem_usage_by_chunk(int fd,
const char * const cmd_filesystem_usage_usage[] = {
"btrfs filesystem usage [options] <path> [<path>..]",
"Show detailed information about internal filesystem usage .",
- HELPINFO_OUTPUT_UNIT_DF,
+ HELPINFO_UNITS_SHORT_LONG,
"-T show data in tabular format",
NULL
};
unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
- optind = 1;
while (1) {
int c;
return !!ret;
}
-void print_device_chunks(int fd, struct device_info *devinfo,
+void print_device_chunks(struct device_info *devinfo,
struct chunk_info *chunks_info_ptr,
int chunks_info_count, unsigned unit_mode)
{
}
printf(" Unallocated: %*s%10s\n",
(int)(20 - strlen("Unallocated")), "",
- pretty_size_mode(devinfo->size - allocated, unit_mode));
+ pretty_size_mode(devinfo->size - allocated,
+ unit_mode | UNITS_NEGATIVE));
}
-void print_device_sizes(int fd, struct device_info *devinfo, unsigned unit_mode)
+void print_device_sizes(struct device_info *devinfo, unsigned unit_mode)
{
printf(" Device size: %*s%10s\n",
(int)(20 - strlen("Device size")), "",
pretty_size_mode(devinfo->device_size, unit_mode));
-#if 0
- /*
- * The term has not seen an agreement and we don't want to change it
- * once it's in non-development branches or even released.
- */
- printf(" FS occupied: %*s%10s\n",
- (int)(20 - strlen("FS occupied")), "",
- pretty_size_mode(devinfo->size, unit_mode));
-#endif
+ printf(" Device slack: %*s%10s\n",
+ (int)(20 - strlen("Device slack")), "",
+ pretty_size_mode(devinfo->device_size > 0 ?
+ devinfo->device_size - devinfo->size : 0,
+ unit_mode));
}