| // 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; |
| } |