diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/util/Makemodule.am | 2 | ||||
-rw-r--r-- | lib/util/src/unix_dir_iterator.c | 169 | ||||
-rw-r--r-- | lib/util/src/w32_dir_iterator.c | 8 |
3 files changed, 179 insertions, 0 deletions
diff --git a/lib/util/Makemodule.am b/lib/util/Makemodule.am index 8e9b01d..2c50594 100644 --- a/lib/util/Makemodule.am +++ b/lib/util/Makemodule.am @@ -16,6 +16,8 @@ libutil_a_CPPFLAGS = $(AM_CPPFLAGS) if WINDOWS libutil_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 libutil_a_SOURCES += lib/util/src/w32_dir_iterator.c +else +libutil_a_SOURCES += lib/util/src/unix_dir_iterator.c endif if HAVE_PTHREAD diff --git a/lib/util/src/unix_dir_iterator.c b/lib/util/src/unix_dir_iterator.c new file mode 100644 index 0000000..e712a45 --- /dev/null +++ b/lib/util/src/unix_dir_iterator.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * unix_dir_iterator.c + * + * Copyright (C) 2023 David Oberhollenzer <goliath@infraroot.at> + */ +#include "config.h" +#include "util/dir_iterator.h" +#include "util/util.h" +#include "sqfs/error.h" + +#include <sys/stat.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> + +typedef struct { + dir_iterator_t base; + + struct dirent *ent; + struct stat sb; + int state; + DIR *dir; +} unix_dir_iterator_t; + +static void dir_destroy(sqfs_object_t *obj) +{ + unix_dir_iterator_t *it = (unix_dir_iterator_t *)obj; + + closedir(it->dir); + free(it); +} + +static int dir_read_link(dir_iterator_t *base, char **out) +{ + unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; + size_t size; + char *str; + + *out = NULL; + + if (it->state != 0 || it->ent == NULL) { + fputs("[BUG] no entry loaded, cannot readlink\n", stderr); + return SQFS_ERROR_INTERNAL; + } + + if (S_ISLNK(it->sb.st_mode)) { + fprintf(stderr, "[BUG] %s is not a symlink, cannot readlink\n", + it->ent->d_name); + it->state = SQFS_ERROR_INTERNAL; + return SQFS_ERROR_INTERNAL; + } + + if ((sizeof(it->sb.st_size) > sizeof(size_t)) && + it->sb.st_size > SIZE_MAX) { + goto fail_ov; + } + + if (SZ_ADD_OV((size_t)it->sb.st_size, 1, &size)) + goto fail_ov; + + str = calloc(1, size); + if (str == NULL) { + perror(it->ent->d_name); + it->state = SQFS_ERROR_ALLOC; + return it->state; + } + + if (readlinkat(dirfd(it->dir), it->ent->d_name, + str, (size_t)it->sb.st_size) < 0) { + fprintf(stderr, "%s: readlink: %s\n", it->ent->d_name, + strerror(errno)); + free(str); + it->state = SQFS_ERROR_INTERNAL; + return it->state; + } + + str[it->sb.st_size] = '\0'; + + *out = str; + return it->state; +fail_ov: + fprintf(stderr, "%s: link target too long\n", it->ent->d_name); + return SQFS_ERROR_OVERFLOW; +} + +static int dir_next(dir_iterator_t *base, dir_entry_t **out) +{ + unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; + dir_entry_t *decoded; + size_t len; + + if (it->state != 0) + return it->state; + + errno = 0; + it->ent = readdir(it->dir); + + if (it->ent == NULL) { + if (errno != 0) { + perror("readdir"); + it->state = SQFS_ERROR_INTERNAL; + } else { + it->state = 1; + } + + return it->state; + } + + if (fstatat(dirfd(it->dir), it->ent->d_name, &it->sb, AT_SYMLINK_NOFOLLOW)) { + perror(it->ent->d_name); + it->state = SQFS_ERROR_INTERNAL; + return it->state; + } + + len = strlen(it->ent->d_name); + + decoded = alloc_flex(sizeof(*decoded), 1, len + 1); + if (decoded == NULL) { + perror(it->ent->d_name); + it->state = SQFS_ERROR_ALLOC; + return it->state; + } + + memcpy(decoded->name, it->ent->d_name, len); + decoded->mtime = it->sb.st_mtime; + decoded->dev = it->sb.st_dev; + decoded->rdev = it->sb.st_rdev; + decoded->uid = it->sb.st_uid; + decoded->gid = it->sb.st_gid; + decoded->mode = it->sb.st_mode; + + *out = decoded; + return it->state; +} + +dir_iterator_t *dir_iterator_create(const char *path) +{ + unix_dir_iterator_t *it = calloc(1, sizeof(*it)); + + if (it == NULL) { + perror(path); + return NULL; + } + + it->state = 0; + it->dir = opendir(path); + + if (it->dir == NULL) { + perror(path); + free(it); + return NULL; + } + + if (fstat(dirfd(it->dir), &it->sb)) { + perror(path); + closedir(it->dir); + free(it); + return NULL; + } + + sqfs_object_init(it, dir_destroy, NULL); + ((dir_iterator_t *)it)->dev = it->sb.st_dev; + ((dir_iterator_t *)it)->next = dir_next; + ((dir_iterator_t *)it)->read_link = dir_read_link; + + return (dir_iterator_t *)it; +} diff --git a/lib/util/src/w32_dir_iterator.c b/lib/util/src/w32_dir_iterator.c index 1a330ae..9ec8b18 100644 --- a/lib/util/src/w32_dir_iterator.c +++ b/lib/util/src/w32_dir_iterator.c @@ -47,6 +47,13 @@ static sqfs_s64 w32time_to_unix(const FILETIME *ft) return w32ts - UNIX_EPOCH_ON_W32; } +static int dir_iterator_read_link(dir_iterator_t *it, char **out) +{ + (void)it; + *out = NULL; + return SQFS_ERROR_UNSUPPORTED; +} + static int dir_iterator_next(dir_iterator_t *it, dir_entry_t **out) { dir_iterator_win32_t *w32 = (dir_iterator_win32_t *)it; @@ -177,6 +184,7 @@ dir_iterator_t *dir_iterator_create(const char *path) sqfs_object_init(it, dir_iterator_destroy, NULL); ((dir_iterator_t *)it)->next = dir_iterator_next; + ((dir_iterator_t *)it)->read_link = dir_iterator_read_link; it->state = STATE_HAVE_ENTRY; it->dirhnd = dirhnd; |