summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--bftw.c27
-rw-r--r--bftw.h6
-rw-r--r--color.c23
-rw-r--r--eval.c176
-rw-r--r--expr.h19
-rw-r--r--mtab.c24
-rw-r--r--mtab.h6
-rw-r--r--parse.c109
-rw-r--r--printf.c92
-rw-r--r--printf.h2
-rw-r--r--stat.c243
-rw-r--r--stat.h94
-rw-r--r--util.c11
-rw-r--r--util.h30
15 files changed, 565 insertions, 299 deletions
diff --git a/Makefile b/Makefile
index 411128c..e3eadb1 100644
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ ALL_LDFLAGS = $(ALL_CFLAGS) $(LDFLAGS)
all: bfs
-bfs: bftw.o color.o dstring.o eval.o exec.o main.o mtab.o opt.o parse.o printf.o typo.o util.o
+bfs: bftw.o color.o dstring.o eval.o exec.o main.o mtab.o opt.o parse.o printf.o stat.o typo.o util.o
$(CC) $(ALL_LDFLAGS) $^ -o $@
release: CFLAGS := -O3 -flto $(WFLAGS) -DNDEBUG -g
diff --git a/bftw.c b/bftw.c
index f52d4e2..82d216b 100644
--- a/bftw.c
+++ b/bftw.c
@@ -35,6 +35,7 @@
#include "bftw.h"
#include "dstring.h"
+#include "stat.h"
#include "util.h"
#include <assert.h>
#include <dirent.h>
@@ -45,8 +46,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
/**
@@ -550,11 +549,11 @@ static void bftw_queue_destroy(struct bftw_queue *queue) {
}
/** Call stat() and use the results. */
-static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb) {
- int ret = xfstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, ftwbuf->at_flags);
+static int ftwbuf_stat(struct BFTW *ftwbuf, struct bfs_stat *sb) {
+ int ret = bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags, BFS_STAT_BROKEN_OK, sb);
if (ret == 0) {
ftwbuf->statbuf = sb;
- ftwbuf->typeflag = mode_to_typeflag(sb->st_mode);
+ ftwbuf->typeflag = mode_to_typeflag(sb->mode);
}
return ret;
}
@@ -606,8 +605,8 @@ struct bftw_state {
/** Extra data about the current file. */
struct BFTW ftwbuf;
- /** stat() buffer for the current file. */
- struct stat statbuf;
+ /** bfs_stat() buffer for the current file. */
+ struct bfs_stat statbuf;
};
/**
@@ -840,8 +839,8 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de)
}
if (ftwbuf->typeflag == BFTW_DIR && detect_cycles) {
- dev_t dev = ftwbuf->statbuf->st_dev;
- ino_t ino = ftwbuf->statbuf->st_ino;
+ dev_t dev = ftwbuf->statbuf->dev;
+ ino_t ino = ftwbuf->statbuf->ino;
for (const struct bftw_dir *dir = current; dir; dir = dir->parent) {
if (dev == dir->dev && ino == dir->ino) {
bftw_set_error(state, ELOOP);
@@ -890,10 +889,10 @@ static struct bftw_dir *bftw_add(struct bftw_state *state, const char *name) {
return NULL;
}
- const struct stat *statbuf = state->ftwbuf.statbuf;
+ const struct bfs_stat *statbuf = state->ftwbuf.statbuf;
if (statbuf) {
- dir->dev = statbuf->st_dev;
- dir->ino = statbuf->st_ino;
+ dir->dev = statbuf->dev;
+ dir->ino = statbuf->ino;
}
return dir;
@@ -1083,10 +1082,10 @@ int bftw(const char *path, bftw_fn *fn, int nopenfd, enum bftw_flags flags, void
}
if (state.ftwbuf.typeflag == BFTW_DIR) {
- const struct stat *statbuf = state.ftwbuf.statbuf;
+ const struct bfs_stat *statbuf = state.ftwbuf.statbuf;
if ((flags & BFTW_XDEV)
&& statbuf
- && statbuf->st_dev != state.current->dev) {
+ && statbuf->dev != state.current->dev) {
continue;
}
diff --git a/bftw.h b/bftw.h
index 77148f1..ebdd5d3 100644
--- a/bftw.h
+++ b/bftw.h
@@ -17,8 +17,8 @@
#ifndef BFS_BFTW_H
#define BFS_BFTW_H
+#include "stat.h"
#include <stddef.h>
-#include <sys/stat.h>
/**
* Possible file types.
@@ -81,8 +81,8 @@ struct BFTW {
/** The errno that occurred, if typeflag == BFTW_ERROR. */
int error;
- /** A stat() buffer; may be NULL if no stat() call was needed. */
- const struct stat *statbuf;
+ /** A bfs_stat() buffer; may be NULL if no stat() call was needed. */
+ const struct bfs_stat *statbuf;
/** A parent file descriptor for the *at() family of calls. */
int at_fd;
diff --git a/color.c b/color.c
index 594c5c9..cb73d3f 100644
--- a/color.c
+++ b/color.c
@@ -16,6 +16,7 @@
#include "color.h"
#include "bftw.h"
+#include "stat.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
@@ -296,24 +297,24 @@ int cfclose(CFILE *cfile) {
}
static const char *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf) {
- const struct stat *sb = ftwbuf->statbuf;
+ const struct bfs_stat *sb = ftwbuf->statbuf;
if (!sb) {
return colors->orphan;
}
const char *color = NULL;
- switch (sb->st_mode & S_IFMT) {
+ switch (sb->mode & S_IFMT) {
case S_IFREG:
- if (sb->st_mode & S_ISUID) {
+ if (sb->mode & S_ISUID) {
color = colors->setuid;
- } else if (sb->st_mode & S_ISGID) {
+ } else if (sb->mode & S_ISGID) {
color = colors->setgid;
- } else if (sb->st_mode & 0111) {
+ } else if (sb->mode & 0111) {
color = colors->exec;
}
- if (!color && sb->st_nlink > 1) {
+ if (!color && sb->nlink > 1) {
color = colors->multi_hard;
}
@@ -335,13 +336,13 @@ static const char *file_color(const struct colors *colors, const char *filename,
break;
case S_IFDIR:
- if (sb->st_mode & S_ISVTX) {
- if (sb->st_mode & S_IWOTH) {
+ if (sb->mode & S_ISVTX) {
+ if (sb->mode & S_IWOTH) {
color = colors->sticky_ow;
} else {
color = colors->sticky;
}
- } else if (sb->st_mode & S_IWOTH) {
+ } else if (sb->mode & S_IWOTH) {
color = colors->ow;
}
@@ -455,8 +456,8 @@ static int print_link(CFILE *cfile, const struct BFTW *ftwbuf) {
altbuf.path = target;
altbuf.nameoff = xbasename(target) - target;
- struct stat statbuf;
- if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &statbuf, 0) == 0) {
+ struct bfs_stat statbuf;
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, 0, 0, &statbuf) == 0) {
altbuf.statbuf = &statbuf;
} else {
altbuf.statbuf = NULL;
diff --git a/eval.c b/eval.c
index 88616cc..343c3f7 100644
--- a/eval.c
+++ b/eval.c
@@ -22,6 +22,7 @@
#include "exec.h"
#include "mtab.h"
#include "printf.h"
+#include "stat.h"
#include "util.h"
#include <assert.h>
#include <dirent.h>
@@ -51,7 +52,9 @@ struct eval_state {
/** Whether to quit immediately. */
bool *quit;
/** A stat() buffer, if necessary. */
- struct stat statbuf;
+ struct bfs_stat statbuf;
+ /** A bfs_stat() buffer, if necessary. */
+ struct bfs_stat bfs_statbuf;
};
/**
@@ -74,13 +77,13 @@ static void eval_error(struct eval_state *state) {
}
/**
- * Perform a stat() call if necessary.
+ * Perform a bfs_stat() call if necessary.
*/
-static const struct stat *fill_statbuf(struct eval_state *state) {
+static const struct bfs_stat *eval_bfs_stat(struct eval_state *state) {
struct BFTW *ftwbuf = state->ftwbuf;
if (!ftwbuf->statbuf) {
- if (xfstatat(ftwbuf->at_fd, ftwbuf->at_path, &state->statbuf, ftwbuf->at_flags) == 0) {
- ftwbuf->statbuf = &state->statbuf;
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags, BFS_STAT_BROKEN_OK, &state->bfs_statbuf) == 0) {
+ ftwbuf->statbuf = &state->bfs_statbuf;
} else {
eval_error(state);
}
@@ -135,34 +138,42 @@ bool eval_access(const struct expr *expr, struct eval_state *state) {
}
/**
- * -[aBcm]?newer tests.
+ * Get the given timespec field out of a stat buffer.
*/
-bool eval_newer(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+static const struct timespec *eval_stat_time(const struct expr *expr, struct eval_state *state) {
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
- return false;
+ return NULL;
}
- const struct timespec *time;
- switch (expr->time_field) {
- case ATIME:
- time = &statbuf->st_atim;
- break;
- case CTIME:
- time = &statbuf->st_ctim;
- break;
- case MTIME:
- time = &statbuf->st_mtim;
- break;
-
-#if BFS_HAVE_ST_BIRTHTIM
- case BTIME:
- time = &statbuf->st_birthtim;
- break;
-#endif
-
+ if (!(statbuf->mask & expr->stat_field)) {
+ assert(expr->stat_field == BFS_STAT_BTIME);
+ cfprintf(state->cmdline->cerr, "%{er}error: '%s': Couldn't get file birth time.%{rs}\n", state->ftwbuf->path);
+ *state->ret = EXIT_FAILURE;
+ return NULL;
+ }
+
+ switch (expr->stat_field) {
+ case BFS_STAT_ATIME:
+ return &statbuf->atime;
+ case BFS_STAT_BTIME:
+ return &statbuf->btime;
+ case BFS_STAT_CTIME:
+ return &statbuf->ctime;
+ case BFS_STAT_MTIME:
+ return &statbuf->mtime;
default:
assert(false);
+ return NULL;
+ }
+}
+
+/**
+ * -[aBcm]?newer tests.
+ */
+bool eval_newer(const struct expr *expr, struct eval_state *state) {
+ const struct timespec *time = eval_stat_time(expr, state);
+ if (!time) {
return false;
}
@@ -174,31 +185,8 @@ bool eval_newer(const struct expr *expr, struct eval_state *state) {
* -[aBcm]{min,time} tests.
*/
bool eval_time(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
- if (!statbuf) {
- return false;
- }
-
- const struct timespec *time;
- switch (expr->time_field) {
- case ATIME:
- time = &statbuf->st_atim;
- break;
- case CTIME:
- time = &statbuf->st_ctim;
- break;
- case MTIME:
- time = &statbuf->st_mtim;
- break;
-
-#if BFS_HAVE_ST_BIRTHTIM
- case BTIME:
- time = &statbuf->st_birthtim;
- break;
-#endif
-
- default:
- assert(false);
+ const struct timespec *time = eval_stat_time(expr, state);
+ if (!time) {
return false;
}
@@ -219,12 +207,12 @@ bool eval_time(const struct expr *expr, struct eval_state *state) {
* -used test.
*/
bool eval_used(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- time_t diff = timespec_diff(&statbuf->st_atim, &statbuf->st_ctim);
+ time_t diff = timespec_diff(&statbuf->atime, &statbuf->ctime);
diff /= 60*60*24;
return expr_cmp(expr, diff);
}
@@ -233,48 +221,48 @@ bool eval_used(const struct expr *expr, struct eval_state *state) {
* -gid test.
*/
bool eval_gid(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return expr_cmp(expr, statbuf->st_gid);
+ return expr_cmp(expr, statbuf->gid);
}
/**
* -uid test.
*/
bool eval_uid(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return expr_cmp(expr, statbuf->st_uid);
+ return expr_cmp(expr, statbuf->uid);
}
/**
* -nogroup test.
*/
bool eval_nogroup(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return getgrgid(statbuf->st_gid) == NULL;
+ return getgrgid(statbuf->gid) == NULL;
}
/**
* -nouser test.
*/
bool eval_nouser(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return getpwuid(statbuf->st_uid) == NULL;
+ return getpwuid(statbuf->uid) == NULL;
}
/**
@@ -390,9 +378,9 @@ bool eval_empty(const struct expr *expr, struct eval_state *state) {
done_dir:
closedir(dir);
} else {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (statbuf) {
- ret = statbuf->st_size == 0;
+ ret = statbuf->size == 0;
}
}
@@ -404,7 +392,7 @@ done:
* -fstype test.
*/
bool eval_fstype(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
@@ -437,24 +425,24 @@ bool eval_nohidden(const struct expr *expr, struct eval_state *state) {
* -inum test.
*/
bool eval_inum(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return expr_cmp(expr, statbuf->st_ino);
+ return expr_cmp(expr, statbuf->ino);
}
/**
* -links test.
*/
bool eval_links(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return expr_cmp(expr, statbuf->st_nlink);
+ return expr_cmp(expr, statbuf->nlink);
}
/**
@@ -469,12 +457,12 @@ bool eval_lname(const struct expr *expr, struct eval_state *state) {
goto done;
}
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
goto done;
}
- name = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, statbuf->st_size);
+ name = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, statbuf->size);
if (!name) {
eval_error(state);
goto done;
@@ -526,12 +514,12 @@ bool eval_path(const struct expr *expr, struct eval_state *state) {
* -perm test.
*/
bool eval_perm(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- mode_t mode = statbuf->st_mode;
+ mode_t mode = statbuf->mode;
mode_t target;
if (state->ftwbuf->typeflag == BFTW_DIR) {
target = expr->dir_mode;
@@ -560,21 +548,21 @@ bool eval_fls(const struct expr *expr, struct eval_state *state) {
CFILE *cfile = expr->cfile;
FILE *file = cfile->file;
const struct BFTW *ftwbuf = state->ftwbuf;
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
goto done;
}
- uintmax_t ino = statbuf->st_ino;
- uintmax_t blocks = (statbuf->st_blocks + 1)/2;
+ uintmax_t ino = statbuf->ino;
+ uintmax_t blocks = (statbuf->blocks + 1)/2;
char mode[11];
- format_mode(statbuf->st_mode, mode);
- uintmax_t nlink = statbuf->st_nlink;
+ format_mode(statbuf->mode, mode);
+ uintmax_t nlink = statbuf->nlink;
if (fprintf(file, "%9ju %6ju %s %3ju ", ino, blocks, mode, nlink) < 0) {
goto error;
}
- uintmax_t uid = statbuf->st_uid;
+ uintmax_t uid = statbuf->uid;
struct passwd *pwd = getpwuid(uid);
if (pwd) {
if (fprintf(file, " %-8s", pwd->pw_name) < 0) {
@@ -586,7 +574,7 @@ bool eval_fls(const struct expr *expr, struct eval_state *state) {
}
}
- uintmax_t gid = statbuf->st_gid;
+ uintmax_t gid = statbuf->gid;
struct group *grp = getgrgid(gid);
if (grp) {
if (fprintf(file, " %-8s", grp->gr_name) < 0) {
@@ -598,12 +586,12 @@ bool eval_fls(const struct expr *expr, struct eval_state *state) {
}
}
- uintmax_t size = statbuf->st_size;
+ uintmax_t size = statbuf->size;
if (fprintf(file, " %8ju", size) < 0) {
goto error;
}
- time_t time = statbuf->st_mtim.tv_sec;
+ time_t time = statbuf->mtime.tv_sec;
time_t now = expr->reftime.tv_sec;
time_t six_months_ago = now - 6*30*24*60*60;
time_t tomorrow = now + 24*60*60;
@@ -652,7 +640,7 @@ error:
bool eval_fprint(const struct expr *expr, struct eval_state *state) {
CFILE *cfile = expr->cfile;
if (cfile->colors) {
- fill_statbuf(state);
+ eval_bfs_stat(state);
}
if (cfprintf(cfile, "%P\n", state->ftwbuf) < 0) {
@@ -679,7 +667,7 @@ bool eval_fprint0(const struct expr *expr, struct eval_state *state) {
*/
bool eval_fprintf(const struct expr *expr, struct eval_state *state) {
if (expr->printf->needs_stat) {
- if (!fill_statbuf(state)) {
+ if (!eval_bfs_stat(state)) {
goto done;
}
}
@@ -784,19 +772,19 @@ bool eval_regex(const struct expr *expr, struct eval_state *state) {
* -samefile test.
*/
bool eval_samefile(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- return statbuf->st_dev == expr->dev && statbuf->st_ino == expr->ino;
+ return statbuf->dev == expr->dev && statbuf->ino == expr->ino;
}
/**
* -size test.
*/
bool eval_size(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
@@ -813,7 +801,7 @@ bool eval_size(const struct expr *expr, struct eval_state *state) {
};
off_t scale = scales[expr->size_unit];
- off_t size = (statbuf->st_size + scale - 1)/scale; // Round up
+ off_t size = (statbuf->size + scale - 1)/scale; // Round up
return expr_cmp(expr, size);
}
@@ -821,13 +809,13 @@ bool eval_size(const struct expr *expr, struct eval_state *state) {
* -sparse test.
*/
bool eval_sparse(const struct expr *expr, struct eval_state *state) {
- const struct stat *statbuf = fill_statbuf(state);
+ const struct bfs_stat *statbuf = eval_bfs_stat(state);
if (!statbuf) {
return false;
}
- blkcnt_t expected = (statbuf->st_size + 511)/512;
- return statbuf->st_blocks < expected;
+ blkcnt_t expected = (statbuf->size + 511)/512;
+ return statbuf->blocks < expected;
}
/**
@@ -852,8 +840,8 @@ bool eval_xtype(const struct expr *expr, struct eval_state *state) {
// -xtype does the opposite of everything else
int at_flags = ftwbuf->at_flags ^ AT_SYMLINK_NOFOLLOW;
- struct stat sb;
- if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &sb, at_flags) != 0) {
+ struct bfs_stat sb;
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, at_flags, 0, &sb) != 0) {
if (!follow && is_nonexistence_error(errno)) {
// Broken symlink
return eval_type(expr, state);
@@ -863,7 +851,7 @@ bool eval_xtype(const struct expr *expr, struct eval_state *state) {
}
}
- return mode_to_typeflag(sb.st_mode) & expr->idata;
+ return mode_to_typeflag(sb.mode) & expr->idata;
}
#if _POSIX_MONOTONIC_CLOCK > 0
diff --git a/expr.h b/expr.h
index 5eff72f..9644a0d 100644
--- a/expr.h
+++ b/expr.h
@@ -20,6 +20,7 @@
#include "color.h"
#include "exec.h"
#include "printf.h"
+#include "stat.h"
#include <regex.h>
#include <stdbool.h>
#include <stddef.h>
@@ -73,20 +74,6 @@ enum mode_cmp {
};
/**
- * Available struct stat time fields.
- */
-enum time_field {
- /** Access time. */
- ATIME,
- /** Status change time. */
- CTIME,
- /** Modification time. */
- MTIME,
- /** Birth time. */
- BTIME,
-};
-
-/**
* Possible time units.
*/
enum time_unit {
@@ -160,10 +147,10 @@ struct expr {
/** Mode to use for directories (different due to X). */
mode_t dir_mode;
+ /** The optional stat field to look at. */
+ enum bfs_stat_field stat_field;
/** The optional reference time. */
struct timespec reftime;
- /** The optional time field. */
- enum time_field time_field;
/** The optional time unit. */
enum time_unit time_unit;
diff --git a/mtab.c b/mtab.c
index fb3f85b..e04f68f 100644
--- a/mtab.c
+++ b/mtab.c
@@ -15,7 +15,9 @@
****************************************************************************/
#include "mtab.h"
+#include "util.h"
#include <errno.h>
+#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
@@ -110,12 +112,12 @@ struct bfs_mtab *parse_bfs_mtab() {
struct mntent *mnt;
while ((mnt = getmntent(file))) {
- struct stat sb;
- if (stat(mnt->mnt_dir, &sb) != 0) {
+ struct bfs_stat sb;
+ if (bfs_stat(AT_FDCWD, mnt->mnt_dir, 0, 0, &sb) != 0) {
continue;
}
- if (bfs_mtab_push(mtab, sb.st_dev, mnt->mnt_type) != 0) {
+ if (bfs_mtab_push(mtab, sb.dev, mnt->mnt_type) != 0) {
goto fail_mtab;
}
}
@@ -151,12 +153,12 @@ fail:
mtab->capacity = size;
for (struct statfs *mnt = mntbuf; mnt < mntbuf + size; ++mnt) {
- struct stat sb;
- if (stat(mnt->f_mntonname, &sb) != 0) {
+ struct bfs_stat sb;
+ if (bfs_stat(AT_FDCWD, mnt->f_mntonname, 0, 0, &sb) != 0) {
continue;
}
- if (bfs_mtab_push(mtab, sb.st_dev, mnt->f_fstypename) != 0) {
+ if (bfs_mtab_push(mtab, sb.dev, mnt->f_fstypename) != 0) {
goto fail_mtab;
}
}
@@ -185,12 +187,12 @@ fail:
struct mnttab mnt;
while (getmntent(file, &mnt) == 0) {
- struct stat sb;
- if (stat(mnt.mnt_mountp, &sb) != 0) {
+ struct bfs_stat sb;
+ if (bfs_stat(AT_FDCWD, mnt.mnt_mountp, 0, 0, &sb) != 0) {
continue;
}
- if (bfs_mtab_push(mtab, sb.st_dev, mnt.mnt_fstype) != 0) {
+ if (bfs_mtab_push(mtab, sb.dev, mnt.mnt_fstype) != 0) {
goto fail_mtab;
}
}
@@ -212,9 +214,9 @@ fail:
#endif
}
-const char *bfs_fstype(const struct bfs_mtab *mtab, const struct stat *statbuf) {
+const char *bfs_fstype(const struct bfs_mtab *mtab, const struct bfs_stat *statbuf) {
for (struct bfs_mtab_entry *mnt = mtab->table; mnt < mtab->table + mtab->size; ++mnt) {
- if (statbuf->st_dev == mnt->dev) {
+ if (statbuf->dev == mnt->dev) {
return mnt->type;
}
}
diff --git a/mtab.h b/mtab.h
index cb29fa3..f23c036 100644
--- a/mtab.h
+++ b/mtab.h
@@ -17,7 +17,7 @@
#ifndef BFS_MTAB_H
#define BFS_MTAB_H
-#include <sys/stat.h>
+#include "stat.h"
/**
* A file system mount table.
@@ -37,11 +37,11 @@ struct bfs_mtab *parse_bfs_mtab(void);
* @param mtab
* The current mount table.
* @param statbuf
- * The stat() buffer for the file in question.
+ * The bfs_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);
+const char *bfs_fstype(const struct bfs_mtab *mtab, const struct bfs_stat *statbuf);
/**
* Free a mount table.
diff --git a/parse.c b/parse.c
index bfc93af..e71aa47 100644
--- a/parse.c
+++ b/parse.c
@@ -22,6 +22,7 @@
#include "expr.h"
#include "mtab.h"
#include "printf.h"
+#include "stat.h"
#include "typo.h"
#include "util.h"
#include <ctype.h>
@@ -389,14 +390,14 @@ static int expr_open(struct parser_state *state, struct expr *expr, const char *
goto out;
}
- struct stat sb;
- if (fstat(fileno(cfile->file), &sb) != 0) {
+ struct bfs_stat sb;
+ if (bfs_fstat(fileno(cfile->file), &sb) != 0) {
cfprintf(cmdline->cerr, "%{er}error: '%s': %m%{rs}\n", path);
goto out_close;
}
for (struct open_file *ofile = cmdline->open_files; ofile; ofile = ofile->next) {
- if (ofile->dev == sb.st_dev && ofile->ino == sb.st_ino) {
+ if (ofile->dev == sb.dev && ofile->ino == sb.ino) {
expr->cfile = ofile->cfile;
ret = 0;
goto out_close;
@@ -410,8 +411,8 @@ static int expr_open(struct parser_state *state, struct expr *expr, const char *
ofile->cfile = cfile;
ofile->path = path;
- ofile->dev = sb.st_dev;
- ofile->ino = sb.st_ino;
+ ofile->dev = sb.dev;
+ ofile->ino = sb.ino;
ofile->next = cmdline->open_files;
cmdline->open_files = ofile;
++cmdline->nopen_files;
@@ -428,15 +429,15 @@ out:
}
/**
- * Invoke stat() on an argument.
+ * Invoke bfs_stat() on an argument.
*/
-static int stat_arg(const struct parser_state *state, struct expr *expr, struct stat *sb) {
+static int stat_arg(const struct parser_state *state, struct expr *expr, struct bfs_stat *sb) {
const struct cmdline *cmdline = state->cmdline;
bool follow = cmdline->flags & (BFTW_COMFOLLOW | BFTW_LOGICAL);
- int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
+ int at_flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
- int ret = xfstatat(AT_FDCWD, expr->sdata, sb, flags);
+ int ret = bfs_stat(AT_FDCWD, expr->sdata, at_flags, BFS_STAT_BROKEN_OK, sb);
if (ret != 0) {
cfprintf(cmdline->cerr, "%{er}error: '%s': %m%{rs}\n", expr->sdata);
}
@@ -911,21 +912,14 @@ static struct expr *parse_newer(struct parser_state *state, int field, int arg2)
return NULL;
}
-#if !BFS_HAVE_ST_BIRTHTIM
- if (field == BTIME) {
- cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times are not supported on this platform.%{rs}\n", expr->argv[0]);
- goto fail;
- }
-#endif
-
- struct stat sb;
+ struct bfs_stat sb;
if (stat_arg(state, expr, &sb) != 0) {
goto fail;
}
expr->cost = STAT_COST;
- expr->reftime = sb.st_mtim;
- expr->time_field = field;
+ expr->reftime = sb.mtime;
+ expr->stat_field = field;
return expr;
fail:
@@ -942,17 +936,9 @@ static struct expr *parse_time(struct parser_state *state, int field, int unit)
return NULL;
}
-#if !BFS_HAVE_ST_BIRTHTIM
- if (field == BTIME) {
- cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times are not supported on this platform.%{rs}\n", expr->argv[0]);
- free_expr(expr);
- return NULL;
- }
-#endif
-
expr->cost = STAT_COST;
expr->reftime = state->now;
- expr->time_field = field;
+ expr->stat_field = field;
expr->time_unit = unit;
return expr;
}
@@ -1496,23 +1482,17 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2
switch (arg[6]) {
case 'a':
- expr->time_field = ATIME;
+ expr->stat_field = BFS_STAT_ATIME;
break;
case 'c':
- expr->time_field = CTIME;
+ expr->stat_field = BFS_STAT_CTIME;
break;
case 'm':
- expr->time_field = MTIME;
+ expr->stat_field = BFS_STAT_MTIME;
break;
-
case 'B':
-#if BFS_HAVE_ST_BIRTHTIM
- expr->time_field = BTIME;
+ expr->stat_field = BFS_STAT_BTIME;
break;
-#else
- cfprintf(cerr, "%{er}error: %s: File birth times ('B') are not supported on this platform.%{rs}\n", arg);
- goto fail;
-#endif
default:
cfprintf(cerr, "%{er}error: %s: For -newerXY, X should be 'a', 'c', 'm', or 'B'.%{rs}\n", arg);
@@ -1523,29 +1503,30 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2
cfprintf(cerr, "%{er}error: %s: Explicit reference times ('t') are not supported.%{rs}\n", arg);
goto fail;
} else {
- struct stat sb;
+ struct bfs_stat sb;
if (stat_arg(state, expr, &sb) != 0) {
goto fail;
}
switch (arg[7]) {
case 'a':
- expr->reftime = sb.st_atim;
+ expr->reftime = sb.atime;
break;
case 'c':
- expr->reftime = sb.st_ctim;
+ expr->reftime = sb.ctime;
break;
case 'm':
- expr->reftime = sb.st_mtim;
+ expr->reftime = sb.mtime;
break;
case 'B':
-#if BFS_HAVE_ST_BIRTHTIM
- expr->reftime = sb.st_birthtim;
-#else
- cfprintf(cerr, "%{er}error: %s: File birth times ('B') are not supported on this platform.%{rs}\n", arg);
- goto fail;
-#endif
+ if (sb.mask & BFS_STAT_BTIME) {
+ expr->reftime = sb.btime;
+ } else {
+ cfprintf(cerr, "%{er}error: '%s': Couldn't get file birth time.%{rs}\n", expr->sdata);
+ goto fail;
+ }
+ break;
default:
cfprintf(cerr, "%{er}error: %s: For -newerXY, Y should be 'a', 'c', 'm', 'B', or 't'.%{rs}\n", arg);
@@ -2033,14 +2014,14 @@ static struct expr *parse_samefile(struct parser_state *state, int arg1, int arg
return NULL;
}
- struct stat sb;
+ struct bfs_stat sb;
if (stat_arg(state, expr, &sb) != 0) {
free_expr(expr);
return NULL;
}
- expr->dev = sb.st_dev;
- expr->ino = sb.st_ino;
+ expr->dev = sb.dev;
+ expr->ino = sb.ino;
expr->cost = STAT_COST;
expr->probability = 0.01;
@@ -2502,9 +2483,9 @@ struct table_entry {
* The parse table for literals.
*/
static const struct table_entry parse_table[] = {
- {"-Bmin", false, parse_time, BTIME, MINUTES},
- {"-Bnewer", false, parse_newer, BTIME},
- {"-Btime", false, parse_time, BTIME, DAYS},
+ {"-Bmin", false, parse_time, BFS_STAT_BTIME, MINUTES},
+ {"-Bnewer", false, parse_newer, BFS_STAT_BTIME},
+ {"-Btime", false, parse_time, BFS_STAT_BTIME, DAYS},
{"-D", false, parse_debug},
{"-E", false, parse_regex_extended},
{"-O", true, parse_optlevel},
@@ -2513,13 +2494,13 @@ static const struct table_entry parse_table[] = {
{"-L", false, parse_follow, BFTW_LOGICAL | BFTW_DETECT_CYCLES, false},
{"-X", false, parse_xargs_safe},
{"-a"},
- {"-amin", false, parse_time, ATIME, MINUTES},
+ {"-amin", false, parse_time, BFS_STAT_ATIME, MINUTES},
{"-and"},
- {"-anewer", false, parse_newer, ATIME},
- {"-atime", false, parse_time, ATIME, DAYS},
- {"-cmin", false, parse_time, CTIME, MINUTES},
- {"-cnewer", false, parse_newer, CTIME},
- {"-ctime", false, parse_time, CTIME, DAYS},
+ {"-anewer", false, parse_newer, BFS_STAT_ATIME},
+ {"-atime", false, parse_time, BFS_STAT_ATIME, DAYS},
+ {"-cmin", false, parse_time, BFS_STAT_CTIME, MINUTES},
+ {"-cnewer", false, parse_newer, BFS_STAT_CTIME},
+ {"-ctime", false, parse_time, BFS_STAT_CTIME, DAYS},
{"-color", false, parse_color, true},
{"-d", false, parse_depth},
{"-daystart", false, parse_daystart},
@@ -2554,12 +2535,12 @@ static const struct table_entry parse_table[] = {
{"-ls", false, parse_ls},
{"-maxdepth", false, parse_depth_limit, false},
{"-mindepth", false, parse_depth_limit, true},
- {"-mmin", false, parse_time, MTIME, MINUTES},
- {"-mnewer", false, parse_newer, MTIME},
+ {"-mmin", false, parse_time, BFS_STAT_MTIME, MINUTES},
+ {"-mnewer", false, parse_newer, BFS_STAT_MTIME},
{"-mount", false, parse_mount},
- {"-mtime", false, parse_time, MTIME, DAYS},
+ {"-mtime", false, parse_time, BFS_STAT_MTIME, DAYS},
{"-name", false, parse_name, false},
- {"-newer", false, parse_newer, MTIME},
+ {"-newer", false, parse_newer, BFS_STAT_MTIME},
{"-newer", true, parse_newerxy},
{"-nocolor", false, parse_color, false},
{"-nogroup", false, parse_nogroup},
diff --git a/printf.c b/printf.c
index 2d48cb6..27d9521 100644
--- a/printf.c
+++ b/printf.c
@@ -20,6 +20,7 @@
#include "dstring.h"
#include "expr.h"
#include "mtab.h"
+#include "stat.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
@@ -29,7 +30,6 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
-#include <sys/stat.h>
#include <time.h>
typedef int bfs_printf_fn(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf);
@@ -42,8 +42,8 @@ struct bfs_printf_directive {
bfs_printf_fn *fn;
/** String data associated with this directive. */
char *str;
- /** The time field to print. */
- enum time_field time_field;
+ /** The stat field to print. */
+ enum bfs_stat_field stat_field;
/** Character data associated with this directive. */
char c;
/** The current mount table. */
@@ -77,22 +77,22 @@ static int bfs_printf_flush(FILE *file, const struct bfs_printf_directive *direc
(void)ret
/**
- * Get a particular time field from a struct stat.
+ * Get a particular time field from a struct bfs_stat.
*/
-static const struct timespec *get_time_field(const struct stat *statbuf, enum time_field time_field) {
- switch (time_field) {
- case ATIME:
- return &statbuf->st_atim;
- case CTIME:
- return &statbuf->st_ctim;
- case MTIME:
- return &statbuf->st_mtim;
-
-#ifdef BFS_HAVE_ST_BIRTHTIM
- case BTIME:
- return &statbuf->st_birthtim;
-#endif
+static const struct timespec *get_time_field(const struct bfs_stat *statbuf, enum bfs_stat_field stat_field) {
+ if (!(statbuf->mask & stat_field)) {
+ return NULL;
+ }
+ switch (stat_field) {
+ case BFS_STAT_ATIME:
+ return &statbuf->atime;
+ case BFS_STAT_BTIME:
+ return &statbuf->btime;
+ case BFS_STAT_CTIME:
+ return &statbuf->ctime;
+ case BFS_STAT_MTIME:
+ return &statbuf->mtime;
default:
assert(false);
return NULL;
@@ -105,7 +105,11 @@ static int bfs_printf_ctime(FILE *file, const struct bfs_printf_directive *direc
static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- const struct timespec *ts = get_time_field(ftwbuf->statbuf, directive->time_field);
+ const struct timespec *ts = get_time_field(ftwbuf->statbuf, directive->stat_field);
+ if (!ts) {
+ return -1;
+ }
+
struct tm tm;
if (xlocaltime(&ts->tv_sec, &tm) != 0) {
return -1;
@@ -126,7 +130,11 @@ static int bfs_printf_ctime(FILE *file, const struct bfs_printf_directive *direc
/** %A, %C, %T: strftime() */
static int bfs_printf_strftime(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- const struct timespec *ts = get_time_field(ftwbuf->statbuf, directive->time_field);
+ const struct timespec *ts = get_time_field(ftwbuf->statbuf, directive->stat_field);
+ if (!ts) {
+ return -1;
+ }
+
struct tm tm;
if (xlocaltime(&ts->tv_sec, &tm) != 0) {
return -1;
@@ -175,7 +183,7 @@ static int bfs_printf_strftime(FILE *file, const struct bfs_printf_directive *di
/** %b: blocks */
static int bfs_printf_b(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_blocks);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->blocks);
return fprintf(file, directive->str, buf);
}
@@ -186,7 +194,7 @@ static int bfs_printf_d(FILE *file, const struct bfs_printf_directive *directive
/** %D: device */
static int bfs_printf_D(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_dev);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->dev);
return fprintf(file, directive->str, buf);
}
@@ -203,13 +211,13 @@ static int bfs_printf_F(FILE *file, const struct bfs_printf_directive *directive
/** %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);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->gid);
return fprintf(file, directive->str, buf);
}
/** %g: group name */
static int bfs_printf_g(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- struct group *grp = getgrgid(ftwbuf->statbuf->st_gid);
+ struct group *grp = getgrgid(ftwbuf->statbuf->gid);
if (!grp) {
return bfs_printf_G(file, directive, ftwbuf);
}
@@ -251,13 +259,13 @@ static int bfs_printf_H(FILE *file, const struct bfs_printf_directive *directive
/** %i: inode */
static int bfs_printf_i(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_ino);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->ino);
return fprintf(file, directive->str, buf);
}
/** %k: 1K blocks */
static int bfs_printf_k(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)(ftwbuf->statbuf->st_blocks + 1)/2);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)(ftwbuf->statbuf->blocks + 1)/2);
return fprintf(file, directive->str, buf);
}
@@ -279,19 +287,19 @@ static int bfs_printf_l(FILE *file, const struct bfs_printf_directive *directive
/** %m: mode */
static int bfs_printf_m(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- return fprintf(file, directive->str, (unsigned int)(ftwbuf->statbuf->st_mode & 07777));
+ return fprintf(file, directive->str, (unsigned int)(ftwbuf->statbuf->mode & 07777));
}
/** %M: symbolic mode */
static int bfs_printf_M(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
char buf[11];
- format_mode(ftwbuf->statbuf->st_mode, buf);
+ format_mode(ftwbuf->statbuf->mode, buf);
return fprintf(file, directive->str, buf);
}
/** %n: link count */
static int bfs_printf_n(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_nlink);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->nlink);
return fprintf(file, directive->str, buf);
}
@@ -311,25 +319,25 @@ static int bfs_printf_P(FILE *file, const struct bfs_printf_directive *directive
/** %s: size */
static int bfs_printf_s(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_size);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->size);
return fprintf(file, directive->str, buf);
}
/** %S: sparseness */
static int bfs_printf_S(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- double sparsity = 512.0 * ftwbuf->statbuf->st_blocks / ftwbuf->statbuf->st_size;
+ double sparsity = 512.0 * ftwbuf->statbuf->blocks / ftwbuf->statbuf->size;
return fprintf(file, directive->str, sparsity);
}
/** %U: uid */
static int bfs_printf_U(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_uid);
+ BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->uid);
return fprintf(file, directive->str, buf);
}
/** %u: user name */
static int bfs_printf_u(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- struct passwd *pwd = getpwuid(ftwbuf->statbuf->st_uid);
+ struct passwd *pwd = getpwuid(ftwbuf->statbuf->uid);
if (!pwd) {
return bfs_printf_U(file, directive, ftwbuf);
}
@@ -374,9 +382,9 @@ static int bfs_printf_Y(FILE *file, const struct bfs_printf_directive *directive
const char *type = "U";
- struct stat sb;
- if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &sb, 0) == 0) {
- type = bfs_printf_type(mode_to_typeflag(sb.st_mode));
+ struct bfs_stat sb;
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, 0, 0, &sb) == 0) {
+ type = bfs_printf_type(mode_to_typeflag(sb.mode));
} else {
switch (errno) {
case ELOOP:
@@ -418,7 +426,7 @@ static struct bfs_printf_directive *new_directive() {
perror("dstralloc()");
goto error;
}
- directive->time_field = 0;
+ directive->stat_field = 0;
directive->c = 0;
directive->mtab = NULL;
directive->next = NULL;
@@ -591,7 +599,7 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline)
switch (c) {
case 'a':
directive->fn = bfs_printf_ctime;
- directive->time_field = ATIME;
+ directive->stat_field = BFS_STAT_ATIME;
command->needs_stat = true;
break;
case 'b':
@@ -600,7 +608,7 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline)
break;
case 'c':
directive->fn = bfs_printf_ctime;
- directive->time_field = CTIME;
+ directive->stat_field = BFS_STAT_CTIME;
command->needs_stat = true;
break;
case 'd':
@@ -681,7 +689,7 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline)
break;
case 't':
directive->fn = bfs_printf_ctime;
- directive->time_field = MTIME;
+ directive->stat_field = BFS_STAT_MTIME;
command->needs_stat = true;
break;
case 'u':
@@ -700,13 +708,13 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline)
break;
case 'A':
- directive->time_field = ATIME;
+ directive->stat_field = BFS_STAT_ATIME;
goto directive_strftime;
case 'C':
- directive->time_field = CTIME;
+ directive->stat_field = BFS_STAT_CTIME;
goto directive_strftime;
case 'T':
- directive->time_field = MTIME;
+ directive->stat_field = BFS_STAT_MTIME;
goto directive_strftime;
directive_strftime:
diff --git a/printf.h b/printf.h
index 6316c5d..353c82d 100644
--- a/printf.h
+++ b/printf.h
@@ -31,7 +31,7 @@ struct bfs_printf_directive;
struct bfs_printf {
/** The chain of printf directives. */
struct bfs_printf_directive *directives;
- /** Whether the struct stat must be filled in. */
+ /** Whether the struct bfs_stat must be filled in. */
bool needs_stat;
};
diff --git a/stat.c b/stat.c
new file mode 100644
index 0000000..4f1c2fc
--- /dev/null
+++ b/stat.c
@@ -0,0 +1,243 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2018 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+ ****************************************************************************/
+
+#include "stat.h"
+#include "util.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if __linux__
+# include <linux/stat.h>
+# include <sys/syscall.h>
+# include <sys/sysmacros.h>
+#elif __APPLE__
+# define st_atim st_atimespec
+# define st_ctim st_ctimespec
+# define st_mtim st_mtimespec
+# define st_birthtim st_birthtimespec
+#endif
+
+/**
+ * Check if we should retry a failed stat() due to a potentially broken link.
+ */
+static bool bfs_stat_retry(int ret, int at_flags, enum bfs_stat_flag flags) {
+ return ret != 0
+ && !(at_flags & AT_SYMLINK_NOFOLLOW)
+ && (flags & BFS_STAT_BROKEN_OK)
+ && is_nonexistence_error(errno);
+}
+
+/**
+ * Convert a struct stat to a struct bfs_stat.
+ */
+static void bfs_stat_convert(const struct stat *statbuf, struct bfs_stat *buf) {
+ buf->mask = 0;
+
+ buf->dev = statbuf->st_dev;
+ buf->mask |= BFS_STAT_DEV;
+
+ buf->ino = statbuf->st_ino;
+ buf->mask |= BFS_STAT_INO;
+
+ buf->mode = statbuf->st_mode;
+ buf->mask |= BFS_STAT_TYPE | BFS_STAT_MODE;
+
+ buf->nlink = statbuf->st_nlink;
+ buf->mask |= BFS_STAT_NLINK;
+
+ buf->gid = statbuf->st_gid;
+ buf->mask |= BFS_STAT_GID;
+
+ buf->uid = statbuf->st_uid;
+ buf->mask |= BFS_STAT_UID;
+
+ buf->size = statbuf->st_size;
+ buf->mask |= BFS_STAT_SIZE;
+
+ buf->blocks = statbuf->st_blocks;
+ buf->mask |= BFS_STAT_BLOCKS;
+
+ buf->atime = statbuf->st_atim;
+ buf->mask |= BFS_STAT_ATIME;
+
+ buf->ctime = statbuf->st_ctim;
+ buf->mask |= BFS_STAT_CTIME;
+
+ buf->mtime = statbuf->st_mtim;
+ buf->mask |= BFS_STAT_MTIME;
+
+#if __APPLE__ || __FreeBSD__ || __NetBSD__
+ buf->btime = statbuf->st_birthtim;
+ buf->mask |= BFS_STAT_BTIME;
+#endif
+}
+
+/**
+ * bfs_stat() implementation backed by stat().
+ */
+static int bfs_stat_impl(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf) {
+ struct stat statbuf;
+ int ret = fstatat(at_fd, at_path, &statbuf, at_flags);
+
+ if (bfs_stat_retry(ret, at_flags, flags)) {
+ at_flags |= AT_SYMLINK_NOFOLLOW;
+ ret = fstatat(at_fd, at_path, &statbuf, at_flags);
+ }
+
+ if (ret == 0) {
+ bfs_stat_convert(&statbuf, buf);
+ }
+
+ return ret;
+}
+
+#ifdef STATX_BASIC_STATS
+
+/**
+ * Wrapper for the statx() system call, which has no native glibc wrapper.
+ */
+static int bfs_statx(int at_fd, const char *at_path, int at_flags, unsigned int mask, struct statx *buf) {
+ return syscall(__NR_statx, at_fd, at_path, at_flags, mask, buf);
+}
+
+/**
+ * bfs_stat() implementation backed by statx().
+ */
+static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf) {
+ unsigned int mask = STATX_BASIC_STATS | STATX_BTIME;
+ struct statx xbuf;
+ int ret = bfs_statx(at_fd, at_path, at_flags, mask, &xbuf);
+
+ if (bfs_stat_retry(ret, at_flags, flags)) {
+ at_flags |= AT_SYMLINK_NOFOLLOW;
+ ret = bfs_statx(at_fd, at_path, at_flags, mask, &xbuf);
+ }
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ if ((xbuf.stx_mask & STATX_BASIC_STATS) != STATX_BASIC_STATS) {
+ // Callers shouldn't have to check anything except BFS_STAT_BTIME
+ errno = EINVAL;
+ return -1;
+ }
+
+ buf->mask = 0;
+
+ buf->dev = makedev(xbuf.stx_dev_major, xbuf.stx_dev_minor);
+ buf->mask |= BFS_STAT_DEV;
+
+ if (xbuf.stx_mask & STATX_INO) {
+ buf->ino = xbuf.stx_ino;
+ buf->mask |= BFS_STAT_INO;
+ }
+
+ buf->mode = xbuf.stx_mode;
+ if (xbuf.stx_mask & STATX_TYPE) {
+ buf->mask |= BFS_STAT_TYPE;
+ }
+ if (xbuf.stx_mask & STATX_MODE) {
+ buf->mask |= BFS_STAT_MODE;
+ }
+
+ if (xbuf.stx_mask & STATX_NLINK) {
+ buf->nlink = xbuf.stx_nlink;
+ buf->mask |= BFS_STAT_NLINK;
+ }
+
+ if (xbuf.stx_mask & STATX_GID) {
+ buf->gid = xbuf.stx_gid;
+ buf->mask |= BFS_STAT_GID;
+ }
+
+ if (xbuf.stx_mask & STATX_UID) {
+ buf->uid = xbuf.stx_uid;
+ buf->mask |= BFS_STAT_UID;
+ }
+
+ if (xbuf.stx_mask & STATX_SIZE) {
+ buf->size = xbuf.stx_size;
+ buf->mask |= BFS_STAT_SIZE;
+ }
+
+ if (xbuf.stx_mask & STATX_BLOCKS) {
+ buf->blocks = xbuf.stx_blocks;
+ buf->mask |= BFS_STAT_BLOCKS;
+ }
+
+ if (xbuf.stx_mask & STATX_ATIME) {
+ buf->atime.tv_sec = xbuf.stx_atime.tv_sec;
+ buf->atime.tv_nsec = xbuf.stx_atime.tv_nsec;
+ buf->mask |= BFS_STAT_ATIME;
+ }
+
+ if (xbuf.stx_mask & STATX_BTIME) {
+ buf->btime.tv_sec = xbuf.stx_btime.tv_sec;
+ buf->btime.tv_nsec = xbuf.stx_btime.tv_nsec;
+ buf->mask |= BFS_STAT_BTIME;
+ }
+
+ if (xbuf.stx_mask & STATX_CTIME) {
+ buf->ctime.tv_sec = xbuf.stx_ctime.tv_sec;
+ buf->ctime.tv_nsec = xbuf.stx_ctime.tv_nsec;
+ buf->mask |= BFS_STAT_CTIME;
+ }
+
+ if (xbuf.stx_mask & STATX_MTIME) {
+ buf->mtime.tv_sec = xbuf.stx_mtime.tv_sec;
+ buf->mtime.tv_nsec = xbuf.stx_mtime.tv_nsec;
+ buf->mask |= BFS_STAT_MTIME;
+ }
+
+ return ret;
+}
+
+#endif // STATX_BASIC_STATS
+
+int bfs_stat(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf) {
+#ifdef STATX_BASIC_STATS
+ static bool has_statx = true;
+
+ if (has_statx) {
+ int ret = bfs_statx_impl(at_fd, at_path, at_flags, flags, buf);
+ if (ret != 0 && errno == ENOSYS) {
+ has_statx = false;
+ } else {
+ return ret;
+ }
+ }
+#endif
+
+ return bfs_stat_impl(at_fd, at_path, at_flags, flags, buf);
+}
+
+int bfs_fstat(int fd, struct bfs_stat *buf) {
+#ifdef AT_EMPTY_PATH
+ return bfs_stat(fd, "", AT_EMPTY_PATH, 0, buf);
+#else
+ struct stat statbuf;
+ int ret = fstat(fd, &statbuf);
+ if (ret == 0) {
+ bfs_stat_convert(&statbuf, buf);
+ }
+ return ret;
+#endif
+}
diff --git a/stat.h b/stat.h
new file mode 100644
index 0000000..d47d248
--- /dev/null
+++ b/stat.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2018 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+ ****************************************************************************/
+
+#ifndef BFS_STAT_H
+#define BFS_STAT_H
+
+#include <sys/types.h>
+#include <time.h>
+
+/**
+ * bfs_stat field bitmask.
+ */
+enum bfs_stat_field {
+ BFS_STAT_DEV = 1 << 0,
+ BFS_STAT_INO = 1 << 1,
+ BFS_STAT_TYPE = 1 << 2,
+ BFS_STAT_MODE = 1 << 3,
+ BFS_STAT_NLINK = 1 << 4,
+ BFS_STAT_GID = 1 << 5,
+ BFS_STAT_UID = 1 << 6,
+ BFS_STAT_SIZE = 1 << 7,
+ BFS_STAT_BLOCKS = 1 << 8,
+ BFS_STAT_ATIME = 1 << 9,
+ BFS_STAT_BTIME = 1 << 10,
+ BFS_STAT_CTIME = 1 << 11,
+ BFS_STAT_MTIME = 1 << 12,
+};
+
+/**
+ * bfs_stat() flags.
+ */
+enum bfs_stat_flag {
+ /** Fall back to the link itself on broken symlinks. */
+ BFS_STAT_BROKEN_OK = 1 << 0,
+};
+
+/**
+ * Facade over struct stat.
+ */
+struct bfs_stat {
+ /** Bitmask indicating filled fields. */
+ enum bfs_stat_field mask;
+
+ /** Device ID containing the file. */
+ dev_t dev;
+ /** Inode number. */
+ ino_t ino;
+ /** File type and access mode. */
+ mode_t mode;
+ /** Number of hard links. */
+ nlink_t nlink;
+ /** Owner group ID. */
+ gid_t gid;
+ /** Owner user ID. */
+ uid_t uid;
+ /** File size in bytes. */
+ off_t size;
+ /** Number of 512B blocks allocated. */
+ blkcnt_t blocks;
+
+ /** Access time. */
+ struct timespec atime;
+ /** Birth/creation time. */
+ struct timespec btime;
+ /** Status change time. */
+ struct timespec ctime;
+ /** Modification time. */
+ struct timespec mtime;
+};
+
+/**
+ * Facade over fstatat().
+ */
+int bfs_stat(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf);
+
+/**
+ * Facade over fstat().
+ */
+int bfs_fstat(int fd, struct bfs_stat *buf);
+
+#endif // BFS_STAT_H
diff --git a/util.c b/util.c
index 79bb2d2..9182a80 100644
--- a/util.c
+++ b/util.c
@@ -262,17 +262,6 @@ bool is_nonexistence_error(int error) {
return error == ENOENT || errno == ENOTDIR;
}
-int xfstatat(int fd, const char *path, struct stat *buf, int flags) {
- int ret = fstatat(fd, path, buf, flags);
-
- if (ret != 0 && !(flags & AT_SYMLINK_NOFOLLOW) && is_nonexistence_error(errno)) {
- flags |= AT_SYMLINK_NOFOLLOW;
- ret = fstatat(fd, path, buf, flags);
- }
-
- return ret;
-}
-
enum bftw_typeflag mode_to_typeflag(mode_t mode) {
switch (mode & S_IFMT) {
#ifdef S_IFBLK
diff --git a/util.h b/util.h
index efeea19..81caf12 100644
--- a/util.h
+++ b/util.h
@@ -23,22 +23,11 @@
#include <fnmatch.h>
#include <regex.h>
#include <stdbool.h>
-#include <sys/stat.h>
+#include <sys/types.h>
#include <time.h>
// Some portability concerns
-#if __APPLE__
-# define st_atim st_atimespec
-# define st_ctim st_ctimespec
-# define st_mtim st_mtimespec
-# define st_birthtim st_birthtimespec
-#endif
-
-#if __APPLE__ || __FreeBSD__ || __NetBSD__
-# define BFS_HAVE_ST_BIRTHTIM true
-#endif
-
#if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE)
# define FNM_CASEFOLD FNM_IGNORECASE
#endif
@@ -167,22 +156,7 @@ int xfaccessat(int fd, const char *path, int amode);
bool is_nonexistence_error(int error);
/**
- * stat() a file, falling back on the link itself for broken symbolic links.
- *
- * @param fd
- * The base directory descriptor.
- * @param path
- * The path to the file, relative to fd.
- * @param buf
- * The stat buffer to fill.
- * @param flags
- * AT_* flags for this call.
- * @return 0 on success, -1 on failure.
- */
-int xfstatat(int fd, const char *path, struct stat *buf, int flags);
-
-/**
- * Convert a stat() st_mode to a bftw() typeflag.
+ * Convert a bfs_stat() mode to a bftw() typeflag.
*/
enum bftw_typeflag mode_to_typeflag(mode_t mode);