| // 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(xfs_dinode_t)+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; | 
 | } |