commit edbcc223ea9ef9c3f039352f787af0644d816ee7 Author: Michael Forney <mfor...@mforney.org> AuthorDate: Sun Dec 22 13:53:46 2019 -0800 Commit: Michael Forney <mfor...@mforney.org> CommitDate: Thu Mar 5 00:45:53 2020 -0800
libutil/recurse: Use a single path buffer, and directory fd This way, we don't use PATH_MAX bytes on the stack per path component, and don't have to keep copying the complete path around. diff --git a/chgrp.c b/chgrp.c index 04b2dd0..d336c88 100644 --- a/chgrp.c +++ b/chgrp.c @@ -14,18 +14,17 @@ static gid_t gid = -1; static int ret = 0; static void -chgrp(const char *path, struct stat *st, void *data, struct recursor *r) +chgrp(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r) { int flags = 0; if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth) || (hflag && !(r->depth))) flags |= AT_SYMLINK_NOFOLLOW; - - if (fchownat(AT_FDCWD, path, -1, gid, flags) < 0) { - weprintf("chown %s:", path); + if (fchownat(dirfd, name, -1, gid, flags) < 0) { + weprintf("chown %s:", r->path); ret = 1; } else if (S_ISDIR(st->st_mode)) { - recurse(path, NULL, r); + recurse(dirfd, name, NULL, r); } } @@ -71,7 +70,7 @@ main(int argc, char *argv[]) } for (argc--, argv++; *argv; argc--, argv++) - recurse(*argv, NULL, &r); + recurse(AT_FDCWD, *argv, NULL, &r); return ret || recurse_status; } diff --git a/chmod.c b/chmod.c index 7b9afe8..554959f 100644 --- a/chmod.c +++ b/chmod.c @@ -1,4 +1,5 @@ /* See LICENSE file for copyright and license details. */ +#include <fcntl.h> #include <sys/stat.h> #include "fs.h" @@ -9,16 +10,16 @@ static mode_t mask = 0; static int ret = 0; static void -chmodr(const char *path, struct stat *st, void *data, struct recursor *r) +chmodr(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r) { mode_t m; m = parsemode(modestr, st->st_mode, mask); - if (!S_ISLNK(st->st_mode) && chmod(path, m) < 0) { - weprintf("chmod %s:", path); + if (!S_ISLNK(st->st_mode) && fchmodat(dirfd, name, m, 0) < 0) { + weprintf("chmod %s:", r->path); ret = 1; } else if (S_ISDIR(st->st_mode)) { - recurse(path, NULL, r); + recurse(dirfd, name, NULL, r); } } @@ -71,7 +72,7 @@ done: usage(); for (--argc, ++argv; *argv; argc--, argv++) - recurse(*argv, NULL, &r); + recurse(AT_FDCWD, *argv, NULL, &r); return ret || recurse_status; } diff --git a/chown.c b/chown.c index dcd4914..3aaedef 100644 --- a/chown.c +++ b/chown.c @@ -17,18 +17,18 @@ static gid_t gid = -1; static int ret = 0; static void -chownpwgr(const char *path, struct stat *st, void *data, struct recursor *r) +chownpwgr(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r) { int flags = 0; if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth) || (hflag && !(r->depth))) flags |= AT_SYMLINK_NOFOLLOW; - if (fchownat(AT_FDCWD, path, uid, gid, flags) < 0) { - weprintf("chown %s:", path); + if (fchownat(dirfd, name, uid, gid, flags) < 0) { + weprintf("chown %s:", r->path); ret = 1; } else if (S_ISDIR(st->st_mode)) { - recurse(path, NULL, r); + recurse(dirfd, name, NULL, r); } } @@ -99,7 +99,7 @@ main(int argc, char *argv[]) usage(); for (argc--, argv++; *argv; argc--, argv++) - recurse(*argv, NULL, &r); + recurse(AT_FDCWD, *argv, NULL, &r); return ret || recurse_status; } diff --git a/du.c b/du.c index 4ceac66..fa6a8d6 100644 --- a/du.c +++ b/du.c @@ -3,6 +3,7 @@ #include <sys/types.h> #include <errno.h> +#include <fcntl.h> #include <limits.h> #include <stdint.h> #include <stdlib.h> @@ -35,19 +36,19 @@ nblks(blkcnt_t blocks) } static void -du(const char *path, struct stat *st, void *data, struct recursor *r) +du(int dirfd, const char *path, struct stat *st, void *data, struct recursor *r) { off_t *total = data, subtotal; subtotal = nblks(st->st_blocks); if (S_ISDIR(st->st_mode)) - recurse(path, &subtotal, r); + recurse(dirfd, path, &subtotal, r); *total += subtotal; if (!r->depth) - printpath(*total, path); + printpath(*total, r->path); else if (!sflag && r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag)) - printpath(subtotal, path); + printpath(subtotal, r->path); } static void @@ -104,11 +105,11 @@ main(int argc, char *argv[]) blksize = 1024; if (!argc) { - recurse(".", &n, &r); + recurse(AT_FDCWD, ".", &n, &r); } else { for (; *argv; argc--, argv++) { n = 0; - recurse(*argv, &n, &r); + recurse(AT_FDCWD, *argv, &n, &r); } } diff --git a/fs.h b/fs.h index 15ae5f4..00ecd3b 100644 --- a/fs.h +++ b/fs.h @@ -1,4 +1,5 @@ /* See LICENSE file for copyright and license details. */ +#include <limits.h> #include <sys/stat.h> #include <sys/types.h> @@ -9,7 +10,9 @@ struct history { }; struct recursor { - void (*fn)(const char *, struct stat *st, void *, struct recursor *); + void (*fn)(int, const char *, struct stat *, void *, struct recursor *); + char path[PATH_MAX]; + size_t pathlen; struct history *hist; int depth; int maxdepth; @@ -37,7 +40,7 @@ extern int rm_status; extern int recurse_status; -void recurse(const char *, void *, struct recursor *); +void recurse(int, const char *, void *, struct recursor *); int cp(const char *, const char *, int); -void rm(const char *, struct stat *st, void *, struct recursor *); +void rm(int, const char *, struct stat *st, void *, struct recursor *); diff --git a/libutil/recurse.c b/libutil/recurse.c index e150fb9..e21d815 100644 --- a/libutil/recurse.c +++ b/libutil/recurse.c @@ -16,27 +16,30 @@ int recurse_status = 0; void -recurse(const char *path, void *data, struct recursor *r) +recurse(int dirfd, const char *name, void *data, struct recursor *r) { struct dirent *d; struct history *new, *h; struct stat st, dst; DIR *dp; - char subpath[PATH_MAX]; - int flags = 0; + int flags = 0, fd; + size_t pathlen = r->pathlen; + + if (dirfd == AT_FDCWD) + pathlen = estrlcpy(r->path, name, sizeof(r->path)); if (r->follow == 'P' || (r->follow == 'H' && r->depth)) flags |= AT_SYMLINK_NOFOLLOW; - if (fstatat(AT_FDCWD, path, &st, flags) < 0) { + if (fstatat(dirfd, name, &st, flags) < 0) { if (!(r->flags & SILENT)) { - weprintf("stat %s:", path); + weprintf("stat %s:", r->path); recurse_status = 1; } return; } if (!S_ISDIR(st.st_mode)) { - r->fn(path, &st, data, r); + r->fn(dirfd, name, &st, data, r); return; } @@ -51,35 +54,39 @@ recurse(const char *path, void *data, struct recursor *r) return; if (!r->depth && (r->flags & DIRFIRST)) - r->fn(path, &st, data, r); + r->fn(dirfd, name, &st, data, r); if (!r->maxdepth || r->depth + 1 < r->maxdepth) { - if (!(dp = opendir(path))) { + fd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (fd < 0) { + weprintf("open %s:", r->path); + recurse_status = 1; + } + if (!(dp = fdopendir(fd))) { if (!(r->flags & SILENT)) { - weprintf("opendir %s:", path); + weprintf("fdopendir:"); recurse_status = 1; } return; } + if (r->path[pathlen - 1] != '/') + pathlen += estrlcpy(r->path + pathlen, "/", sizeof(r->path) - pathlen); + if (r->follow == 'H') + flags |= AT_SYMLINK_NOFOLLOW; while ((d = readdir(dp))) { - if (r->follow == 'H') - flags |= AT_SYMLINK_NOFOLLOW; if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; - estrlcpy(subpath, path, sizeof(subpath)); - if (path[strlen(path) - 1] != '/') - estrlcat(subpath, "/", sizeof(subpath)); - estrlcat(subpath, d->d_name, sizeof(subpath)); - if (fstatat(AT_FDCWD, subpath, &dst, flags) < 0) { + r->pathlen = pathlen + estrlcpy(r->path + pathlen, d->d_name, sizeof(r->path) - pathlen); + if (fstatat(fd, d->d_name, &dst, flags) < 0) { if (!(r->flags & SILENT)) { - weprintf("stat %s:", subpath); + weprintf("stat %s:", r->path); recurse_status = 1; } } else if ((r->flags & SAMEDEV) && dst.st_dev != st.st_dev) { continue; } else { r->depth++; - r->fn(subpath, &dst, data, r); + r->fn(fd, d->d_name, &dst, data, r); r->depth--; } } @@ -88,7 +95,7 @@ recurse(const char *path, void *data, struct recursor *r) if (!r->depth) { if (!(r->flags & DIRFIRST)) - r->fn(path, &st, data, r); + r->fn(dirfd, name, &st, data, r); while (r->hist) { h = r->hist; diff --git a/libutil/rm.c b/libutil/rm.c index 11c20fb..8d4be08 100644 --- a/libutil/rm.c +++ b/libutil/rm.c @@ -2,6 +2,7 @@ #include <sys/stat.h> #include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <unistd.h> @@ -11,20 +12,20 @@ int rm_status = 0; void -rm(const char *path, struct stat *st, void *data, struct recursor *r) +rm(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r) { if (!r->maxdepth && S_ISDIR(st->st_mode)) { - recurse(path, NULL, r); + recurse(dirfd, name, NULL, r); - if (rmdir(path) < 0) { + if (unlinkat(dirfd, name, AT_REMOVEDIR) < 0) { if (!(r->flags & SILENT)) - weprintf("rmdir %s:", path); + weprintf("rmdir %s:", r->path); if (!((r->flags & SILENT) && errno == ENOENT)) rm_status = 1; } - } else if (unlink(path) < 0) { + } else if (unlinkat(dirfd, name, 0) < 0) { if (!(r->flags & SILENT)) - weprintf("unlink %s:", path); + weprintf("unlink %s:", r->path); if (!((r->flags & SILENT) && errno == ENOENT)) rm_status = 1; } diff --git a/mv.c b/mv.c index 2cf98d1..c93f72d 100644 --- a/mv.c +++ b/mv.c @@ -2,6 +2,7 @@ #include <sys/stat.h> #include <errno.h> +#include <fcntl.h> #include <stdio.h> #include "fs.h" @@ -21,7 +22,7 @@ mv(const char *s1, const char *s2, int depth) cp_aflag = cp_rflag = cp_pflag = 1; cp_follow = 'P'; cp(s1, s2, depth); - recurse(s1, NULL, &r); + recurse(AT_FDCWD, s1, NULL, &r); return (mv_status = cp_status || rm_status); } mv_status = 1; diff --git a/rm.c b/rm.c index 42bc022..b776d0f 100644 --- a/rm.c +++ b/rm.c @@ -1,4 +1,6 @@ /* See LICENSE file for copyright and license details. */ +#include <fcntl.h> + #include "fs.h" #include "util.h" @@ -34,7 +36,7 @@ main(int argc, char *argv[]) } for (; *argv; argc--, argv++) - recurse(*argv, NULL, &r); + recurse(AT_FDCWD, *argv, NULL, &r); return rm_status || recurse_status; } diff --git a/tar.c b/tar.c index f9158c2..603e544 100644 --- a/tar.c +++ b/tar.c @@ -368,14 +368,14 @@ print(char *fname, ssize_t l, char b[BLKSIZ]) } static void -c(const char *path, struct stat *st, void *data, struct recursor *r) +c(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r) { - archive(path); + archive(r->path); if (vflag) - puts(path); + puts(r->path); if (S_ISDIR(st->st_mode)) - recurse(path, NULL, r); + recurse(dirfd, name, NULL, r); } static void @@ -578,7 +578,7 @@ main(int argc, char *argv[]) if (chdir(dir) < 0) eprintf("chdir %s:", dir); for (; *argv; argc--, argv++) - recurse(*argv, NULL, &r); + recurse(AT_FDCWD, *argv, NULL, &r); break; case 't': case 'x':