btrfs-progs: Add command btrfs filesystem disk-usage

Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it>
Signed-off-by: David Sterba <dsterba@suse.cz>
master
Goffredo Baroncelli 2014-02-13 20:19:50 +01:00 committed by David Sterba
parent cbda6f2527
commit 9002666a2b
5 changed files with 455 additions and 0 deletions

View File

@ -20,10 +20,12 @@
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdarg.h>
#include "utils.h"
#include "kerncompat.h"
#include "ctree.h"
#include "string-table.h"
#include "commands.h"
@ -44,6 +46,13 @@ struct chunk_info {
u64 num_stripes;
};
/* to store information about the disks */
struct disk_info {
u64 devid;
char path[BTRFS_DEVICE_PATH_NAME_MAX];
u64 size;
};
/*
* Pretty print the size
* PAY ATTENTION: it return a statically buffer
@ -514,3 +523,422 @@ int cmd_filesystem_df(int argc, char **argv)
return 0;
}
/*
* Helper to sort the disk_info structure
*/
static int cmp_disk_info(const void *a, const void *b)
{
return strcmp(((struct disk_info *)a)->path,
((struct disk_info *)b)->path);
}
/*
* This function load the disk_info structure and put them in an array
*/
static int load_disks_info(int fd,
struct disk_info **disks_info_ptr,
int *disks_info_count)
{
int ret, i, ndevs;
struct btrfs_ioctl_fs_info_args fi_args;
struct btrfs_ioctl_dev_info_args dev_info;
struct disk_info *info;
*disks_info_count = 0;
*disks_info_ptr = 0;
ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
if (ret < 0) {
fprintf(stderr, "ERROR: cannot get filesystem info\n");
return -1;
}
info = malloc(sizeof(struct disk_info) * fi_args.num_devices);
if (!info) {
fprintf(stderr, "ERROR: not enough memory\n");
return -1;
}
for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) {
BUG_ON(ndevs >= fi_args.num_devices);
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 -1;
}
info[ndevs].devid = dev_info.devid;
strcpy(info[ndevs].path, (char *)dev_info.path);
info[ndevs].size = get_partition_size((char *)dev_info.path);
++ndevs;
}
BUG_ON(ndevs != fi_args.num_devices);
qsort(info, fi_args.num_devices,
sizeof(struct disk_info), cmp_disk_info);
*disks_info_count = fi_args.num_devices;
*disks_info_ptr = info;
return 0;
}
/*
* This function computes the size of a chunk in a disk
*/
static u64 calc_chunk_size(struct chunk_info *ci)
{
if (ci->type & BTRFS_BLOCK_GROUP_RAID0)
return ci->size / ci->num_stripes;
else if (ci->type & BTRFS_BLOCK_GROUP_RAID1)
return ci->size ;
else if (ci->type & BTRFS_BLOCK_GROUP_DUP)
return ci->size ;
else if (ci->type & BTRFS_BLOCK_GROUP_RAID5)
return ci->size / (ci->num_stripes -1);
else if (ci->type & BTRFS_BLOCK_GROUP_RAID6)
return ci->size / (ci->num_stripes -2);
else if (ci->type & BTRFS_BLOCK_GROUP_RAID10)
return ci->size / ci->num_stripes;
return ci->size;
}
/*
* This function print the results of the command btrfs fi disk-usage
* in tabular format
*/
static void _cmd_filesystem_disk_usage_tabular(int mode,
struct btrfs_ioctl_space_args *sargs,
struct chunk_info *chunks_info_ptr,
int chunks_info_count,
struct disk_info *disks_info_ptr,
int disks_info_count)
{
int i;
u64 total_unused = 0;
struct string_table *matrix = 0;
int ncols, nrows;
ncols = sargs->total_spaces + 2;
nrows = 2 + 1 + disks_info_count + 1 + 2;
matrix = table_create(ncols, nrows);
if (!matrix) {
fprintf(stderr, "ERROR: not enough memory\n");
return;
}
/* header */
for (i = 0; i < sargs->total_spaces; i++) {
const char *description;
u64 flags = sargs->spaces[i].flags;
description = btrfs_group_type_str(flags);
table_printf(matrix, 1+i, 0, "<%s", description);
}
for (i = 0; i < sargs->total_spaces; i++) {
const char *r_mode;
u64 flags = sargs->spaces[i].flags;
r_mode = btrfs_group_profile_str(flags);
table_printf(matrix, 1+i, 1, "<%s", r_mode);
}
table_printf(matrix, 1+sargs->total_spaces, 1, "<Unallocated");
/* body */
for (i = 0 ; i < disks_info_count ; i++) {
int k, col;
char *p;
u64 total_allocated = 0, unused;
p = strrchr(disks_info_ptr[i].path, '/');
if (!p)
p = disks_info_ptr[i].path;
else
p++;
table_printf(matrix, 0, i+3, "<%s",
disks_info_ptr[i].path);
for (col = 1, k = 0 ; k < sargs->total_spaces ; k++) {
u64 flags = sargs->spaces[k].flags;
u64 devid = disks_info_ptr[i].devid;
int j;
u64 size = 0;
for (j = 0 ; j < chunks_info_count ; j++) {
if (chunks_info_ptr[j].type != flags )
continue;
if (chunks_info_ptr[j].devid != devid)
continue;
size += calc_chunk_size(chunks_info_ptr+j);
}
if (size)
table_printf(matrix, col, i+3,
">%s", df_pretty_sizes(size, mode));
else
table_printf(matrix, col, i+3, ">-");
total_allocated += size;
col++;
}
unused = get_partition_size(disks_info_ptr[i].path) -
total_allocated;
table_printf(matrix, sargs->total_spaces + 1, i + 3,
">%s", df_pretty_sizes(unused, mode));
total_unused += unused;
}
for (i = 0; i <= sargs->total_spaces; i++)
table_printf(matrix, i + 1, disks_info_count + 3, "=");
/* footer */
table_printf(matrix, 0, disks_info_count + 4, "<Total");
for (i = 0; i < sargs->total_spaces; i++)
table_printf(matrix, 1 + i, disks_info_count + 4,
">%s",
df_pretty_sizes(sargs->spaces[i].total_bytes, mode));
table_printf(matrix, sargs->total_spaces+1, disks_info_count+4,
">%s", df_pretty_sizes(total_unused, mode));
table_printf(matrix, 0, disks_info_count+5, "<Used");
for (i = 0; i < sargs->total_spaces; i++)
table_printf(matrix, 1+i, disks_info_count+5, ">%s",
df_pretty_sizes(sargs->spaces[i].used_bytes, mode));
table_dump(matrix);
table_free(matrix);
}
/*
* This function prints the unused space per every disk
*/
static void print_unused(struct chunk_info *info_ptr,
int info_count,
struct disk_info *disks_info_ptr,
int disks_info_count,
int mode)
{
int i;
for (i = 0 ; i < disks_info_count ; i++) {
int j;
u64 total = 0;
for (j = 0 ; j < info_count ; j++)
if (info_ptr[j].devid == disks_info_ptr[i].devid)
total += calc_chunk_size(info_ptr+j);
printf(" %s\t%10s\n",
disks_info_ptr[i].path,
df_pretty_sizes(disks_info_ptr[i].size - total, mode));
}
}
/*
* This function prints the allocated chunk per every disk
*/
static void print_chunk_disks(u64 chunk_type,
struct chunk_info *chunks_info_ptr,
int chunks_info_count,
struct disk_info *disks_info_ptr,
int disks_info_count,
int mode)
{
int i;
for (i = 0 ; i < disks_info_count ; i++) {
int j;
u64 total = 0;
for (j = 0 ; j < chunks_info_count ; j++) {
if (chunks_info_ptr[j].type != chunk_type)
continue;
if (chunks_info_ptr[j].devid != disks_info_ptr[i].devid)
continue;
total += calc_chunk_size(&(chunks_info_ptr[j]));
//total += chunks_info_ptr[j].size;
}
if (total > 0)
printf(" %s\t%10s\n",
disks_info_ptr[i].path,
df_pretty_sizes(total, mode));
}
}
/*
* This function print the results of the command btrfs fi disk-usage
* in linear format
*/
static void _cmd_filesystem_disk_usage_linear(int mode,
struct btrfs_ioctl_space_args *sargs,
struct chunk_info *info_ptr,
int info_count,
struct disk_info *disks_info_ptr,
int disks_info_count)
{
int i;
for (i = 0; i < sargs->total_spaces; i++) {
const char *description;
const char *r_mode;
u64 flags = sargs->spaces[i].flags;
description = btrfs_group_type_str(flags);
r_mode = btrfs_group_profile_str(flags);
printf("%s,%s: Size:%s, ",
description,
r_mode,
df_pretty_sizes(sargs->spaces[i].total_bytes ,
mode));
printf("Used:%s\n",
df_pretty_sizes(sargs->spaces[i].used_bytes,
mode));
print_chunk_disks(flags, info_ptr, info_count,
disks_info_ptr, disks_info_count,
mode);
printf("\n");
}
printf("Unallocated:\n");
print_unused(info_ptr, info_count,
disks_info_ptr, disks_info_count,
mode);
}
static int _cmd_filesystem_disk_usage(int fd, char *path, int mode, int tabular)
{
struct btrfs_ioctl_space_args *sargs = 0;
int info_count = 0;
struct chunk_info *info_ptr = 0;
struct disk_info *disks_info_ptr = 0;
int disks_info_count = 0;
int ret = 0;
if (load_chunk_info(fd, &info_ptr, &info_count) ||
load_disks_info(fd, &disks_info_ptr, &disks_info_count)) {
ret = -1;
goto exit;
}
if ((sargs = load_space_info(fd, path)) == NULL) {
ret = -1;
goto exit;
}
if (tabular)
_cmd_filesystem_disk_usage_tabular(mode, sargs,
info_ptr, info_count,
disks_info_ptr, disks_info_count);
else
_cmd_filesystem_disk_usage_linear(mode, sargs,
info_ptr, info_count,
disks_info_ptr, disks_info_count);
exit:
if (sargs)
free(sargs);
if (disks_info_ptr)
free(disks_info_ptr);
if (info_ptr)
free(info_ptr);
return ret;
}
const char * const cmd_filesystem_disk_usage_usage[] = {
"btrfs filesystem disk-usage [-b][-t] <path> [<path>..]",
"Show in which disk the chunks are allocated.",
"",
"-b\tSet byte as unit",
"-t\tShow data in tabular format",
NULL
};
int cmd_filesystem_disk_usage(int argc, char **argv)
{
int flags = DF_HUMAN_UNIT;
int i, more_than_one = 0;
int tabular = 0;
optind = 1;
while (1) {
char c = getopt(argc, argv, "bt");
if (c < 0)
break;
switch (c) {
case 'b':
flags &= ~DF_HUMAN_UNIT;
break;
case 't':
tabular = 1;
break;
default:
usage(cmd_filesystem_disk_usage_usage);
}
}
if (check_argc_min(argc - optind, 1)) {
usage(cmd_filesystem_disk_usage_usage);
return 21;
}
for (i = optind; i < argc ; i++) {
int r, fd;
DIR *dirstream = NULL;
if (more_than_one)
printf("\n");
fd = open_file_or_dir(argv[i], &dirstream);
if (fd < 0) {
fprintf(stderr, "ERROR: can't access to '%s'\n",
argv[1]);
return 12;
}
r = _cmd_filesystem_disk_usage(fd, argv[i], flags, tabular);
close_file_or_dir(fd, dirstream);
if (r)
return r;
more_than_one = 1;
}
return 0;
}

