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