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 | |
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>
-rw-r--r-- | bin/gensquashfs/src/fstree_from_dir.c | 40 | ||||
-rw-r--r-- | include/util/dir_iterator.h | 3 | ||||
-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 |
5 files changed, 267 insertions, 20 deletions
diff --git a/bin/gensquashfs/src/fstree_from_dir.c b/bin/gensquashfs/src/fstree_from_dir.c index 27576ac..85381f9 100644 --- a/bin/gensquashfs/src/fstree_from_dir.c +++ b/bin/gensquashfs/src/fstree_from_dir.c @@ -69,15 +69,9 @@ static void discard_node(tree_node_t *root, tree_node_t *n) free(n); } -static int scan_dir(fstree_t *fs, tree_node_t *root, const char *path, +static int scan_dir(fstree_t *fs, tree_node_t *root, dir_iterator_t *dir, scan_node_callback cb, void *user, unsigned int flags) { - dir_iterator_t *dir; - - dir = dir_iterator_create(path); - if (dir == NULL) - return -1; - for (;;) { dir_entry_t *ent = NULL; tree_node_t *n = NULL; @@ -87,7 +81,7 @@ static int scan_dir(fstree_t *fs, tree_node_t *root, const char *path, if (ret > 0) break; if (ret < 0) - goto fail; + return -1; if (should_skip(dir, ent, flags)) { free(ent); @@ -128,7 +122,7 @@ static int scan_dir(fstree_t *fs, tree_node_t *root, const char *path, perror("creating tree node"); free(extra); free(ent); - goto fail; + return -1; } ret = (cb == NULL) ? 0 : cb(user, fs, n); @@ -138,7 +132,7 @@ static int scan_dir(fstree_t *fs, tree_node_t *root, const char *path, free(extra); if (ret < 0) - goto fail; + return -1; if (ret > 0) { discard_node(root, n); @@ -146,18 +140,22 @@ static int scan_dir(fstree_t *fs, tree_node_t *root, const char *path, } if (!(flags & DIR_SCAN_NO_RECURSION) && S_ISDIR(n->mode)) { - if (fstree_from_subdir(fs, n, path, n->name, - cb, user, flags)) { - goto fail; + dir_iterator_t *sub; + + ret = dir->open_subdir(dir, &sub); + if (ret != 0) { + sqfs_perror(n->name, "opening directory", ret); + return -1; } + + ret = scan_dir(fs, n, sub, cb, user, flags); + sqfs_drop(sub); + if (ret) + return -1; } } - sqfs_drop(dir); return 0; -fail: - sqfs_drop(dir); - return -1; } int fstree_from_subdir(fstree_t *fs, tree_node_t *root, @@ -165,6 +163,7 @@ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, scan_node_callback cb, void *user, unsigned int flags) { + dir_iterator_t *dir; size_t plen, slen; char *temp = NULL; int ret; @@ -195,8 +194,13 @@ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, path = temp; } - ret = scan_dir(fs, root, path, cb, user, flags); + dir = dir_iterator_create(path); free(temp); + if (dir == NULL) + return -1; + + ret = scan_dir(fs, root, dir, cb, user, flags); + sqfs_drop(dir); return ret; } diff --git a/include/util/dir_iterator.h b/include/util/dir_iterator.h index 77849e7..4d7607f 100644 --- a/include/util/dir_iterator.h +++ b/include/util/dir_iterator.h @@ -29,6 +29,9 @@ typedef struct dir_iterator_t { dir_entry_t **out); int (*read_link)(struct dir_iterator_t *it, char **out); + + int (*open_subdir)(struct dir_iterator_t *it, + struct dir_iterator_t **out); } dir_iterator_t; dir_iterator_t *dir_iterator_create(const char *path); 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; } |