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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
29 #pragma ident "%Z%%M% %I% %E% SMI"
32 * du -- summarize disk usage
33 * du [-dorx] [-a|-s] [-h|-k|-m] [-H|-L] [file...]
36 #include <sys/types.h>
47 #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. Solaris 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
, "adhHkLmorsx")) != EOF
)
167 /* -H and -L are mutually exclusive */
174 /* -H and -L are mutually exclusive */
179 (void) fprintf(stderr
, gettext(
180 "usage: du [-dorx] [-a|-s] [-h|-k|-m] [-H|-L] "
184 if (optind
== argc
) {
190 /* "-o" and "-s" don't make any sense together. */
194 if ((base
= (char *)calloc(base_len
, sizeof (char))) == NULL
) {
198 if ((name
= (char *)calloc(name_len
, sizeof (char))) == NULL
) {
204 if (optind
< argc
- 1) {
206 if (pid
== (pid_t
)-1) {
207 perror(gettext("du: No more processes"));
211 while ((wpid
= wait(&status
)) != pid
&&
214 if (pid
!= (pid_t
)-1 && status
!= 0)
218 if (optind
== argc
- 1 || pid
== 0) {
219 while (base_len
< (strlen(argv
[optind
]) + 1)) {
220 base_len
= base_len
* 2;
221 if ((base
= (char *)realloc(base
, base_len
*
222 sizeof (char))) == NULL
) {
224 (void) fprintf(stderr
, gettext(
225 "du: can't process %s"),
232 if (base_len
> name_len
) {
234 if ((name
= (char *)realloc(name
, name_len
*
235 sizeof (char))) == NULL
) {
237 (void) fprintf(stderr
, gettext(
238 "du: can't process %s"),
245 (void) strcpy(base
, argv
[optind
]);
246 (void) strcpy(name
, argv
[optind
]);
247 if (np
= strrchr(name
, '/')) {
249 if (chdir(*name
? name
: "/") < 0) {
251 (void) fprintf(stderr
, "du: ");
252 perror(*name
? name
: "/");
259 blocks
= descend(*np
? np
: ".", 0, &retcode
,
262 printsize(blocks
, base
);
263 if (optind
< argc
- 1)
267 } while (optind
< argc
);
274 * descend recursively, adding up the allocated blocks.
275 * If curname is NULL, curfd is used.
278 descend(char *curname
, int curfd
, int *retcode
, dev_t device
)
280 static DIR *dirp
= NULL
;
281 char *ebase0
, *ebase
;
282 struct stat stb
, stb1
;
283 int i
, j
, ret
, fd
, tmpflg
;
290 char dirbuf
[PATH_MAX
+ 1];
293 ebase0
= ebase
= strchr(base
, 0);
294 if (ebase
> base
&& ebase
[-1] == '/')
296 offset
= ebase
- base
;
297 offset0
= ebase0
- base
;
303 * If neither a -L or a -H was specified, don't follow symlinks.
304 * If a -H was specified, don't follow symlinks if the file is
305 * not a command line argument.
307 follow_symlinks
= (Lflg
|| (Hflg
&& cmdarg
));
308 if (follow_symlinks
) {
309 i
= fstatat(curfd
, curname
, &stb
, 0);
310 j
= fstatat(curfd
, curname
, &stb1
, AT_SYMLINK_NOFOLLOW
);
313 * Make sure any files encountered while traversing the
314 * hierarchy are not considered command line arguments.
320 i
= fstatat(curfd
, curname
, &stb
, AT_SYMLINK_NOFOLLOW
);
324 if ((i
< 0) || (j
< 0)) {
326 (void) fprintf(stderr
, "du: ");
331 * POSIX states that non-zero status codes are only set
332 * when an error message is printed out on stderr
334 *retcode
= (rflg
? 1 : 0);
339 if (dflg
&& stb
.st_dev
!= device
) {
348 * If following links (-L) we need to keep track of all inodes
349 * visited so they are only visited/reported once and cycles
350 * are avoided. Otherwise, only keep track of files which are
351 * hard links so they only get reported once, and of directories
352 * so we don't report a directory and its hierarchy more than
353 * once in the special case in which it lies under the
354 * hierarchy of a directory which is a hard link.
355 * Note: Files with multiple links should only be counted
356 * once. Since each inode could possibly be referenced by a
357 * symbolic link, we need to keep track of all inodes when -L
360 if (Lflg
|| ((stb
.st_mode
& S_IFMT
) == S_IFDIR
) ||
361 (stb
.st_nlink
> 1)) {
363 if ((rc
= add_tnode(&tree
, stb
.st_dev
, stb
.st_ino
)) != 1) {
366 * This hierarchy, or file with multiple
367 * links, has already been visited/reported.
372 * An error occurred while trying to add the
382 blocks
= stb
.st_blocks
;
384 * If there are extended attributes on the current file, add their
385 * block usage onto the block count. Note: Since pathconf() always
386 * follows symlinks, only test for extended attributes using pathconf()
387 * if we are following symlinks or the current file is not a symlink.
389 if (curname
&& (follow_symlinks
||
390 ((stb
.st_mode
& S_IFMT
) != S_IFLNK
)) &&
391 pathconf(curname
, _PC_XATTR_EXISTS
) == 1) {
392 if ((fd
= attropen(curname
, ".", O_RDONLY
)) < 0) {
395 "du: can't access extended attributes"));
401 blocks
+= descend(NULL
, fd
, retcode
, device
);
405 if ((stb
.st_mode
& S_IFMT
) != S_IFDIR
) {
407 * Don't print twice: if sflg, file will get printed in main().
408 * Otherwise, level == 0 means this file is listed on the
409 * command line, so print here; aflg means print all files.
411 if (sflg
== 0 && (aflg
|| level
== 0))
412 printsize(blocks
, base
);
417 * Close the parent directory descriptor, we will reopen
418 * the directory when we pop up from this level of the
421 (void) closedir(dirp
);
423 dirp
= fdopendir(curfd
);
425 dirp
= opendir(curname
);
428 (void) fprintf(stderr
, "du: ");
436 if (curname
== NULL
|| (Lflg
&& S_ISLNK(stb1
.st_mode
))) {
437 if (getcwd(dirbuf
, PATH_MAX
) == NULL
) {
439 (void) fprintf(stderr
, "du: ");
445 if ((curname
? (chdir(curname
) < 0) : (fchdir(curfd
) < 0))) {
447 (void) fprintf(stderr
, "du: ");
452 (void) closedir(dirp
);
457 while (dp
= readdir(dirp
)) {
458 if ((strcmp(dp
->d_name
, ".") == 0) ||
459 (strcmp(dp
->d_name
, "..") == 0))
462 * we're about to append "/" + dp->d_name
463 * onto end of base; make sure there's enough
466 while ((offset
+ strlen(dp
->d_name
) + 2) > base_len
) {
467 base_len
= base_len
* 2;
468 if ((base
= (char *)realloc(base
,
469 base_len
* sizeof (char))) == NULL
) {
475 ebase
= base
+ offset
;
476 ebase0
= base
+ offset0
;
478 /* LINTED - unbounded string specifier */
479 (void) sprintf(ebase
, "/%s", dp
->d_name
);
480 curoff
= telldir(dirp
);
481 retval
= descend(ebase
+ 1, 0, retcode
, device
);
482 /* base may have been moved via realloc in descend() */
483 ebase
= base
+ offset
;
484 ebase0
= base
+ offset0
;
488 if ((dirp
= opendir(".")) == NULL
) {
490 (void) fprintf(stderr
,
491 gettext("du: Can't reopen in "));
498 seekdir(dirp
, curoff
);
501 (void) closedir(dirp
);
505 printsize(blocks
, base
);
506 if (curname
== NULL
|| (Lflg
&& S_ISLNK(stb1
.st_mode
)))
512 (void) sprintf(strchr(base
, '\0'), "/..");
513 (void) fprintf(stderr
,
514 gettext("du: Can't change dir to '..' in "));
527 * Convert an unsigned long long to a string representation and place the
528 * result in the caller-supplied buffer.
529 * The given number is in units of "unit_from" size,
530 * this will first be converted to a number in 1024 or 1000 byte size,
531 * depending on the scaling factor.
532 * Then the number is scaled down until it is small enough to be in a good
533 * human readable format i.e. in the range 0 thru scale-1.
534 * If it's smaller than 10 there's room enough to provide one decimal place.
535 * The value "(unsigned long long)-1" is a special case and is always
537 * Returns a pointer to the caller-supplied buffer.
540 number_to_scaled_string(
541 numbuf_t buf
, /* put the result here */
542 unsigned long long number
, /* convert this number */
543 unsigned long long unit_from
, /* number of bytes per input unit */
544 unsigned long long scale
) /* 1024 (-h) or 1000 (-H) */
546 unsigned long long save
= 0;
547 char *M
= "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
548 char *uom
= M
; /* unit of measurement, initially 'K' (=M[0]) */
550 if ((long long)number
== (long long)-1) {
551 (void) strcpy(buf
, "-1");
556 * Convert number from unit_from to given scale (1024 or 1000)
557 * This means multiply number with unit_from and divide by scale.
558 * if number is large enough, we first divide and then multiply
559 * to avoid an overflow
560 * (large enough here means 100 (rather arbitrary value)
561 * times scale in order to reduce rounding errors)
562 * otherwise, we first multiply and then divide
563 * to avoid an underflow
565 if (number
>= 100L * scale
) {
566 number
= number
/ scale
;
567 number
= number
* unit_from
;
569 number
= number
* unit_from
;
570 number
= number
/ scale
;
574 * Now we have number as a count of scale units.
575 * Stop scaling when we reached exa bytes, then something is
576 * probably wrong with our number.
578 while ((number
>= scale
) && (*uom
!= 'E')) {
579 uom
++; /* next unit of measurement */
581 number
= (number
+ (scale
/ 2)) / scale
;
584 /* check if we should output a decimal place after the point */
585 if (save
&& ((save
/ scale
) < 10)) {
586 /* sprintf() will round for us */
587 float fnum
= (float)save
/ scale
;
588 (void) sprintf(buf
, "%4.1f%c", fnum
, *uom
);
590 (void) sprintf(buf
, "%4llu%c", number
, *uom
);
596 printsize(blkcnt_t blocks
, char *path
)
600 unsigned long long scale
= 1024L;
601 (void) printf(FORMAT1
,
602 number_to_scaled_string(numbuf
, blocks
, DEV_BSIZE
, scale
),
605 (void) printf(FORMAT2
, (long long)kb(blocks
), path
);
607 (void) printf(FORMAT2
, (long long)mb(blocks
), path
);
609 (void) printf(FORMAT2
, (long long)blocks
, path
);