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.
25 * Copyright 2017 Jason King
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 * du -- summarize disk usage
33 * du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] [file...]
36 #include <sys/types.h>
47 #include <libcmdutils.h>
61 static int cmdarg
= 0; /* Command line argument */
62 static char *dot
= ".";
63 static int level
= 0; /* Level of recursion */
67 static size_t base_len
= PATH_MAX
+ 1; /* # of chars for base */
68 static size_t name_len
= PATH_MAX
+ 1; /* # of chars for name */
71 * Output formats. illumos uses a tab as separator, XPG4 a space.
74 #define FORMAT1 "%s %s\n"
75 #define FORMAT2 "%lld %s\n"
77 #define FORMAT1 "%s\t%s\n"
78 #define FORMAT2 "%lld\t%s\n"
82 * convert DEV_BSIZE blocks to K blocks
87 #define kb(n) (((u_longlong_t)(n)) >> DEV_KSHIFT)
88 #define mb(n) (((u_longlong_t)(n)) >> DEV_MSHIFT)
91 static u_longlong_t
descend(char *curname
, int curfd
, int *retcode
,
93 static void printsize(blkcnt_t blocks
, char *path
);
94 static void exitdu(int exitcode
);
96 static avl_tree_t
*tree
= NULL
;
99 main(int argc
, char **argv
)
106 int status
, retcode
= 0;
107 setbuf(stderr
, NULL
);
108 (void) setlocale(LC_ALL
, "");
109 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
110 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
112 (void) textdomain(TEXT_DOMAIN
);
115 rflg
++; /* "-r" is not an option but ON always */
118 while ((c
= getopt(argc
, argv
, "aAdhHkLmorsx")) != EOF
)
169 /* -H and -L are mutually exclusive */
176 /* -H and -L are mutually exclusive */
181 (void) fprintf(stderr
, gettext(
182 "usage: du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] "
186 if (optind
== argc
) {
192 /* "-o" and "-s" don't make any sense together. */
196 if ((base
= (char *)calloc(base_len
, sizeof (char))) == NULL
) {
200 if ((name
= (char *)calloc(name_len
, sizeof (char))) == NULL
) {
206 if (optind
< argc
- 1) {
208 if (pid
== (pid_t
)-1) {
209 perror(gettext("du: No more processes"));
213 while ((wpid
= wait(&status
)) != pid
&&
216 if (pid
!= (pid_t
)-1 && status
!= 0)
220 if (optind
== argc
- 1 || pid
== 0) {
221 while (base_len
< (strlen(argv
[optind
]) + 1)) {
222 base_len
= base_len
* 2;
223 if ((base
= (char *)realloc(base
, base_len
*
224 sizeof (char))) == NULL
) {
226 (void) fprintf(stderr
, gettext(
227 "du: can't process %s"),
234 if (base_len
> name_len
) {
236 if ((name
= (char *)realloc(name
, name_len
*
237 sizeof (char))) == NULL
) {
239 (void) fprintf(stderr
, gettext(
240 "du: can't process %s"),
247 (void) strcpy(base
, argv
[optind
]);
248 (void) strcpy(name
, argv
[optind
]);
249 if (np
= strrchr(name
, '/')) {
251 if (chdir(*name
? name
: "/") < 0) {
253 (void) fprintf(stderr
, "du: ");
254 perror(*name
? name
: "/");
261 blocks
= descend(*np
? np
: ".", 0, &retcode
,
264 printsize(blocks
, base
);
265 if (optind
< argc
- 1)
269 } while (optind
< argc
);
276 * descend recursively, adding up the allocated blocks.
277 * If curname is NULL, curfd is used.
280 descend(char *curname
, int curfd
, int *retcode
, dev_t device
)
282 static DIR *dirp
= NULL
;
283 char *ebase0
, *ebase
;
284 struct stat stb
, stb1
;
285 int i
, j
, ret
, fd
, tmpflg
;
292 char dirbuf
[PATH_MAX
+ 1];
295 ebase0
= ebase
= strchr(base
, 0);
296 if (ebase
> base
&& ebase
[-1] == '/')
298 offset
= ebase
- base
;
299 offset0
= ebase0
- base
;
305 * If neither a -L or a -H was specified, don't follow symlinks.
306 * If a -H was specified, don't follow symlinks if the file is
307 * not a command line argument.
309 follow_symlinks
= (Lflg
|| (Hflg
&& cmdarg
));
310 if (follow_symlinks
) {
311 i
= fstatat(curfd
, curname
, &stb
, 0);
312 j
= fstatat(curfd
, curname
, &stb1
, AT_SYMLINK_NOFOLLOW
);
315 * Make sure any files encountered while traversing the
316 * hierarchy are not considered command line arguments.
322 i
= fstatat(curfd
, curname
, &stb
, AT_SYMLINK_NOFOLLOW
);
326 if ((i
< 0) || (j
< 0)) {
328 (void) fprintf(stderr
, "du: ");
333 * POSIX states that non-zero status codes are only set
334 * when an error message is printed out on stderr
336 *retcode
= (rflg
? 1 : 0);
341 if (dflg
&& stb
.st_dev
!= device
) {
350 * If following links (-L) we need to keep track of all inodes
351 * visited so they are only visited/reported once and cycles
352 * are avoided. Otherwise, only keep track of files which are
353 * hard links so they only get reported once, and of directories
354 * so we don't report a directory and its hierarchy more than
355 * once in the special case in which it lies under the
356 * hierarchy of a directory which is a hard link.
357 * Note: Files with multiple links should only be counted
358 * once. Since each inode could possibly be referenced by a
359 * symbolic link, we need to keep track of all inodes when -L
362 if (Lflg
|| ((stb
.st_mode
& S_IFMT
) == S_IFDIR
) ||
363 (stb
.st_nlink
> 1)) {
365 if ((rc
= add_tnode(&tree
, stb
.st_dev
, stb
.st_ino
)) != 1) {
368 * This hierarchy, or file with multiple
369 * links, has already been visited/reported.
374 * An error occurred while trying to add the
384 blocks
= Aflg
? stb
.st_size
: stb
.st_blocks
;
387 * If there are extended attributes on the current file, add their
388 * block usage onto the block count. Note: Since pathconf() always
389 * follows symlinks, only test for extended attributes using pathconf()
390 * if we are following symlinks or the current file is not a symlink.
392 if (curname
&& (follow_symlinks
||
393 ((stb
.st_mode
& S_IFMT
) != S_IFLNK
)) &&
394 pathconf(curname
, _PC_XATTR_EXISTS
) == 1) {
395 if ((fd
= attropen(curname
, ".", O_RDONLY
)) < 0) {
398 "du: can't access extended attributes"));
404 blocks
+= descend(NULL
, fd
, retcode
, device
);
408 if ((stb
.st_mode
& S_IFMT
) != S_IFDIR
) {
410 * Don't print twice: if sflg, file will get printed in main().
411 * Otherwise, level == 0 means this file is listed on the
412 * command line, so print here; aflg means print all files.
414 if (sflg
== 0 && (aflg
|| level
== 0))
415 printsize(blocks
, base
);
420 * Close the parent directory descriptor, we will reopen
421 * the directory when we pop up from this level of the
424 (void) closedir(dirp
);
426 dirp
= fdopendir(curfd
);
428 dirp
= opendir(curname
);
431 (void) fprintf(stderr
, "du: ");
439 if (curname
== NULL
|| (Lflg
&& S_ISLNK(stb1
.st_mode
))) {
440 if (getcwd(dirbuf
, PATH_MAX
) == NULL
) {
442 (void) fprintf(stderr
, "du: ");
448 if ((curname
? (chdir(curname
) < 0) : (fchdir(curfd
) < 0))) {
450 (void) fprintf(stderr
, "du: ");
455 (void) closedir(dirp
);
460 while (dp
= readdir(dirp
)) {
461 if ((strcmp(dp
->d_name
, ".") == 0) ||
462 (strcmp(dp
->d_name
, "..") == 0))
465 * we're about to append "/" + dp->d_name
466 * onto end of base; make sure there's enough
469 while ((offset
+ strlen(dp
->d_name
) + 2) > base_len
) {
470 base_len
= base_len
* 2;
471 if ((base
= (char *)realloc(base
,
472 base_len
* sizeof (char))) == NULL
) {
478 ebase
= base
+ offset
;
479 ebase0
= base
+ offset0
;
481 /* LINTED - unbounded string specifier */
482 (void) sprintf(ebase
, "/%s", dp
->d_name
);
483 curoff
= telldir(dirp
);
484 retval
= descend(ebase
+ 1, 0, retcode
, device
);
485 /* base may have been moved via realloc in descend() */
486 ebase
= base
+ offset
;
487 ebase0
= base
+ offset0
;
491 if ((dirp
= opendir(".")) == NULL
) {
493 (void) fprintf(stderr
,
494 gettext("du: Can't reopen in "));
501 seekdir(dirp
, curoff
);
504 (void) closedir(dirp
);
508 printsize(blocks
, base
);
509 if (curname
== NULL
|| (Lflg
&& S_ISLNK(stb1
.st_mode
)))
515 (void) sprintf(strchr(base
, '\0'), "/..");
516 (void) fprintf(stderr
,
517 gettext("du: Can't change dir to '..' in "));
530 printsize(blkcnt_t blocks
, char *path
)
534 bsize
= Aflg
? 1 : DEV_BSIZE
;
537 char buf
[NN_NUMBUF_SZ
] = { 0 };
539 nicenum_scale(blocks
, bsize
, buf
, sizeof (buf
), 0);
540 (void) printf(FORMAT1
, buf
, path
);
542 (void) printf(FORMAT2
, (long long)kb(blocks
), path
);
544 (void) printf(FORMAT2
, (long long)mb(blocks
), path
);
546 (void) printf(FORMAT2
, (long long)blocks
, path
);