From 72674fc880fa3a5e121e07f9dcdf6f79437a3011 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 10 Apr 2023 16:36:12 +0200 Subject: Split out Windows directory iteration code to a dir_iterator_t type Signed-off-by: David Oberhollenzer --- bin/gensquashfs/Makemodule.am | 2 +- bin/gensquashfs/src/fstree_from_dir.c | 178 +++++++++----------------------- bin/gensquashfs/src/mkfs.h | 1 + include/util/dir_iterator.h | 32 ++++++ lib/util/Makemodule.am | 2 + lib/util/src/w32_dir_iterator.c | 185 ++++++++++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 131 deletions(-) create mode 100644 include/util/dir_iterator.h create mode 100644 lib/util/src/w32_dir_iterator.c diff --git a/bin/gensquashfs/Makemodule.am b/bin/gensquashfs/Makemodule.am index de9ea4c..ff426ae 100644 --- a/bin/gensquashfs/Makemodule.am +++ b/bin/gensquashfs/Makemodule.am @@ -67,7 +67,7 @@ fstree_fuzz_SOURCES = bin/gensquashfs/test/fstree_fuzz.c \ bin/gensquashfs/src/fstree_from_dir.c \ bin/gensquashfs/src/mkfs.h fstree_fuzz_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs/src -fstree_fuzz_LDADD = libfstree.a libio.a libutil.a libcompat.a +fstree_fuzz_LDADD = libcommon.a libfstree.a libio.a libutil.a libcompat.a GENSQUASHFS_TESTS = \ test_filemap_xattr test_fstree_from_file test_fstree_from_dir \ diff --git a/bin/gensquashfs/src/fstree_from_dir.c b/bin/gensquashfs/src/fstree_from_dir.c index d117435..2ece45a 100644 --- a/bin/gensquashfs/src/fstree_from_dir.c +++ b/bin/gensquashfs/src/fstree_from_dir.c @@ -13,48 +13,28 @@ #include #if defined(_WIN32) || defined(__WINDOWS__) -#define UNIX_EPOCH_ON_W32 11644473600UL -#define W32_TICS_PER_SEC 10000000UL - -static sqfs_u32 w32time_to_sqfs_time(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 0; - - w32ts -= UNIX_EPOCH_ON_W32; - - return (w32ts < 0x0FFFFFFFFUL) ? w32ts : 0xFFFFFFFF; -} - static int add_node(fstree_t *fs, tree_node_t *root, scan_node_callback cb, void *user, unsigned int flags, - const LPWIN32_FIND_DATAW entry) + const dir_entry_t *entry) { + size_t length = strlen(entry->name); tree_node_t *n; - DWORD length; - if (entry->cFileName[0] == '.') { - if (entry->cFileName[1] == '\0') + if (entry->name[0] == '.') { + if (length == 1) return 0; - if (entry->cFileName[1] == '.' && entry->cFileName[2] == '\0') + if (entry->name[1] == '.' && length == 2) return 0; } - length = WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, - -1, NULL, 0, NULL, NULL); - if (length <= 0) { - w32_perror("converting path to UTF-8"); - return -1; + if (S_ISDIR(entry->mode)) { + if (flags & DIR_SCAN_NO_DIR) + return 0; + } else { + if (flags & DIR_SCAN_NO_FILE) + return 0; } n = calloc(1, sizeof(*n) + length + 1); @@ -63,25 +43,9 @@ static int add_node(fstree_t *fs, tree_node_t *root, return -1; } + n->mode = entry->mode; n->name = (char *)n->payload; - WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, -1, - n->name, length + 1, NULL, NULL); - - if (entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (flags & DIR_SCAN_NO_DIR) { - free(n); - return 0; - } - - n->mode = S_IFDIR | 0755; - } else { - if (flags & DIR_SCAN_NO_FILE) { - free(n); - return 0; - } - - n->mode = S_IFREG | 0644; - } + memcpy(n->name, entry->name, length); if (cb != NULL) { int ret = cb(user, fs, n); @@ -93,7 +57,13 @@ static int add_node(fstree_t *fs, tree_node_t *root, } if (flags & DIR_SCAN_KEEP_TIME) { - n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime)); + if (entry->mtime < 0) { + n->mod_time = 0; + } else if (entry->mtime > 0x0FFFFFFFFLL) { + n->mod_time = 0xFFFFFFFF; + } else { + n->mod_time = entry->mtime; + } } else { n->mod_time = fs->defaults.mtime; } @@ -103,81 +73,49 @@ static int add_node(fstree_t *fs, tree_node_t *root, } static int scan_dir(fstree_t *fs, tree_node_t *root, - const char *path, const WCHAR *wpath, + const char *path, scan_node_callback cb, void *user, unsigned int flags) { - WIN32_FIND_DATAW entry; - HANDLE dirhnd; + dir_iterator_t *it = dir_iterator_create(path); - dirhnd = FindFirstFileW(wpath, &entry); + if (it == NULL) + return -1; - if (dirhnd == INVALID_HANDLE_VALUE) - goto fail_perror; + for (;;) { + dir_entry_t *ent; + int ret; - do { - if (add_node(fs, root, cb, user, flags, &entry)) - goto fail; - } while (FindNextFileW(dirhnd, &entry)); + ret = it->next(it, &ent); + if (ret > 0) + break; + if (ret < 0) { + sqfs_perror(path, "reading directory entry", ret); + sqfs_drop(it); + return -1; + } - if (GetLastError() != ERROR_NO_MORE_FILES) - goto fail_perror; + ret = add_node(fs, root, cb, user, flags, ent); + free(ent); + if (ret != 0) { + sqfs_drop(it); + return -1; + } + } - FindClose(dirhnd); + sqfs_drop(it); return 0; -fail_perror: - w32_perror(path); -fail: - if (dirhnd != INVALID_HANDLE_VALUE) - FindClose(dirhnd); - return -1; } int fstree_from_dir(fstree_t *fs, tree_node_t *root, const char *path, scan_node_callback cb, void *user, unsigned int flags) { - WCHAR *wpath = NULL, *new = NULL; - size_t len, newlen; tree_node_t *n; - /* path -> to_wchar(path) + L"\*" */ - wpath = path_to_windows(path); - if (wpath == NULL) { - fprintf(stderr, "%s: allocation failure.\n", path); + if (scan_dir(fs, root, path, cb, user, flags)) return -1; - } - - for (len = 0; wpath[len] != '\0'; ++len) - ; - - newlen = len + 1; - - if (len > 0 && wpath[len - 1] != '\\') - newlen += 1; - - new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); - if (new == NULL) { - fprintf(stderr, "%s: allocation failure.\n", path); - goto fail; - } - - wpath = new; - if (len > 0 && wpath[len - 1] != '\\') - wpath[len++] = '\\'; - - wpath[len++] = '*'; - wpath[len++] = '\0'; - - /* scan directory contents */ - if (scan_dir(fs, root, path, wpath, cb, user, flags)) - goto fail; - - free(wpath); - wpath = NULL; - - /* recursion step */ if (flags & DIR_SCAN_NO_RECURSION) return 0; @@ -190,9 +128,6 @@ int fstree_from_dir(fstree_t *fs, tree_node_t *root, } return 0; -fail: - free(wpath); - return -1; } int fstree_from_subdir(fstree_t *fs, tree_node_t *root, @@ -200,8 +135,7 @@ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, scan_node_callback cb, void *user, unsigned int flags) { - size_t len, plen, slen; - WCHAR *wpath = NULL; + size_t plen, slen; char *temp = NULL; tree_node_t *n; @@ -211,9 +145,7 @@ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, if (slen == 0) return fstree_from_dir(fs, root, path, cb, user, flags); - len = plen + 1 + slen + 2; - - temp = calloc(1, len + 1); + temp = calloc(1, plen + 1 + slen + 1); if (temp == NULL) { fprintf(stderr, "%s/%s: allocation failure.\n", path, subdir); return -1; @@ -222,29 +154,16 @@ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, memcpy(temp, path, plen); temp[plen] = '/'; memcpy(temp + plen + 1, subdir, slen); - temp[plen + 1 + slen ] = '/'; - temp[plen + 1 + slen + 1] = '*'; - temp[plen + 1 + slen + 2] = '\0'; - - wpath = path_to_windows(temp); - if (wpath == NULL) { - fprintf(stderr, "%s: allocation failure.\n", temp); - goto fail; - } + temp[plen + 1 + slen] = '\0'; - if (scan_dir(fs, root, temp, wpath, cb, user, flags)) + if (scan_dir(fs, root, temp, cb, user, flags)) goto fail; - free(wpath); - wpath = NULL; - if (flags & DIR_SCAN_NO_RECURSION) { free(temp); return 0; } - temp[plen + 1 + slen] = '\0'; - for (n = root->data.dir.children; n != NULL; n = n->next) { if (!S_ISDIR(n->mode)) continue; @@ -257,7 +176,6 @@ int fstree_from_subdir(fstree_t *fs, tree_node_t *root, return 0; fail: free(temp); - free(wpath); return -1; } diff --git a/bin/gensquashfs/src/mkfs.h b/bin/gensquashfs/src/mkfs.h index 516ab87..6b69465 100644 --- a/bin/gensquashfs/src/mkfs.h +++ b/bin/gensquashfs/src/mkfs.h @@ -12,6 +12,7 @@ #include "common.h" #include "fstree.h" +#include "util/dir_iterator.h" #include "util/util.h" #include "io/file.h" diff --git a/include/util/dir_iterator.h b/include/util/dir_iterator.h new file mode 100644 index 0000000..66c13f3 --- /dev/null +++ b/include/util/dir_iterator.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * dir_iterator.h + * + * Copyright (C) 2023 David Oberhollenzer + */ +#ifndef UTIL_DIR_ITERATOR_H +#define UTIL_DIR_ITERATOR_H + +#include "sqfs/predef.h" + +typedef struct { + sqfs_s64 mtime; + sqfs_u64 dev; + sqfs_u64 rdev; + sqfs_u32 uid; + sqfs_u32 gid; + sqfs_u16 mode; + + char name[]; +} dir_entry_t; + +typedef struct dir_iterator_t { + sqfs_object_t obj; + + int (*next)(struct dir_iterator_t *it, + dir_entry_t **out); +} dir_iterator_t; + +dir_iterator_t *dir_iterator_create(const char *path); + +#endif /* UTIL_DIR_ITERATOR_H */ diff --git a/lib/util/Makemodule.am b/lib/util/Makemodule.am index 0a0e50e..8e9b01d 100644 --- a/lib/util/Makemodule.am +++ b/lib/util/Makemodule.am @@ -2,6 +2,7 @@ libutil_a_SOURCES = include/util/util.h include/util/str_table.h \ include/util/hash_table.h include/util/test.h include/util/rbtree.h \ include/util/array.h include/util/threadpool.h \ include/util/w32threadwrap.h include/util/mempool.h \ + include/util/dir_iterator.h \ lib/util/src/str_table.c lib/util/src/alloc.c lib/util/src/rbtree.c \ lib/util/src/array.c lib/util/src/xxhash.c lib/util/src/hash_table.c \ lib/util/src/fast_urem_by_const.h lib/util/src/threadpool_serial.c \ @@ -14,6 +15,7 @@ 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 endif if HAVE_PTHREAD diff --git a/lib/util/src/w32_dir_iterator.c b/lib/util/src/w32_dir_iterator.c new file mode 100644 index 0000000..1a330ae --- /dev/null +++ b/lib/util/src/w32_dir_iterator.c @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * w32_dir_iterator.c + * + * Copyright (C) 2023 David Oberhollenzer + */ +#include "config.h" +#include "util/dir_iterator.h" +#include "util/util.h" +#include "sqfs/error.h" + +#include +#include +#include + +#define UNIX_EPOCH_ON_W32 11644473600UL +#define W32_TICS_PER_SEC 10000000UL + +enum { + STATE_HAVE_ENTRY = 0, + STATE_NEED_ENTRY = 1, + STATE_ERROR = 2, + STATE_EOF = 3, +}; + +typedef struct { + dir_iterator_t base; + + WIN32_FIND_DATAW ent; + HANDLE dirhnd; + int state; +} 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_next(dir_iterator_t *it, dir_entry_t **out) +{ + dir_iterator_win32_t *w32 = (dir_iterator_win32_t *)it; + dir_entry_t *ent = NULL; + DWORD length; + + *out = NULL; + + switch (w32->state) { + case STATE_HAVE_ENTRY: + break; + case STATE_NEED_ENTRY: + if (!FindNextFileW(w32->dirhnd, &w32->ent)) { + if (GetLastError() == ERROR_NO_MORE_FILES) { + w32->state = STATE_EOF; + return 1; + } + w32->state = STATE_ERROR; + return SQFS_ERROR_INTERNAL; + } + + w32->state = STATE_HAVE_ENTRY; + break; + case STATE_EOF: + return 1; + default: + return SQFS_ERROR_INTERNAL; + } + + /* allocate */ + length = WideCharToMultiByte(CP_UTF8, 0, w32->ent.cFileName, + -1, NULL, 0, NULL, NULL); + if (length <= 0) { + w32->state = STATE_ERROR; + return SQFS_ERROR_ALLOC; + } + + ent = alloc_flex(sizeof(*ent), 1, length + 1); + if (ent == NULL) { + w32->state = STATE_ERROR; + return SQFS_ERROR_ALLOC; + } + + /* convert and fill entry fields */ + 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->mtime = w32time_to_unix(&(w32->ent.ftLastWriteTime)); + + /* return and update state */ + *out = ent; + w32->state = STATE_NEED_ENTRY; + return 0; +} + +static void dir_iterator_destroy(sqfs_object_t *obj) +{ + dir_iterator_win32_t *dir = (dir_iterator_win32_t *)obj; + + FindClose(dir->dirhnd); + free(dir); +} + +dir_iterator_t *dir_iterator_create(const char *path) +{ + WCHAR *wpath = NULL, *new = NULL; + WIN32_FIND_DATAW first_entry; + dir_iterator_win32_t *it; + size_t len, newlen; + HANDLE dirhnd; + + /* convert path to UTF-16, append "\\*" */ + wpath = path_to_windows(path); + if (wpath == NULL) { + fprintf(stderr, "%s: allocation failure.\n", path); + return NULL; + } + + for (len = 0; wpath[len] != '\0'; ++len) + ; + + newlen = len + 1; + + if (len > 0 && wpath[len - 1] != '\\') + newlen += 1; + + new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); + if (new == NULL) { + fprintf(stderr, "%s: allocation failure.\n", path); + free(wpath); + return NULL; + } + + wpath = new; + + if (len > 0 && wpath[len - 1] != '\\') + wpath[len++] = '\\'; + + wpath[len++] = '*'; + wpath[len++] = '\0'; + + /* get the directory handle AND the first entry */ + dirhnd = FindFirstFileW(wpath, &first_entry); + + if (dirhnd == INVALID_HANDLE_VALUE) { + w32_perror(path); + free(wpath); + return NULL; + } + + free(wpath); + wpath = NULL; + + /* wrap it up */ + it = calloc(1, sizeof(*it)); + if (it == NULL) { + fprintf(stderr, "%s: allocation failure.\n", path); + FindClose(dirhnd); + return NULL; + } + + sqfs_object_init(it, dir_iterator_destroy, NULL); + + ((dir_iterator_t *)it)->next = dir_iterator_next; + + it->state = STATE_HAVE_ENTRY; + it->dirhnd = dirhnd; + it->ent = first_entry; + return (dir_iterator_t *)it; +} -- cgit v1.2.3