1 /* $NetBSD: du.c,v 1.19 2002/09/28 21:14:03 provos Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Newcomb. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\ 42 The Regents of the University of California. All rights reserved.\n"); 43 #endif /* not lint */ 44 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; 48 #else 49 __RCSID("$NetBSD: du.c,v 1.19 2002/09/28 21:14:03 provos Exp $"); 50 #endif 51 #endif /* not lint */ 52 53 #include <sys/types.h> 54 #include <sys/stat.h> 55 56 #include <dirent.h> 57 #include <err.h> 58 #include <errno.h> 59 #include <fts.h> 60 #include <util.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 66 int linkchk __P((FTSENT *)); 67 int main __P((int, char **)); 68 void prstat __P((const char *, int64_t)); 69 void usage __P((void)); 70 71 int hflag; 72 long blocksize; 73 74 int 75 main(argc, argv) 76 int argc; 77 char *argv[]; 78 { 79 FTS *fts; 80 FTSENT *p; 81 int64_t totalblocks; 82 int ftsoptions, listdirs, listfiles; 83 int Hflag, Lflag, Pflag, aflag, ch, cflag, kmflag, notused, rval, sflag; 84 char **save; 85 86 save = argv; 87 Hflag = Lflag = Pflag = aflag = cflag = kmflag = sflag = 0; 88 totalblocks = 0; 89 ftsoptions = FTS_PHYSICAL; 90 while ((ch = getopt(argc, argv, "HLPachkmrsx")) != -1) 91 switch (ch) { 92 case 'H': 93 Hflag = 1; 94 Lflag = Pflag = 0; 95 break; 96 case 'L': 97 Lflag = 1; 98 Hflag = Pflag = 0; 99 break; 100 case 'P': 101 Pflag = 1; 102 Hflag = Lflag = 0; 103 break; 104 case 'a': 105 aflag = 1; 106 break; 107 case 'c': 108 cflag = 1; 109 break; 110 case 'h': 111 hflag = 1; 112 break; 113 case 'k': 114 blocksize = 1024; 115 kmflag = 1; 116 break; 117 case 'm': 118 blocksize = 1024 * 1024; 119 kmflag = 1; 120 break; 121 case 'r': 122 break; 123 case 's': 124 sflag = 1; 125 break; 126 case 'x': 127 ftsoptions |= FTS_XDEV; 128 break; 129 case '?': 130 default: 131 usage(); 132 } 133 argc -= optind; 134 argv += optind; 135 136 /* 137 * XXX 138 * Because of the way that fts(3) works, logical walks will not count 139 * the blocks actually used by symbolic links. We rationalize this by 140 * noting that users computing logical sizes are likely to do logical 141 * copies, so not counting the links is correct. The real reason is 142 * that we'd have to re-implement the kernel's symbolic link traversing 143 * algorithm to get this right. If, for example, you have relative 144 * symbolic links referencing other relative symbolic links, it gets 145 * very nasty, very fast. The bottom line is that it's documented in 146 * the man page, so it's a feature. 147 */ 148 if (Hflag) 149 ftsoptions |= FTS_COMFOLLOW; 150 if (Lflag) { 151 ftsoptions &= ~FTS_PHYSICAL; 152 ftsoptions |= FTS_LOGICAL; 153 } 154 155 if (aflag) { 156 if (sflag) 157 usage(); 158 listdirs = listfiles = 1; 159 } else if (sflag) 160 listdirs = listfiles = 0; 161 else { 162 listfiles = 0; 163 listdirs = 1; 164 } 165 166 if (!*argv) { 167 argv = save; 168 argv[0] = "."; 169 argv[1] = NULL; 170 } 171 172 if (!kmflag) 173 (void)getbsize(¬used, &blocksize); 174 blocksize /= 512; 175 176 if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) 177 err(1, "fts_open `%s'", *argv); 178 179 for (rval = 0; (p = fts_read(fts)) != NULL;) 180 switch (p->fts_info) { 181 case FTS_D: /* Ignore. */ 182 break; 183 case FTS_DP: 184 p->fts_parent->fts_number += 185 p->fts_number += p->fts_statp->st_blocks; 186 if (cflag) 187 totalblocks += p->fts_statp->st_blocks; 188 /* 189 * If listing each directory, or not listing files 190 * or directories and this is post-order of the 191 * root of a traversal, display the total. 192 */ 193 if (listdirs || (!listfiles && !p->fts_level)) 194 prstat(p->fts_path, p->fts_number); 195 break; 196 case FTS_DC: /* Ignore. */ 197 break; 198 case FTS_DNR: /* Warn, continue. */ 199 case FTS_ERR: 200 case FTS_NS: 201 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 202 rval = 1; 203 break; 204 default: 205 if (p->fts_statp->st_nlink > 1 && linkchk(p)) 206 break; 207 /* 208 * If listing each file, or a non-directory file was 209 * the root of a traversal, display the total. 210 */ 211 if (listfiles || !p->fts_level) 212 prstat(p->fts_path, p->fts_statp->st_blocks); 213 p->fts_parent->fts_number += p->fts_statp->st_blocks; 214 if (cflag) 215 totalblocks += p->fts_statp->st_blocks; 216 } 217 if (errno) 218 err(1, "fts_read"); 219 if (cflag) 220 prstat("total", totalblocks); 221 exit(rval); 222 } 223 224 void 225 prstat(const char *fname, int64_t blocks) 226 { 227 if (hflag) { 228 char buf[5]; 229 int64_t sz = blocks * 512; 230 231 humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE, 232 HN_B | HN_NOSPACE | HN_DECIMAL); 233 234 (void)printf("%s\t%s\n", buf, fname); 235 } else 236 (void)printf("%lld\t%s\n", 237 (long long)howmany(blocks, (int64_t)blocksize), 238 fname); 239 } 240 241 242 typedef struct _ID { 243 dev_t dev; 244 ino_t inode; 245 } ID; 246 247 int 248 linkchk(p) 249 FTSENT *p; 250 { 251 static ID *files; 252 static int maxfiles, nfiles; 253 ID *fp, *start; 254 ino_t ino; 255 dev_t dev; 256 257 ino = p->fts_statp->st_ino; 258 dev = p->fts_statp->st_dev; 259 if ((start = files) != NULL) 260 for (fp = start + nfiles - 1; fp >= start; --fp) 261 if (ino == fp->inode && dev == fp->dev) 262 return (1); 263 264 if (nfiles == maxfiles && (files = realloc((char *)files, 265 (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) 266 err(1, "realloc"); 267 files[nfiles].inode = ino; 268 files[nfiles].dev = dev; 269 ++nfiles; 270 return (0); 271 } 272 273 void 274 usage() 275 { 276 277 (void)fprintf(stderr, 278 "usage: du [-H | -L | -P] [-a | -s] [-chkmrx] [file ...]\n"); 279 exit(1); 280 } 281