/* du - report on disk usage Author: Alistair G. Crooks */ /* * du.c 1.1 27/5/87 agc Joypace Ltd. * 1.2 24 Mar 89 nick@nswitgould.oz * 1.3 31 Mar 89 nick@nswitgould.oz * 1.4 22 Feb 90 meulenbr@cst.prl.philips.nl * 1.5 09 Jul 91 hp@vmars.tuwien.ac.at * 1.6 01 Oct 92 kjb@cs.vu.nl * 1.7 04 Jan 93 bde * 1.8 19 Sep 94 kjb * 1.9 28 Oct 99 kjb * * Copyright 1987, Joypace Ltd., London UK. All rights reserved. * This code may be freely distributed, provided that this notice * remains attached. * * du - a public domain interpretation of du(1). * * 1.2: Fixed bug involving 14 character long filenames * 1.3: Add [-l levels] option to restrict printing. * 1.4: Added processing of multiple arguments * 1.5: Fixed processing of multiple arguments. General cleanup. * 1.6: Use readdir * 1.7: Merged 1.5 and 1.6. * Print totals even for non-dirs at top level. * Count blocks for each dir before printing total for the dir. * Count blocks for all non-special files. * Don't clutter link buffer with directories. * 1.8: Remember all links. * 1.9: Added -x flag to not cross device boundaries. Type fixes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern char *optarg; extern int optind; #define LINELEN 256 #define NR_ALREADY 512 #ifdef S_IFLNK #define LSTAT lstat #else #define LSTAT stat #endif typedef struct already { struct already *al_next; int al_dev; ino_t al_inum; nlink_t al_nlink; } ALREADY; _PROTOTYPE(int main, (int argc, char **argv)); _PROTOTYPE(int makedname, (char *d, char *f, char *out, int outlen)); _PROTOTYPE(int done, (Dev_t dev, Ino_t inum, Nlink_t nlink)); _PROTOTYPE(long dodir, (char *d, int thislev, Dev_t dev)); char *prog; /* program name */ char *optstr = "asxdl:"; /* options */ int silent = 0; /* silent mode */ int all = 0; /* all directory entries mode */ int crosschk = 0; /* do not cross device boundaries mode */ char *startdir = "."; /* starting from here */ int levels = 20000; /* # of directory levels to print */ ALREADY *already[NR_ALREADY]; int alc; /* * makedname - make the pathname from the directory name, and the * directory entry, placing it in out. If this would overflow, * return 0, otherwise 1. */ int makedname(d, f, out, outlen) char *d; char *f; char *out; int outlen; { char *cp; int length; length = strlen(f); if (strlen(d) + length + 2 > outlen) return(0); for (cp = out; *d; *cp++ = *d++); if (*(cp - 1) != '/') *cp++ = '/'; while (length--) *cp++ = *f++; *cp = '\0'; return(1); } /* * done - have we encountered (dev, inum) before? Returns 1 for yes, * 0 for no, and remembers (dev, inum, nlink). */ int done(dev, inum, nlink) dev_t dev; ino_t inum; nlink_t nlink; { register ALREADY **pap, *ap; pap = &already[(unsigned) inum % NR_ALREADY]; while ((ap = *pap) != NULL) { if (ap->al_inum == inum && ap->al_dev == dev) { if (--ap->al_nlink == 0) { *pap = ap->al_next; free(ap); } return(1); } pap = &ap->al_next; } if ((ap = malloc(sizeof(*ap))) == NULL) { fprintf(stderr, "du: Out of memory\n"); exit(1); } ap->al_next = NULL; ap->al_inum = inum; ap->al_dev = dev; ap->al_nlink = nlink - 1; *pap = ap; return(0); } int get_block_size(char *dir, struct stat *st) { struct statfs stfs; static int fs_block_size = -1, fs_dev = -1; int d; if(st->st_dev == fs_dev) return fs_block_size; if((d = open(dir, O_RDONLY)) < 0) { perror(dir); return 0; } if(fstatfs(d, &stfs) < 0) { perror(dir); return 0; } fs_block_size = stfs.f_bsize; fs_dev = st->st_dev; return fs_block_size; } /* * dodir - process the directory d. Return the long size (in blocks) * of d and its descendants. */ long dodir(d, thislev, dev) char *d; int thislev; dev_t dev; { int maybe_print; struct stat s; long total_kb; char dent[LINELEN]; DIR *dp; struct dirent *entry; int block_size; if (LSTAT(d, &s) < 0) { fprintf(stderr, "%s: %s: %s\n", prog, d, strerror(errno)); return 0L; } if (s.st_dev != dev && dev != 0 && crosschk) return 0; block_size = get_block_size(d, &s); if(block_size < 1) { fprintf(stderr, "%s: %s: funny block size found (%d)\n", prog, d, block_size); return 0L; } total_kb = ((s.st_size + (block_size - 1)) / block_size) * block_size / 1024; switch (s.st_mode & S_IFMT) { case S_IFDIR: /* Directories should not be linked except to "." and "..", so this * directory should not already have been done. */ maybe_print = !silent; if ((dp = opendir(d)) == NULL) break; while ((entry = readdir(dp)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; if (!makedname(d, entry->d_name, dent, sizeof(dent))) continue; total_kb += dodir(dent, thislev - 1, s.st_dev); } closedir(dp); break; case S_IFBLK: case S_IFCHR: /* st_size for special files is not related to blocks used. */ total_kb = 0; /* Fall through. */ default: if (s.st_nlink > 1 && done(s.st_dev, s.st_ino, s.st_nlink)) return 0L; maybe_print = all; break; } if (thislev >= levels || (maybe_print && thislev >= 0)) { printf("%ld\t%s\n", total_kb, d); } return(total_kb); } int main(argc, argv) int argc; char **argv; { int c; prog = argv[0]; while ((c = getopt(argc, argv, optstr)) != EOF) switch (c) { case 'a': all = 1; break; case 's': silent = 1; break; case 'x': case 'd': crosschk = 1; break; case 'l': levels = atoi(optarg); break; default: fprintf(stderr, "Usage: %s [-asx] [-l levels] [startdir]\n", prog); exit(1); } do { if (optind < argc) startdir = argv[optind++]; alc = 0; (void) dodir(startdir, levels, 0); } while (optind < argc); return(0); }