aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/dirtree.c30
-rw-r--r--lib/lib.h4
2 files changed, 20 insertions, 14 deletions
diff --git a/lib/dirtree.c b/lib/dirtree.c
index b11cdacc..6cb180eb 100644
--- a/lib/dirtree.c
+++ b/lib/dirtree.c
@@ -29,28 +29,32 @@ struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, int flags)
{
struct dirtree *dt = NULL;
struct stat st;
- int len = 0, linklen = 0;
+ int len = 0, linklen = 0, statless = 0;
if (name) {
// open code this because haven't got node to call dirtree_parentfd() on yet
int fd = parent ? parent->dirfd : AT_FDCWD;
- if (fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW)))
- goto error;
+ if (fstatat(fd, name, &st,AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW))) {
+ if (flags&DIRTREE_STATLESS) statless++;
+ else goto error;
+ }
if (S_ISLNK(st.st_mode)) {
if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error;
libbuf[linklen++]=0;
}
len = strlen(name);
}
- dt = xzalloc((len = sizeof(struct dirtree)+len+1)+linklen);
- dt->parent = parent;
- if (name) {
- memcpy(&(dt->st), &st, sizeof(struct stat));
- strcpy(dt->name, name);
- if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen);
- }
+ // Allocate/populate return structure
+ dt = xmalloc((len = sizeof(struct dirtree)+len+1)+linklen);
+ memset(dt, 0, statless ? offsetof(struct dirtree, again)
+ : offsetof(struct dirtree, st));
+ dt->parent = parent;
+ dt->again = statless ? 2 : 0;
+ if (!statless) memcpy(&dt->st, &st, sizeof(struct stat));
+ strcpy(dt->name, name ? name : "");
+ if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen);
return dt;
@@ -86,7 +90,7 @@ char *dirtree_path(struct dirtree *node, int *plen)
len = (plen ? *plen : 0)+strlen(node->name)+1;
path = dirtree_path(node->parent, &len);
if (len && path[len-1] != '/') path[len++]='/';
- len = (stpcpy(path+len, node->name) - path);
+ len = stpcpy(path+len, node->name) - path;
if (plen) *plen = len;
return path;
@@ -113,7 +117,7 @@ struct dirtree *dirtree_handle_callback(struct dirtree *new,
if (S_ISDIR(new->st.st_mode) && (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)))
flags = dirtree_recurse(new, callback,
- openat(dirtree_parentfd(new), new->name, O_CLOEXEC), flags);
+ openat(dirtree_parentfd(new), new->name, O_PATH|O_CLOEXEC), flags);
// If this had children, it was callback's job to free them already.
if (!(flags & DIRTREE_SAVE)) {
@@ -162,7 +166,7 @@ int dirtree_recurse(struct dirtree *node,
}
if (flags & DIRTREE_COMEAGAIN) {
- node->again++;
+ node->again |= 1;
flags = callback(node);
}
diff --git a/lib/lib.h b/lib/lib.h
index c3b3671c..0da3d9d6 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -76,6 +76,8 @@ void get_optflags(void);
#define DIRTREE_BREADTH 32
// skip non-numeric entries
#define DIRTREE_PROC 64
+// Return files we can't stat
+#define DIRTREE_STATLESS 128
// Don't look at any more files in this directory.
#define DIRTREE_ABORT 256
@@ -84,9 +86,9 @@ void get_optflags(void);
struct dirtree {
struct dirtree *next, *parent, *child;
long extra; // place for user to store their stuff (can be pointer)
- struct stat st;
char *symlink;
int dirfd;
+ struct stat st;
char again;
char name[];
};