summaryrefslogtreecommitdiff
path: root/usr/src/cmd/du
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/du
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/du')
-rw-r--r--usr/src/cmd/du/Makefile61
-rw-r--r--usr/src/cmd/du/du.c616
2 files changed, 677 insertions, 0 deletions
diff --git a/usr/src/cmd/du/Makefile b/usr/src/cmd/du/Makefile
new file mode 100644
index 0000000000..4ddc1646ef
--- /dev/null
+++ b/usr/src/cmd/du/Makefile
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=du
+XPG4PROG= du
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+$(XPG4) := CFLAGS += -DXPG4
+CPPFLAGS += -D_FILE_OFFSET_BITS=64
+POFILE = du_cmd.po
+POFILES = du.po
+POFILES_XPG4 = du.po.xpg4
+$(POFILES_XPG4) := CFLAGS += -DXPG4
+CLOBBERFILES += $(POFILES_XPG4)
+LDLIBS += -lcmdutils
+
+.KEEP_STATE:
+
+all: $(PROG) $(XPG4)
+
+install: all $(ROOTPROG) $(ROOTXPG4PROG)
+
+$(POFILE): $(POFILES) $(POFILES_XPG4)
+ $(RM) $@; cat $(POFILES) $(POFILES_XPG4) > $@
+
+%.po.xpg4: %.c
+ $(COMPILE.cpp) $< > $<.i
+ $(BUILD.po)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/du/du.c b/usr/src/cmd/du/du.c
new file mode 100644
index 0000000000..90f6d19af2
--- /dev/null
+++ b/usr/src/cmd/du/du.c
@@ -0,0 +1,616 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * du -- summarize disk usage
+ * /bin/du [-a][-d][-h|-k][-H|-L][-r][-o|-s] [file ...]
+ * /usr/xpg4/bin/du [-a][-h|-k][-H|-L][-r][-s][-x] [file ...]
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/avl.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libcmdutils.h>
+
+
+static int aflg = 0;
+static int rflg = 0;
+static int sflg = 0;
+static int kflg = 0;
+static int oflg = 0;
+static int dflg = 0;
+static int hflg = 0;
+static int Hflg = 0;
+static int Lflg = 0;
+static int cmdarg = 0; /* Command line argument */
+static char *dot = ".";
+static int level = 0; /* Level of recursion */
+
+static char *base;
+static char *name;
+static size_t base_len = PATH_MAX + 1; /* # of chars for base */
+static size_t name_len = PATH_MAX + 1; /* # of chars for name */
+
+#define NUMBER_WIDTH 64
+typedef char numbuf_t[NUMBER_WIDTH];
+
+/*
+ * convert DEV_BSIZE blocks to K blocks
+ */
+#define DEV_BSIZE 512
+#define DEV_KSHIFT 1
+#define kb(n) (((u_longlong_t)(n)) >> DEV_KSHIFT)
+
+long wait();
+static u_longlong_t descend(char *curname, int curfd, int *retcode,
+ dev_t device);
+static void printsize(blkcnt_t blocks, char *path);
+static void exitdu(int exitcode);
+
+static avl_tree_t *tree = NULL;
+
+int
+main(int argc, char **argv)
+{
+ blkcnt_t blocks = 0;
+ int c;
+ extern int optind;
+ char *np;
+ pid_t pid, wpid;
+ int status, retcode = 0;
+ setbuf(stderr, NULL);
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+#ifdef XPG4
+ rflg++; /* "-r" is not an option but ON always */
+#endif
+
+#ifdef XPG4
+ while ((c = getopt(argc, argv, "ahHkLrsx")) != EOF)
+#else
+ while ((c = getopt(argc, argv, "adhHkLors")) != EOF)
+#endif
+ switch (c) {
+
+ case 'a':
+ aflg++;
+ continue;
+
+ case 'h':
+ hflg++;
+ continue;
+
+ case 'r':
+ rflg++;
+ continue;
+
+ case 's':
+ sflg++;
+ continue;
+
+ case 'k':
+ kflg++;
+ continue;
+
+ case 'o':
+ oflg++;
+ continue;
+
+ case 'd':
+ dflg++;
+ continue;
+
+ case 'x':
+ dflg++;
+ continue;
+
+ case 'H':
+ Hflg++;
+ /* -H and -L are mutually exclusive */
+ Lflg = 0;
+ cmdarg++;
+ continue;
+
+ case 'L':
+ Lflg++;
+ /* -H and -L are mutually exclusive */
+ Hflg = 0;
+ cmdarg = 0;
+ continue;
+#ifdef XPG4
+ case '?':
+ (void) fprintf(stderr, gettext(
+ "usage: du [-a] [-h|-k] [-r] [-s] [-x] [-H|-L]"
+ " [file ...]\n"));
+ exit(2);
+#else
+ case '?':
+ (void) fprintf(stderr, gettext(
+ "usage: du [-a] [-d] [-h|-k] [-r] [-o|-s] [-H|-L]"
+ " [file ...]\n"));
+ exit(2);
+#endif
+ }
+ if (optind == argc) {
+ argv = &dot;
+ argc = 1;
+ optind = 0;
+ }
+
+ /* "-o" and "-s" don't make any sense together. */
+ if (oflg && sflg)
+ oflg = 0;
+
+ if ((base = (char *)calloc(base_len, sizeof (char))) == NULL) {
+ perror("du");
+ exit(1);
+ }
+ if ((name = (char *)calloc(name_len, sizeof (char))) == NULL) {
+ perror("du");
+ free(base);
+ exit(1);
+ }
+ do {
+ if (optind < argc - 1) {
+ pid = fork();
+ if (pid == (pid_t)-1) {
+ perror(gettext("du: No more processes"));
+ exitdu(1);
+ }
+ if (pid != 0) {
+ while ((wpid = wait(&status)) != pid &&
+ wpid != (pid_t)-1)
+ ;
+ if (pid != (pid_t)-1 && status != 0)
+ retcode = 1;
+ }
+ }
+ if (optind == argc - 1 || pid == 0) {
+ while (base_len < (strlen(argv[optind]) + 1)) {
+ base_len = base_len * 2;
+ if ((base = (char *)realloc(base, base_len *
+ sizeof (char))) == NULL) {
+ if (rflg) {
+ (void) fprintf(stderr, gettext(
+ "du: can't process %s"),
+ argv[optind]);
+ perror("");
+ }
+ exitdu(1);
+ }
+ }
+ if (base_len > name_len) {
+ name_len = base_len;
+ if ((name = (char *)realloc(name, name_len *
+ sizeof (char))) == NULL) {
+ if (rflg) {
+ (void) fprintf(stderr, gettext(
+ "du: can't process %s"),
+ argv[optind]);
+ perror("");
+ }
+ exitdu(1);
+ }
+ }
+ (void) strcpy(base, argv[optind]);
+ (void) strcpy(name, argv[optind]);
+ if (np = strrchr(name, '/')) {
+ *np++ = '\0';
+ if (chdir(*name ? name : "/") < 0) {
+ if (rflg) {
+ (void) fprintf(stderr, "du: ");
+ perror(*name ? name : "/");
+ exitdu(1);
+ }
+ exitdu(0);
+ }
+ } else
+ np = base;
+ blocks = descend(*np ? np : ".", 0, &retcode,
+ (dev_t)0);
+ if (sflg)
+ printsize(blocks, base);
+ if (optind < argc - 1)
+ exitdu(retcode);
+ }
+ optind++;
+ } while (optind < argc);
+ exitdu(retcode);
+
+ return (retcode);
+}
+
+/*
+ * descend recursively, adding up the allocated blocks.
+ * If curname is NULL, curfd is used.
+ */
+static u_longlong_t
+descend(char *curname, int curfd, int *retcode, dev_t device)
+{
+ static DIR *dirp = NULL;
+ char *ebase0, *ebase;
+ struct stat stb, stb1;
+ int i, j, ret, fd, tmpflg;
+ blkcnt_t blocks = 0;
+ off_t curoff = 0;
+ ptrdiff_t offset;
+ ptrdiff_t offset0;
+ struct dirent *dp;
+ char dirbuf[PATH_MAX + 1];
+ u_longlong_t retval;
+
+ ebase0 = ebase = strchr(base, 0);
+ if (ebase > base && ebase[-1] == '/')
+ ebase--;
+ offset = ebase - base;
+ offset0 = ebase0 - base;
+
+ if (curname)
+ curfd = AT_FDCWD;
+
+ /*
+ * If neither a -L or a -H was specified, don't follow symlinks.
+ * If a -H was specified, don't follow symlinks if the file is
+ * not a command line argument.
+ */
+ if (((Lflg == 0) && (Hflg == 0)) || ((Hflg) && !(cmdarg))) {
+ i = fstatat(curfd, curname, &stb, AT_SYMLINK_NOFOLLOW);
+ j = 0;
+ } else {
+ i = fstatat(curfd, curname, &stb, 0);
+ j = fstatat(curfd, curname, &stb1, AT_SYMLINK_NOFOLLOW);
+
+ /*
+ * Make sure any files encountered while traversing the
+ * hierarchy are not considered command line arguments.
+ */
+ if (Hflg) {
+ cmdarg = 0;
+ }
+ }
+
+ if ((i < 0) || (j < 0)) {
+ if (rflg) {
+ (void) fprintf(stderr, "du: ");
+ perror(base);
+ }
+
+ /*
+ * POSIX states that non-zero status codes are only set
+ * when an error message is printed out on stderr
+ */
+ *retcode = (rflg ? 1 : 0);
+ *ebase0 = 0;
+ return (0);
+ }
+ if (device) {
+ if (dflg && stb.st_dev != device) {
+ *ebase0 = 0;
+ return (0);
+ }
+ }
+ else
+ device = stb.st_dev;
+
+ /*
+ * If following links (-L) we need to keep track of all inodes
+ * visited so they are only visited/reported once and cycles
+ * are avoided. Otherwise, only keep track of files which are
+ * hard links so they only get reported once, and of directories
+ * so we don't report a directory and its hierarchy more than
+ * once in the special case in which it lies under the
+ * hierarchy of a directory which is a hard link.
+ * Note: Files with multiple links should only be counted
+ * once. Since each inode could possibly be referenced by a
+ * symbolic link, we need to keep track of all inodes when -L
+ * is specified.
+ */
+ if ((Lflg) || ((stb.st_mode & S_IFMT) == S_IFDIR) ||
+ (stb.st_nlink > 1)) {
+ int rc;
+ if ((rc = add_tnode(&tree, stb.st_dev, stb.st_ino)) != 1) {
+ if (rc == 0) {
+ /*
+ * This hierarchy, or file with multiple
+ * links, has already been visited/reported.
+ */
+ return (0);
+ } else {
+ /*
+ * An error occurred while trying to add the
+ * node to the tree.
+ */
+ if (rflg) {
+ perror("du");
+ }
+ exitdu(1);
+ }
+ }
+ }
+ blocks = stb.st_blocks;
+ /*
+ * If there are extended attributes on the current file, add their
+ * block usage onto the block count.
+ */
+ if (curname && pathconf(curname, _PC_XATTR_EXISTS) == 1) {
+ if ((fd = attropen(curname, ".", O_RDONLY)) < 0) {
+ if (rflg)
+ perror(gettext(
+ "du: can't access extended attributes"));
+ }
+ else
+ {
+ tmpflg = sflg;
+ sflg = 1;
+ blocks += descend(NULL, fd, retcode, device);
+ sflg = tmpflg;
+ }
+ }
+ if ((stb.st_mode & S_IFMT) != S_IFDIR) {
+ /*
+ * Don't print twice: if sflg, file will get printed in main().
+ * Otherwise, level == 0 means this file is listed on the
+ * command line, so print here; aflg means print all files.
+ */
+ if (sflg == 0 && (aflg || level == 0))
+ printsize(blocks, base);
+ return (blocks);
+ }
+ if (dirp != NULL)
+ /*
+ * Close the parent directory descriptor, we will reopen
+ * the directory when we pop up from this level of the
+ * recursion.
+ */
+ (void) closedir(dirp);
+ if (curname == NULL)
+ dirp = fdopendir(curfd);
+ else
+ dirp = opendir(curname);
+ if (dirp == NULL) {
+ if (rflg) {
+ (void) fprintf(stderr, "du: ");
+ perror(base);
+ }
+ *retcode = 1;
+ *ebase0 = 0;
+ return (0);
+ }
+ level++;
+ if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode))) {
+ if (getcwd(dirbuf, PATH_MAX) == NULL) {
+ if (rflg) {
+ (void) fprintf(stderr, "du: ");
+ perror(base);
+ }
+ exitdu(1);
+ }
+ }
+ if ((curname ? (chdir(curname) < 0) : (fchdir(curfd) < 0))) {
+ if (rflg) {
+ (void) fprintf(stderr, "du: ");
+ perror(base);
+ }
+ *retcode = 1;
+ *ebase0 = 0;
+ (void) closedir(dirp);
+ dirp = NULL;
+ level--;
+ return (0);
+ }
+ while (dp = readdir(dirp)) {
+ if ((strcmp(dp->d_name, ".") == 0) ||
+ (strcmp(dp->d_name, "..") == 0))
+ continue;
+ /*
+ * we're about to append "/" + dp->d_name
+ * onto end of base; make sure there's enough
+ * space
+ */
+ while ((offset + strlen(dp->d_name) + 2) > base_len) {
+ base_len = base_len * 2;
+ if ((base = (char *)realloc(base,
+ base_len * sizeof (char))) == NULL) {
+ if (rflg) {
+ perror("du");
+ }
+ exitdu(1);
+ }
+ ebase = base + offset;
+ ebase0 = base + offset0;
+ }
+ /* LINTED - unbounded string specifier */
+ (void) sprintf(ebase, "/%s", dp->d_name);
+ curoff = telldir(dirp);
+ retval = descend(ebase + 1, 0, retcode, device);
+ /* base may have been moved via realloc in descend() */
+ ebase = base + offset;
+ ebase0 = base + offset0;
+ *ebase = 0;
+ blocks += retval;
+ if (dirp == NULL) {
+ if ((dirp = opendir(".")) == NULL) {
+ if (rflg) {
+ (void) fprintf(stderr,
+ gettext("du: Can't reopen in "));
+ perror(base);
+ }
+ *retcode = 1;
+ level--;
+ return (0);
+ }
+ seekdir(dirp, curoff);
+ }
+ }
+ (void) closedir(dirp);
+ level--;
+ dirp = NULL;
+ if (sflg == 0)
+ printsize(blocks, base);
+ if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode)))
+ ret = chdir(dirbuf);
+ else
+ ret = chdir("..");
+ if (ret < 0) {
+ if (rflg) {
+ (void) sprintf(strchr(base, '\0'), "/..");
+ (void) fprintf(stderr,
+ gettext("du: Can't change dir to '..' in "));
+ perror(base);
+ }
+ exitdu(1);
+ }
+ *ebase0 = 0;
+ if (oflg)
+ return (0);
+ else
+ return (blocks);
+}
+
+/*
+ * Convert an unsigned long long to a string representation and place the
+ * result in the caller-supplied buffer.
+ * The given number is in units of "unit_from" size,
+ * this will first be converted to a number in 1024 or 1000 byte size,
+ * depending on the scaling factor.
+ * Then the number is scaled down until it is small enough to be in a good
+ * human readable format i.e. in the range 0 thru scale-1.
+ * If it's smaller than 10 there's room enough to provide one decimal place.
+ * The value "(unsigned long long)-1" is a special case and is always
+ * converted to "-1".
+ * Returns a pointer to the caller-supplied buffer.
+ */
+static char *
+number_to_scaled_string(
+ numbuf_t buf, /* put the result here */
+ unsigned long long number, /* convert this number */
+ unsigned long long unit_from, /* number of bytes per input unit */
+ unsigned long long scale) /* 1024 (-h) or 1000 (-H) */
+{
+ unsigned long long save = 0;
+ char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
+ char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */
+
+ if ((long long)number == (long long)-1) {
+ (void) strcpy(buf, "-1");
+ return (buf);
+ }
+
+ /*
+ * Convert number from unit_from to given scale (1024 or 1000)
+ * This means multiply number with unit_from and divide by scale.
+ * if number is large enough, we first divide and then multiply
+ * to avoid an overflow
+ * (large enough here means 100 (rather arbitrary value)
+ * times scale in order to reduce rounding errors)
+ * otherwise, we first multiply and then divide
+ * to avoid an underflow
+ */
+ if (number >= 100L * scale) {
+ number = number / scale;
+ number = number * unit_from;
+ } else {
+ number = number * unit_from;
+ number = number / scale;
+ }
+
+ /*
+ * Now we have number as a count of scale units.
+ * Stop scaling when we reached exa bytes, then something is
+ * probably wrong with our number.
+ */
+ while ((number >= scale) && (*uom != 'E')) {
+ uom++; /* next unit of measurement */
+ save = number;
+ number = (number + (scale / 2)) / scale;
+ }
+
+ /* check if we should output a decimal place after the point */
+ if (save && ((save / scale) < 10)) {
+ /* sprintf() will round for us */
+ float fnum = (float)save / scale;
+ (void) sprintf(buf, "%4.1f%c", fnum, *uom);
+ } else {
+ (void) sprintf(buf, "%4llu%c", number, *uom);
+ }
+ return (buf);
+}
+
+static void
+printsize(blkcnt_t blocks, char *path)
+{
+ if (hflg) {
+ numbuf_t numbuf;
+ unsigned long long scale = 1024L;
+#ifdef XPG4
+ (void) printf("%s %s\n",
+ number_to_scaled_string(numbuf, blocks, DEV_BSIZE, scale),
+ path);
+#else
+ (void) printf("%s\t%s\n",
+ number_to_scaled_string(numbuf, blocks, DEV_BSIZE, scale),
+ path);
+#endif
+ } else if (kflg) {
+#ifdef XPG4
+ (void) printf("%lld %s\n", (long long)kb(blocks), path);
+#else
+ (void) printf("%lld\t%s\n", (long long)kb(blocks), path);
+#endif
+ } else {
+#ifdef XPG4
+ (void) printf("%lld %s\n", (long long)blocks, path);
+#else
+ (void) printf("%lld\t%s\n", (long long)blocks, path);
+#endif
+ }
+}
+
+static void
+exitdu(int exitcode)
+{
+ free(base);
+ free(name);
+ exit(exitcode);
+}