View File

@ -22,4 +22,7 @@
extern const char * const cmd_filesystem_df_usage[];
int cmd_filesystem_df(int argc, char **argv);
extern const char * const cmd_filesystem_disk_usage_usage[];
int cmd_filesystem_disk_usage(int argc, char **argv);
#endif

View File

@ -1298,6 +1298,9 @@ const struct cmd_group filesystem_cmd_group = {
{ "balance", cmd_balance, NULL, &balance_cmd_group, 1 },
{ "resize", cmd_resize, cmd_resize_usage, NULL, 0 },
{ "label", cmd_label, cmd_label_usage, NULL, 0 },
{ "disk-usage", cmd_filesystem_disk_usage,
cmd_filesystem_disk_usage_usage, NULL, 0 },
NULL_CMD_STRUCT
}
};

16
utils.c
View File

@ -2505,3 +2505,19 @@ u64 disk_size(char *path)
else
return sfs.f_bsize * sfs.f_blocks;
}
u64 get_partition_size(char *dev)
{
u64 result;
int fd = open(dev, O_RDONLY);
if (fd < 0)
return 0;
if (ioctl(fd, BLKGETSIZE64, &result) < 0) {
close(fd);
return 0;
}
close(fd);
return result;
}

View File

@ -135,6 +135,11 @@ int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args);
int test_uuid_unique(char *fs_uuid);
u64 disk_size(char *path);
int get_device_info(int fd, u64 devid,
struct btrfs_ioctl_dev_info_args *di_args);
u64 get_partition_size(char *dev);
const char* group_type_str(u64 flags);
const char* group_profile_str(u64 flags);
int test_minimum_size(const char *file, u32 leafsize);
int test_issubvolname(const char *name);