aboutsummaryrefslogtreecommitdiff
path: root/lib/util
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-04-21 19:53:57 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-04-21 20:16:46 +0200
commit73d342861a03c38528ca5f97cdd479b4fdb5b3fd (patch)
tree00cecdb98b09dfa4b61e4c055a4bf75a15edfa9d /lib/util
parentf8270c05898313a8e75c367172958335dbec4a36 (diff)
libutil: Add a method to the directory iterator to open a sub directory
This is also the reason we need to lug around the original directory path on Windows. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/util')
-rw-r--r--lib/util/src/unix_dir_iterator.c49
-rw-r--r--lib/util/src/w32_dir_iterator.c46
-rw-r--r--lib/util/test/dir_iterator.c149
3 files changed, 242 insertions, 2 deletions
diff --git a/lib/util/src/unix_dir_iterator.c b/lib/util/src/unix_dir_iterator.c
index 69a4251..ae878a2 100644
--- a/lib/util/src/unix_dir_iterator.c
+++ b/lib/util/src/unix_dir_iterator.c
@@ -136,6 +136,54 @@ static int dir_next(dir_iterator_t *base, dir_entry_t **out)
return it->state;
}
+static int dir_open_subdir(dir_iterator_t *base, dir_iterator_t **out)
+{
+ const unix_dir_iterator_t *it = (const unix_dir_iterator_t *)base;
+ unix_dir_iterator_t *sub = NULL;
+ int fd;
+
+ *out = NULL;
+
+ if (it->state < 0)
+ return it->state;
+
+ if (it->state > 0 || it->ent == NULL)
+ return SQFS_ERROR_NO_ENTRY;
+
+ fd = openat(dirfd(it->dir), it->ent->d_name, O_RDONLY | O_DIRECTORY);
+ if (fd < 0) {
+ if (errno == ENOTDIR)
+ return SQFS_ERROR_NOT_DIR;
+ return SQFS_ERROR_IO;
+ }
+
+ sub = calloc(1, sizeof(*sub));
+ if (sub == NULL)
+ goto fail_alloc;
+
+ sub->dir = fdopendir(fd);
+ if (sub->dir == NULL)
+ goto fail_alloc;
+
+ if (fstat(dirfd(sub->dir), &sub->sb)) {
+ free(sub);
+ return SQFS_ERROR_IO;
+ }
+
+ sqfs_object_init(sub, dir_destroy, NULL);
+ ((dir_iterator_t *)sub)->dev = sub->sb.st_dev;
+ ((dir_iterator_t *)sub)->next = dir_next;
+ ((dir_iterator_t *)sub)->read_link = dir_read_link;
+ ((dir_iterator_t *)sub)->open_subdir = dir_open_subdir;
+
+ *out = (dir_iterator_t *)sub;
+ return 0;
+fail_alloc:
+ free(sub);
+ close(fd);
+ return SQFS_ERROR_ALLOC;
+}
+
dir_iterator_t *dir_iterator_create(const char *path)
{
unix_dir_iterator_t *it = calloc(1, sizeof(*it));
@@ -165,6 +213,7 @@ dir_iterator_t *dir_iterator_create(const char *path)
((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;
+ ((dir_iterator_t *)it)->open_subdir = dir_open_subdir;
return (dir_iterator_t *)it;
}
diff --git a/lib/util/src/w32_dir_iterator.c b/lib/util/src/w32_dir_iterator.c
index 5400975..f931a26 100644
--- a/lib/util/src/w32_dir_iterator.c
+++ b/lib/util/src/w32_dir_iterator.c
@@ -107,6 +107,51 @@ static void dir_iterator_destroy(sqfs_object_t *obj)
free(dir);
}
+static int dir_iterator_open_subdir(dir_iterator_t *it, dir_iterator_t **out)
+{
+ const dir_iterator_win32_t *dir = (const dir_iterator_win32_t *)it;
+ dir_iterator_win32_t *sub = NULL;
+ size_t plen, slen, total;
+
+ *out = NULL;
+
+ if (dir->state != 0)
+ return (dir->state > 0) ? SQFS_ERROR_NO_ENTRY : dir->state;
+
+ if (!(dir->ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ return SQFS_ERROR_NOT_DIR;
+
+ plen = wcslen(dir->path) - 1;
+ slen = wcslen(dir->ent.cFileName);
+ total = plen + slen + 3;
+
+ sub = alloc_flex(sizeof(*sub), sizeof(WCHAR), total);
+ if (sub == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ memcpy(sub->path, dir->path, plen * sizeof(WCHAR));
+ memcpy(sub->path + plen, dir->ent.cFileName, slen * sizeof(WCHAR));
+ sub->path[plen + slen ] = '\\';
+ sub->path[plen + slen + 1] = '*';
+ sub->path[plen + slen + 2] = '\0';
+
+ sqfs_object_init(sub, dir_iterator_destroy, NULL);
+ ((dir_iterator_t *)sub)->next = dir_iterator_next;
+ ((dir_iterator_t *)sub)->read_link = dir_iterator_read_link;
+ ((dir_iterator_t *)sub)->open_subdir = dir_iterator_open_subdir;
+ sub->is_first = true;
+ sub->state = 0;
+
+ sub->dirhnd = FindFirstFileW(sub->path, &sub->ent);
+ if (sub->dirhnd == INVALID_HANDLE_VALUE) {
+ free(sub);
+ return SQFS_ERROR_IO;
+ }
+
+ *out = (dir_iterator_t *)sub;
+ return 0;
+}
+
dir_iterator_t *dir_iterator_create(const char *path)
{
dir_iterator_win32_t *it;
@@ -152,6 +197,7 @@ dir_iterator_t *dir_iterator_create(const char *path)
((dir_iterator_t *)it)->next = dir_iterator_next;
((dir_iterator_t *)it)->read_link = dir_iterator_read_link;
+ ((dir_iterator_t *)it)->open_subdir = dir_iterator_open_subdir;
it->is_first = true;
it->state = 0;
diff --git a/lib/util/test/dir_iterator.c b/lib/util/test/dir_iterator.c
index 22b05dc..d209afd 100644
--- a/lib/util/test/dir_iterator.c
+++ b/lib/util/test/dir_iterator.c
@@ -7,6 +7,7 @@
#include "config.h"
#include "util/dir_iterator.h"
+#include "sqfs/error.h"
#include "util/test.h"
#include "compat.h"
@@ -20,8 +21,8 @@ static int compare_entries(const void *a, const void *b)
int main(int argc, char **argv)
{
- dir_iterator_t *dir;
- dir_entry_t *ent[6];
+ dir_iterator_t *dir, *suba, *subb, *subc, *sub;
+ dir_entry_t *dent, *ent[6];
size_t i;
int ret;
(void)argc; (void)argv;
@@ -210,5 +211,149 @@ int main(int argc, char **argv)
for (i = 0; i < 5; ++i)
free(ent[i]);
+ /* test sub directory iterators */
+ suba = NULL;
+ subb = NULL;
+ subc = NULL;
+
+ dir = dir_iterator_create(TEST_PATH);
+ TEST_NOT_NULL(dir);
+
+ for (i = 0; i < 5; ++i) {
+ ret = dir->next(dir, &dent);
+ TEST_NOT_NULL(dent);
+ TEST_EQUAL_I(ret, 0);
+
+ if (!strcmp(dent->name, "dira")) {
+ TEST_NULL(suba);
+ ret = dir->open_subdir(dir, &suba);
+ TEST_NOT_NULL(suba);
+ TEST_EQUAL_I(ret, 0);
+ } else if (!strcmp(dent->name, "dirb")) {
+ TEST_NULL(subb);
+ ret = dir->open_subdir(dir, &subb);
+ TEST_NOT_NULL(subb);
+ TEST_EQUAL_I(ret, 0);
+ } else if (!strcmp(dent->name, "dirc")) {
+ TEST_NULL(subc);
+ ret = dir->open_subdir(dir, &subc);
+ TEST_NOT_NULL(subc);
+ TEST_EQUAL_I(ret, 0);
+ }
+
+ free(dent);
+ }
+
+ ret = dir->next(dir, &dent);
+ TEST_NULL(dent);
+ TEST_ASSERT(ret > 0);
+ dir = sqfs_drop(dir);
+
+ TEST_NOT_NULL(suba);
+ TEST_NOT_NULL(subb);
+ TEST_NOT_NULL(subc);
+
+ /* sub iterator a */
+ for (i = 0; i < 5; ++i) {
+ ret = suba->next(suba, &ent[i]);
+ TEST_NOT_NULL(ent[0]);
+ TEST_EQUAL_I(ret, 0);
+
+ if (S_ISREG(ent[i]->mode)) {
+ ret = suba->open_subdir(suba, &sub);
+ TEST_NULL(sub);
+ TEST_EQUAL_I(ret, SQFS_ERROR_NOT_DIR);
+ }
+ }
+
+ ret = suba->next(suba, &dent);
+ TEST_NULL(dent);
+ TEST_ASSERT(ret > 0);
+ suba = sqfs_drop(suba);
+
+ qsort(ent, 5, sizeof(ent[0]), compare_entries);
+
+ TEST_STR_EQUAL(ent[0]->name, ".");
+ TEST_ASSERT(S_ISDIR(ent[0]->mode));
+ TEST_STR_EQUAL(ent[1]->name, "..");
+ TEST_ASSERT(S_ISDIR(ent[1]->mode));
+ TEST_STR_EQUAL(ent[2]->name, "file_a0");
+ TEST_ASSERT(S_ISREG(ent[2]->mode));
+ TEST_STR_EQUAL(ent[3]->name, "file_a1");
+ TEST_ASSERT(S_ISREG(ent[3]->mode));
+ TEST_STR_EQUAL(ent[4]->name, "file_a2");
+ TEST_ASSERT(S_ISREG(ent[4]->mode));
+
+ for (i = 0; i < 5; ++i)
+ free(ent[i]);
+
+ /* sub iterator b */
+ for (i = 0; i < 5; ++i) {
+ ret = subb->next(subb, &ent[i]);
+ TEST_NOT_NULL(ent[0]);
+ TEST_EQUAL_I(ret, 0);
+
+ if (S_ISREG(ent[i]->mode)) {
+ ret = subb->open_subdir(subb, &sub);
+ TEST_NULL(sub);
+ TEST_EQUAL_I(ret, SQFS_ERROR_NOT_DIR);
+ }
+ }
+
+ ret = subb->next(subb, &dent);
+ TEST_NULL(dent);
+ TEST_ASSERT(ret > 0);
+ subb = sqfs_drop(subb);
+
+ qsort(ent, 5, sizeof(ent[0]), compare_entries);
+
+ TEST_STR_EQUAL(ent[0]->name, ".");
+ TEST_ASSERT(S_ISDIR(ent[0]->mode));
+ TEST_STR_EQUAL(ent[1]->name, "..");
+ TEST_ASSERT(S_ISDIR(ent[1]->mode));
+ TEST_STR_EQUAL(ent[2]->name, "file_b0");
+ TEST_ASSERT(S_ISREG(ent[2]->mode));
+ TEST_STR_EQUAL(ent[3]->name, "file_b1");
+ TEST_ASSERT(S_ISREG(ent[3]->mode));
+ TEST_STR_EQUAL(ent[4]->name, "file_b2");
+ TEST_ASSERT(S_ISREG(ent[4]->mode));
+
+ for (i = 0; i < 5; ++i)
+ free(ent[i]);
+
+ /* sub iterator c */
+ for (i = 0; i < 5; ++i) {
+ ret = subc->next(subc, &ent[i]);
+ TEST_NOT_NULL(ent[0]);
+ TEST_EQUAL_I(ret, 0);
+
+ if (S_ISREG(ent[i]->mode)) {
+ ret = subc->open_subdir(subc, &sub);
+ TEST_NULL(sub);
+ TEST_EQUAL_I(ret, SQFS_ERROR_NOT_DIR);
+ }
+ }
+
+ ret = subc->next(subc, &dent);
+ TEST_NULL(dent);
+ TEST_ASSERT(ret > 0);
+ subc = sqfs_drop(subc);
+
+ qsort(ent, 5, sizeof(ent[0]), compare_entries);
+
+ TEST_STR_EQUAL(ent[0]->name, ".");
+ TEST_ASSERT(S_ISDIR(ent[0]->mode));
+ TEST_STR_EQUAL(ent[1]->name, "..");
+ TEST_ASSERT(S_ISDIR(ent[1]->mode));
+ TEST_STR_EQUAL(ent[2]->name, "file_c0");
+ TEST_ASSERT(S_ISREG(ent[2]->mode));
+ TEST_STR_EQUAL(ent[3]->name, "file_c1");
+ TEST_ASSERT(S_ISREG(ent[3]->mode));
+ TEST_STR_EQUAL(ent[4]->name, "file_c2");
+ TEST_ASSERT(S_ISREG(ent[4]->mode));
+
+ for (i = 0; i < 5; ++i)
+ free(ent[i]);
+
return EXIT_SUCCESS;
}