From d62e5c1df92ee159d0203b75adebb50faf45badb Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Thu, 18 Jun 2020 19:46:25 -0700 Subject: feat: small filesystem walker added --- sys/cmd/walk/rules.mk | 13 ++++ sys/cmd/walk/walk.c | 82 ++++++++++++++++++++ sys/libn/arg.c | 1 + sys/libn/fs.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 298 insertions(+) create mode 100644 sys/cmd/walk/rules.mk create mode 100644 sys/cmd/walk/walk.c create mode 100644 sys/libn/arg.c create mode 100644 sys/libn/fs.c (limited to 'sys') diff --git a/sys/cmd/walk/rules.mk b/sys/cmd/walk/rules.mk new file mode 100644 index 0000000..ee2321a --- /dev/null +++ b/sys/cmd/walk/rules.mk @@ -0,0 +1,13 @@ +include share/push.mk + +# Local sources +SRCS_$(d) := $(d)/walk.c +BINS_$(d) := $(d)/walk + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a + $(COMPLINK) + +include share/pop.mk diff --git a/sys/cmd/walk/walk.c b/sys/cmd/walk/walk.c new file mode 100644 index 0000000..12f6c55 --- /dev/null +++ b/sys/cmd/walk/walk.c @@ -0,0 +1,82 @@ +#include +#include + +static char buf[4*1024], *c = buf; /* should be greater or equal to PATH_MAX */ + +static +void +flush(void) +{ + *c = 0; + puts(buf); + c = buf; +} + +static +void +print(void *data, char *rel, char *abs, io·Stat *info) +{ +copy: + while (*abs && c < (arrend(buf)-2)) + *c++ = *abs++; + + if (*abs) { + flush(); + goto copy; + } + *c++ = '\n'; +} + +static +void +usage(void) +{ + fprintf(stderr, "usage: walk [-dlpv] file ...\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int i, f = fs·nolinks, err, max = 0; + char *p; + static fs·Walker walker; + + ARGBEGIN{ + case 'd': + max = atoi(ARGF()); + break; + case 'l': + f ^= fs·nolinks; + break; + case 'p': + f |= fs·preorder; + break; + case 'v': + f |= fs·verbose; + break; + default: + usage(); + }ARGEND; + + walker.flags = f; + walker.func = print; + walker.data = nil; + walker.max = max; + + if (argc == 0) { + fs·init(&walker, ""); + fs·walk(&walker); + return(err = walker.err); + } else { + err = 0; + for (i=0; i +#include +#include +#include + +/* + * path history + */ + +struct Key +{ + ino_t ino; + dev_t dev; +}; + +static +void* +xalloc(void *_, int n, uintptr size) +{ + return malloc(n*size); +} + +static +void +xfree(void *_, void *ptr) +{ + return free(ptr); +} + +#define hash(k) ((int32)k.ino ^ (int32)k.dev) +#define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev) + +struct fs·History +{ + SET_STRUCT_BODY(struct Key); +}; + +static +int +morehistory(fs·History *h, int n) +{ + SET_GROW(h, struct Key, n, hash, xalloc, xfree, nil); +} + +static +int +addentry(fs·History *h, struct Key key, int *err) +{ + SET_PUT(h, key, hash, equal, morehistory, err); +} + +static +void +forget(fs·History *h) +{ + if (!h) + return; + + SET_RESET(h); +} + +static +void +delete(fs·History *h) +{ + SET_FREE(h, xfree, nil); +} + +#undef hash +#undef equal + +static +char * +strcpyn(char *dst, char *src, int n) +{ + while(*src && n-- > 0) + *dst++ = *src++; + + *dst = 0; + return dst; +} + +/* + * main functions + */ + +int +fs·init(fs·Walker *fs, char *path) +{ + fs->base = fs->end = fs->path; + + if (!path || !path[0]) { + path = getcwd(fs->path, arrlen(fs->path)); + if (!path) + return 1; + fs->end += strlen(path); + } else + fs->end = strcpyn(fs->base, path, arrlen(fs->path)); + + if (fs->path[0] != '/') + fs->fd = AT_FDCWD; + + if (!fs->hist && !(fs->flags & fs·nolinks)) + fs->hist = calloc(1, sizeof(*fs->hist)); + + return 0; +} + +void +fs·fini(fs·Walker *fs) +{ + if (fs->hist) { + delete(fs->hist); + free(fs->hist); + } +} + +void +fs·walk(fs·Walker *fs) +{ + char *e, *b; + DIR *dir; + int new, fd, ofd, flags; + fs·History *h; + struct dirent *d; + io·Stat cwd; + struct fs·Entry *it; + + flags = 0; + if(fs->flags & fs·nolinks) + flags |= AT_SYMLINK_NOFOLLOW; + + /* get info for base relative to current fd */ + if(fstatat(fs->fd, fs->base, &cwd, flags) < 0){ + if(fs->flags & fs·verbose) + errorf("stat: %s", fs->path); + return; + } + + /* if we hit a file, finish! */ + if(!S_ISDIR(cwd.st_mode)) { + fs->func(fs->data, fs->base, fs->path, &cwd); + return; + } + + /* have we been here before? (cycle detection) */ + /* if not, add to our path history */ + if (!(fs->flags & fs·nolinks)) { + addentry(fs->hist, (struct Key){.dev=cwd.st_dev, .ino=cwd.st_ino}, &new); + if (!new) + return; + } + + /* operate on directory if preorder traversal */ + if (fs->flags & fs·preorder) + fs->func(fs->data, fs->base, fs->path, &cwd); + + /* open directory */ + if(!fs->max || fs->lev + 1 < fs->max) { + fd = openat(fs->fd, fs->base, O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (fd < 0) + errorf("open %s:", fs->path); + + if (!(dir=fdopendir(fd))) { + if(fs->flags & fs·verbose) + errorf("fdopendir: %s", fs->path); + return; + } + + ofd = fs->fd, fs->fd = fd; + + /* traverse children */ + e = fs->end, b = fs->base; + if (fs->end[-1] != '/') + *fs->end++ = '/'; + + fs->base = fs->end; + while((d = readdir(dir))) { + if (*d->d_name == '.') + if (d->d_name[1] == 0 || /* . */ + (d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */ + continue; + + fs->end = strcpyn(fs->base, d->d_name, arrend(fs->path) - fs->base); + + fs->lev++; + fs·walk(fs); + fs->lev--; + } + *e = 0; + fs->fd = ofd; + fs->end = e, fs->base = b; + closedir(dir); + } + + /* operate on directory if postorder (default) traversal */ + if (!(fs->flags & fs·preorder)) + fs->func(fs->data, fs->base, fs->path, &cwd); + + if (!fs->lev) + forget(fs->hist); +} -- cgit v1.2.1