Print this page
7967 Want apparent size option for du(1)
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Peter Tribble <peter.tribble@gmail.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/du/du.c
+++ new/usr/src/cmd/du/du.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
↓ open down ↓ |
11 lines elided |
↑ open up ↑ |
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 + * Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved.
22 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 24 * Use is subject to license terms.
24 25 */
25 26
26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 28 /* All Rights Reserved */
28 29
29 -#pragma ident "%Z%%M% %I% %E% SMI"
30 -
31 30 /*
32 31 * du -- summarize disk usage
33 - * du [-dorx] [-a|-s] [-h|-k|-m] [-H|-L] [file...]
32 + * du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] [file...]
34 33 */
35 34
36 35 #include <sys/types.h>
37 36 #include <sys/stat.h>
38 37 #include <sys/avl.h>
39 38 #include <fcntl.h>
40 39 #include <dirent.h>
41 40 #include <limits.h>
42 41 #include <stdio.h>
43 42 #include <stdlib.h>
44 43 #include <string.h>
45 44 #include <unistd.h>
46 45 #include <locale.h>
47 46 #include <libcmdutils.h>
↓ open down ↓ |
4 lines elided |
↑ open up ↑ |
48 47
49 48
50 49 static int aflg = 0;
51 50 static int rflg = 0;
52 51 static int sflg = 0;
53 52 static int kflg = 0;
54 53 static int mflg = 0;
55 54 static int oflg = 0;
56 55 static int dflg = 0;
57 56 static int hflg = 0;
57 +static int Aflg = 0;
58 58 static int Hflg = 0;
59 59 static int Lflg = 0;
60 60 static int cmdarg = 0; /* Command line argument */
61 61 static char *dot = ".";
62 62 static int level = 0; /* Level of recursion */
63 63
64 64 static char *base;
65 65 static char *name;
66 66 static size_t base_len = PATH_MAX + 1; /* # of chars for base */
67 67 static size_t name_len = PATH_MAX + 1; /* # of chars for name */
68 68
69 69 #define NUMBER_WIDTH 64
70 70 typedef char numbuf_t[NUMBER_WIDTH];
71 71
72 72 /*
73 - * Output formats. Solaris uses a tab as separator, XPG4 a space.
73 + * Output formats. illumos uses a tab as separator, XPG4 a space.
74 74 */
75 75 #ifdef XPG4
76 76 #define FORMAT1 "%s %s\n"
77 77 #define FORMAT2 "%lld %s\n"
78 78 #else
79 79 #define FORMAT1 "%s\t%s\n"
80 80 #define FORMAT2 "%lld\t%s\n"
81 81 #endif
82 82
83 83 /*
84 84 * convert DEV_BSIZE blocks to K blocks
85 85 */
86 86 #define DEV_BSIZE 512
87 87 #define DEV_KSHIFT 1
88 88 #define DEV_MSHIFT 11
89 89 #define kb(n) (((u_longlong_t)(n)) >> DEV_KSHIFT)
90 90 #define mb(n) (((u_longlong_t)(n)) >> DEV_MSHIFT)
91 91
92 92 long wait();
93 93 static u_longlong_t descend(char *curname, int curfd, int *retcode,
94 94 dev_t device);
95 95 static void printsize(blkcnt_t blocks, char *path);
96 96 static void exitdu(int exitcode);
97 97
98 98 static avl_tree_t *tree = NULL;
99 99
100 100 int
101 101 main(int argc, char **argv)
102 102 {
103 103 blkcnt_t blocks = 0;
104 104 int c;
105 105 extern int optind;
106 106 char *np;
107 107 pid_t pid, wpid;
108 108 int status, retcode = 0;
109 109 setbuf(stderr, NULL);
↓ open down ↓ |
26 lines elided |
↑ open up ↑ |
110 110 (void) setlocale(LC_ALL, "");
111 111 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
112 112 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
113 113 #endif
114 114 (void) textdomain(TEXT_DOMAIN);
115 115
116 116 #ifdef XPG4
117 117 rflg++; /* "-r" is not an option but ON always */
118 118 #endif
119 119
120 - while ((c = getopt(argc, argv, "adhHkLmorsx")) != EOF)
120 + while ((c = getopt(argc, argv, "aAdhHkLmorsx")) != EOF)
121 121 switch (c) {
122 122
123 123 case 'a':
124 124 aflg++;
125 125 continue;
126 126
127 127 case 'h':
128 128 hflg++;
129 129 kflg = 0;
130 130 mflg = 0;
131 131 continue;
132 132
133 133 case 'r':
134 134 rflg++;
135 135 continue;
136 136
137 137 case 's':
138 138 sflg++;
139 139 continue;
140 140
141 141 case 'k':
142 142 kflg++;
143 143 hflg = 0;
144 144 mflg = 0;
145 145 continue;
146 146
147 147 case 'm':
148 148 mflg++;
149 149 hflg = 0;
150 150 kflg = 0;
151 151 continue;
152 152
153 153 case 'o':
154 154 oflg++;
↓ open down ↓ |
24 lines elided |
↑ open up ↑ |
155 155 continue;
156 156
157 157 case 'd':
158 158 dflg++;
159 159 continue;
160 160
161 161 case 'x':
162 162 dflg++;
163 163 continue;
164 164
165 + case 'A':
166 + Aflg++;
167 + continue;
168 +
165 169 case 'H':
166 170 Hflg++;
167 171 /* -H and -L are mutually exclusive */
168 172 Lflg = 0;
169 173 cmdarg++;
170 174 continue;
171 175
172 176 case 'L':
173 177 Lflg++;
174 178 /* -H and -L are mutually exclusive */
175 179 Hflg = 0;
176 180 cmdarg = 0;
177 181 continue;
178 182 case '?':
179 183 (void) fprintf(stderr, gettext(
180 - "usage: du [-dorx] [-a|-s] [-h|-k|-m] [-H|-L] "
184 + "usage: du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] "
181 185 "[file...]\n"));
182 186 exit(2);
183 187 }
184 188 if (optind == argc) {
185 189 argv = ˙
186 190 argc = 1;
187 191 optind = 0;
188 192 }
189 193
190 194 /* "-o" and "-s" don't make any sense together. */
191 195 if (oflg && sflg)
192 196 oflg = 0;
193 197
194 198 if ((base = (char *)calloc(base_len, sizeof (char))) == NULL) {
195 199 perror("du");
196 200 exit(1);
197 201 }
198 202 if ((name = (char *)calloc(name_len, sizeof (char))) == NULL) {
199 203 perror("du");
200 204 free(base);
201 205 exit(1);
202 206 }
203 207 do {
204 208 if (optind < argc - 1) {
205 209 pid = fork();
206 210 if (pid == (pid_t)-1) {
207 211 perror(gettext("du: No more processes"));
208 212 exitdu(1);
209 213 }
210 214 if (pid != 0) {
211 215 while ((wpid = wait(&status)) != pid &&
212 216 wpid != (pid_t)-1)
213 217 ;
214 218 if (pid != (pid_t)-1 && status != 0)
215 219 retcode = 1;
216 220 }
217 221 }
218 222 if (optind == argc - 1 || pid == 0) {
219 223 while (base_len < (strlen(argv[optind]) + 1)) {
220 224 base_len = base_len * 2;
221 225 if ((base = (char *)realloc(base, base_len *
222 226 sizeof (char))) == NULL) {
223 227 if (rflg) {
224 228 (void) fprintf(stderr, gettext(
225 229 "du: can't process %s"),
226 230 argv[optind]);
227 231 perror("");
228 232 }
229 233 exitdu(1);
230 234 }
231 235 }
232 236 if (base_len > name_len) {
233 237 name_len = base_len;
234 238 if ((name = (char *)realloc(name, name_len *
235 239 sizeof (char))) == NULL) {
236 240 if (rflg) {
237 241 (void) fprintf(stderr, gettext(
238 242 "du: can't process %s"),
239 243 argv[optind]);
240 244 perror("");
241 245 }
242 246 exitdu(1);
243 247 }
244 248 }
245 249 (void) strcpy(base, argv[optind]);
246 250 (void) strcpy(name, argv[optind]);
247 251 if (np = strrchr(name, '/')) {
248 252 *np++ = '\0';
249 253 if (chdir(*name ? name : "/") < 0) {
250 254 if (rflg) {
251 255 (void) fprintf(stderr, "du: ");
252 256 perror(*name ? name : "/");
253 257 exitdu(1);
254 258 }
255 259 exitdu(0);
256 260 }
257 261 } else
258 262 np = base;
259 263 blocks = descend(*np ? np : ".", 0, &retcode,
260 264 (dev_t)0);
261 265 if (sflg)
262 266 printsize(blocks, base);
263 267 if (optind < argc - 1)
264 268 exitdu(retcode);
265 269 }
266 270 optind++;
267 271 } while (optind < argc);
268 272 exitdu(retcode);
269 273
270 274 return (retcode);
271 275 }
272 276
273 277 /*
274 278 * descend recursively, adding up the allocated blocks.
275 279 * If curname is NULL, curfd is used.
276 280 */
277 281 static u_longlong_t
278 282 descend(char *curname, int curfd, int *retcode, dev_t device)
279 283 {
280 284 static DIR *dirp = NULL;
281 285 char *ebase0, *ebase;
282 286 struct stat stb, stb1;
283 287 int i, j, ret, fd, tmpflg;
284 288 int follow_symlinks;
285 289 blkcnt_t blocks = 0;
286 290 off_t curoff = 0;
287 291 ptrdiff_t offset;
288 292 ptrdiff_t offset0;
289 293 struct dirent *dp;
290 294 char dirbuf[PATH_MAX + 1];
291 295 u_longlong_t retval;
292 296
293 297 ebase0 = ebase = strchr(base, 0);
294 298 if (ebase > base && ebase[-1] == '/')
295 299 ebase--;
296 300 offset = ebase - base;
297 301 offset0 = ebase0 - base;
298 302
299 303 if (curname)
300 304 curfd = AT_FDCWD;
301 305
302 306 /*
303 307 * If neither a -L or a -H was specified, don't follow symlinks.
304 308 * If a -H was specified, don't follow symlinks if the file is
305 309 * not a command line argument.
306 310 */
307 311 follow_symlinks = (Lflg || (Hflg && cmdarg));
308 312 if (follow_symlinks) {
309 313 i = fstatat(curfd, curname, &stb, 0);
310 314 j = fstatat(curfd, curname, &stb1, AT_SYMLINK_NOFOLLOW);
311 315
312 316 /*
313 317 * Make sure any files encountered while traversing the
314 318 * hierarchy are not considered command line arguments.
315 319 */
316 320 if (Hflg) {
317 321 cmdarg = 0;
318 322 }
319 323 } else {
320 324 i = fstatat(curfd, curname, &stb, AT_SYMLINK_NOFOLLOW);
321 325 j = 0;
322 326 }
323 327
324 328 if ((i < 0) || (j < 0)) {
325 329 if (rflg) {
326 330 (void) fprintf(stderr, "du: ");
327 331 perror(base);
328 332 }
329 333
330 334 /*
331 335 * POSIX states that non-zero status codes are only set
332 336 * when an error message is printed out on stderr
333 337 */
334 338 *retcode = (rflg ? 1 : 0);
335 339 *ebase0 = 0;
336 340 return (0);
337 341 }
338 342 if (device) {
339 343 if (dflg && stb.st_dev != device) {
340 344 *ebase0 = 0;
341 345 return (0);
342 346 }
343 347 }
344 348 else
345 349 device = stb.st_dev;
346 350
347 351 /*
348 352 * If following links (-L) we need to keep track of all inodes
349 353 * visited so they are only visited/reported once and cycles
350 354 * are avoided. Otherwise, only keep track of files which are
351 355 * hard links so they only get reported once, and of directories
352 356 * so we don't report a directory and its hierarchy more than
353 357 * once in the special case in which it lies under the
354 358 * hierarchy of a directory which is a hard link.
355 359 * Note: Files with multiple links should only be counted
356 360 * once. Since each inode could possibly be referenced by a
357 361 * symbolic link, we need to keep track of all inodes when -L
358 362 * is specified.
359 363 */
360 364 if (Lflg || ((stb.st_mode & S_IFMT) == S_IFDIR) ||
361 365 (stb.st_nlink > 1)) {
362 366 int rc;
363 367 if ((rc = add_tnode(&tree, stb.st_dev, stb.st_ino)) != 1) {
364 368 if (rc == 0) {
365 369 /*
366 370 * This hierarchy, or file with multiple
367 371 * links, has already been visited/reported.
368 372 */
369 373 return (0);
370 374 } else {
371 375 /*
↓ open down ↓ |
181 lines elided |
↑ open up ↑ |
372 376 * An error occurred while trying to add the
373 377 * node to the tree.
374 378 */
375 379 if (rflg) {
376 380 perror("du");
377 381 }
378 382 exitdu(1);
379 383 }
380 384 }
381 385 }
382 - blocks = stb.st_blocks;
386 + blocks = Aflg ? stb.st_size : stb.st_blocks;
387 +
383 388 /*
384 389 * If there are extended attributes on the current file, add their
385 390 * block usage onto the block count. Note: Since pathconf() always
386 391 * follows symlinks, only test for extended attributes using pathconf()
387 392 * if we are following symlinks or the current file is not a symlink.
388 393 */
389 394 if (curname && (follow_symlinks ||
390 395 ((stb.st_mode & S_IFMT) != S_IFLNK)) &&
391 396 pathconf(curname, _PC_XATTR_EXISTS) == 1) {
392 397 if ((fd = attropen(curname, ".", O_RDONLY)) < 0) {
393 398 if (rflg)
394 399 perror(gettext(
395 400 "du: can't access extended attributes"));
396 401 }
397 402 else
398 403 {
399 404 tmpflg = sflg;
400 405 sflg = 1;
401 406 blocks += descend(NULL, fd, retcode, device);
402 407 sflg = tmpflg;
403 408 }
404 409 }
405 410 if ((stb.st_mode & S_IFMT) != S_IFDIR) {
406 411 /*
407 412 * Don't print twice: if sflg, file will get printed in main().
408 413 * Otherwise, level == 0 means this file is listed on the
409 414 * command line, so print here; aflg means print all files.
410 415 */
411 416 if (sflg == 0 && (aflg || level == 0))
412 417 printsize(blocks, base);
413 418 return (blocks);
414 419 }
415 420 if (dirp != NULL)
416 421 /*
417 422 * Close the parent directory descriptor, we will reopen
418 423 * the directory when we pop up from this level of the
419 424 * recursion.
420 425 */
421 426 (void) closedir(dirp);
422 427 if (curname == NULL)
423 428 dirp = fdopendir(curfd);
424 429 else
425 430 dirp = opendir(curname);
426 431 if (dirp == NULL) {
427 432 if (rflg) {
428 433 (void) fprintf(stderr, "du: ");
429 434 perror(base);
430 435 }
431 436 *retcode = 1;
432 437 *ebase0 = 0;
433 438 return (0);
434 439 }
435 440 level++;
436 441 if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode))) {
437 442 if (getcwd(dirbuf, PATH_MAX) == NULL) {
438 443 if (rflg) {
439 444 (void) fprintf(stderr, "du: ");
440 445 perror(base);
441 446 }
442 447 exitdu(1);
443 448 }
444 449 }
445 450 if ((curname ? (chdir(curname) < 0) : (fchdir(curfd) < 0))) {
446 451 if (rflg) {
447 452 (void) fprintf(stderr, "du: ");
448 453 perror(base);
449 454 }
450 455 *retcode = 1;
451 456 *ebase0 = 0;
452 457 (void) closedir(dirp);
453 458 dirp = NULL;
454 459 level--;
455 460 return (0);
456 461 }
457 462 while (dp = readdir(dirp)) {
458 463 if ((strcmp(dp->d_name, ".") == 0) ||
459 464 (strcmp(dp->d_name, "..") == 0))
460 465 continue;
461 466 /*
462 467 * we're about to append "/" + dp->d_name
463 468 * onto end of base; make sure there's enough
464 469 * space
465 470 */
466 471 while ((offset + strlen(dp->d_name) + 2) > base_len) {
467 472 base_len = base_len * 2;
468 473 if ((base = (char *)realloc(base,
469 474 base_len * sizeof (char))) == NULL) {
470 475 if (rflg) {
471 476 perror("du");
472 477 }
473 478 exitdu(1);
474 479 }
475 480 ebase = base + offset;
476 481 ebase0 = base + offset0;
477 482 }
478 483 /* LINTED - unbounded string specifier */
479 484 (void) sprintf(ebase, "/%s", dp->d_name);
480 485 curoff = telldir(dirp);
481 486 retval = descend(ebase + 1, 0, retcode, device);
482 487 /* base may have been moved via realloc in descend() */
483 488 ebase = base + offset;
484 489 ebase0 = base + offset0;
485 490 *ebase = 0;
486 491 blocks += retval;
487 492 if (dirp == NULL) {
488 493 if ((dirp = opendir(".")) == NULL) {
489 494 if (rflg) {
490 495 (void) fprintf(stderr,
491 496 gettext("du: Can't reopen in "));
492 497 perror(base);
493 498 }
494 499 *retcode = 1;
495 500 level--;
496 501 return (0);
497 502 }
498 503 seekdir(dirp, curoff);
499 504 }
500 505 }
501 506 (void) closedir(dirp);
502 507 level--;
503 508 dirp = NULL;
504 509 if (sflg == 0)
505 510 printsize(blocks, base);
506 511 if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode)))
507 512 ret = chdir(dirbuf);
508 513 else
509 514 ret = chdir("..");
510 515 if (ret < 0) {
511 516 if (rflg) {
512 517 (void) sprintf(strchr(base, '\0'), "/..");
513 518 (void) fprintf(stderr,
514 519 gettext("du: Can't change dir to '..' in "));
515 520 perror(base);
516 521 }
517 522 exitdu(1);
518 523 }
519 524 *ebase0 = 0;
520 525 if (oflg)
521 526 return (0);
522 527 else
523 528 return (blocks);
524 529 }
525 530
526 531 /*
527 532 * Convert an unsigned long long to a string representation and place the
528 533 * result in the caller-supplied buffer.
529 534 * The given number is in units of "unit_from" size,
530 535 * this will first be converted to a number in 1024 or 1000 byte size,
531 536 * depending on the scaling factor.
532 537 * Then the number is scaled down until it is small enough to be in a good
533 538 * human readable format i.e. in the range 0 thru scale-1.
534 539 * If it's smaller than 10 there's room enough to provide one decimal place.
535 540 * The value "(unsigned long long)-1" is a special case and is always
536 541 * converted to "-1".
537 542 * Returns a pointer to the caller-supplied buffer.
538 543 */
539 544 static char *
540 545 number_to_scaled_string(
541 546 numbuf_t buf, /* put the result here */
542 547 unsigned long long number, /* convert this number */
543 548 unsigned long long unit_from, /* number of bytes per input unit */
544 549 unsigned long long scale) /* 1024 (-h) or 1000 (-H) */
545 550 {
546 551 unsigned long long save = 0;
547 552 char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
548 553 char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */
549 554
550 555 if ((long long)number == (long long)-1) {
551 556 (void) strcpy(buf, "-1");
552 557 return (buf);
553 558 }
554 559
555 560 /*
556 561 * Convert number from unit_from to given scale (1024 or 1000)
557 562 * This means multiply number with unit_from and divide by scale.
558 563 * if number is large enough, we first divide and then multiply
559 564 * to avoid an overflow
560 565 * (large enough here means 100 (rather arbitrary value)
561 566 * times scale in order to reduce rounding errors)
562 567 * otherwise, we first multiply and then divide
563 568 * to avoid an underflow
564 569 */
565 570 if (number >= 100L * scale) {
566 571 number = number / scale;
567 572 number = number * unit_from;
568 573 } else {
569 574 number = number * unit_from;
570 575 number = number / scale;
571 576 }
572 577
573 578 /*
574 579 * Now we have number as a count of scale units.
575 580 * Stop scaling when we reached exa bytes, then something is
576 581 * probably wrong with our number.
577 582 */
578 583 while ((number >= scale) && (*uom != 'E')) {
579 584 uom++; /* next unit of measurement */
580 585 save = number;
581 586 number = (number + (scale / 2)) / scale;
582 587 }
583 588
584 589 /* check if we should output a decimal place after the point */
585 590 if (save && ((save / scale) < 10)) {
586 591 /* sprintf() will round for us */
587 592 float fnum = (float)save / scale;
↓ open down ↓ |
195 lines elided |
↑ open up ↑ |
588 593 (void) sprintf(buf, "%4.1f%c", fnum, *uom);
589 594 } else {
590 595 (void) sprintf(buf, "%4llu%c", number, *uom);
591 596 }
592 597 return (buf);
593 598 }
594 599
595 600 static void
596 601 printsize(blkcnt_t blocks, char *path)
597 602 {
603 + u_longlong_t bsize;
604 +
605 + bsize = Aflg ? 1 : DEV_BSIZE;
606 +
598 607 if (hflg) {
599 608 numbuf_t numbuf;
600 609 unsigned long long scale = 1024L;
601 610 (void) printf(FORMAT1,
602 - number_to_scaled_string(numbuf, blocks, DEV_BSIZE, scale),
611 + number_to_scaled_string(numbuf, blocks, bsize, scale),
603 612 path);
604 613 } else if (kflg) {
605 614 (void) printf(FORMAT2, (long long)kb(blocks), path);
606 615 } else if (mflg) {
607 616 (void) printf(FORMAT2, (long long)mb(blocks), path);
608 617 } else {
609 618 (void) printf(FORMAT2, (long long)blocks, path);
610 619 }
611 620 }
612 621
613 622 static void
614 623 exitdu(int exitcode)
615 624 {
616 625 free(base);
617 626 free(name);
618 627 exit(exitcode);
619 628 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX