#include <sys/ioctl.h>
#include <linux/fs.h>
+#include <linux/version.h>
#include <linux/fiemap.h>
+#if !defined(FIEMAP_EXTENT_SHARED) && (HAVE_OWN_FIEMAP_EXTENT_SHARED_DEFINE == 1)
+#define FIEMAP_EXTENT_SHARED 0x00002000
+#endif
+
#include "utils.h"
#include "commands.h"
#include "kerncompat.h"
#include "rbtree.h"
#include "interval_tree_generic.h"
+#include "help.h"
+#include "fsfeatures.h"
static int summarize = 0;
static unsigned unit_mode = UNITS_RAW;
{
struct shared_extent *sh;
- BUG_ON(len == 0);
+ ASSERT(len != 0);
sh = calloc(1, sizeof(*sh));
if (!sh)
- return ENOMEM;
+ return -ENOMEM;
sh->start = start;
sh->last = (start + len - 1);
}
}
-#define dprintf(...)
+#define dbgprintf(...)
/*
* Find all extents which overlap 'n', calculate the space
u64 wstart = n->start;
u64 wlast = n->last;
- dprintf("Count overlaps:");
+ dbgprintf("Count overlaps:");
do {
/*
if (wlast < n->last)
wlast = n->last;
- dprintf(" (%llu, %llu)", n->start, n->last);
+ dbgprintf(" (%llu, %llu)", n->start, n->last);
tmp = n;
n = extent_tree_iter_next(n, wstart, wlast);
free(tmp);
} while (n);
- dprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart,
+ dbgprintf("; wstart: %llu wlast: %llu total: %llu\n", wstart,
wlast, wlast - wstart + 1);
return wlast - wstart + 1;
/*
* What we want to do here is get a count of shared bytes within the
- * set of extents we have collected. Specifcally, we don't want to
+ * set of extents we have collected. Specifically, we don't want to
* count any byte more than once, so just adding them up doesn't
* work.
*
else if (cmp > 0)
p = &(*p)->rb_right;
else
- BUG();
+ return -EEXIST;
}
si = calloc(1, sizeof(*si));
if (!si)
- return ENOMEM;
+ return -ENOMEM;
si->i_ino = ino;
si->i_subvol = subvol;
else if (cmp > 0)
n = n->rb_right;
else
- return EEXIST;
+ return -EEXIST;
}
return 0;
}
}
}
-const char * const cmd_filesystem_du_usage[] = {
- "btrfs filesystem du [options] <path> [<path>..]",
- "Summarize disk usage of each file.",
- "-h|--human-readable",
- " human friendly numbers, base 1024 (default)",
- "-s display only a total for each argument",
- NULL
-};
-
/*
* Inline extents are skipped because they do not take data space,
* delalloc and unknown are skipped because we do not know how much
*/
#define SKIP_FLAGS (FIEMAP_EXTENT_UNKNOWN|FIEMAP_EXTENT_DELALLOC|FIEMAP_EXTENT_DATA_INLINE)
static int du_calc_file_space(int fd, struct rb_root *shared_extents,
- uint64_t *ret_total, uint64_t *ret_shared)
+ u64 *ret_total, u64 *ret_shared)
{
char buf[16384];
struct fiemap *fiemap = (struct fiemap *)buf;
fiemap->fm_extent_count = count;
rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
if (rc < 0) {
- ret = errno;
+ ret = -errno;
goto out;
}
if (flags & SKIP_FLAGS)
continue;
+ if (ext_len == 0) {
+ warning("extent %llu has length 0, skipping",
+ (unsigned long long)fm_ext[i].fe_physical);
+ continue;
+ }
+
file_total += ext_len;
if (flags & FIEMAP_EXTENT_SHARED) {
file_shared += ext_len;
}
struct du_dir_ctxt {
- uint64_t bytes_total;
- uint64_t bytes_shared;
+ u64 bytes_total;
+ u64 bytes_shared;
DIR *dirstream;
struct rb_root shared_extents;
};
#define INIT_DU_DIR_CTXT (struct du_dir_ctxt) { 0ULL, 0ULL, NULL, RB_ROOT }
static int du_add_file(const char *filename, int dirfd,
- struct rb_root *shared_extents, uint64_t *ret_total,
- uint64_t *ret_shared, int top_level);
+ struct rb_root *shared_extents, u64 *ret_total,
+ u64 *ret_shared, int top_level);
static int du_walk_dir(struct du_dir_ctxt *ctxt, struct rb_root *shared_extents)
{
ret = 0;
do {
- uint64_t tot, shr;
+ u64 tot, shr;
errno = 0;
entry = readdir(dirstream);
dirfd(dirstream),
shared_extents, &tot, &shr,
0);
- if (ret)
+ if (ret == -ENOTTY) {
+ ret = 0;
+ continue;
+ } else if (ret) {
+ fprintf(stderr,
+ "failed to walk dir/file: %s :%s\n",
+ entry->d_name, strerror(-ret));
break;
+ }
ctxt->bytes_total += tot;
ctxt->bytes_shared += shr;
}
static int du_add_file(const char *filename, int dirfd,
- struct rb_root *shared_extents, uint64_t *ret_total,
- uint64_t *ret_shared, int top_level)
+ struct rb_root *shared_extents, u64 *ret_total,
+ u64 *ret_shared, int top_level)
{
int ret, len = strlen(filename);
char *pathtmp;
struct stat st;
struct du_dir_ctxt dir = INIT_DU_DIR_CTXT;
int is_dir = 0;
- uint64_t file_total = 0;
- uint64_t file_shared = 0;
+ u64 file_total = 0;
+ u64 file_shared = 0;
u64 dir_set_shared = 0;
- u64 subvol;
int fd;
DIR *dirstream = NULL;
ret = fstatat(dirfd, filename, &st, 0);
- if (ret) {
- ret = errno;
- return ret;
- }
+ if (ret)
+ return -errno;
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
return 0;
if (len > (path_max - pathp)) {
- fprintf(stderr, "ERROR: Path max exceeded: %s %s\n", path,
- filename);
- return ENAMETOOLONG;
+ error("path too long: %s %s", path, filename);
+ return -ENAMETOOLONG;
}
pathtmp = pathp;
ret = sprintf(pathp, "/%s", filename);
pathp += ret;
- fd = open_file_or_dir(path, &dirstream);
+ fd = open_file_or_dir3(path, &dirstream, O_RDONLY);
if (fd < 0) {
- ret = fd;
+ ret = -errno;
goto out;
}
- ret = lookup_ino_rootid(fd, &subvol);
- if (ret)
- goto out_close;
+ /*
+ * If st.st_ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID ==2, there is no any
+ * related tree
+ */
+ if (st.st_ino != BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
+ u64 subvol;
+
+ ret = lookup_path_rootid(fd, &subvol);
+ if (ret)
+ goto out_close;
- if (inode_seen(st.st_ino, subvol))
- goto out_close;
+ if (inode_seen(st.st_ino, subvol))
+ goto out_close;
- ret = mark_inode_seen(st.st_ino, subvol);
- if (ret)
- goto out_close;
+ ret = mark_inode_seen(st.st_ino, subvol);
+ if (ret)
+ goto out_close;
+ }
if (S_ISREG(st.st_mode)) {
ret = du_calc_file_space(fd, shared_extents, &file_total,
if (is_dir)
set_shared = dir_set_shared;
- printf("%s\t%s\t%s\t%s\n",
+ printf("%10s %10s %10s %s\n",
pretty_size_mode(file_total, unit_mode),
pretty_size_mode(excl, unit_mode),
pretty_size_mode(set_shared, unit_mode),
path);
} else {
- printf("%s\t%s\t\t\t%s\n",
+ printf("%10s %10s %10s %s\n",
pretty_size_mode(file_total, unit_mode),
- pretty_size_mode(excl, unit_mode), path);
+ pretty_size_mode(excl, unit_mode),
+ "-", path);
}
}
return ret;
}
+const char * const cmd_filesystem_du_usage[] = {
+ "btrfs filesystem du [options] <path> [<path>..]",
+ "Summarize disk usage of each file.",
+ "-s|--summarize display only a total for each argument",
+ HELPINFO_UNITS_LONG,
+ NULL
+};
+
int cmd_filesystem_du(int argc, char **argv)
{
- int ret = 0, error = 0;
+ int ret = 0, err = 0;
int i;
+ u32 kernel_version;
+
+ unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
- optind = 1;
while (1) {
- int long_index;
static const struct option long_options[] = {
{ "summarize", no_argument, NULL, 's'},
- { "human-readable", no_argument, NULL, 'h'},
{ NULL, 0, NULL, 0 }
};
- int c = getopt_long(argc, argv, "sh", long_options,
- &long_index);
+ int c = getopt_long(argc, argv, "s", long_options, NULL);
if (c < 0)
break;
switch (c) {
- case 'h':
- unit_mode = UNITS_HUMAN;
- break;
case 's':
summarize = 1;
break;
if (check_argc_min(argc - optind, 1))
usage(cmd_filesystem_du_usage);
- printf("total\texclusive\tset shared\tfilename\n");
+ kernel_version = get_running_kernel_version();
+
+ if (kernel_version < KERNEL_VERSION(2,6,33)) {
+ warning(
+"old kernel version detected, shared space will be reported as exclusive\n"
+"due to missing support for FIEMAP_EXTENT_SHARED flag");
+ }
+
+ printf("%10s %10s %10s %s\n", "Total", "Exclusive", "Set shared",
+ "Filename");
for (i = optind; i < argc; i++) {
ret = du_add_file(argv[i], AT_FDCWD, NULL, NULL, NULL, 1);
if (ret) {
- fprintf(stderr, "ERROR: can't check space of '%s': %s\n",
- argv[i], strerror(ret));
- error = 1;
+ error("cannot check space of '%s': %s", argv[i],
+ strerror(-ret));
+ err = 1;
}
/* reset hard-link detection for each argument */
clear_seen_inodes();
}
- return error;
+ return err;
}