blob: 5c7dbccd932124833c5932a87d5794f711fcae8e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002 Silicon Graphics, Inc.
* All Rights Reserved.
*/
/*
* Estimate space of an XFS filesystem
*
* XXX: assumes dirv1 format.
*/
#include "libxfs.h"
#include <sys/stat.h>
#include <ftw.h>
static unsigned long long
cvtnum(char *s)
{
unsigned long long i;
char *sp;
i = strtoll(s, &sp, 0);
if (i == 0 && sp == s)
return 0LL;
if (*sp == '\0')
return i;
if (*sp =='k' && sp[1] == '\0')
return 1024LL * i;
if (*sp =='m' && sp[1] == '\0')
return 1024LL * 1024LL * i;
if (*sp =='g' && sp[1] == '\0')
return 1024LL * 1024LL * 1024LL * i;
return 0LL;
}
int ffn(const char *, const struct stat *, int, struct FTW *);
#define BLOCKSIZE 4096
#define INODESIZE 256
#define PERDIRENTRY \
(sizeof(xfs_dir2_leaf_entry_t) + sizeof(xfs_dir2_data_entry_t))
#define LOGSIZE 1000
#define FBLOCKS(n) ((n)/blocksize)
static unsigned long long dirsize=0; /* bytes */
static unsigned long long logsize=LOGSIZE*BLOCKSIZE; /* bytes */
static unsigned long long fullblocks=0; /* FS blocks */
static unsigned long long isize=0; /* inodes bytes */
static unsigned long long blocksize=BLOCKSIZE;
static unsigned long long nslinks=0; /* number of symbolic links */
static unsigned long long nfiles=0; /* number of regular files */
static unsigned long long ndirs=0; /* number of directories */
static unsigned long long nspecial=0; /* number of special files */
static unsigned long long verbose=0; /* verbose mode TRUE/FALSE */
static int __debug = 0;
static int ilog = 0;
static int elog = 0;
static void
usage(char *progname)
{
fprintf(stderr,
_("Usage: %s [opts] directory [directory ...]\n"
"\t-b blocksize (fundamental filesystem blocksize)\n"
"\t-i logsize (internal log size)\n"
"\t-e logsize (external log size)\n"
"\t-v prints more verbose messages\n"
"\t-V prints version and exits\n"
"\t-h prints this usage message\n\n"
"Note:\tblocksize may have 'k' appended to indicate x1024\n"
"\tlogsize may also have 'm' appended to indicate (1024 x 1024)\n"),
basename(progname));
exit(1);
}
int
main(int argc, char **argv)
{
unsigned long long est;
extern int optind;
extern char *optarg;
char dname[40];
int c;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
while ((c = getopt (argc, argv, "b:hdve:i:V")) != EOF) {
switch (c) {
case 'b':
blocksize=cvtnum(optarg);
if (blocksize <= 0LL) {
fprintf(stderr, _("blocksize %llu too small\n"),
blocksize);
usage(argv[0]);
}
else if (blocksize > 64LL * 1024LL) {
fprintf(stderr, _("blocksize %llu too large\n"),
blocksize);
usage(argv[0]);
}
break;
case 'i':
if (elog) {
fprintf(stderr, _("already have external log "
"noted, can't have both\n"));
usage(argv[0]);
}
logsize=cvtnum(optarg);
ilog++;
break;
case 'e':
if (ilog) {
fprintf(stderr, _("already have internal log "
"noted, can't have both\n"));
usage(argv[0]);
}
logsize=cvtnum(optarg);
elog++;
break;
case 'v':
verbose = 1;
break;
case 'd':
__debug++;
break;
case 'V':
printf(_("%s version %s\n"), basename(argv[0]), VERSION);
exit(0);
default:
case 'h':
usage(argv[0]);
}
}
if (optind == argc)
usage(argv[0]);
if (!elog && !ilog) {
ilog=1;
logsize=LOGSIZE * blocksize;
}
if (verbose)
printf(_("directory bsize blocks megabytes logsize\n"));
for ( ; optind < argc; optind++) {
dirsize=0LL; /* bytes */
fullblocks=0LL; /* FS blocks */
isize=0LL; /* inodes bytes */
nslinks=0LL; /* number of symbolic links */
nfiles=0LL; /* number of regular files */
ndirs=0LL; /* number of directories */
nspecial=0LL; /* number of special files */
nftw(argv[optind], ffn, 40, FTW_PHYS | FTW_MOUNT);
if (__debug) {
printf(_("dirsize=%llu\n"), dirsize);
printf(_("fullblocks=%llu\n"), fullblocks);
printf(_("isize=%llu\n"), isize);
printf(_("%llu regular files\n"), nfiles);
printf(_("%llu symbolic links\n"), nslinks);
printf(_("%llu directories\n"), ndirs);
printf(_("%llu special files\n"), nspecial);
}
est = FBLOCKS(isize) + 8 /* blocks for inodes */
+ FBLOCKS(dirsize) + 1 /* blocks for directories */
+ fullblocks /* blocks for file contents */
+ (8 * 16) /* fudge for overhead blks (per ag) */
+ FBLOCKS(isize / INODESIZE); /* 1 byte/inode for map */
if (ilog)
est += (logsize / blocksize);
if (!verbose) {
printf(_("%s will take about %.1f megabytes\n"),
argv[optind],
(double)est*(double)blocksize/(1024.0*1024.0));
} else {
/* avoid running over 39 characters in field */
strncpy(dname, argv[optind], 40);
dname[39] = '\0';
printf(_("%-39s %5llu %8llu %10.1fMB %10llu\n"),
dname, blocksize, est,
(double)est*(double)blocksize/(1024.0*1024.0), logsize);
}
if (!verbose && elog) {
printf(_("\twith the external log using %llu blocks "),
logsize/blocksize);
printf(_("or about %.1f megabytes\n"),
(double)logsize/(1024.0*1024.0));
}
}
return 0;
}
int
ffn(const char *path, const struct stat *stb, int flags, struct FTW *f)
{
/* cases are in most-encountered to least-encountered order */
dirsize+=PERDIRENTRY+strlen(path);
isize+=INODESIZE;
switch (S_IFMT & stb->st_mode) {
case S_IFREG: /* regular files */
fullblocks+=FBLOCKS(stb->st_blocks * 512 + blocksize-1);
if (stb->st_blocks * 512 < stb->st_size)
fullblocks++; /* add one bmap block here */
nfiles++;
break;
case S_IFLNK: /* symbolic links */
if (stb->st_size >= (INODESIZE - (sizeof(struct xfs_dinode)+4)))
fullblocks+=FBLOCKS(stb->st_size + blocksize-1);
nslinks++;
break;
case S_IFDIR: /* directories */
dirsize+=blocksize; /* fudge upwards */
if (stb->st_size >= blocksize)
dirsize+=blocksize;
ndirs++;
break;
case S_IFIFO: /* named pipes */
case S_IFCHR: /* Character Special device */
case S_IFBLK: /* Block Special device */
case S_IFSOCK: /* socket */
nspecial++;
break;
}
return 0;
}