/usr.bin/du/du.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 547 lines · 431 code · 58 blank · 58 comment · 105 complexity · 03f4a0dc170c638e724e3e6d03c96e34 MD5 · raw file

  1. /*
  2. * Copyright (c) 1989, 1993, 1994
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * This code is derived from software contributed to Berkeley by
  6. * Chris Newcomb.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 4. Neither the name of the University nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. */
  32. #ifndef lint
  33. static const char copyright[] =
  34. "@(#) Copyright (c) 1989, 1993, 1994\n\
  35. The Regents of the University of California. All rights reserved.\n";
  36. #endif /* not lint */
  37. #ifndef lint
  38. #if 0
  39. static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95";
  40. #endif
  41. #endif /* not lint */
  42. #include <sys/cdefs.h>
  43. __FBSDID("$FreeBSD$");
  44. #include <sys/param.h>
  45. #include <sys/queue.h>
  46. #include <sys/stat.h>
  47. #include <err.h>
  48. #include <errno.h>
  49. #include <fnmatch.h>
  50. #include <fts.h>
  51. #include <libutil.h>
  52. #include <locale.h>
  53. #include <stdint.h>
  54. #include <stdio.h>
  55. #include <stdlib.h>
  56. #include <string.h>
  57. #include <sysexits.h>
  58. #include <unistd.h>
  59. static SLIST_HEAD(ignhead, ignentry) ignores;
  60. struct ignentry {
  61. char *mask;
  62. SLIST_ENTRY(ignentry) next;
  63. };
  64. static int linkchk(FTSENT *);
  65. static void usage(void);
  66. static void prthumanval(int64_t);
  67. static void ignoreadd(const char *);
  68. static void ignoreclean(void);
  69. static int ignorep(FTSENT *);
  70. static void siginfo(int __unused);
  71. static int nodumpflag = 0;
  72. static int Aflag;
  73. static long blocksize, cblocksize;
  74. static volatile sig_atomic_t info;
  75. int
  76. main(int argc, char *argv[])
  77. {
  78. FTS *fts;
  79. FTSENT *p;
  80. off_t savednumber, curblocks;
  81. off_t threshold, threshold_sign;
  82. int ftsoptions;
  83. int depth;
  84. int Hflag, Lflag, aflag, sflag, dflag, cflag;
  85. int hflag, lflag, ch, notused, rval;
  86. char **save;
  87. static char dot[] = ".";
  88. setlocale(LC_ALL, "");
  89. Hflag = Lflag = aflag = sflag = dflag = cflag = hflag =
  90. lflag = Aflag = 0;
  91. save = argv;
  92. ftsoptions = FTS_PHYSICAL;
  93. savednumber = 0;
  94. threshold = 0;
  95. threshold_sign = 1;
  96. cblocksize = DEV_BSIZE;
  97. blocksize = 0;
  98. depth = INT_MAX;
  99. SLIST_INIT(&ignores);
  100. while ((ch = getopt(argc, argv, "AB:HI:LPasd:cghklmnrt:x")) != -1)
  101. switch (ch) {
  102. case 'A':
  103. Aflag = 1;
  104. break;
  105. case 'B':
  106. errno = 0;
  107. cblocksize = atoi(optarg);
  108. if (errno == ERANGE || cblocksize <= 0) {
  109. warnx("invalid argument to option B: %s",
  110. optarg);
  111. usage();
  112. }
  113. break;
  114. case 'H':
  115. Hflag = 1;
  116. Lflag = 0;
  117. break;
  118. case 'I':
  119. ignoreadd(optarg);
  120. break;
  121. case 'L':
  122. Lflag = 1;
  123. Hflag = 0;
  124. break;
  125. case 'P':
  126. Hflag = Lflag = 0;
  127. break;
  128. case 'a':
  129. aflag = 1;
  130. break;
  131. case 's':
  132. sflag = 1;
  133. break;
  134. case 'd':
  135. dflag = 1;
  136. errno = 0;
  137. depth = atoi(optarg);
  138. if (errno == ERANGE || depth < 0) {
  139. warnx("invalid argument to option d: %s",
  140. optarg);
  141. usage();
  142. }
  143. break;
  144. case 'c':
  145. cflag = 1;
  146. break;
  147. case 'g':
  148. hflag = 0;
  149. blocksize = 1073741824;
  150. break;
  151. case 'h':
  152. hflag = 1;
  153. break;
  154. case 'k':
  155. hflag = 0;
  156. blocksize = 1024;
  157. break;
  158. case 'l':
  159. lflag = 1;
  160. break;
  161. case 'm':
  162. hflag = 0;
  163. blocksize = 1048576;
  164. break;
  165. case 'n':
  166. nodumpflag = 1;
  167. break;
  168. case 'r': /* Compatibility. */
  169. break;
  170. case 't' :
  171. if (expand_number(optarg, &threshold) != 0 ||
  172. threshold == 0) {
  173. warnx("invalid threshold: %s", optarg);
  174. usage();
  175. } else if (threshold < 0)
  176. threshold_sign = -1;
  177. break;
  178. case 'x':
  179. ftsoptions |= FTS_XDEV;
  180. break;
  181. case '?':
  182. default:
  183. usage();
  184. /* NOTREACHED */
  185. }
  186. argc -= optind;
  187. argv += optind;
  188. /*
  189. * XXX
  190. * Because of the way that fts(3) works, logical walks will not count
  191. * the blocks actually used by symbolic links. We rationalize this by
  192. * noting that users computing logical sizes are likely to do logical
  193. * copies, so not counting the links is correct. The real reason is
  194. * that we'd have to re-implement the kernel's symbolic link traversing
  195. * algorithm to get this right. If, for example, you have relative
  196. * symbolic links referencing other relative symbolic links, it gets
  197. * very nasty, very fast. The bottom line is that it's documented in
  198. * the man page, so it's a feature.
  199. */
  200. if (Hflag)
  201. ftsoptions |= FTS_COMFOLLOW;
  202. if (Lflag) {
  203. ftsoptions &= ~FTS_PHYSICAL;
  204. ftsoptions |= FTS_LOGICAL;
  205. }
  206. if (!Aflag && (cblocksize % DEV_BSIZE) != 0)
  207. cblocksize = howmany(cblocksize, DEV_BSIZE) * DEV_BSIZE;
  208. if (aflag + dflag + sflag > 1)
  209. usage();
  210. if (sflag)
  211. depth = 0;
  212. if (!*argv) {
  213. argv = save;
  214. argv[0] = dot;
  215. argv[1] = NULL;
  216. }
  217. if (blocksize == 0)
  218. (void)getbsize(&notused, &blocksize);
  219. if (!Aflag) {
  220. cblocksize /= DEV_BSIZE;
  221. blocksize /= DEV_BSIZE;
  222. }
  223. if (threshold != 0)
  224. threshold = howmany(threshold / DEV_BSIZE * cblocksize,
  225. blocksize);
  226. rval = 0;
  227. (void)signal(SIGINFO, siginfo);
  228. if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
  229. err(1, "fts_open");
  230. while ((p = fts_read(fts)) != NULL) {
  231. switch (p->fts_info) {
  232. case FTS_D: /* Ignore. */
  233. if (ignorep(p))
  234. fts_set(fts, p, FTS_SKIP);
  235. break;
  236. case FTS_DP:
  237. if (ignorep(p))
  238. break;
  239. curblocks = Aflag ?
  240. howmany(p->fts_statp->st_size, cblocksize) :
  241. howmany(p->fts_statp->st_blocks, cblocksize);
  242. p->fts_parent->fts_bignum += p->fts_bignum +=
  243. curblocks;
  244. if (p->fts_level <= depth && threshold <=
  245. threshold_sign * howmany(p->fts_bignum *
  246. cblocksize, blocksize)) {
  247. if (hflag) {
  248. prthumanval(p->fts_bignum);
  249. (void)printf("\t%s\n", p->fts_path);
  250. } else {
  251. (void)printf("%jd\t%s\n",
  252. (intmax_t)howmany(p->fts_bignum *
  253. cblocksize, blocksize),
  254. p->fts_path);
  255. }
  256. }
  257. if (info) {
  258. info = 0;
  259. (void)printf("\t%s\n", p->fts_path);
  260. }
  261. break;
  262. case FTS_DC: /* Ignore. */
  263. break;
  264. case FTS_DNR: /* Warn, continue. */
  265. case FTS_ERR:
  266. case FTS_NS:
  267. warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
  268. rval = 1;
  269. break;
  270. default:
  271. if (ignorep(p))
  272. break;
  273. if (lflag == 0 && p->fts_statp->st_nlink > 1 &&
  274. linkchk(p))
  275. break;
  276. curblocks = Aflag ?
  277. howmany(p->fts_statp->st_size, cblocksize) :
  278. howmany(p->fts_statp->st_blocks, cblocksize);
  279. if (aflag || p->fts_level == 0) {
  280. if (hflag) {
  281. prthumanval(curblocks);
  282. (void)printf("\t%s\n", p->fts_path);
  283. } else {
  284. (void)printf("%jd\t%s\n",
  285. (intmax_t)howmany(curblocks *
  286. cblocksize, blocksize),
  287. p->fts_path);
  288. }
  289. }
  290. p->fts_parent->fts_bignum += curblocks;
  291. }
  292. savednumber = p->fts_parent->fts_bignum;
  293. }
  294. if (errno)
  295. err(1, "fts_read");
  296. if (cflag) {
  297. if (hflag) {
  298. prthumanval(savednumber);
  299. (void)printf("\ttotal\n");
  300. } else {
  301. (void)printf("%jd\ttotal\n", (intmax_t)howmany(
  302. savednumber * cblocksize, blocksize));
  303. }
  304. }
  305. ignoreclean();
  306. exit(rval);
  307. }
  308. static int
  309. linkchk(FTSENT *p)
  310. {
  311. struct links_entry {
  312. struct links_entry *next;
  313. struct links_entry *previous;
  314. int links;
  315. dev_t dev;
  316. ino_t ino;
  317. };
  318. static const size_t links_hash_initial_size = 8192;
  319. static struct links_entry **buckets;
  320. static struct links_entry *free_list;
  321. static size_t number_buckets;
  322. static unsigned long number_entries;
  323. static char stop_allocating;
  324. struct links_entry *le, **new_buckets;
  325. struct stat *st;
  326. size_t i, new_size;
  327. int hash;
  328. st = p->fts_statp;
  329. /* If necessary, initialize the hash table. */
  330. if (buckets == NULL) {
  331. number_buckets = links_hash_initial_size;
  332. buckets = malloc(number_buckets * sizeof(buckets[0]));
  333. if (buckets == NULL)
  334. errx(1, "No memory for hardlink detection");
  335. for (i = 0; i < number_buckets; i++)
  336. buckets[i] = NULL;
  337. }
  338. /* If the hash table is getting too full, enlarge it. */
  339. if (number_entries > number_buckets * 10 && !stop_allocating) {
  340. new_size = number_buckets * 2;
  341. new_buckets = malloc(new_size * sizeof(struct links_entry *));
  342. /* Try releasing the free list to see if that helps. */
  343. if (new_buckets == NULL && free_list != NULL) {
  344. while (free_list != NULL) {
  345. le = free_list;
  346. free_list = le->next;
  347. free(le);
  348. }
  349. new_buckets = malloc(new_size *
  350. sizeof(new_buckets[0]));
  351. }
  352. if (new_buckets == NULL) {
  353. stop_allocating = 1;
  354. warnx("No more memory for tracking hard links");
  355. } else {
  356. memset(new_buckets, 0,
  357. new_size * sizeof(struct links_entry *));
  358. for (i = 0; i < number_buckets; i++) {
  359. while (buckets[i] != NULL) {
  360. /* Remove entry from old bucket. */
  361. le = buckets[i];
  362. buckets[i] = le->next;
  363. /* Add entry to new bucket. */
  364. hash = (le->dev ^ le->ino) % new_size;
  365. if (new_buckets[hash] != NULL)
  366. new_buckets[hash]->previous =
  367. le;
  368. le->next = new_buckets[hash];
  369. le->previous = NULL;
  370. new_buckets[hash] = le;
  371. }
  372. }
  373. free(buckets);
  374. buckets = new_buckets;
  375. number_buckets = new_size;
  376. }
  377. }
  378. /* Try to locate this entry in the hash table. */
  379. hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
  380. for (le = buckets[hash]; le != NULL; le = le->next) {
  381. if (le->dev == st->st_dev && le->ino == st->st_ino) {
  382. /*
  383. * Save memory by releasing an entry when we've seen
  384. * all of it's links.
  385. */
  386. if (--le->links <= 0) {
  387. if (le->previous != NULL)
  388. le->previous->next = le->next;
  389. if (le->next != NULL)
  390. le->next->previous = le->previous;
  391. if (buckets[hash] == le)
  392. buckets[hash] = le->next;
  393. number_entries--;
  394. /* Recycle this node through the free list */
  395. if (stop_allocating) {
  396. free(le);
  397. } else {
  398. le->next = free_list;
  399. free_list = le;
  400. }
  401. }
  402. return (1);
  403. }
  404. }
  405. if (stop_allocating)
  406. return (0);
  407. /* Add this entry to the links cache. */
  408. if (free_list != NULL) {
  409. /* Pull a node from the free list if we can. */
  410. le = free_list;
  411. free_list = le->next;
  412. } else
  413. /* Malloc one if we have to. */
  414. le = malloc(sizeof(struct links_entry));
  415. if (le == NULL) {
  416. stop_allocating = 1;
  417. warnx("No more memory for tracking hard links");
  418. return (0);
  419. }
  420. le->dev = st->st_dev;
  421. le->ino = st->st_ino;
  422. le->links = st->st_nlink - 1;
  423. number_entries++;
  424. le->next = buckets[hash];
  425. le->previous = NULL;
  426. if (buckets[hash] != NULL)
  427. buckets[hash]->previous = le;
  428. buckets[hash] = le;
  429. return (0);
  430. }
  431. static void
  432. prthumanval(int64_t bytes)
  433. {
  434. char buf[5];
  435. bytes *= cblocksize;
  436. if (!Aflag)
  437. bytes *= DEV_BSIZE;
  438. humanize_number(buf, sizeof(buf), bytes, "", HN_AUTOSCALE,
  439. HN_B | HN_NOSPACE | HN_DECIMAL);
  440. (void)printf("%4s", buf);
  441. }
  442. static void
  443. usage(void)
  444. {
  445. (void)fprintf(stderr,
  446. "usage: du [-Aclnx] [-H | -L | -P] [-g | -h | -k | -m] "
  447. "[-a | -s | -d depth] [-B blocksize] [-I mask] "
  448. "[-t threshold] [file ...]\n");
  449. exit(EX_USAGE);
  450. }
  451. static void
  452. ignoreadd(const char *mask)
  453. {
  454. struct ignentry *ign;
  455. ign = calloc(1, sizeof(*ign));
  456. if (ign == NULL)
  457. errx(1, "cannot allocate memory");
  458. ign->mask = strdup(mask);
  459. if (ign->mask == NULL)
  460. errx(1, "cannot allocate memory");
  461. SLIST_INSERT_HEAD(&ignores, ign, next);
  462. }
  463. static void
  464. ignoreclean(void)
  465. {
  466. struct ignentry *ign;
  467. while (!SLIST_EMPTY(&ignores)) {
  468. ign = SLIST_FIRST(&ignores);
  469. SLIST_REMOVE_HEAD(&ignores, next);
  470. free(ign->mask);
  471. free(ign);
  472. }
  473. }
  474. static int
  475. ignorep(FTSENT *ent)
  476. {
  477. struct ignentry *ign;
  478. if (nodumpflag && (ent->fts_statp->st_flags & UF_NODUMP))
  479. return 1;
  480. SLIST_FOREACH(ign, &ignores, next)
  481. if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
  482. return 1;
  483. return 0;
  484. }
  485. static void
  486. siginfo(int sig __unused)
  487. {
  488. info = 1;
  489. }