summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eval.c13
-rw-r--r--util.c30
-rw-r--r--util.h13
3 files changed, 44 insertions, 12 deletions
diff --git a/eval.c b/eval.c
index 6a3da7a..4494cff 100644
--- a/eval.c
+++ b/eval.c
@@ -599,23 +599,12 @@ bool eval_lname(const struct expr *expr, struct eval_state *state) {
goto done;
}
- size_t size = statbuf->st_size + 1;
- name = malloc(size);
+ name = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, statbuf->st_size);
if (!name) {
eval_error(state);
goto done;
}
- ssize_t len = readlinkat(ftwbuf->at_fd, ftwbuf->at_path, name, size);
- if (len < 0) {
- eval_error(state);
- goto done;
- } else if (len >= size) {
- goto done;
- }
-
- name[len] = '\0';
-
ret = fnmatch(expr->sdata, name, expr->idata) == 0;
done:
diff --git a/util.c b/util.c
index ce0b458..f3f2c5f 100644
--- a/util.c
+++ b/util.c
@@ -29,6 +29,36 @@ int xreaddir(DIR *dir, struct dirent **de) {
}
}
+char *xreadlinkat(int fd, const char *path, size_t size) {
+ ++size; // NUL-terminator
+ ssize_t len;
+ char *name = NULL;
+
+ while (true) {
+ char *new_name = realloc(name, size);
+ if (!new_name) {
+ goto error;
+ }
+ name = new_name;
+
+ len = readlinkat(fd, path, name, size);
+ if (len < 0) {
+ goto error;
+ } else if (len >= size) {
+ size *= 2;
+ } else {
+ break;
+ }
+ }
+
+ name[len] = '\0';
+ return name;
+
+error:
+ free(name);
+ return NULL;
+}
+
bool isopen(int fd) {
return fcntl(fd, F_GETFD) >= 0 || errno != EBADF;
}
diff --git a/util.h b/util.h
index 4fd5962..8fa775d 100644
--- a/util.h
+++ b/util.h
@@ -49,6 +49,19 @@
int xreaddir(DIR *dir, struct dirent **de);
/**
+ * readlinkat() wrapper that dynamically allocates the result.
+ *
+ * @param fd
+ * The base directory descriptor.
+ * @param path
+ * The path to the link, relative to fd.
+ * @param size
+ * An estimate for the size of the link name (pass 0 if unknown).
+ * @return The target of the link, allocated with malloc(), or NULL on failure.
+ */
+char *xreadlinkat(int fd, const char *path, size_t size);
+
+/**
* Check if a file descriptor is open.
*/
bool isopen(int fd);