[9] | 1 | /* du - report on disk usage Author: Alistair G. Crooks */
|
---|
| 2 |
|
---|
| 3 | /*
|
---|
| 4 | * du.c 1.1 27/5/87 agc Joypace Ltd.
|
---|
| 5 | * 1.2 24 Mar 89 nick@nswitgould.oz
|
---|
| 6 | * 1.3 31 Mar 89 nick@nswitgould.oz
|
---|
| 7 | * 1.4 22 Feb 90 meulenbr@cst.prl.philips.nl
|
---|
| 8 | * 1.5 09 Jul 91 hp@vmars.tuwien.ac.at
|
---|
| 9 | * 1.6 01 Oct 92 kjb@cs.vu.nl
|
---|
| 10 | * 1.7 04 Jan 93 bde
|
---|
| 11 | * 1.8 19 Sep 94 kjb
|
---|
| 12 | * 1.9 28 Oct 99 kjb
|
---|
| 13 | *
|
---|
| 14 | * Copyright 1987, Joypace Ltd., London UK. All rights reserved.
|
---|
| 15 | * This code may be freely distributed, provided that this notice
|
---|
| 16 | * remains attached.
|
---|
| 17 | *
|
---|
| 18 | * du - a public domain interpretation of du(1).
|
---|
| 19 | *
|
---|
| 20 | * 1.2: Fixed bug involving 14 character long filenames
|
---|
| 21 | * 1.3: Add [-l levels] option to restrict printing.
|
---|
| 22 | * 1.4: Added processing of multiple arguments
|
---|
| 23 | * 1.5: Fixed processing of multiple arguments. General cleanup.
|
---|
| 24 | * 1.6: Use readdir
|
---|
| 25 | * 1.7: Merged 1.5 and 1.6.
|
---|
| 26 | * Print totals even for non-dirs at top level.
|
---|
| 27 | * Count blocks for each dir before printing total for the dir.
|
---|
| 28 | * Count blocks for all non-special files.
|
---|
| 29 | * Don't clutter link buffer with directories.
|
---|
| 30 | * 1.8: Remember all links.
|
---|
| 31 | * 1.9: Added -x flag to not cross device boundaries. Type fixes.
|
---|
| 32 | */
|
---|
| 33 |
|
---|
| 34 |
|
---|
| 35 | #include <sys/types.h>
|
---|
| 36 | #include <sys/stat.h>
|
---|
| 37 | #include <sys/statfs.h>
|
---|
| 38 | #include <fcntl.h>
|
---|
| 39 | #include <errno.h>
|
---|
| 40 | #include <limits.h>
|
---|
| 41 | #include <stdlib.h>
|
---|
| 42 | #include <string.h>
|
---|
| 43 | #include <unistd.h>
|
---|
| 44 | #include <stdio.h>
|
---|
| 45 | #include <dirent.h>
|
---|
| 46 | #include <minix/config.h>
|
---|
| 47 | #include <minix/const.h>
|
---|
| 48 |
|
---|
| 49 | extern char *optarg;
|
---|
| 50 | extern int optind;
|
---|
| 51 |
|
---|
| 52 | #define LINELEN 256
|
---|
| 53 | #define NR_ALREADY 512
|
---|
| 54 |
|
---|
| 55 | #ifdef S_IFLNK
|
---|
| 56 | #define LSTAT lstat
|
---|
| 57 | #else
|
---|
| 58 | #define LSTAT stat
|
---|
| 59 | #endif
|
---|
| 60 |
|
---|
| 61 | typedef struct already {
|
---|
| 62 | struct already *al_next;
|
---|
| 63 | int al_dev;
|
---|
| 64 | ino_t al_inum;
|
---|
| 65 | nlink_t al_nlink;
|
---|
| 66 | } ALREADY;
|
---|
| 67 |
|
---|
| 68 | _PROTOTYPE(int main, (int argc, char **argv));
|
---|
| 69 | _PROTOTYPE(int makedname, (char *d, char *f, char *out, int outlen));
|
---|
| 70 | _PROTOTYPE(int done, (Dev_t dev, Ino_t inum, Nlink_t nlink));
|
---|
| 71 | _PROTOTYPE(long dodir, (char *d, int thislev, Dev_t dev));
|
---|
| 72 |
|
---|
| 73 | char *prog; /* program name */
|
---|
| 74 | char *optstr = "asxdl:"; /* options */
|
---|
| 75 | int silent = 0; /* silent mode */
|
---|
| 76 | int all = 0; /* all directory entries mode */
|
---|
| 77 | int crosschk = 0; /* do not cross device boundaries mode */
|
---|
| 78 | char *startdir = "."; /* starting from here */
|
---|
| 79 | int levels = 20000; /* # of directory levels to print */
|
---|
| 80 | ALREADY *already[NR_ALREADY];
|
---|
| 81 | int alc;
|
---|
| 82 |
|
---|
| 83 |
|
---|
| 84 | /*
|
---|
| 85 | * makedname - make the pathname from the directory name, and the
|
---|
| 86 | * directory entry, placing it in out. If this would overflow,
|
---|
| 87 | * return 0, otherwise 1.
|
---|
| 88 | */
|
---|
| 89 | int makedname(d, f, out, outlen)
|
---|
| 90 | char *d;
|
---|
| 91 | char *f;
|
---|
| 92 | char *out;
|
---|
| 93 | int outlen;
|
---|
| 94 | {
|
---|
| 95 | char *cp;
|
---|
| 96 | int length;
|
---|
| 97 |
|
---|
| 98 | length = strlen(f);
|
---|
| 99 | if (strlen(d) + length + 2 > outlen) return(0);
|
---|
| 100 | for (cp = out; *d; *cp++ = *d++);
|
---|
| 101 | if (*(cp - 1) != '/') *cp++ = '/';
|
---|
| 102 | while (length--) *cp++ = *f++;
|
---|
| 103 | *cp = '\0';
|
---|
| 104 | return(1);
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | /*
|
---|
| 108 | * done - have we encountered (dev, inum) before? Returns 1 for yes,
|
---|
| 109 | * 0 for no, and remembers (dev, inum, nlink).
|
---|
| 110 | */
|
---|
| 111 | int done(dev, inum, nlink)
|
---|
| 112 | dev_t dev;
|
---|
| 113 | ino_t inum;
|
---|
| 114 | nlink_t nlink;
|
---|
| 115 | {
|
---|
| 116 | register ALREADY **pap, *ap;
|
---|
| 117 |
|
---|
| 118 | pap = &already[(unsigned) inum % NR_ALREADY];
|
---|
| 119 | while ((ap = *pap) != NULL) {
|
---|
| 120 | if (ap->al_inum == inum && ap->al_dev == dev) {
|
---|
| 121 | if (--ap->al_nlink == 0) {
|
---|
| 122 | *pap = ap->al_next;
|
---|
| 123 | free(ap);
|
---|
| 124 | }
|
---|
| 125 | return(1);
|
---|
| 126 | }
|
---|
| 127 | pap = &ap->al_next;
|
---|
| 128 | }
|
---|
| 129 | if ((ap = malloc(sizeof(*ap))) == NULL) {
|
---|
| 130 | fprintf(stderr, "du: Out of memory\n");
|
---|
| 131 | exit(1);
|
---|
| 132 | }
|
---|
| 133 | ap->al_next = NULL;
|
---|
| 134 | ap->al_inum = inum;
|
---|
| 135 | ap->al_dev = dev;
|
---|
| 136 | ap->al_nlink = nlink - 1;
|
---|
| 137 | *pap = ap;
|
---|
| 138 | return(0);
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | int get_block_size(char *dir, struct stat *st)
|
---|
| 142 | {
|
---|
| 143 | struct statfs stfs;
|
---|
| 144 | static int fs_block_size = -1, fs_dev = -1;
|
---|
| 145 | int d;
|
---|
| 146 |
|
---|
| 147 | if(st->st_dev == fs_dev)
|
---|
| 148 | return fs_block_size;
|
---|
| 149 |
|
---|
| 150 | if((d = open(dir, O_RDONLY)) < 0) {
|
---|
| 151 | perror(dir);
|
---|
| 152 | return 0;
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | if(fstatfs(d, &stfs) < 0) {
|
---|
| 156 | perror(dir);
|
---|
| 157 | return 0;
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | fs_block_size = stfs.f_bsize;
|
---|
| 161 | fs_dev = st->st_dev;
|
---|
| 162 |
|
---|
| 163 | return fs_block_size;
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 |
|
---|
| 167 | /*
|
---|
| 168 | * dodir - process the directory d. Return the long size (in blocks)
|
---|
| 169 | * of d and its descendants.
|
---|
| 170 | */
|
---|
| 171 | long dodir(d, thislev, dev)
|
---|
| 172 | char *d;
|
---|
| 173 | int thislev;
|
---|
| 174 | dev_t dev;
|
---|
| 175 | {
|
---|
| 176 | int maybe_print;
|
---|
| 177 | struct stat s;
|
---|
| 178 | long total_kb;
|
---|
| 179 | char dent[LINELEN];
|
---|
| 180 | DIR *dp;
|
---|
| 181 | struct dirent *entry;
|
---|
| 182 | int block_size;
|
---|
| 183 |
|
---|
| 184 | if (LSTAT(d, &s) < 0) {
|
---|
| 185 | fprintf(stderr,
|
---|
| 186 | "%s: %s: %s\n", prog, d, strerror(errno));
|
---|
| 187 | return 0L;
|
---|
| 188 | }
|
---|
| 189 | if (s.st_dev != dev && dev != 0 && crosschk) return 0;
|
---|
| 190 | block_size = get_block_size(d, &s);
|
---|
| 191 | if(block_size < 1) {
|
---|
| 192 | fprintf(stderr,
|
---|
| 193 | "%s: %s: funny block size found (%d)\n",
|
---|
| 194 | prog, d, block_size);
|
---|
| 195 | return 0L;
|
---|
| 196 | }
|
---|
| 197 | total_kb = ((s.st_size + (block_size - 1)) / block_size) * block_size / 1024;
|
---|
| 198 | switch (s.st_mode & S_IFMT) {
|
---|
| 199 | case S_IFDIR:
|
---|
| 200 | /* Directories should not be linked except to "." and "..", so this
|
---|
| 201 | * directory should not already have been done.
|
---|
| 202 | */
|
---|
| 203 | maybe_print = !silent;
|
---|
| 204 | if ((dp = opendir(d)) == NULL) break;
|
---|
| 205 | while ((entry = readdir(dp)) != NULL) {
|
---|
| 206 | if (strcmp(entry->d_name, ".") == 0 ||
|
---|
| 207 | strcmp(entry->d_name, "..") == 0)
|
---|
| 208 | continue;
|
---|
| 209 | if (!makedname(d, entry->d_name, dent, sizeof(dent))) continue;
|
---|
| 210 | total_kb += dodir(dent, thislev - 1, s.st_dev);
|
---|
| 211 | }
|
---|
| 212 | closedir(dp);
|
---|
| 213 | break;
|
---|
| 214 | case S_IFBLK:
|
---|
| 215 | case S_IFCHR:
|
---|
| 216 | /* st_size for special files is not related to blocks used. */
|
---|
| 217 | total_kb = 0;
|
---|
| 218 | /* Fall through. */
|
---|
| 219 | default:
|
---|
| 220 | if (s.st_nlink > 1 && done(s.st_dev, s.st_ino, s.st_nlink)) return 0L;
|
---|
| 221 | maybe_print = all;
|
---|
| 222 | break;
|
---|
| 223 | }
|
---|
| 224 | if (thislev >= levels || (maybe_print && thislev >= 0)) {
|
---|
| 225 | printf("%ld\t%s\n", total_kb, d);
|
---|
| 226 | }
|
---|
| 227 | return(total_kb);
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | int main(argc, argv)
|
---|
| 231 | int argc;
|
---|
| 232 | char **argv;
|
---|
| 233 | {
|
---|
| 234 | int c;
|
---|
| 235 |
|
---|
| 236 | prog = argv[0];
|
---|
| 237 | while ((c = getopt(argc, argv, optstr)) != EOF) switch (c) {
|
---|
| 238 | case 'a': all = 1; break;
|
---|
| 239 | case 's': silent = 1; break;
|
---|
| 240 | case 'x':
|
---|
| 241 | case 'd': crosschk = 1; break;
|
---|
| 242 | case 'l': levels = atoi(optarg); break;
|
---|
| 243 | default:
|
---|
| 244 | fprintf(stderr,
|
---|
| 245 | "Usage: %s [-asx] [-l levels] [startdir]\n", prog);
|
---|
| 246 | exit(1);
|
---|
| 247 | }
|
---|
| 248 | do {
|
---|
| 249 | if (optind < argc) startdir = argv[optind++];
|
---|
| 250 | alc = 0;
|
---|
| 251 | (void) dodir(startdir, levels, 0);
|
---|
| 252 | } while (optind < argc);
|
---|
| 253 | return(0);
|
---|
| 254 | }
|
---|