[hackers] [sbase] Audit du(1) and refactor recurse() || FRIGN
commit 25d0ca0e0877310496368b69aeb56db042a58975
Author: FRIGN <dev_AT_frign.de>
Date: Wed Mar 11 23:21:52 2015 +0100
Audit du(1) and refactor recurse()
While auditing du(1) I realized that there's no way the over 100 lines
of procedures in du() would pass the audit.
Instead, I decided to rewrite this section using recurse() from libutil.
However, the issue was that you'd need some kind of payload to count
the number of bytes in the subdirectories and use them in the higher
hierarchies.
The solution is to add a "void *data" data pointer to each recurse-
function-prototype, which we might also be able to use in other
recurse-applications.
recurse() itself had to be augmented with a recurse_samedev-flag, which
basically prevents recurse from leaving the current device.
Now, let's take a closer look at the audit:
1) Removing the now unnecessary util-functions push, pop, xrealpath,
rename print() to printpath(), localize some global variables.
2) Only pass the block count to nblks instead of the entire stat-
pointer.
3) Fix estrtonum to use the minimum of LLONG_MAX and SIZE_MAX.
4) Use idiomatic argv+argc-loop
5) Report proper exit-status.
diff --git a/chgrp.c b/chgrp.c
index 17a23f9..da483d2 100644
--- a/chgrp.c
+++ b/chgrp.c
_AT_@ -14,7 +14,7 @@ static gid_t gid = -1;
static int ret = 0;
static void
-chgrp(const char *path, int depth)
+chgrp(const char *path, int depth, void *data)
{
char *chownf_name;
int (*chownf)(const char *, uid_t, gid_t);
_AT_@ -31,7 +31,7 @@ chgrp(const char *path, int depth)
weprintf("%s %s:", chownf_name, path);
ret = 1;
} else if (Rflag) {
- recurse(path, chgrp, depth);
+ recurse(path, chgrp, depth, NULL);
}
}
_AT_@ -80,7 +80,7 @@ main(int argc, char *argv[])
ret = 1;
continue;
}
- chgrp(*argv, 0);
+ chgrp(*argv, 0, NULL);
}
return ret;
diff --git a/chmod.c b/chmod.c
index f7ab866..fcbf65a 100644
--- a/chmod.c
+++ b/chmod.c
_AT_@ -9,7 +9,7 @@ static mode_t mask = 0;
static int ret = 0;
static void
-chmodr(const char *path, int depth)
+chmodr(const char *path, int depth, void *data)
{
struct stat st;
mode_t m;
_AT_@ -25,7 +25,7 @@ chmodr(const char *path, int depth)
weprintf("chmod %s:", path);
ret = 1;
} else if (Rflag)
- recurse(path, chmodr, depth);
+ recurse(path, chmodr, depth, NULL);
}
static void
_AT_@ -80,7 +80,7 @@ done:
usage();
for (--argc, ++argv; *argv; argc--, argv++)
- chmodr(*argv, 0);
+ chmodr(*argv, 0, NULL);
return ret;
}
diff --git a/chown.c b/chown.c
index 417944b..8d7ff16 100644
--- a/chown.c
+++ b/chown.c
_AT_@ -16,7 +16,7 @@ static gid_t gid = -1;
static int ret = 0;
static void
-chownpwgr(const char *path, int depth)
+chownpwgr(const char *path, int depth, void *data)
{
char *chownf_name;
int (*chownf)(const char *, uid_t, gid_t);
_AT_@ -33,7 +33,7 @@ chownpwgr(const char *path, int depth)
weprintf("%s %s:", chownf_name, path);
ret = 1;
} else if (Rflag) {
- recurse(path, chownpwgr, depth);
+ recurse(path, chownpwgr, depth, NULL);
}
}
_AT_@ -97,7 +97,7 @@ main(int argc, char *argv[])
}
}
for (argc--, argv++; *argv; argc--, argv++)
- chownpwgr(*argv, 0);
+ chownpwgr(*argv, 0, NULL);
return ret;
}
diff --git a/du.c b/du.c
index 56b4b9f..0c46629 100644
--- a/du.c
+++ b/du.c
_AT_@ -2,140 +2,56 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <stdio.h>
#include "util.h"
-static size_t blksize = 512;
-static char file[PATH_MAX];
-static size_t depth = -1;
-static size_t curdepth = 0;
+static size_t maxdepth = SIZE_MAX;
+static size_t blksize = 512;
static int aflag = 0;
-static int dflag = 0;
static int sflag = 0;
-static int kflag = 0;
static int hflag = 0;
-static int xflag = 0;
-static int HLPflag = 'P';
-
-static char *
-xrealpath(const char *pathname, char *resolved)
-{
- char *r;
-
- r = realpath(pathname, resolved);
- if (!r)
- eprintf("realpath: %s:", pathname);
- return r;
-}
+static int ret = 0;
static void
-print(size_t n, char *path)
+printpath(size_t n, const char *path)
{
if (hflag)
printf("%s\t%s\n", humansize(n * blksize), path);
else
- printf("%lu\t%s\n", n, path);
-}
-
-static char *
-push(const char *path)
-{
- char *cwd;
-
- cwd = agetcwd();
- if (chdir(path) < 0)
- weprintf("chdir: %s:", path);
- return cwd;
-}
-
-static void
-pop(char *path)
-{
- if (chdir(path) < 0)
- weprintf("chdir: %s:", path);
- free(path);
+ printf("%zu\t%s\n", n, path);
}
static size_t
-nblks(struct stat *st)
+nblks(blkcnt_t blocks)
{
- return (512 * st->st_blocks + blksize - 1) / blksize;
+ return (512 * blocks + blksize - 1) / blksize;
}
-static size_t
-du(const char *path, char follow)
+void
+du(const char *path, int depth, void *total)
{
- struct dirent *dent;
- struct stat pst, st;
- DIR *dp;
- size_t n = 0, m, t;
- int r;
- char *cwd;
-
- if (lstat(path, &pst) < 0)
- eprintf("stat: %s:", path);
- n = nblks(&pst);
-
- if (!(S_ISDIR(pst.st_mode) ||
- (follow != 'P' && S_ISLNK(pst.st_mode) &&
- stat(path, &pst) == 0 && S_ISDIR(pst.st_mode))))
- goto done;
-
- follow = follow == 'H' ? 'P' : follow;
-
- dp = opendir(path);
- if (!dp) {
- weprintf("opendir %s:", path);
- goto done;
+ struct stat st;
+ size_t subtotal = 0;
+
+ if (lstat(path, &st) < 0) {
+ if (!depth || errno != ENOENT)
+ weprintf("stat %s:", path);
+ if (!depth)
+ ret = 1;
+ return;
}
- cwd = push(path);
- while ((dent = readdir(dp))) {
- if (strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0)
- continue;
- if (lstat(dent->d_name, &st) < 0)
- eprintf("stat: %s:", dent->d_name);
- if (xflag && st.st_dev != pst.st_dev)
- continue;
- if (S_ISDIR(st.st_mode) ||
- (follow != 'P' && S_ISLNK(st.st_mode) &&
- stat(dent->d_name, &st) == 0 && S_ISDIR(st.st_mode))) {
- t = curdepth;
- curdepth++;
- n += du(dent->d_name, follow);
- curdepth = t;
- continue;
- }
- m = nblks(&st);
- n += m;
- if (aflag && !sflag) {
- if (S_ISLNK(st.st_mode)) {
- r = snprintf(file, sizeof(file), "%s/%s",
- cwd, dent->d_name);
- if (r >= sizeof(file) || r < 0)
- eprintf("path too long\n");
- } else {
- xrealpath(dent->d_name, file);
- }
- if (!dflag || (depth != -1 && curdepth < depth))
- print(m, file);
- }
- }
- pop(cwd);
- closedir(dp);
+ recurse(path, du, depth, &subtotal);
+ *((size_t *)total) += subtotal + nblks(st.st_blocks);
-done:
- if (!sflag && (!dflag || (depth != -1 && curdepth <= depth)))
- print(n, xrealpath(path, file));
- return n;
+ if (!sflag && depth <= maxdepth && (S_ISDIR(st.st_mode) || aflag))
+ printpath(subtotal + nblks(st.st_blocks), path);
}
static void
_AT_@ -147,7 +63,8 @@ usage(void)
int
main(int argc, char *argv[])
{
- size_t n;
+ int kflag = 0, dflag = 0;
+ size_t n = 0;
char *bsize;
ARGBEGIN {
_AT_@ -156,24 +73,24 @@ main(int argc, char *argv[])
break;
case 'd':
dflag = 1;
- depth = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
+ maxdepth = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
break;
- case 's':
- sflag = 1;
+ case 'h':
+ hflag = 1;
break;
case 'k':
kflag = 1;
break;
- case 'h':
- hflag = 1;
+ case 's':
+ sflag = 1;
+ break;
+ case 'x':
+ recurse_samedev = 1;
break;
case 'H':
case 'L':
case 'P':
- HLPflag = ARGC();
- break;
- case 'x':
- xflag = 1;
+ recurse_follow = ARGC();
break;
default:
usage();
_AT_@ -184,22 +101,21 @@ main(int argc, char *argv[])
bsize = getenv("BLOCKSIZE");
if (bsize)
- blksize = estrtonum(bsize, 0, LONG_MAX);
-
+ blksize = estrtonum(bsize, 0, MIN(LLONG_MAX, SIZE_MAX));
if (kflag)
blksize = 1024;
- if (argc < 1) {
- n = du(".", HLPflag);
- if (sflag)
- print(n, xrealpath(".", file));
+ if (!argc) {
+ du(".", 0, &n);
+ if (sflag && !ret)
+ printpath(nblks(n), ".");
} else {
- for (; argc > 0; argc--, argv++) {
- curdepth = 0;
- n = du(argv[0], HLPflag);
- if (sflag)
- print(n, xrealpath(argv[0], file));
+ for (; *argv; argc--, argv++) {
+ du(argv[0], 0, &n);
+ if (sflag && !ret)
+ printpath(n, argv[0]);
}
}
- return 0;
+
+ return ret;
}
diff --git a/fs.h b/fs.h
index 53cc39a..d826625 100644
--- a/fs.h
+++ b/fs.h
_AT_@ -12,4 +12,4 @@ extern int rm_rflag;
extern int rm_status;
int cp(const char *, const char *, int);
-void rm(const char *, int);
+void rm(const char *, int, void *);
diff --git a/libutil/recurse.c b/libutil/recurse.c
index f7b1f61..3c30a7e 100644
--- a/libutil/recurse.c
+++ b/libutil/recurse.c
_AT_@ -1,5 +1,6 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
+#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
_AT_@ -10,21 +11,28 @@
#include "../util.h"
-int recurse_follow = 'P';
+int recurse_follow = 'P';
+int recurse_samedev = 0;
void
-recurse(const char *path, void (*fn)(const char *, int), int depth)
+recurse(const char *path, void (*fn)(const char *, int, void *), int depth, void *data)
{
struct dirent *d;
- struct stat lst, st;
+ struct stat lst, st, dst;
DIR *dp;
size_t len;
char *buf;
- if (lstat(path, &lst) < 0)
- eprintf("lstat %s:", path);
- if (stat(path, &st) < 0)
- eprintf("stat %s:", path);
+ if (lstat(path, &lst) < 0) {
+ if (errno != ENOENT)
+ weprintf("lstat %s:", path);
+ return;
+ }
+ if (stat(path, &st) < 0) {
+ if (errno != ENOENT)
+ weprintf("stat %s:", path);
+ return;
+ }
if (!S_ISDIR(lst.st_mode) && !(S_ISLNK(lst.st_mode) && S_ISDIR(st.st_mode) &&
!(recurse_follow == 'P' || (recurse_follow == 'H' && depth > 0))))
return;
_AT_@ -36,9 +44,13 @@ recurse(const char *path, void (*fn)(const char *, int), int depth)
while ((d = readdir(dp))) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
- buf = emalloc(len + (path[len] != '/') + strlen(d->d_name) + 1);
- sprintf(buf, "%s%s%s", path, (path[len] == '/') ? "" : "/", d->d_name);
- fn(buf, depth + 1);
+ buf = emalloc(len + (path[len - 1] != '/') + strlen(d->d_name) + 1);
+ sprintf(buf, "%s%s%s", path, (path[len - 1] == '/') ? "" : "/", d->d_name);
+ if (recurse_samedev && lstat(buf, &dst) < 0) {
+ if (errno != ENOENT)
+ weprintf("stat %s:", buf);
+ } else if (!(recurse_samedev && dst.st_dev != lst.st_dev))
+ fn(buf, depth + 1, data);
free(buf);
}
diff --git a/libutil/rm.c b/libutil/rm.c
index 60c97c1..88ac788 100644
--- a/libutil/rm.c
+++ b/libutil/rm.c
_AT_@ -10,10 +10,10 @@ int rm_rflag = 0;
int rm_status = 0;
void
-rm(const char *path, int depth)
+rm(const char *path, int depth, void *data)
{
if (rm_rflag)
- recurse(path, rm, depth);
+ recurse(path, rm, depth, NULL);
if (remove(path) < 0) {
if (!rm_fflag)
weprintf("remove %s:", path);
diff --git a/mv.c b/mv.c
index 6734ee9..78e58c7 100644
--- a/mv.c
+++ b/mv.c
_AT_@ -19,7 +19,7 @@ mv(const char *s1, const char *s2, int depth)
cp_HLPflag = 'P';
rm_rflag = 1;
cp(s1, s2, depth);
- rm(s1, 0);
+ rm(s1, 0, NULL);
return (mv_status = cp_status || rm_status);
}
mv_status = 1;
diff --git a/rm.c b/rm.c
index 3b539e6..6738ef1 100644
--- a/rm.c
+++ b/rm.c
_AT_@ -31,7 +31,7 @@ main(int argc, char *argv[])
}
for (; *argv; argc--, argv++)
- rm(*argv, 0);
+ rm(*argv, 0, NULL);
return rm_status;
}
diff --git a/tar.c b/tar.c
index 8c6fdac..d423d63 100644
--- a/tar.c
+++ b/tar.c
_AT_@ -234,10 +234,10 @@ print(char * fname, int l, char b[BLKSIZ])
}
static void
-c(const char *path, int depth)
+c(const char *path, int depth, void *data)
{
archive(path);
- recurse(path, c, depth);
+ recurse(path, c, depth, NULL);
}
static void
_AT_@ -316,7 +316,7 @@ main(int argc, char *argv[])
tarfile = stdout;
}
chdir(dir);
- c(argv[0], 0);
+ c(argv[0], 0, NULL);
break;
case 't':
case 'x':
diff --git a/util.h b/util.h
index c400a50..2beae2d 100644
--- a/util.h
+++ b/util.h
_AT_@ -66,7 +66,8 @@ char *humansize(double);
mode_t parsemode(const char *, mode_t, mode_t);
void putword(const char *);
extern int recurse_follow;
-void recurse(const char *, void (*)(const char *, int), int);
+extern int recurse_samedev;
+void recurse(const char *, void (*)(const char *, int, void *), int, void *);
#undef strtonum
long long strtonum(const char *, long long, long long, const char **);
long long enstrtonum(int, const char *, long long, long long);
Received on Thu Mar 12 2015 - 00:55:49 CET
This archive was generated by hypermail 2.3.0
: Thu Mar 12 2015 - 01:00:17 CET