btrfs-progs: fsck-tests: Introduce test case with keyed data backref with the extent...
[platform/upstream/btrfs-progs.git] / cmds-fi-usage.c
index 50d6333..de7ad66 100644 (file)
@@ -22,6 +22,7 @@
 #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
@@ -47,7 +50,7 @@ static int add_info_to_list(struct chunk_info **info_ptr,
 
        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;
 
@@ -63,12 +66,12 @@ static int add_info_to_list(struct chunk_info **info_ptr,
                        }
 
                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;
                        }
 
@@ -161,9 +164,7 @@ static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count
                        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 */
@@ -182,15 +183,15 @@ static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count
 
                        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 */
@@ -228,12 +229,12 @@ static int cmp_btrfs_ioctl_space_info(const void *a, const void *b)
  */
 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;
        }
 
@@ -241,11 +242,8 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
        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;
        }
@@ -261,7 +259,7 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
                        (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;
        }
 
@@ -269,12 +267,9 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
        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;
        }
@@ -286,12 +281,12 @@ static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
 }
 
 /*
- * 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;
@@ -307,12 +302,12 @@ static void get_raid56_used(int fd, struct chunk_info *chunks, int chunkcount,
        }
 }
 
-#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 */
@@ -343,6 +338,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
        u64 free_estimated = 0;
        u64 free_min = 0;
        int max_data_ratio = 1;
+       int mixed = 0;
 
        sargs = load_space_info(fd, path);
        if (!sargs) {
@@ -358,14 +354,12 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
        }
 
        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;
@@ -391,7 +385,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
                        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;
@@ -401,10 +395,9 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
                        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;
@@ -421,13 +414,20 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
                }
        }
 
-       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 */
@@ -444,6 +444,13 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
         * 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 */
@@ -464,7 +471,7 @@ static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
        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,
@@ -498,6 +505,33 @@ static int cmp_device_info(const void *a, const void *b)
                        ((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
  */
@@ -508,40 +542,54 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
        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");
@@ -554,7 +602,12 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
                ++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);
 
@@ -562,6 +615,10 @@ static int load_device_info(int fd, struct device_info **device_info_ptr,
        *device_info_ptr = info;
 
        return 0;
+
+out:
+       free(info);
+       return ret;
 }
 
 int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo,
@@ -571,16 +628,16 @@ 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;
        }
 
@@ -620,45 +677,59 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
 {
        int i;
        u64 total_unused = 0;
-       struct string_table *matrix = 0;
+       struct string_table *matrix = NULL;
        int  ncols, nrows;
+       int col;
+       int unallocated_col;
+       int spaceinfos_col;
+       const int vhdr_skip = 3;        /* amount of vertical header space */
+
+       /* id, path, unallocated */
+       ncols = 3;
+       spaceinfos_col = 2;
+       /* Properly count the real space infos */
+       for (i = 0; i < sargs->total_spaces; i++) {
+               if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+                       continue;
+               ncols++;
+       }
 
-       ncols = sargs->total_spaces + 2;
-       nrows = 2 + 1 + device_info_count + 1 + 2;
+       /* 2 for header, empty line, devices, ===, total, used */
+       nrows = vhdr_skip + device_info_count + 1 + 2;
 
        matrix = table_create(ncols, nrows);
        if (!matrix) {
-               fprintf(stderr, "ERROR: not enough memory\n");
+               error("not enough memory");
                return;
        }
 
+       /*
+        * We have to skip the global block reserve everywhere as it's an
+        * artificial blockgroup
+        */
+
        /* header */
-       for (i = 0; i < sargs->total_spaces; i++) {
-               const char *description;
+       for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
                u64 flags = sargs->spaces[i].flags;
 
                if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
                        continue;
 
-               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, col, 0, "<%s",
+                               btrfs_group_type_str(flags));
+               table_printf(matrix, col, 1, "<%s",
+                               btrfs_group_profile_str(flags));
+               col++;
        }
+       unallocated_col = col;
 
