diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-04-21 19:53:57 +0200 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-04-21 20:16:46 +0200 |
commit | 73d342861a03c38528ca5f97cdd479b4fdb5b3fd (patch) | |
tree | 00cecdb98b09dfa4b61e4c055a4bf75a15edfa9d /lib | |
parent | f8270c05898313a8e75c367172958335dbec4a36 (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')
-rw-r--r-- | lib/util/src/unix_dir_iterator.c | 49 | ||||
-rw-r--r-- | lib/util/src/w32_dir_iterator.c | 46 | ||||
-rw-r--r-- | lib/util/test/dir_iterator.c | 149 |
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; } |