From 0a1d93062463133e6f40e3398c0fe53371c47ab0 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sat, 15 Jul 2023 17:13:48 +0200 Subject: Move dir_iterator_t from libio into libsquashfs Signed-off-by: David Oberhollenzer --- include/io/dir_iterator.h | 100 ------------ include/sqfs/io.h | 107 +++++++++++++ include/sqfs/predef.h | 1 + lib/io/Makemodule.am | 7 - lib/io/src/dir_tree_iterator.c | 7 +- lib/io/src/unix/dir_iterator.c | 241 ----------------------------- lib/io/src/win32/dir_iterator.c | 309 ------------------------------------- lib/io/test/dir_iterator.c | 15 +- lib/io/test/dir_tree_iterator.c | 1 + lib/io/test/dir_tree_iterator2.c | 1 + lib/io/test/dir_tree_iterator3.c | 1 + lib/sqfs/Makemodule.am | 4 +- lib/sqfs/src/io/dir_unix.c | 245 +++++++++++++++++++++++++++++ lib/sqfs/src/io/dir_win32.c | 322 +++++++++++++++++++++++++++++++++++++++ 14 files changed, 695 insertions(+), 666 deletions(-) delete mode 100644 lib/io/src/unix/dir_iterator.c delete mode 100644 lib/io/src/win32/dir_iterator.c create mode 100644 lib/sqfs/src/io/dir_unix.c create mode 100644 lib/sqfs/src/io/dir_win32.c diff --git a/include/io/dir_iterator.h b/include/io/dir_iterator.h index e14b2ee..210316f 100644 --- a/include/io/dir_iterator.h +++ b/include/io/dir_iterator.h @@ -10,87 +10,6 @@ #include "sqfs/dir_entry.h" #include "sqfs/predef.h" -/** - * @interface sqfs_dir_iterator_t - * - * @brief An iterator over entries in a filesystem directory. - */ -typedef struct sqfs_dir_iterator_t { - sqfs_object_t obj; - - /** - * @brief Read the next entry and update internal state relating to it - * - * @param it A pointer to the iterator itself - * @param out Returns a pointer to an entry on success - * - * @return Zero on success, postivie value if the end of the list was - * reached, negative @ref SQFS_ERROR value on failure. - */ - int (*next)(struct sqfs_dir_iterator_t *it, sqfs_dir_entry_t **out); - - /** - * @brief If the last entry was a symlink, extract the target path - * - * @param it A pointer to the iterator itself. - * @param out Returns a pointer to a string on success. Has to be - * released with free(). - * - * @return Zero on success, negative @ref SQFS_ERROR value on failure. - */ - int (*read_link)(struct sqfs_dir_iterator_t *it, char **out); - - /** - * @brief If the last entry was a directory, open it. - * - * If next() returned a directory, this can be used to create a brand - * new sqfs_dir_iterator_t for it, that is independent of the current - * one and returns the sub-directories entries. - * - * @param it A pointer to the iterator itself. - * @param out Returns a pointer to a directory iterator on success. - * - * @return Zero on success, negative @ref SQFS_ERROR value on failure. - */ - int (*open_subdir)(struct sqfs_dir_iterator_t *it, - struct sqfs_dir_iterator_t **out); - - /** - * @brief Skip a sub-hierarchy on a stacked iterator - * - * If an iterator would ordinarily recurse into a sub-directory, - * tell it to skip those entries. On simple, flag iterators like the - * one returned by @ref dir_iterator_create, this has no effect. - * - * @param it A pointer to the iterator itself. - */ - void (*ignore_subdir)(struct sqfs_dir_iterator_t *it); - - /** - * @brief If the last entry was a regular file, open it. - * - * If next() returned a file, this can be used to create an istream - * to read from it. - * - * @param it A pointer to the iterator itself. - * @param out Returns a pointer to a @ref sqfs_istream_t on success. - * - * @return Zero on success, negative @ref SQFS_ERROR value on failure. - */ - int (*open_file_ro)(struct sqfs_dir_iterator_t *it, - sqfs_istream_t **out); - - /** - * @brief Read extended attributes associated with the current entry - * - * @param it A pointer to the iterator itself. - * @param out Returns a linked list of xattr entries. - * - * @return Zero on success, negative @ref SQFS_ERROR value on failure. - */ - int (*read_xattr)(struct sqfs_dir_iterator_t *it, sqfs_xattr_t **out); -} sqfs_dir_iterator_t; - enum { DIR_SCAN_NO_SOCK = 0x0001, DIR_SCAN_NO_SLINK = 0x0002, @@ -142,25 +61,6 @@ typedef struct { extern "C" { #endif -/** - * @brief Construct a simple directory iterator given a path - * - * On systems with encoding aware file I/O (like Windows), the path is - * interpreted to be UTF-8 encoded and converted to the native system API - * encoding to open the directory. For each directory entry, the name in - * the native encoding is converted back to UTF-8 when reading. - * - * The implementation returned by this is simple, non-recursive, reporting - * directory contents as returned by the OS native API, i.e. not sorted, - * and including the "." and ".." entries. - * - * @param path A path to a directory on the file system. - * - * @return A pointer to a sqfs_dir_iterator_t implementation on success, - * NULL on error (message is printed to stderr). - */ -SQFS_INTERNAL sqfs_dir_iterator_t *dir_iterator_create(const char *path); - /** * @brief Create a stacked, recursive directory tree iterator * diff --git a/include/sqfs/io.h b/include/sqfs/io.h index 389f875..e851ea1 100644 --- a/include/sqfs/io.h +++ b/include/sqfs/io.h @@ -292,6 +292,90 @@ struct sqfs_ostream_t { const char *(*get_filename)(sqfs_ostream_t *strm); }; +/** + * @interface sqfs_dir_iterator_t + * + * @extends sqfs_object_t + * + * @brief An iterator over entries in a filesystem directory. + */ +struct sqfs_dir_iterator_t { + sqfs_object_t obj; + + /** + * @brief Read the next entry and update internal state relating to it + * + * @param it A pointer to the iterator itself + * @param out Returns a pointer to an entry on success. Has to be + * released with @ref sqfs_free(). + * + * @return Zero on success, postivie value if the end of the list was + * reached, negative @ref SQFS_ERROR value on failure. + */ + int (*next)(sqfs_dir_iterator_t *it, sqfs_dir_entry_t **out); + + /** + * @brief If the last entry was a symlink, extract the target path + * + * @param it A pointer to the iterator itself. + * @param out Returns a pointer to a string on success. Has to be + * released with @ref sqfs_free(). + * + * @return Zero on success, negative @ref SQFS_ERROR value on failure. + */ + int (*read_link)(sqfs_dir_iterator_t *it, char **out); + + /** + * @brief If the last entry was a directory, open it. + * + * If next() returned a directory, this can be used to create a brand + * new sqfs_dir_iterator_t for it, that is independent of the current + * one and returns the sub-directories entries. + * + * @param it A pointer to the iterator itself. + * @param out Returns a pointer to a directory iterator on success. + * + * @return Zero on success, negative @ref SQFS_ERROR value on failure. + */ + int (*open_subdir)(sqfs_dir_iterator_t *it, + sqfs_dir_iterator_t **out); + + /** + * @brief Skip a sub-hierarchy on a stacked iterator + * + * If an iterator would ordinarily recurse into a sub-directory, + * tell it to skip those entries. On simple, flag iterators like the + * one returned by @ref dir_iterator_create, this has no effect. + * + * @param it A pointer to the iterator itself. + */ + void (*ignore_subdir)(sqfs_dir_iterator_t *it); + + /** + * @brief If the last entry was a regular file, open it. + * + * If next() returned a file, this can be used to create an istream + * to read from it. + * + * @param it A pointer to the iterator itself. + * @param out Returns a pointer to a @ref sqfs_istream_t on success. + * + * @return Zero on success, negative @ref SQFS_ERROR value on failure. + */ + int (*open_file_ro)(sqfs_dir_iterator_t *it, sqfs_istream_t **out); + + /** + * @brief Read extended attributes associated with the current entry + * + * @param it A pointer to the iterator itself. + * @param out Returns a linked list of xattr entries. Has to be + * released with @ref sqfs_xattr_list_free(). + * + * @return Zero on success, negative @ref SQFS_ERROR value on failure. + */ + int (*read_xattr)(sqfs_dir_iterator_t *it, sqfs_xattr_t **out); +}; + #ifdef __cplusplus extern "C" { #endif @@ -499,6 +583,29 @@ SQFS_API int sqfs_istream_skip(sqfs_istream_t *strm, sqfs_u64 size); SQFS_API sqfs_s32 sqfs_istream_splice(sqfs_istream_t *in, sqfs_ostream_t *out, sqfs_u32 size); +/** + * @brief Construct a simple directory iterator given a path + * + * @memberof sqfs_dir_iterator_t + * + * On systems with encoding aware file I/O (like Windows), the path is + * interpreted to be UTF-8 encoded and converted to the native system API + * encoding to open the directory. For each directory entry, the name in + * the native encoding is converted back to UTF-8 when reading. + * + * The implementation returned by this is simple, non-recursive, reporting + * directory contents as returned by the OS native API, i.e. not sorted, + * and including the "." and ".." entries. + * + * @param path A path to a directory on the file system. + * + * @return A pointer to a sqfs_dir_iterator_t implementation on success, + * NULL on error (message is printed to stderr). + */ +SQFS_API int sqfs_dir_iterator_create_native(sqfs_dir_iterator_t **out, + const char *path, + sqfs_u32 flags); + #ifdef __cplusplus } #endif diff --git a/include/sqfs/predef.h b/include/sqfs/predef.h index 12ed7c7..9e32b42 100644 --- a/include/sqfs/predef.h +++ b/include/sqfs/predef.h @@ -99,6 +99,7 @@ typedef struct sqfs_xattr_t sqfs_xattr_t; typedef struct sqfs_istream_t sqfs_istream_t; typedef struct sqfs_ostream_t sqfs_ostream_t; typedef struct sqfs_dir_entry_t sqfs_dir_entry_t; +typedef struct sqfs_dir_iterator_t sqfs_dir_iterator_t; typedef struct sqfs_fragment_t sqfs_fragment_t; typedef struct sqfs_dir_header_t sqfs_dir_header_t; diff --git a/lib/io/Makemodule.am b/lib/io/Makemodule.am index c2d1a15..d342063 100644 --- a/lib/io/Makemodule.am +++ b/lib/io/Makemodule.am @@ -6,13 +6,6 @@ libio_a_SOURCES = include/io/xfrm.h include/io/std.h \ libio_a_CFLAGS = $(AM_CFLAGS) $(ZLIB_CFLAGS) $(XZ_CFLAGS) libio_a_CFLAGS += $(ZSTD_CFLAGS) $(BZIP2_CFLAGS) -if WINDOWS -libio_a_SOURCES += lib/io/src/win32/dir_iterator.c -libio_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -else -libio_a_SOURCES += lib/io/src/unix/dir_iterator.c -endif - noinst_LIBRARIES += libio.a LIBIO_TESTS = test_istream_mem test_dir_iterator \ diff --git a/lib/io/src/dir_tree_iterator.c b/lib/io/src/dir_tree_iterator.c index 6370743..0ec478c 100644 --- a/lib/io/src/dir_tree_iterator.c +++ b/lib/io/src/dir_tree_iterator.c @@ -8,6 +8,7 @@ #include "io/dir_iterator.h" #include "util/util.h" #include "sqfs/error.h" +#include "sqfs/io.h" #include #include @@ -311,9 +312,11 @@ sqfs_dir_iterator_t *dir_tree_iterator_create(const char *path, it->cfg = *cfg; - dir = dir_iterator_create(path); - if (dir == NULL) + ret = sqfs_dir_iterator_create_native(&dir, path, 0); + if (ret) { + perror(path); goto fail; + } ret = push(it, "", dir); dir = sqfs_drop(dir); diff --git a/lib/io/src/unix/dir_iterator.c b/lib/io/src/unix/dir_iterator.c deleted file mode 100644 index 74ac953..0000000 --- a/lib/io/src/unix/dir_iterator.c +++ /dev/null @@ -1,241 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * dir_iterator.c - * - * Copyright (C) 2023 David Oberhollenzer - */ -#include "config.h" -#include "io/dir_iterator.h" -#include "util/util.h" -#include "sqfs/error.h" -#include "sqfs/io.h" - -#include -#include -#include -#include -#include - -typedef struct { - sqfs_dir_iterator_t base; - - struct dirent *ent; - struct stat sb; - dev_t device; - 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(sqfs_dir_iterator_t *base, char **out) -{ - unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; - ssize_t ret; - size_t size; - char *str; - - *out = NULL; - - if (it->state < 0) - return it->state; - - if (it->state > 0 || it->ent == NULL) - return SQFS_ERROR_NO_ENTRY; - - if ((sizeof(it->sb.st_size) > sizeof(size_t)) && - it->sb.st_size > SIZE_MAX) { - return SQFS_ERROR_ALLOC; - } - - if (SZ_ADD_OV((size_t)it->sb.st_size, 1, &size)) - return SQFS_ERROR_ALLOC; - - str = calloc(1, size); - if (str == NULL) - return SQFS_ERROR_ALLOC; - - ret = readlinkat(dirfd(it->dir), it->ent->d_name, - str, (size_t)it->sb.st_size); - if (ret < 0) { - free(str); - return SQFS_ERROR_IO; - } - - str[ret] = '\0'; - - *out = str; - return 0; -} - -static int dir_next(sqfs_dir_iterator_t *base, sqfs_dir_entry_t **out) -{ - unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; - - *out = NULL; - if (it->state != 0) - return it->state; - - errno = 0; - it->ent = readdir(it->dir); - - if (it->ent == NULL) { - if (errno != 0) { - it->state = SQFS_ERROR_IO; - } else { - it->state = 1; - } - - return it->state; - } - - if (fstatat(dirfd(it->dir), it->ent->d_name, - &it->sb, AT_SYMLINK_NOFOLLOW)) { - it->state = SQFS_ERROR_IO; - return it->state; - } - - *out = sqfs_dir_entry_create(it->ent->d_name, it->sb.st_mode, 0); - if ((*out) == NULL) { - it->state = SQFS_ERROR_ALLOC; - return it->state; - } - - (*out)->mtime = it->sb.st_mtime; - (*out)->dev = it->sb.st_dev; - (*out)->rdev = it->sb.st_rdev; - (*out)->uid = it->sb.st_uid; - (*out)->gid = it->sb.st_gid; - - if (S_ISREG(it->sb.st_mode)) - (*out)->size = it->sb.st_size; - - if ((*out)->dev != it->device) - (*out)->flags |= SQFS_DIR_ENTRY_FLAG_MOUNT_POINT; - - return it->state; -} - -static void dir_ignore_subdir(sqfs_dir_iterator_t *it) -{ - (void)it; -} - -static int dir_open_file_ro(sqfs_dir_iterator_t *base, sqfs_istream_t **out) -{ - unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; - int fd, ret; - - *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); - if (fd < 0) - return SQFS_ERROR_IO; - - ret = sqfs_istream_open_handle(out, it->ent->d_name, - fd, SQFS_FILE_OPEN_READ_ONLY); - if (ret != 0) { - int err = errno; - close(fd); - errno = err; - } - return ret; -} - -static int dir_read_xattr(sqfs_dir_iterator_t *it, sqfs_xattr_t **out) -{ - (void)it; - *out = NULL; - return 0; -} - -static int create_iterator(sqfs_dir_iterator_t **out, DIR *dir); - -static int dir_open_subdir(sqfs_dir_iterator_t *base, sqfs_dir_iterator_t **out) -{ - const unix_dir_iterator_t *it = (const unix_dir_iterator_t *)base; - DIR *dir; - 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; - } - - dir = fdopendir(fd); - if (dir == NULL) { - int err = errno; - close(fd); - errno = err; - return SQFS_ERROR_IO; - } - - return create_iterator(out, dir); -} - -static int create_iterator(sqfs_dir_iterator_t **out, DIR *dir) -{ - unix_dir_iterator_t *it = calloc(1, sizeof(*it)); - - if (it == NULL) { - closedir(dir); - return SQFS_ERROR_ALLOC; - } - - it->dir = dir; - - if (fstat(dirfd(dir), &it->sb)) { - int err = errno; - closedir(dir); - free(it); - errno = err; - return SQFS_ERROR_IO; - } - - sqfs_object_init(it, dir_destroy, NULL); - it->device = it->sb.st_dev; - ((sqfs_dir_iterator_t *)it)->next = dir_next; - ((sqfs_dir_iterator_t *)it)->read_link = dir_read_link; - ((sqfs_dir_iterator_t *)it)->open_subdir = dir_open_subdir; - ((sqfs_dir_iterator_t *)it)->ignore_subdir = dir_ignore_subdir; - ((sqfs_dir_iterator_t *)it)->open_file_ro = dir_open_file_ro; - ((sqfs_dir_iterator_t *)it)->read_xattr = dir_read_xattr; - - *out = (sqfs_dir_iterator_t *)it; - return 0; -} - -sqfs_dir_iterator_t *dir_iterator_create(const char *path) -{ - sqfs_dir_iterator_t *out; - DIR *dir; - - dir = opendir(path); - if (dir == NULL || create_iterator(&out, dir) != 0) { - perror(path); - return NULL; - } - - return out; -} diff --git a/lib/io/src/win32/dir_iterator.c b/lib/io/src/win32/dir_iterator.c deleted file mode 100644 index 15cfbc5..0000000 --- a/lib/io/src/win32/dir_iterator.c +++ /dev/null @@ -1,309 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * dir_iterator.c - * - * Copyright (C) 2023 David Oberhollenzer - */ -#include "config.h" -#include "io/dir_iterator.h" -#include "util/util.h" -#include "sqfs/error.h" -#include "sqfs/io.h" - -#include -#include -#include - -#define UNIX_EPOCH_ON_W32 11644473600UL -#define W32_TICS_PER_SEC 10000000UL - -typedef struct { - sqfs_dir_iterator_t base; - - WIN32_FIND_DATAW ent; - HANDLE dirhnd; - int state; - bool is_first; - - WCHAR path[]; -} dir_iterator_win32_t; - -static sqfs_s64 w32time_to_unix(const FILETIME *ft) -{ - sqfs_u64 w32ts; - - w32ts = ft->dwHighDateTime; - w32ts <<= 32UL; - w32ts |= ft->dwLowDateTime; - - w32ts /= W32_TICS_PER_SEC; - - if (w32ts <= UNIX_EPOCH_ON_W32) - return -((sqfs_s64)(UNIX_EPOCH_ON_W32 - w32ts)); - - return w32ts - UNIX_EPOCH_ON_W32; -} - -static int dir_iterator_read_link(sqfs_dir_iterator_t *it, char **out) -{ - (void)it; - *out = NULL; - return SQFS_ERROR_UNSUPPORTED; -} - -static int dir_iterator_next(sqfs_dir_iterator_t *it, sqfs_dir_entry_t **out) -{ - dir_iterator_win32_t *w32 = (dir_iterator_win32_t *)it; - sqfs_dir_entry_t *ent = NULL; - DWORD length; - - if (w32->state == 0 && !w32->is_first) { - if (!FindNextFileW(w32->dirhnd, &w32->ent)) { - os_error_t err = get_os_error_state(); - - if (err.w32_errno == ERROR_NO_MORE_FILES) { - w32->state = 1; - } else { - w32->state = SQFS_ERROR_IO; - } - - set_os_error_state(err); - } - } - - w32->is_first = false; - - if (w32->state != 0) - goto out; - - length = WideCharToMultiByte(CP_UTF8, 0, w32->ent.cFileName, - -1, NULL, 0, NULL, NULL); - if (length <= 0) { - w32->state = SQFS_ERROR_ALLOC; - goto out; - } - - ent = alloc_flex(sizeof(*ent), 1, length + 1); - if (ent == NULL) { - w32->state = SQFS_ERROR_ALLOC; - goto out; - } - - WideCharToMultiByte(CP_UTF8, 0, w32->ent.cFileName, -1, - ent->name, length + 1, NULL, NULL); - - if (w32->ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - ent->mode = S_IFDIR | 0755; - } else { - ent->mode = S_IFREG | 0644; - ent->size = w32->ent.nFileSizeHigh; - ent->size <<= 32UL; - ent->size |= w32->ent.nFileSizeLow; - } - - ent->mtime = w32time_to_unix(&(w32->ent.ftLastWriteTime)); -out: - *out = ent; - return w32->state; -} - -static void dir_iterator_destroy(sqfs_object_t *obj) -{ - dir_iterator_win32_t *dir = (dir_iterator_win32_t *)obj; - - FindClose(dir->dirhnd); - free(dir); -} - -static void dir_iterator_ignore_subdir(sqfs_dir_iterator_t *it) -{ - (void)it; -} - -static int dir_iterator_open_file_ro(sqfs_dir_iterator_t *it, - sqfs_istream_t **out) -{ - dir_iterator_win32_t *dir = (dir_iterator_win32_t *)it; - size_t plen, slen; - os_error_t err; - WCHAR *str16; - DWORD length; - HANDLE hnd; - char *str8; - int ret; - - *out = NULL; - - if (dir->state != 0) - return (dir->state > 0) ? SQFS_ERROR_NO_ENTRY : dir->state; - - plen = wcslen(dir->path) - 1; - slen = wcslen(dir->ent.cFileName); - - str16 = calloc(plen + slen + 1, sizeof(WCHAR)); - if (str16 == NULL) - return SQFS_ERROR_ALLOC; - - memcpy(str16, dir->path, plen * sizeof(WCHAR)); - memcpy(str16 + plen, dir->ent.cFileName, slen * sizeof(WCHAR)); - - hnd = CreateFileW(str16, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); - - err = get_os_error_state(); - free(str16); - set_os_error_state(err); - - if (hnd == INVALID_HANDLE_VALUE) - return SQFS_ERROR_IO; - - length = WideCharToMultiByte(CP_UTF8, 0, dir->ent.cFileName, - -1, NULL, 0, NULL, NULL); - if (length <= 0) { - CloseHandle(hnd); - return SQFS_ERROR_ALLOC; - } - - str8 = calloc(1, length + 1); - if (str8 == NULL) { - CloseHandle(hnd); - return SQFS_ERROR_ALLOC; - } - - WideCharToMultiByte(CP_UTF8, 0, dir->ent.cFileName, -1, str8, - length + 1, NULL, NULL); - - ret = sqfs_istream_open_handle(out, str8, hnd, - SQFS_FILE_OPEN_READ_ONLY); - err = get_os_error_state(); - free(str8); - if (ret != 0) - CloseHandle(hnd); - set_os_error_state(err); - - return ret; -} - -static int dir_iterator_read_xattr(sqfs_dir_iterator_t *it, sqfs_xattr_t **out) -{ - (void)it; - *out = NULL; - return 0; -} - -static int dir_iterator_init(dir_iterator_win32_t *it); - -static int dir_iterator_open_subdir(sqfs_dir_iterator_t *it, - sqfs_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; - int ret; - - *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'; - - ret = dir_iterator_init(sub); - if (ret != 0) { - free(sub); - sub = NULL; - } - - *out = (sqfs_dir_iterator_t *)sub; - return ret; -} - -static int dir_iterator_init(dir_iterator_win32_t *it) -{ - sqfs_object_init(it, dir_iterator_destroy, NULL); - - ((sqfs_dir_iterator_t *)it)->next = dir_iterator_next; - ((sqfs_dir_iterator_t *)it)->read_link = dir_iterator_read_link; - ((sqfs_dir_iterator_t *)it)->open_subdir = dir_iterator_open_subdir; - ((sqfs_dir_iterator_t *)it)->ignore_subdir = dir_iterator_ignore_subdir; - ((sqfs_dir_iterator_t *)it)->open_file_ro = dir_iterator_open_file_ro; - ((sqfs_dir_iterator_t *)it)->read_xattr = dir_iterator_read_xattr; - it->is_first = true; - it->state = 0; - - it->dirhnd = FindFirstFileW(it->path, &it->ent); - if (it->dirhnd == INVALID_HANDLE_VALUE) - return SQFS_ERROR_IO; - - return 0; -} - -sqfs_dir_iterator_t *dir_iterator_create(const char *path) -{ - dir_iterator_win32_t *it; - size_t len, newlen; - WCHAR *wpath = NULL; - void *new = NULL; - - /* convert path to UTF-16, append "\\*" */ - wpath = path_to_windows(path); - if (wpath == NULL) - goto fail_alloc; - - len = wcslen(wpath); - newlen = len + 1; - - if (len > 0 && wpath[len - 1] != '\\') - newlen += 1; - - new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); - if (new == NULL) - goto fail_alloc; - - wpath = new; - - if (len > 0 && wpath[len - 1] != '\\') - wpath[len++] = '\\'; - - wpath[len++] = '*'; - wpath[len++] = '\0'; - - /* create the sourrounding iterator structure */ - new = realloc(wpath, sizeof(*it) + len * sizeof(wpath[0])); - if (new == NULL) - goto fail_alloc; - - it = new; - wpath = NULL; - memmove(it->path, new, len * sizeof(wpath[0])); - - /* initialize */ - memset(it, 0, offsetof(dir_iterator_win32_t, path)); - if (dir_iterator_init(it) != 0) { - w32_perror(path); - free(it); - it = NULL; - } - - return (sqfs_dir_iterator_t *)it; -fail_alloc: - fprintf(stderr, "%s: allocation failure.\n", path); - free(wpath); - return NULL; -} diff --git a/lib/io/test/dir_iterator.c b/lib/io/test/dir_iterator.c index 013e41e..56610b6 100644 --- a/lib/io/test/dir_iterator.c +++ b/lib/io/test/dir_iterator.c @@ -40,7 +40,8 @@ int main(int argc, char **argv) (void)argc; (void)argv; /* scan the top level hierarchy */ - dir = dir_iterator_create(TEST_PATH); + ret = sqfs_dir_iterator_create_native(&dir, TEST_PATH, 0); + TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(dir); ret = dir->next(dir, &ent[0]); @@ -86,7 +87,8 @@ int main(int argc, char **argv) free(ent[i]); /* scan first sub hierarchy */ - dir = dir_iterator_create(TEST_PATH "/dira"); + ret = sqfs_dir_iterator_create_native(&dir, TEST_PATH "/dira", 0); + TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(dir); ret = dir->next(dir, &ent[0]); @@ -132,7 +134,8 @@ int main(int argc, char **argv) free(ent[i]); /* scan second sub hierarchy */ - dir = dir_iterator_create(TEST_PATH "/dirb"); + ret = sqfs_dir_iterator_create_native(&dir, TEST_PATH "/dirb", 0); + TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(dir); ret = dir->next(dir, &ent[0]); @@ -184,7 +187,8 @@ int main(int argc, char **argv) free(ent[i]); /* scan first sub hierarchy */ - dir = dir_iterator_create(TEST_PATH "/dirc"); + ret = sqfs_dir_iterator_create_native(&dir, TEST_PATH "/dirc", 0); + TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(dir); ret = dir->next(dir, &ent[0]); @@ -234,7 +238,8 @@ int main(int argc, char **argv) subb = NULL; subc = NULL; - dir = dir_iterator_create(TEST_PATH); + ret = sqfs_dir_iterator_create_native(&dir, TEST_PATH, 0); + TEST_EQUAL_I(ret, 0); TEST_NOT_NULL(dir); for (i = 0; i < 5; ++i) { diff --git a/lib/io/test/dir_tree_iterator.c b/lib/io/test/dir_tree_iterator.c index 8e11e2a..a6e7fcb 100644 --- a/lib/io/test/dir_tree_iterator.c +++ b/lib/io/test/dir_tree_iterator.c @@ -9,6 +9,7 @@ #include "io/dir_iterator.h" #include "sqfs/error.h" #include "util/test.h" +#include "sqfs/io.h" #include "compat.h" static int compare_entries(const void *a, const void *b) diff --git a/lib/io/test/dir_tree_iterator2.c b/lib/io/test/dir_tree_iterator2.c index 2833dc5..0ffb86e 100644 --- a/lib/io/test/dir_tree_iterator2.c +++ b/lib/io/test/dir_tree_iterator2.c @@ -9,6 +9,7 @@ #include "io/dir_iterator.h" #include "sqfs/error.h" #include "util/test.h" +#include "sqfs/io.h" #include "compat.h" static int compare_entries(const void *a, const void *b) diff --git a/lib/io/test/dir_tree_iterator3.c b/lib/io/test/dir_tree_iterator3.c index b82934f..4ac947c 100644 --- a/lib/io/test/dir_tree_iterator3.c +++ b/lib/io/test/dir_tree_iterator3.c @@ -9,6 +9,7 @@ #include "io/dir_iterator.h" #include "sqfs/error.h" #include "util/test.h" +#include "sqfs/io.h" #include "compat.h" static int compare_entries(const void *a, const void *b) diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am index 0efd39b..5005c48 100644 --- a/lib/sqfs/Makemodule.am +++ b/lib/sqfs/Makemodule.am @@ -61,13 +61,13 @@ libsquashfs_la_SOURCES += lib/util/src/mempool.c include/util/mempool.h endif if WINDOWS -libsquashfs_la_SOURCES += lib/sqfs/src/io/win32.c +libsquashfs_la_SOURCES += lib/sqfs/src/io/win32.c lib/sqfs/src/io/dir_win32.c libsquashfs_la_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 libsquashfs_la_CFLAGS += -Wc,-static-libgcc libsquashfs_la_LDFLAGS += -no-undefined -avoid-version else -libsquashfs_la_SOURCES += lib/sqfs/src/io/unix.c +libsquashfs_la_SOURCES += lib/sqfs/src/io/unix.c lib/sqfs/src/io/dir_unix.c endif if HAVE_PTHREAD diff --git a/lib/sqfs/src/io/dir_unix.c b/lib/sqfs/src/io/dir_unix.c new file mode 100644 index 0000000..83127ec --- /dev/null +++ b/lib/sqfs/src/io/dir_unix.c @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * dir_unix.c + * + * Copyright (C) 2023 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "util/util.h" +#include "sqfs/dir_entry.h" +#include "sqfs/error.h" +#include "sqfs/io.h" + +#include +#include +#include +#include +#include + +typedef struct { + sqfs_dir_iterator_t base; + + struct dirent *ent; + struct stat sb; + dev_t device; + 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(sqfs_dir_iterator_t *base, char **out) +{ + unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; + ssize_t ret; + size_t size; + char *str; + + *out = NULL; + + if (it->state < 0) + return it->state; + + if (it->state > 0 || it->ent == NULL) + return SQFS_ERROR_NO_ENTRY; + + if ((sizeof(it->sb.st_size) > sizeof(size_t)) && + it->sb.st_size > SIZE_MAX) { + return SQFS_ERROR_ALLOC; + } + + if (SZ_ADD_OV((size_t)it->sb.st_size, 1, &size)) + return SQFS_ERROR_ALLOC; + + str = calloc(1, size); + if (str == NULL) + return SQFS_ERROR_ALLOC; + + ret = readlinkat(dirfd(it->dir), it->ent->d_name, + str, (size_t)it->sb.st_size); + if (ret < 0) { + free(str); + return SQFS_ERROR_IO; + } + + str[ret] = '\0'; + + *out = str; + return 0; +} + +static int dir_next(sqfs_dir_iterator_t *base, sqfs_dir_entry_t **out) +{ + unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; + + *out = NULL; + if (it->state != 0) + return it->state; + + errno = 0; + it->ent = readdir(it->dir); + + if (it->ent == NULL) { + if (errno != 0) { + it->state = SQFS_ERROR_IO; + } else { + it->state = 1; + } + + return it->state; + } + + if (fstatat(dirfd(it->dir), it->ent->d_name, + &it->sb, AT_SYMLINK_NOFOLLOW)) { + it->state = SQFS_ERROR_IO; + return it->state; + } + + *out = sqfs_dir_entry_create(it->ent->d_name, it->sb.st_mode, 0); + if ((*out) == NULL) { + it->state = SQFS_ERROR_ALLOC; + return it->state; + } + + (*out)->mtime = it->sb.st_mtime; + (*out)->dev = it->sb.st_dev; + (*out)->rdev = it->sb.st_rdev; + (*out)->uid = it->sb.st_uid; + (*out)->gid = it->sb.st_gid; + + if (S_ISREG(it->sb.st_mode)) + (*out)->size = it->sb.st_size; + + if ((*out)->dev != it->device) + (*out)->flags |= SQFS_DIR_ENTRY_FLAG_MOUNT_POINT; + + return it->state; +} + +static void dir_ignore_subdir(sqfs_dir_iterator_t *it) +{ + (void)it; +} + +static int dir_open_file_ro(sqfs_dir_iterator_t *base, sqfs_istream_t **out) +{ + unix_dir_iterator_t *it = (unix_dir_iterator_t *)base; + int fd, ret; + + *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); + if (fd < 0) + return SQFS_ERROR_IO; + + ret = sqfs_istream_open_handle(out, it->ent->d_name, + fd, SQFS_FILE_OPEN_READ_ONLY); + if (ret != 0) { + int err = errno; + close(fd); + errno = err; + } + return ret; +} + +static int dir_read_xattr(sqfs_dir_iterator_t *it, sqfs_xattr_t **out) +{ + (void)it; + *out = NULL; + return 0; +} + +static int create_iterator(sqfs_dir_iterator_t **out, DIR *dir); + +static int dir_open_subdir(sqfs_dir_iterator_t *base, sqfs_dir_iterator_t **out) +{ + const unix_dir_iterator_t *it = (const unix_dir_iterator_t *)base; + DIR *dir; + 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; + } + + dir = fdopendir(fd); + if (dir == NULL) { + int err = errno; + close(fd); + errno = err; + return SQFS_ERROR_IO; + } + + return create_iterator(out, dir); +} + +static int create_iterator(sqfs_dir_iterator_t **out, DIR *dir) +{ + unix_dir_iterator_t *it = calloc(1, sizeof(*it)); + + if (it == NULL) { + closedir(dir); + return SQFS_ERROR_ALLOC; + } + + it->dir = dir; + + if (fstat(dirfd(dir), &it->sb)) { + int err = errno; + closedir(dir); + free(it); + errno = err; + return SQFS_ERROR_IO; + } + + sqfs_object_init(it, dir_destroy, NULL); + it->device = it->sb.st_dev; + ((sqfs_dir_iterator_t *)it)->next = dir_next; + ((sqfs_dir_iterator_t *)it)->read_link = dir_read_link; + ((sqfs_dir_iterator_t *)it)->open_subdir = dir_open_subdir; + ((sqfs_dir_iterator_t *)it)->ignore_subdir = dir_ignore_subdir; + ((sqfs_dir_iterator_t *)it)->open_file_ro = dir_open_file_ro; + ((sqfs_dir_iterator_t *)it)->read_xattr = dir_read_xattr; + + *out = (sqfs_dir_iterator_t *)it; + return 0; +} + +int sqfs_dir_iterator_create_native(sqfs_dir_iterator_t **out, + const char *path, sqfs_u32 flags) +{ + DIR *dir; + + *out = NULL; + if (flags & (~SQFS_FILE_OPEN_NO_CHARSET_XFRM)) + return SQFS_ERROR_UNSUPPORTED; + + dir = opendir(path); + if (dir == NULL) + return SQFS_ERROR_IO; + + return create_iterator(out, dir); +} diff --git a/lib/sqfs/src/io/dir_win32.c b/lib/sqfs/src/io/dir_win32.c new file mode 100644 index 0000000..5875ae5 --- /dev/null +++ b/lib/sqfs/src/io/dir_win32.c @@ -0,0 +1,322 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * dir_win32.c + * + * Copyright (C) 2023 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "util/util.h" +#include "sqfs/dir_entry.h" +#include "sqfs/error.h" +#include "sqfs/io.h" + +#include +#include +#include + +#define UNIX_EPOCH_ON_W32 11644473600UL +#define W32_TICS_PER_SEC 10000000UL + +typedef struct { + sqfs_dir_iterator_t base; + + WIN32_FIND_DATAW ent; + HANDLE dirhnd; + int state; + bool is_first; + + WCHAR path[]; +} dir_iterator_win32_t; + +static sqfs_s64 w32time_to_unix(const FILETIME *ft) +{ + sqfs_u64 w32ts; + + w32ts = ft->dwHighDateTime; + w32ts <<= 32UL; + w32ts |= ft->dwLowDateTime; + + w32ts /= W32_TICS_PER_SEC; + + if (w32ts <= UNIX_EPOCH_ON_W32) + return -((sqfs_s64)(UNIX_EPOCH_ON_W32 - w32ts)); + + return w32ts - UNIX_EPOCH_ON_W32; +} + +static int dir_iterator_read_link(sqfs_dir_iterator_t *it, char **out) +{ + (void)it; + *out = NULL; + return SQFS_ERROR_UNSUPPORTED; +} + +static int dir_iterator_next(sqfs_dir_iterator_t *it, sqfs_dir_entry_t **out) +{ + dir_iterator_win32_t *w32 = (dir_iterator_win32_t *)it; + sqfs_dir_entry_t *ent = NULL; + DWORD length; + + if (w32->state == 0 && !w32->is_first) { + if (!FindNextFileW(w32->dirhnd, &w32->ent)) { + os_error_t err = get_os_error_state(); + + if (err.w32_errno == ERROR_NO_MORE_FILES) { + w32->state = 1; + } else { + w32->state = SQFS_ERROR_IO; + } + + set_os_error_state(err); + } + } + + w32->is_first = false; + + if (w32->state != 0) + goto out; + + length = WideCharToMultiByte(CP_UTF8, 0, w32->ent.cFileName, + -1, NULL, 0, NULL, NULL); + if (length <= 0) { + w32->state = SQFS_ERROR_ALLOC; + goto out; + } + + ent = alloc_flex(sizeof(*ent), 1, length + 1); + if (ent == NULL) { + w32->state = SQFS_ERROR_ALLOC; + goto out; + } + + WideCharToMultiByte(CP_UTF8, 0, w32->ent.cFileName, -1, + ent->name, length + 1, NULL, NULL); + + if (w32->ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + ent->mode = S_IFDIR | 0755; + } else { + ent->mode = S_IFREG | 0644; + ent->size = w32->ent.nFileSizeHigh; + ent->size <<= 32UL; + ent->size |= w32->ent.nFileSizeLow; + } + + ent->mtime = w32time_to_unix(&(w32->ent.ftLastWriteTime)); +out: + *out = ent; + return w32->state; +} + +static void dir_iterator_destroy(sqfs_object_t *obj) +{ + dir_iterator_win32_t *dir = (dir_iterator_win32_t *)obj; + + FindClose(dir->dirhnd); + free(dir); +} + +static void dir_iterator_ignore_subdir(sqfs_dir_iterator_t *it) +{ + (void)it; +} + +static int dir_iterator_open_file_ro(sqfs_dir_iterator_t *it, + sqfs_istream_t **out) +{ + dir_iterator_win32_t *dir = (dir_iterator_win32_t *)it; + size_t plen, slen; + os_error_t err; + WCHAR *str16; + DWORD length; + HANDLE hnd; + char *str8; + int ret; + + *out = NULL; + + if (dir->state != 0) + return (dir->state > 0) ? SQFS_ERROR_NO_ENTRY : dir->state; + + plen = wcslen(dir->path) - 1; + slen = wcslen(dir->ent.cFileName); + + str16 = calloc(plen + slen + 1, sizeof(WCHAR)); + if (str16 == NULL) + return SQFS_ERROR_ALLOC; + + memcpy(str16, dir->path, plen * sizeof(WCHAR)); + memcpy(str16 + plen, dir->ent.cFileName, slen * sizeof(WCHAR)); + + hnd = CreateFileW(str16, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); + + err = get_os_error_state(); + free(str16); + set_os_error_state(err); + + if (hnd == INVALID_HANDLE_VALUE) + return SQFS_ERROR_IO; + + length = WideCharToMultiByte(CP_UTF8, 0, dir->ent.cFileName, + -1, NULL, 0, NULL, NULL); + if (length <= 0) { + CloseHandle(hnd); + return SQFS_ERROR_ALLOC; + } + + str8 = calloc(1, length + 1); + if (str8 == NULL) { + CloseHandle(hnd); + return SQFS_ERROR_ALLOC; + } + + WideCharToMultiByte(CP_UTF8, 0, dir->ent.cFileName, -1, str8, + length + 1, NULL, NULL); + + ret = sqfs_istream_open_handle(out, str8, hnd, + SQFS_FILE_OPEN_READ_ONLY); + err = get_os_error_state(); + free(str8); + if (ret != 0) + CloseHandle(hnd); + set_os_error_state(err); + + return ret; +} + +static int dir_iterator_read_xattr(sqfs_dir_iterator_t *it, sqfs_xattr_t **out) +{ + (void)it; + *out = NULL; + return 0; +} + +static int dir_iterator_init(dir_iterator_win32_t *it); + +static int dir_iterator_open_subdir(sqfs_dir_iterator_t *it, + sqfs_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; + int ret; + + *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'; + + ret = dir_iterator_init(sub); + if (ret != 0) { + os_error_t err = get_os_error_state(); + free(sub); + sub = NULL; + set_os_error_state(err); + } + + *out = (sqfs_dir_iterator_t *)sub; + return ret; +} + +static int dir_iterator_init(dir_iterator_win32_t *it) +{ + sqfs_object_init(it, dir_iterator_destroy, NULL); + + ((sqfs_dir_iterator_t *)it)->next = dir_iterator_next; + ((sqfs_dir_iterator_t *)it)->read_link = dir_iterator_read_link; + ((sqfs_dir_iterator_t *)it)->open_subdir = dir_iterator_open_subdir; + ((sqfs_dir_iterator_t *)it)->ignore_subdir = dir_iterator_ignore_subdir; + ((sqfs_dir_iterator_t *)it)->open_file_ro = dir_iterator_open_file_ro; + ((sqfs_dir_iterator_t *)it)->read_xattr = dir_iterator_read_xattr; + it->is_first = true; + it->state = 0; + + it->dirhnd = FindFirstFileW(it->path, &it->ent); + if (it->dirhnd == INVALID_HANDLE_VALUE) + return SQFS_ERROR_IO; + + return 0; +} + +int sqfs_dir_iterator_create_native(sqfs_dir_iterator_t **out, + const char *path, sqfs_u32 flags) +{ + dir_iterator_win32_t *it; + WCHAR *wpath = NULL; + void *new = NULL; + DWORD length; + size_t len; + int ret; + + *out = NULL; + if (flags & (~SQFS_FILE_OPEN_NO_CHARSET_XFRM)) + return SQFS_ERROR_UNSUPPORTED; + + /* convert path to UTF-16, append "\\*" */ + len = strlen(path); + while (len > 0 && (path[len - 1] == '/' || path[len - 1] == '\\')) + --len; + + length = MultiByteToWideChar(CP_UTF8, 0, path, len, NULL, 0); + if (length <= 0) + return SQFS_ERROR_ALLOC; + + wpath = calloc(sizeof(wpath[0]), length + 3); + if (wpath == NULL) + return SQFS_ERROR_ALLOC; + + MultiByteToWideChar(CP_UTF8, 0, path, len, wpath, length + 1); + + for (DWORD i = 0; i < length; ++i) { + if (wpath[i] == '/') + wpath[i] = '\\'; + } + + wpath[length++] = '\\'; + wpath[length++] = '*'; + wpath[length++] = '\0'; + + /* create the sourrounding iterator structure */ + new = realloc(wpath, sizeof(*it) + length * sizeof(wpath[0])); + if (new == NULL) { + free(wpath); + return SQFS_ERROR_ALLOC; + } + + it = new; + wpath = NULL; + memmove(it->path, new, length * sizeof(wpath[0])); + + /* initialize */ + memset(it, 0, offsetof(dir_iterator_win32_t, path)); + ret = dir_iterator_init(it); + if (ret != 0) { + os_error_t err = get_os_error_state(); + free(it); + it = NULL; + set_os_error_state(err); + } + + *out = (sqfs_dir_iterator_t *)it; + return ret; +} -- cgit v1.2.3