-       table_printf(matrix, 1+sargs->total_spaces, 1, "<Unallocated");
+       table_printf(matrix, 0, 1, "<Id");
+       table_printf(matrix, 1, 1, "<Path");
+       table_printf(matrix, unallocated_col, 1, "<Unallocated");
 
        /* body */
        for (i = 0; i < device_info_count; i++) {
-               int k, col;
+               int k;
                char *p;
 
                u64  total_allocated = 0, unused;
@@ -669,14 +740,20 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
                else
                        p++;
 
-               table_printf(matrix, 0, i + 3, "<%s", device_info_ptr[i].path);
+               table_printf(matrix, 0, vhdr_skip + i, ">%llu",
+                               device_info_ptr[i].devid);
+               table_printf(matrix, 1, vhdr_skip + i, "<%s",
+                               device_info_ptr[i].path);
 
-               for (col = 1, k = 0 ; k < sargs->total_spaces ; k++)  {
+               for (col = spaceinfos_col, k = 0; k < sargs->total_spaces; k++) {
                        u64     flags = sargs->spaces[k].flags;
                        u64 devid = device_info_ptr[i].devid;
                        int     j;
                        u64 size = 0;
 
+                       if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
+                               continue;
+
                        for (j = 0 ; j < chunks_info_count ; j++) {
                                if (chunks_info_ptr[j].type != flags )
                                                continue;
@@ -687,10 +764,10 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
                        }
 
                        if (size)
-                               table_printf(matrix, col, i+3,
+                               table_printf(matrix, col, vhdr_skip+ i,
                                        ">%s", pretty_size_mode(size, unit_mode));
                        else
-                               table_printf(matrix, col, i+3, ">-");
+                               table_printf(matrix, col, vhdr_skip + i, ">-");
 
                        total_allocated += size;
                        col++;
@@ -699,28 +776,53 @@ static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
                unused = get_partition_size(device_info_ptr[i].path)
                                - total_allocated;
 
-               table_printf(matrix, sargs->total_spaces + 1, i + 3,
-                              ">%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 <= sargs->total_spaces; i++)
-               table_printf(matrix, i + 1, device_info_count + 3, "=");
+       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 - 1, "*-");
+               table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
+               col++;
+       }
+       /* One for Unallocated */
+       table_printf(matrix, col, vhdr_skip - 1, "*-");
+       table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
 
        /* footer */
-       table_printf(matrix, 0, device_info_count + 4, "<Total");
-       for (i = 0; i < sargs->total_spaces; i++)
-               table_printf(matrix, 1 + i, device_info_count + 4, ">%s",
+       table_printf(matrix, 1, vhdr_skip + device_info_count + 1, "<Total");
+       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 + 1,
+                       ">%s",
                        pretty_size_mode(sargs->spaces[i].total_bytes, unit_mode));
+       }
+
+       table_printf(matrix, unallocated_col, vhdr_skip + device_info_count + 1,
+               ">%s",
+               pretty_size_mode(total_unused, unit_mode | UNITS_NEGATIVE));
 
-       table_printf(matrix, sargs->total_spaces + 1, device_info_count + 4,
-                       ">%s", pretty_size_mode(total_unused, unit_mode));
+       table_printf(matrix, 1, vhdr_skip + device_info_count + 2, "<Used");
+       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, 0, device_info_count + 5, "<Used");
-       for (i = 0; i < sargs->total_spaces; i++)
-               table_printf(matrix, 1 + i, device_info_count+5, ">%s",
+               table_printf(matrix, col++, vhdr_skip + device_info_count + 2,
+                       ">%s",
                        pretty_size_mode(sargs->spaces[i].used_bytes, unit_mode));
+       }
 
        table_dump(matrix);
        table_free(matrix);
@@ -823,7 +925,7 @@ static void _cmd_filesystem_usage_linear(unsigned unit_mode,
 
        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,
@@ -858,7 +960,7 @@ out:
 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
 };
@@ -873,7 +975,6 @@ int cmd_filesystem_usage(int argc, char **argv)
 
        unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
 
-       optind = 1;
        while (1) {
                int c;
 
@@ -901,10 +1002,8 @@ int cmd_filesystem_usage(int argc, char **argv)
                int chunkcount = 0;
                int devcount = 0;
 
-               fd = open_file_or_dir(argv[i], &dirstream);
+               fd = btrfs_open_dir(argv[i], &dirstream, 1);
                if (fd < 0) {
-                       fprintf(stderr, "ERROR: can't access '%s'\n",
-                               argv[i]);
                        ret = 1;
                        goto out;
                }
@@ -937,7 +1036,7 @@ out:
        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)
 {
@@ -969,21 +1068,18 @@ void print_device_chunks(int fd, struct device_info *devinfo,
        }
        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));
 }