4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved.
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * du -- summarize disk usage
32 * du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] [file...]
35 #include <sys/types.h>
46 #include <libcmdutils.h>
60 static int cmdarg
= 0; /* Command line argument */
61 static char *dot
= ".";
62 static int level
= 0; /* Level of recursion */
66 static size_t base_len
= PATH_MAX
+ 1; /* # of chars for base */
67 static size_t name_len
= PATH_MAX
+ 1; /* # of chars for name */
69 #define NUMBER_WIDTH 64
70 typedef char numbuf_t
[NUMBER_WIDTH
];
73 * Output formats. illumos uses a tab as separator, XPG4 a space.
76 #define FORMAT1 "%s %s\n"
77 #define FORMAT2 "%lld %s\n"
79 #define FORMAT1 "%s\t%s\n"
80 #define FORMAT2 "%lld\t%s\n"
84 * convert DEV_BSIZE blocks to K blocks
89 #define kb(n) (((u_longlong_t)(n)) >> DEV_KSHIFT)
90 #define mb(n) (((u_longlong_t)(n)) >> DEV_MSHIFT)
93 static u_longlong_t
descend(char *curname
, int curfd
, int *retcode
,
95 static void printsize(blkcnt_t blocks
, char *path
);
96 static void exitdu(int exitcode
);
98 static avl_tree_t
*tree
= NULL
;
101 main(int argc
, char **argv
)
108 int status
, retcode
= 0;
109 setbuf(stderr
, NULL
);
110 (void) setlocale(LC_ALL
, "");
111 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
112 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
114 (void) textdomain(TEXT_DOMAIN
);
117 rflg
++; /* "-r" is not an option but ON always */
120 while ((c
= getopt(argc
, argv
, "aAdhHkLmorsx")) != EOF
)
171 /* -H and -L are mutually exclusive */
178 /* -H and -L are mutually exclusive */
183 (void) fprintf(stderr
, gettext(
184 "usage: du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] "
188 if (optind
== argc
) {
194 /* "-o" and "-s" don't make any sense together. */
198 if ((base
= (char *)calloc(base_len
, sizeof (char))) == NULL
) {
202 if ((name
= (char *)calloc(name_len
, sizeof (char))) == NULL
) {
208 if (optind
< argc
- 1) {
210 if (pid
== (pid_t
)-1) {
211 perror(gettext("du: No more processes"));
215 while ((wpid
= wait(&status
)) != pid
&&
218 if (pid
!= (pid_t
)-1 && status
!= 0)
222 if (optind
== argc
- 1 || pid
== 0) {
223 while (base_len
< (strlen(argv
[optind
]) + 1)) {
224 base_len
= base_len
* 2;
225 if ((base
= reallocarray(base
, base_len
,
226 sizeof (char))) == NULL
) {
228 (void) fprintf(stderr
, gettext(
229 "du: can't process %s"),
236 if (base_len
> name_len
) {
238 if ((name
= reallocarray(name
, name_len
,
239 sizeof (char))) == NULL
) {
241 (void) fprintf(stderr
, gettext(
242 "du: can't process %s"),
249 (void) strcpy(base
, argv
[optind
]);
250 (void) strcpy(name
, argv
[optind
]);
251 if (np
= strrchr(name
, '/')) {
253 if (chdir(*name
? name
: "/") < 0) {
255 (void) fprintf(stderr
, "du: ");
256 perror(*name
? name
: "/");
263 blocks
= descend(*np
? np
: ".", 0, &retcode
,
266 printsize(blocks
, base
);
267 if (optind
< argc
- 1)
271 } while (optind
< argc
);
278 * descend recursively, adding up the allocated blocks.
279 * If curname is NULL, curfd is used.
282 descend(char *curname
, int curfd
, int *retcode
, dev_t device
)
284 static DIR *dirp
= NULL
;
285 char *ebase0
, *ebase
;
286 struct stat stb
, stb1
;
287 int i
, j
, ret
, fd
, tmpflg
;
294 char dirbuf
[PATH_MAX
+ 1];
297 ebase0
= ebase
= strchr(base
, 0);
298 if (ebase
> base
&& ebase
[-1] == '/')
300 offset
= ebase
- base
;
301 offset0
= ebase0
- base
;
307 * If neither a -L or a -H was specified, don't follow symlinks.
308 * If a -H was specified, don't follow symlinks if the file is
309 * not a command line argument.
311 follow_symlinks
= (Lflg
|| (Hflg
&& cmdarg
));
312 if (follow_symlinks
) {
313 i
= fstatat(curfd
, curname
, &stb
, 0);
314 j
= fstatat(curfd
, curname
, &stb1
, AT_SYMLINK_NOFOLLOW
);
317 * Make sure any files encountered while traversing the
318 * hierarchy are not considered command line arguments.
324 i
= fstatat(curfd
, curname
, &stb
, AT_SYMLINK_NOFOLLOW
);
328 if ((i
< 0) || (j
< 0)) {
330 (void) fprintf(stderr
, "du: ");
335 * POSIX states that non-zero status codes are only set
336 * when an error message is printed out on stderr
338 *retcode
= (rflg
? 1 : 0);
343 if (dflg
&& stb
.st_dev
!= device
) {
352 * If following links (-L) we need to keep track of all inodes
353 * visited so they are only visited/reported once and cycles
354 * are avoided. Otherwise, only keep track of files which are
355 * hard links so they only get reported once, and of directories
356 * so we don't report a directory and its hierarchy more than
357 * once in the special case in which it lies under the
358 * hierarchy of a directory which is a hard link.
359 * Note: Files with multiple links should only be counted
360 * once. Since each inode could possibly be referenced by a
361 * symbolic link, we need to keep track of all inodes when -L
364 if (Lflg
|| ((stb
.st_mode
& S_IFMT
) == S_IFDIR
) ||
365 (stb
.st_nlink
> 1)) {
367 if ((rc
= add_tnode(&tree
, stb
.st_dev
, stb
.st_ino
)) != 1) {
370 * This hierarchy, or file with multiple
371 * links, has already been visited/reported.
376 * An error occurred while trying to add the
386 blocks
= Aflg
? (stb
.st_size
/DEV_BSIZE
) : stb
.st_blocks
;
389 * If there are extended attributes on the current file, add their
390 * block usage onto the block count. Note: Since pathconf() always
391 * follows symlinks, only test for extended attributes using pathconf()
392 * if we are following symlinks or the current file is not a symlink.
394 if (curname
&& (follow_symlinks
||
395 ((stb
.st_mode
& S_IFMT
) != S_IFLNK
)) &&
396 pathconf(curname
, _PC_XATTR_EXISTS
) == 1) {
397 if ((fd
= attropen(curname
, ".", O_RDONLY
)) < 0) {
400 "du: can't access extended attributes"));
406 blocks
+= descend(NULL
, fd
, retcode
, device
);
410 if ((stb
.st_mode
& S_IFMT
) != S_IFDIR
) {
412 * Don't print twice: if sflg, file will get printed in main().
413 * Otherwise, level == 0 means this file is listed on the
414 * command line, so print here; aflg means print all files.
416 if (sflg
== 0 && (aflg
|| level
== 0))
417 printsize(blocks
, base
);
422 * Close the parent directory descriptor, we will reopen
423 * the directory when we pop up from this level of the
426 (void) closedir(dirp
);
428 dirp
= fdopendir(curfd
);
430 dirp
= opendir(curname
);
433 (void) fprintf(stderr
, "du: ");
441 if (curname
== NULL
|| (Lflg
&& S_ISLNK(stb1
.st_mode
))) {
442 if (getcwd(dirbuf
, PATH_MAX
) == NULL
) {
444 (void) fprintf(stderr
, "du: ");
450 if ((curname
? (chdir(curname
) < 0) : (fchdir(curfd
) < 0))) {
452 (void) fprintf(stderr
, "du: ");
457 (void) closedir(dirp
);
462 while (dp
= readdir(dirp
)) {
463 if ((strcmp(dp
->d_name
, ".") == 0) ||
464 (strcmp(dp
->d_name
, "..") == 0))
467 * we're about to append "/" + dp->d_name
468 * onto end of base; make sure there's enough
471 while ((offset
+ strlen(dp
->d_name
) + 2) > base_len
) {
472 base_len
= base_len
* 2;
473 if ((base
= reallocarray(base
, base_len
,
474 sizeof (char))) == NULL
) {
480 ebase
= base
+ offset
;
481 ebase0
= base
+ offset0
;
483 /* LINTED - unbounded string specifier */
484 (void) sprintf(ebase
, "/%s", dp
->d_name
);
485 curoff
= telldir(dirp
);
486 retval
= descend(ebase
+ 1, 0, retcode
, device
);
487 /* base may have been moved via realloc in descend() */
488 ebase
= base
+ offset
;
489 ebase0
= base
+ offset0
;
493 if ((dirp
= opendir(".")) == NULL
) {
495 (void) fprintf(stderr
,
496 gettext("du: Can't reopen in "));
503 seekdir(dirp
, curoff
);
506 (void) closedir(dirp
);
510 printsize(blocks
, base
);
511 if (curname
== NULL
|| (Lflg
&& S_ISLNK(stb1
.st_mode
)))
517 (void) sprintf(strchr(base
, '\0'), "/..");
518 (void) fprintf(stderr
,
519 gettext("du: Can't change dir to '..' in "));
532 * Convert an unsigned long long to a string representation and place the
533 * result in the caller-supplied buffer.
534 * The given number is in units of "unit_from" size,
535 * this will first be converted to a number in 1024 or 1000 byte size,
536 * depending on the scaling factor.
537 * Then the number is scaled down until it is small enough to be in a good
538 * human readable format i.e. in the range 0 thru scale-1.
539 * If it's smaller than 10 there's room enough to provide one decimal place.
540 * The value "(unsigned long long)-1" is a special case and is always
542 * Returns a pointer to the caller-supplied buffer.
545 number_to_scaled_string(
546 numbuf_t buf
, /* put the result here */
547 unsigned long long number
, /* convert this number */
548 unsigned long long unit_from
, /* number of bytes per input unit */
549 unsigned long long scale
) /* 1024 (-h) or 1000 (-H) */
551 unsigned long long save
= 0;
552 char *M
= "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
553 char *uom
= M
; /* unit of measurement, initially 'K' (=M[0]) */
555 if ((long long)number
== (long long)-1) {
556 (void) strcpy(buf
, "-1");
561 * Convert number from unit_from to given scale (1024 or 1000)
562 * This means multiply number with unit_from and divide by scale.
563 * if number is large enough, we first divide and then multiply
564 * to avoid an overflow
565 * (large enough here means 100 (rather arbitrary value)
566 * times scale in order to reduce rounding errors)
567 * otherwise, we first multiply and then divide
568 * to avoid an underflow
570 if (number
>= 100L * scale
) {
571 number
= number
/ scale
;
572 number
= number
* unit_from
;
574 number
= number
* unit_from
;
575 number
= number
/ scale
;
579 * Now we have number as a count of scale units.
580 * Stop scaling when we reached exa bytes, then something is
581 * probably wrong with our number.
583 while ((number
>= scale
) && (*uom
!= 'E')) {
584 uom
++; /* next unit of measurement */
586 number
= (number
+ (scale
/ 2)) / scale
;
589 /* check if we should output a decimal place after the point */
590 if (save
&& ((save
/ scale
) < 10)) {
591 /* sprintf() will round for us */
592 float fnum
= (float)save
/ scale
;
593 (void) sprintf(buf
, "%4.1f%c", fnum
, *uom
);
595 (void) sprintf(buf
, "%4llu%c", number
, *uom
);
601 printsize(blkcnt_t blocks
, char *path
)
605 unsigned long long scale
= 1024L;
606 (void) printf(FORMAT1
,
607 number_to_scaled_string(numbuf
, blocks
, DEV_BSIZE
, scale
),
610 (void) printf(FORMAT2
, (long long)kb(blocks
), path
);
612 (void) printf(FORMAT2
, (long long)mb(blocks
), path
);
614 (void) printf(FORMAT2
, (long long)blocks
, path
);