summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2015-07-25 20:14:34 -0400
committerTavian Barnes <tavianator@tavianator.com>2015-07-25 20:14:34 -0400
commit781f5902b7bbb91811f1f810f8a419607ed36294 (patch)
tree8ff1acf48ba81b2f35190a4f3d811ad1b6bf68f7
parente674297be6be114a530cd06d2ca773baff1ce7cc (diff)
downloadbfs-781f5902b7bbb91811f1f810f8a419607ed36294.tar.xz
Recover from errors in diropen().
Fixes #4.
-rw-r--r--bfs.c11
-rw-r--r--bftw.c200
-rw-r--r--bftw.h13
-rw-r--r--color.c31
-rw-r--r--color.h18
5 files changed, 183 insertions, 90 deletions
diff --git a/bfs.c b/bfs.c
index a3a969c..fcd1383 100644
--- a/bfs.c
+++ b/bfs.c
@@ -23,9 +23,14 @@ typedef struct {
bool hidden;
} options;
-static int callback(const char *fpath, const struct BFTW* ftwbuf, void *ptr) {
+static int callback(const char *fpath, const struct BFTW *ftwbuf, void *ptr) {
const options *opts = ptr;
+ if (ftwbuf->typeflag == BFTW_ERROR) {
+ print_error(opts->colors, fpath, ftwbuf);
+ return BFTW_SKIP_SUBTREE;
+ }
+
if (!opts->hidden) {
if (ftwbuf->base > 0 && fpath[ftwbuf->base] == '.') {
return BFTW_SKIP_SUBTREE;
@@ -36,7 +41,7 @@ static int callback(const char *fpath, const struct BFTW* ftwbuf, void *ptr) {
return BFTW_CONTINUE;
}
-int main(int argc, char* argv[]) {
+int main(int argc, char *argv[]) {
int ret = EXIT_FAILURE;
options opts;
@@ -73,7 +78,7 @@ int main(int argc, char* argv[]) {
opts.path = ".";
}
- int flags = 0;
+ int flags = BFTW_RECOVER;
if (color) {
flags |= BFTW_STAT;
diff --git a/bftw.c b/bftw.c
index 9b990e7..31e6fe2 100644
--- a/bftw.c
+++ b/bftw.c
@@ -105,6 +105,8 @@ struct dircache_entry {
/** Reference count. */
size_t refcount;
+ /** The offset of this directory in the full path. */
+ size_t nameoff;
/** The length of the directory's name. */
size_t namelen;
/** The directory's name. */
@@ -131,17 +133,28 @@ static void dircache_init(dircache *cache, size_t lru_size) {
}
/** Add an entry to the dircache. */
-static dircache_entry *dircache_add(dircache *cache, dircache_entry *parent, const char *path) {
- size_t pathsize = strlen(path) + 1;
- dircache_entry *entry = malloc(sizeof(dircache_entry) + pathsize);
+static dircache_entry *dircache_add(dircache *cache, dircache_entry *parent, const char *name) {
+ size_t namesize = strlen(name) + 1;
+ dircache_entry *entry = malloc(sizeof(dircache_entry) + namesize);
if (entry) {
entry->parent = parent;
- entry->depth = parent ? parent->depth + 1 : 0;
+
+ if (parent) {
+ entry->depth = parent->depth + 1;
+ entry->nameoff = parent->nameoff + parent->namelen;
+ if (parent->namelen > 0 && parent->name[parent->namelen - 1] != '/') {
+ ++entry->nameoff;
+ }
+ } else {
+ entry->depth = 0;
+ entry->nameoff = 0;
+ }
+
entry->lru_prev = entry->lru_next = NULL;
entry->dir = NULL;
entry->refcount = 1;
- entry->namelen = pathsize - 1;
- memcpy(entry->name, path, pathsize);
+ entry->namelen = namesize - 1;
+ memcpy(entry->name, name, namesize);
while (parent) {
++parent->refcount;
@@ -212,81 +225,73 @@ static DIR *opendirat(int fd, const char *name) {
}
/**
- * Open a dircache_entry.
+ * Get the full path do a dircache_entry.
*
- * @param cache
- * The cache containing the entry.
* @param entry
- * The entry to open.
+ * The entry to look up.
* @param[out] path
* Will hold the full path to the entry, with a trailing '/'.
- * @return
- * The opened DIR *, or NULL on error.
*/
-static DIR *dircache_entry_open(dircache *cache, dircache_entry *entry, dynstr *path) {
- assert(!entry->dir);
+static int dircache_entry_path(dircache_entry *entry, dynstr *path) {
+ size_t pathlen = entry->nameoff + entry->namelen + 1;
- if (cache->lru_remaining == 0) {
- dircache_entry_close(cache, cache->lru_tail);
+ if (dynstr_grow(path, pathlen) != 0) {
+ return -1;
}
+ path->length = pathlen;
- // First, reserve enough space for the path
- size_t pathlen = 0;
+ // Build the path backwards
+ path->str[pathlen] = '\0';
- dircache_entry *parent = entry;
do {
- size_t namelen = parent->namelen;
- pathlen += namelen;
+ char *segment = path->str + entry->nameoff;
+ size_t namelen = entry->namelen;
- if (namelen > 0 && parent->name[namelen - 1] != '/') {
- ++pathlen;
+ memcpy(segment, entry->name, namelen);
+ if (namelen > 0 && entry->name[namelen - 1] != '/') {
+ segment[namelen] = '/';
}
- parent = parent->parent;
- } while (parent);
-
- if (dynstr_grow(path, pathlen) != 0) {
- return NULL;
- }
- path->length = pathlen;
-
- // Now, build the path backwards while looking for a parent
- char *segment = path->str + pathlen;
- *segment = '\0';
-
- int fd = AT_FDCWD;
- const char *relpath = path->str;
-
- parent = entry;
- while (true) {
- size_t namelen = parent->namelen;
- bool needs_slash = namelen > 0 && parent->name[namelen - 1] != '/';
+ entry = entry->parent;
+ } while (entry);
- segment -= namelen;
- if (needs_slash) {
- segment -= 1;
- }
+ return 0;
+}
- memcpy(segment, parent->name, namelen);
+/**
+ * Open a dircache_entry.
+ *
+ * @param cache
+ * The cache containing the entry.
+ * @param entry
+ * The entry to open.
+ * @param path
+ * The full path to the entry (see dircache_entry_path()).
+ * @return
+ * The opened DIR *, or NULL on error.
+ */
+static DIR *dircache_entry_open(dircache *cache, dircache_entry *entry, const char *path) {
+ assert(!entry->dir);
- if (needs_slash) {
- segment[namelen] = '/';
- }
+ if (cache->lru_remaining == 0) {
+ dircache_entry_close(cache, cache->lru_tail);
+ }
- parent = parent->parent;
- if (!parent) {
- break;
- }
+ size_t nameoff;
+ dircache_entry *base = entry;
+ do {
+ nameoff = base->nameoff;
+ base = base->parent;
+ } while (base && !base->dir);
- if (parent->dir && fd == AT_FDCWD) {
- dircache_lru_remove(cache, parent);
- dircache_lru_add(cache, parent);
- fd = dirfd(parent->dir);
- relpath = segment;
- }
+ int fd = AT_FDCWD;
+ if (base) {
+ dircache_lru_remove(cache, base);
+ dircache_lru_add(cache, base);
+ fd = dirfd(base->dir);
}
- DIR *dir = opendirat(fd, relpath);
+ DIR *dir = opendirat(fd, path + nameoff);
if (dir) {
entry->dir = dir;
dircache_lru_add(cache, entry);
@@ -395,7 +400,7 @@ static dircache_entry *dirqueue_pop(dirqueue *queue) {
}
int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr) {
- int ret = -1, err;
+ int ret = -1, err = 0;
dircache cache;
dircache_init(&cache, nopenfd);
@@ -408,34 +413,68 @@ int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr) {
dircache_entry *current = dircache_add(&cache, NULL, dirpath);
if (!current) {
- goto done;
+ goto fail;
}
do {
- DIR *dir = dircache_entry_open(&cache, current, &path);
- if (!dir) {
- goto done;
+ if (dircache_entry_path(current, &path) != 0) {
+ goto fail;
}
-
size_t pathlen = path.length;
+ DIR *dir = dircache_entry_open(&cache, current, path.str);
+ if (!dir) {
+ if (!(flags & BFTW_RECOVER)) {
+ goto fail;
+ }
+
+ err = errno;
+
+ struct BFTW ftwbuf = {
+ .statbuf = NULL,
+ .typeflag = BFTW_ERROR,
+ .base = current->nameoff,
+ .level = current->depth,
+ .error = err,
+ };
+
+ int action = fn(path.str, &ftwbuf, ptr);
+
+ switch (action) {
+ case BFTW_CONTINUE:
+ case BFTW_SKIP_SIBLINGS:
+ case BFTW_SKIP_SUBTREE:
+ goto next;
+
+ case BFTW_STOP:
+ goto done;
+
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+
+ goto next;
+ }
+
struct dirent *de;
while ((de = readdir(dir)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
continue;
}
+ if (dynstr_concat(&path, pathlen, de->d_name) != 0) {
+ goto fail;
+ }
+
struct BFTW ftwbuf = {
.statbuf = NULL,
.typeflag = BFTW_UNKNOWN,
.base = pathlen,
.level = current->depth + 1,
+ .error = 0,
};
- if (dynstr_concat(&path, pathlen, de->d_name) != 0) {
- goto done;
- }
-
#if defined(_DIRENT_HAVE_D_TYPE) || defined(DT_DIR)
switch (de->d_type) {
case DT_DIR:
@@ -477,11 +516,11 @@ int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr) {
if (ftwbuf.typeflag == BFTW_D) {
dircache_entry *next = dircache_add(&cache, current, de->d_name);
if (!next) {
- goto done;
+ goto fail;
}
if (dirqueue_push(&queue, next) != 0) {
- goto done;
+ goto fail;
}
}
break;
@@ -493,12 +532,11 @@ int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr) {
break;
case BFTW_STOP:
- ret = 0;
goto done;
default:
- errno = EINVAL;
- goto done;
+ err = EINVAL;
+ goto fail;
}
}
@@ -507,9 +545,15 @@ int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr) {
current = dirqueue_pop(&queue);
} while (current);
- ret = 0;
done:
- err = errno;
+ if (err == 0) {
+ ret = 0;
+ }
+
+fail:
+ if (err == 0) {
+ err = errno;
+ }
while (current) {
dircache_entry_free(&cache, current);
diff --git a/bftw.h b/bftw.h
index 37e1524..45cc0c3 100644
--- a/bftw.h
+++ b/bftw.h
@@ -9,6 +9,9 @@
* the COPYING file or http://www.wtfpl.net/ for more details. *
*********************************************************************/
+#ifndef BFS_BFTW_H
+#define BFS_BFTW_H
+
#include <sys/stat.h>
/**
@@ -23,6 +26,8 @@ struct BFTW {
int base;
/** The depth of this file in the walk. */
int level;
+ /** The errno that occurred, if typeflag == BFTW_ERROR. */
+ int error;
};
/**
@@ -69,6 +74,8 @@ int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr);
#define BFTW_SL 2
/** typeflag: Unknown type. */
#define BFTW_UNKNOWN 3
+/** typeflag: An error occurred for this file. */
+#define BFTW_ERROR 4
/** action: Keep walking. */
#define BFTW_CONTINUE 0
@@ -80,4 +87,8 @@ int bftw(const char *dirpath, bftw_fn *fn, int nopenfd, int flags, void *ptr);
#define BFTW_STOP 3
/** flag: stat() each encountered file. */
-#define BFTW_STAT (1 << 0)
+#define BFTW_STAT (1 << 0)
+/** flag: Attempt to recover from encountered errors. */
+#define BFTW_RECOVER (1 << 1)
+
+#endif // BFS_BFTW_H
diff --git a/color.c b/color.c
index 6dda4cd..521fd09 100644
--- a/color.c
+++ b/color.c
@@ -274,10 +274,10 @@ static const char *file_color(const color_table *colors, const char *filename, c
return color;
}
-static void print_esc(const char *esc) {
- fputs("\033[", stdout);
- fputs(esc, stdout);
- fputs("m", stdout);
+static void print_esc(const char *esc, FILE *file) {
+ fputs("\033[", file);
+ fputs(esc, file);
+ fputs("m", file);
}
void pretty_print(const color_table *colors, const char *fpath, const struct stat *sb) {
@@ -294,24 +294,39 @@ void pretty_print(const color_table *colors, const char *fpath, const struct sta
}
if (colors->dir) {
- print_esc(colors->dir);
+ print_esc(colors->dir, stdout);
}
fwrite(fpath, 1, filename - fpath, stdout);
if (colors->dir) {
- print_esc(colors->reset);
+ print_esc(colors->reset, stdout);
}
const char *color = file_color(colors, filename, sb);
if (color) {
- print_esc(color);
+ print_esc(color, stdout);
}
fputs(filename, stdout);
if (color) {
- print_esc(colors->reset);
+ print_esc(colors->reset, stdout);
}
fputs("\n", stdout);
}
+void print_error(const color_table *colors, const char *fpath, const struct BFTW *ftwbuf) {
+ const char *color = NULL;
+ if (colors) {
+ color = colors->orphan;
+ }
+
+ if (color) {
+ print_esc(color, stderr);
+ }
+ fprintf(stderr, "Error at %s: %s\n", fpath, strerror(ftwbuf->error));
+ if (color) {
+ print_esc(colors->reset, stderr);
+ }
+}
+
void free_colors(color_table *colors) {
if (colors) {
ext_color *ext = colors->ext_list;
diff --git a/color.h b/color.h
index 9bc5b5a..1432072 100644
--- a/color.h
+++ b/color.h
@@ -9,6 +9,10 @@
* the COPYING file or http://www.wtfpl.net/ for more details. *
*********************************************************************/
+#ifndef BFS_COLOR_H
+#define BFS_COLOR_H
+
+#include "bftw.h"
#include <sys/stat.h>
/**
@@ -38,9 +42,23 @@ color_table *parse_colors(char *ls_colors);
void pretty_print(const color_table *colors, const char *fpath, const struct stat *sb);
/**
+ * Pretty-print an error.
+ *
+ * @param colors
+ * The color table to use.
+ * @param fpath
+ * The file path in error.
+ * @param ftwbuf
+ * The bftw() data for fpath.
+ */
+void print_error(const color_table *colors, const char *fpath, const struct BFTW *ftwbuf);
+
+/**
* Free a color table.
*
* @param colors
* The color table to free.
*/
void free_colors(color_table *colors);
+
+#endif // BFS_COLOR_H