From 1efa932e4aeb007eddb6424a90bf0fc05dba7e4d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 23 Apr 2017 00:00:37 -0400 Subject: Implement -fstype Fixes #6! --- Makefile | 2 +- bfs.h | 5 ++ eval.c | 14 ++++ mtab.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++ mtab.h | 46 +++++++++++++ parse.c | 30 ++++++++- printf.c | 23 ++++++- printf.h | 7 +- tests.sh | 6 ++ tests/test_fstype.out | 19 ++++++ 10 files changed, 319 insertions(+), 7 deletions(-) create mode 100644 mtab.c create mode 100644 mtab.h create mode 100644 tests/test_fstype.out diff --git a/Makefile b/Makefile index 50148f5..a4f4cdd 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ ALL_LDFLAGS = $(ALL_CFLAGS) $(LDFLAGS) all: bfs -bfs: bftw.o color.o dstring.o eval.o exec.o main.o parse.o printf.o typo.o util.o +bfs: bftw.o color.o dstring.o eval.o exec.o main.o mtab.o parse.o printf.o typo.o util.o $(CC) $(ALL_LDFLAGS) $^ -o $@ release: CFLAGS := -O3 -flto $(WFLAGS) -DNDEBUG diff --git a/bfs.h b/bfs.h index 191cb9e..3fd66a5 100644 --- a/bfs.h +++ b/bfs.h @@ -15,6 +15,7 @@ #include "color.h" #include "exec.h" #include "printf.h" +#include "mtab.h" #include #include #include @@ -90,6 +91,9 @@ struct cmdline { /** Colored stderr. */ CFILE *cerr; + /** Table of mounted file systems. */ + struct bfs_mtab *mtab; + /** -mindepth option. */ int mindepth; /** -maxdepth option. */ @@ -292,6 +296,7 @@ bool eval_nouser(const struct expr *expr, struct eval_state *state); bool eval_depth(const struct expr *expr, struct eval_state *state); bool eval_empty(const struct expr *expr, struct eval_state *state); +bool eval_fstype(const struct expr *expr, struct eval_state *state); bool eval_hidden(const struct expr *expr, struct eval_state *state); bool eval_inum(const struct expr *expr, struct eval_state *state); bool eval_links(const struct expr *expr, struct eval_state *state); diff --git a/eval.c b/eval.c index 8c33d21..5de25ec 100644 --- a/eval.c +++ b/eval.c @@ -13,6 +13,7 @@ #include "bftw.h" #include "color.h" #include "dstring.h" +#include "mtab.h" #include "util.h" #include #include @@ -366,6 +367,19 @@ done: return ret; } +/** + * -fstype test. + */ +bool eval_fstype(const struct expr *expr, struct eval_state *state) { + const struct stat *statbuf = fill_statbuf(state); + if (!statbuf) { + return false; + } + + const char *type = bfs_fstype(state->cmdline->mtab, statbuf); + return strcmp(type, expr->sdata) == 0; +} + /** * -hidden test. */ diff --git a/mtab.c b/mtab.c new file mode 100644 index 0000000..0311c50 --- /dev/null +++ b/mtab.c @@ -0,0 +1,174 @@ +/********************************************************************* + * bfs * + * Copyright (C) 2017 Tavian Barnes * + * * + * This program is free software. It comes without any warranty, to * + * the extent permitted by applicable law. You can redistribute it * + * and/or modify it under the terms of the Do What The Fuck You Want * + * To Public License, Version 2, as published by Sam Hocevar. See * + * the COPYING file or http://www.wtfpl.net/ for more details. * + *********************************************************************/ + +#include "mtab.h" +#include +#include +#include +#include +#include +#include + +#if __linux__ +# include +#elif BSD +# include +# include +#endif + +/** + * A mount point in the mount table. + */ +struct bfs_mtab_entry { + /** The device number for this mount point. */ + dev_t dev; + /** The file system type of this mount point. */ + char *type; +}; + +struct bfs_mtab { + /** The array of mtab entries. */ + struct bfs_mtab_entry *table; + /** The size of the array. */ + size_t size; + /** Capacity of the array. */ + size_t capacity; +}; + +/** + * Add an entry to the mount table. + */ +static int bfs_mtab_push(struct bfs_mtab *mtab, dev_t dev, const char *type) { + size_t size = mtab->size + 1; + + if (size >= mtab->capacity) { + size_t capacity = 2*size; + struct bfs_mtab_entry *table = realloc(mtab->table, capacity*sizeof(*table)); + if (!table) { + return -1; + } + mtab->table = table; + mtab->capacity = capacity; + } + + struct bfs_mtab_entry *entry = mtab->table + (size - 1); + entry->dev = dev; + entry->type = strdup(type); + if (!entry->type) { + return -1; + } + + mtab->size = size; + return 0; +} + +struct bfs_mtab *parse_bfs_mtab() { +#if __linux__ + + FILE *file = setmntent("/etc/mtab", "r"); + if (!file) { + goto fail; + } + + struct bfs_mtab *mtab = malloc(sizeof(*mtab)); + if (!mtab) { + goto fail_file; + } + mtab->table = NULL; + mtab->size = 0; + mtab->capacity = 0; + + struct mntent *mnt; + while ((mnt = getmntent(file))) { + struct stat sb; + if (stat(mnt->mnt_dir, &sb) != 0) { + continue; + } + + if (bfs_mtab_push(mtab, sb.st_dev, mnt->mnt_type) != 0) { + goto fail_mtab; + } + } + + endmntent(file); + return mtab; + +fail_mtab: + free_bfs_mtab(mtab); +fail_file: + endmntent(file); +fail: + return NULL; + +#elif BSD + + struct statfs *mntbuf; + int size = getmntinfo(&mntbuf, MNT_WAIT); + if (size < 0) { + return NULL; + } + + struct bfs_mtab *mtab = malloc(sizeof(*mtab)); + if (!mtab) { + goto fail; + } + + mtab->size = 0; + mtab->table = malloc(size*sizeof(*mtab->table)); + if (!mtab->table) { + goto fail_mtab; + } + mtab->capacity = size; + + for (struct statfs *mnt = mntbuf; mnt < mntbuf + size; ++mnt) { + struct stat sb; + if (stat(mnt->f_mntonname, &sb) != 0) { + continue; + } + + if (bfs_mtab_push(mtab, sb.st_dev, mnt->f_fstypename) != 0) { + goto fail_mtab; + } + } + + return mtab; + +fail_mtab: + free_bfs_mtab(mtab); +fail: + return NULL; + +#else + + errno = ENOTSUP; + return NULL; +#endif +} + +const char *bfs_fstype(const struct bfs_mtab *mtab, const struct stat *statbuf) { + for (struct bfs_mtab_entry *mnt = mtab->table; mnt < mtab->table + mtab->size; ++mnt) { + if (statbuf->st_dev == mnt->dev) { + return mnt->type; + } + } + + return "unknown"; +} + +void free_bfs_mtab(struct bfs_mtab *mtab) { + if (mtab) { + for (struct bfs_mtab_entry *mnt = mtab->table; mnt < mtab->table + mtab->size; ++mnt) { + free(mnt->type); + } + free(mtab->table); + free(mtab); + } +} diff --git a/mtab.h b/mtab.h new file mode 100644 index 0000000..38407fc --- /dev/null +++ b/mtab.h @@ -0,0 +1,46 @@ +/********************************************************************* + * bfs * + * Copyright (C) 2017 Tavian Barnes * + * * + * This program is free software. It comes without any warranty, to * + * the extent permitted by applicable law. You can redistribute it * + * and/or modify it under the terms of the Do What The Fuck You Want * + * To Public License, Version 2, as published by Sam Hocevar. See * + * the COPYING file or http://www.wtfpl.net/ for more details. * + *********************************************************************/ + +#ifndef BFS_MTAB_H +#define BFS_MTAB_H + +#include + +/** + * A file system mount table. + */ +struct bfs_mtab; + +/** + * Parse the mount table. + * + * @return The parsed mount table, or NULL on error. + */ +struct bfs_mtab *parse_bfs_mtab(void); + +/** + * Determine the file system type that a file is on. + * + * @param mtab + * The current mount table. + * @param statbuf + * The stat() buffer for the file in question. + * @return The type of file system containing this file, "unknown" if not known, + * or NULL on error. + */ +const char *bfs_fstype(const struct bfs_mtab *mtab, const struct stat *statbuf); + +/** + * Free a mount table. + */ +void free_bfs_mtab(struct bfs_mtab *mtab); + +#endif // BFS_MTAB_H diff --git a/parse.c b/parse.c index f8ed09c..25791cf 100644 --- a/parse.c +++ b/parse.c @@ -191,6 +191,8 @@ void free_cmdline(struct cmdline *cmdline) { if (cmdline) { free_expr(cmdline->expr); + free_bfs_mtab(cmdline->mtab); + cfclose(cmdline->cerr); cfclose(cmdline->cout); free_colors(cmdline->colors); @@ -1039,7 +1041,7 @@ static struct expr *parse_fprintf(struct parser_state *state, int arg1, int arg2 goto fail; } - expr->printf = parse_bfs_printf(format, state->cmdline->cerr); + expr->printf = parse_bfs_printf(format, state->cmdline); if (!expr->printf) { goto fail; } @@ -1051,6 +1053,18 @@ fail: return NULL; } +/** + * Parse -fstype TYPE. + */ +static struct expr *parse_fstype(struct parser_state *state, int arg1, int arg2) { + if (!state->cmdline->mtab) { + cfprintf(state->cmdline->cerr, "%{er}error: %s: Couldn't parse the mount table.%{rs}\n", state->argv[0]); + return NULL; + } + + return parse_unary_test(state, eval_fstype); +} + /** * Parse -gid/-group. */ @@ -1598,7 +1612,7 @@ static struct expr *parse_printf(struct parser_state *state, int arg1, int arg2) expr->cfile = state->cmdline->cout; - expr->printf = parse_bfs_printf(expr->sdata, state->cmdline->cerr); + expr->printf = parse_bfs_printf(expr->sdata, state->cmdline); if (!expr->printf) { free_expr(expr); return NULL; @@ -1993,6 +2007,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " %{blu}-false%{rs}\n"); cfprintf(cout, " %{blu}-true%{rs}\n"); cfprintf(cout, " Always false/true\n"); + cfprintf(cout, " %{blu}-fstype%{rs} %{bld}TYPE%{rs}\n"); + cfprintf(cout, " Find files on file systems with the given %{bld}TYPE%{rs}\n"); cfprintf(cout, " %{blu}-gid%{rs} %{bld}[-+]N%{rs}\n"); cfprintf(cout, " %{blu}-uid%{rs} %{bld}[-+]N%{rs}\n"); cfprintf(cout, " Find files owned by group/user ID %{bld}N%{rs}\n"); @@ -2149,6 +2165,7 @@ static const struct table_entry parse_table[] = { {"fprint", false, parse_fprint}, {"fprint0", false, parse_fprint0}, {"fprintf", false, parse_fprintf}, + {"fstype", false, parse_fstype}, {"gid", false, parse_group}, {"group", false, parse_group}, {"help", false, parse_help}, @@ -2801,6 +2818,10 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { } cmdline->roots = NULL; + cmdline->colors = NULL; + cmdline->cout = NULL; + cmdline->cerr = NULL; + cmdline->mtab = NULL; cmdline->mindepth = 0; cmdline->maxdepth = INT_MAX; cmdline->flags = BFTW_RECOVER; @@ -2819,6 +2840,11 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { goto fail; } + cmdline->mtab = parse_bfs_mtab(); + if (!cmdline->mtab) { + cfprintf(cmdline->cerr, "%{wr}warning: Couldn't parse the mount table: %s.%{rs}\n\n", strerror(errno)); + } + struct parser_state state = { .cmdline = cmdline, .argv = argv + 1, diff --git a/printf.c b/printf.c index cc8989f..50a9dcf 100644 --- a/printf.c +++ b/printf.c @@ -13,6 +13,7 @@ #include "bfs.h" #include "color.h" #include "dstring.h" +#include "mtab.h" #include "util.h" #include #include @@ -39,6 +40,8 @@ struct bfs_printf_directive { enum time_field time_field; /** Character data associated with this directive. */ char c; + /** The current mount table. */ + const struct bfs_mtab *mtab; /** The next printf directive in the chain. */ struct bfs_printf_directive *next; }; @@ -175,6 +178,12 @@ static int bfs_printf_f(FILE *file, const struct bfs_printf_directive *directive return fprintf(file, directive->str, ftwbuf->path + ftwbuf->nameoff); } +/** %F: file system type */ +static int bfs_printf_F(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { + const char *type = bfs_fstype(directive->mtab, ftwbuf->statbuf); + return fprintf(file, directive->str, type); +} + /** %G: gid */ static int bfs_printf_G(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_gid); @@ -393,6 +402,7 @@ static struct bfs_printf_directive *new_directive() { } directive->time_field = 0; directive->c = 0; + directive->mtab = NULL; directive->next = NULL; return directive; @@ -433,7 +443,9 @@ static int append_literal(struct bfs_printf_directive ***tail, struct bfs_printf return 0; } -struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr) { +struct bfs_printf *parse_bfs_printf(const char *format, const struct cmdline *cmdline) { + CFILE *cerr = cmdline->cerr; + struct bfs_printf *command = malloc(sizeof(*command)); if (!command) { perror("malloc()"); @@ -584,6 +596,15 @@ struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr) { case 'f': directive->fn = bfs_printf_f; break; + case 'F': + if (!cmdline->mtab) { + cfprintf(cerr, "%{er}error: '%s': Couldn't parse the mount table.%{rs}\n", format); + goto directive_error; + } + directive->fn = bfs_printf_F; + directive->mtab = cmdline->mtab; + command->needs_stat = true; + break; case 'g': directive->fn = bfs_printf_g; command->needs_stat = true; diff --git a/printf.h b/printf.h index 32171ca..d57921f 100644 --- a/printf.h +++ b/printf.h @@ -17,6 +17,7 @@ #include #include +struct cmdline; struct bfs_printf_directive; /** @@ -34,11 +35,11 @@ struct bfs_printf { * * @param format * The format string to parse. - * @param cerr - * For error messages. + * @param cmdline + * The command line. * @return The parsed printf command, or NULL on failure. */ -struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr); +struct bfs_printf *parse_bfs_printf(const char *format, const struct cmdline *cmdline); /** * Evaluate a parsed format string. diff --git a/tests.sh b/tests.sh index 5fed800..e3cdb52 100755 --- a/tests.sh +++ b/tests.sh @@ -292,6 +292,7 @@ gnu_tests=( test_printf_leak test_quit_after_print test_quit_before_print + test_fstype test_not test_and test_or @@ -1019,6 +1020,11 @@ function test_printf_leak() { bfs_diff basic -maxdepth 0 -printf '%p' } +function test_fstype() { + fstype="$($BFS -printf '%F\n' | head -n1)" + bfs_diff basic -fstype "$fstype" +} + function test_path_flag_expr() { bfs_diff links/h -H -type l } diff --git a/tests/test_fstype.out b/tests/test_fstype.out new file mode 100644 index 0000000..bb3cd8d --- /dev/null +++ b/tests/test_fstype.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/e +basic/g +basic/i +basic/j +basic/k +basic/l +basic/c/d +basic/e/f +basic/g/h +basic/j/foo +basic/k/foo +basic/l/foo +basic/k/foo/bar +basic/l/foo/bar +basic/l/foo/bar/baz -- cgit v1.2.3