diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2021-03-20 23:42:53 +0100 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2021-03-24 09:59:07 +0100 |
commit | 42214e1bd028f68b6194d3588d09899015ea163c (patch) | |
tree | 97295ed5bc76072fdf92de2ab9f634092fe63311 /lib | |
parent | 12c7c919772b6a1fc818c73c8abc9e2bb694ead9 (diff) |
libfstree: implement directory scanning code for Windows
It's rather simplistic and doesn't account for junction/reparse
points, which is the closest thing Windows has to symlinks, hard
links and mount points, but it's consistent with the unpacking code
that assumes Windows only has files and directories.
Using the 32 bit mingw toolchain, this seems to satisfy the unit
tests on wine.
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/fstree/fstree_from_dir.c | 271 |
1 files changed, 252 insertions, 19 deletions
diff --git a/lib/fstree/fstree_from_dir.c b/lib/fstree/fstree_from_dir.c index cb066b1..9f8e3ba 100644 --- a/lib/fstree/fstree_from_dir.c +++ b/lib/fstree/fstree_from_dir.c @@ -12,34 +12,126 @@ #include <string.h> #include <errno.h> -static void discard_node(tree_node_t *root, tree_node_t *n) +#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) { - tree_node_t *it; + sqfs_u64 w32ts; - if (n == root->data.dir.children) { - root->data.dir.children = n->next; + 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) +{ + tree_node_t *n; + DWORD length; + + if (entry->cFileName[0] == '.') { + if (entry->cFileName[1] == '\0') + return 0; + + if (entry->cFileName[1] == '.' && entry->cFileName[2] == '\0') + 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; + } + + n = calloc(1, sizeof(*n) + length + 1); + if (n == NULL) { + fprintf(stderr, "creating tree node: out-of-memory\n"); + return -1; + } + + 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 { - it = root->data.dir.children; + if (flags & DIR_SCAN_NO_FILE) { + free(n); + return 0; + } - while (it != NULL && it->next != n) - it = it->next; + n->mode = S_IFREG | 0644; + } - if (it != NULL) - it->next = n->next; + if (cb != NULL) { + int ret = cb(user, fs, n); + + if (ret != 0) { + free(n); + return ret < 0 ? ret : 0; + } } - free(n); + if (flags & DIR_SCAN_KEEP_TIME) { + n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime)); + } else { + n->mod_time = fs->defaults.st_mtime; + } + + n->parent = root; + n->next = root->data.dir.children; + root->data.dir.children = n; + return 0; } -#ifdef _WIN32 -int fstree_from_subdir(fstree_t *fs, tree_node_t *root, - const char *path, const char *subdir, - scan_node_callback cb, void *user, - unsigned int flags) +static int scan_dir(fstree_t *fs, tree_node_t *root, + const char *path, const WCHAR *wpath, + scan_node_callback cb, void *user, + unsigned int flags) { - (void)fs; (void)root; (void)path; (void)subdir; (void)flags; - (void)cb; (void)user; - fputs("Packing a directory is not supported on Windows.\n", stderr); + WIN32_FIND_DATAW entry; + HANDLE dirhnd; + + dirhnd = FindFirstFileW(wpath, &entry); + + if (dirhnd == INVALID_HANDLE_VALUE) + goto fail_perror; + + do { + if (add_node(fs, root, cb, user, flags, &entry)) + goto fail; + } while (FindNextFileW(dirhnd, &entry)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + goto fail_perror; + + FindClose(dirhnd); + return 0; +fail_perror: + w32_perror(path); +fail: + if (dirhnd != INVALID_HANDLE_VALUE) + FindClose(dirhnd); return -1; } @@ -47,9 +139,150 @@ int fstree_from_dir(fstree_t *fs, tree_node_t *root, const char *path, scan_node_callback cb, void *user, unsigned int flags) { - return fstree_from_subdir(fs, root, path, NULL, cb, user, 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); + 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; + + for (n = root->data.dir.children; n != NULL; n = n->next) { + if (!S_ISDIR(n->mode)) + continue; + + if (fstree_from_subdir(fs, n, path, n->name, cb, user, flags)) + return -1; + } + + return 0; +fail: + free(wpath); + return -1; +} + +int fstree_from_subdir(fstree_t *fs, tree_node_t *root, + const char *path, const char *subdir, + scan_node_callback cb, void *user, + unsigned int flags) +{ + size_t len, plen, slen; + WCHAR *wpath = NULL; + char *temp = NULL; + tree_node_t *n; + + plen = strlen(path); + slen = subdir == NULL ? 0 : strlen(subdir); + + if (slen == 0) + return fstree_from_dir(fs, root, path, cb, user, flags); + + len = plen + 1 + slen + 2; + + temp = calloc(1, len + 1); + if (temp == NULL) { + fprintf(stderr, "%s/%s: allocation failure.\n", path, subdir); + return -1; + } + + 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; + } + + if (scan_dir(fs, root, temp, wpath, 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; + + if (fstree_from_subdir(fs, n, temp, n->name, cb, user, flags)) + goto fail; + } + + free(temp); + return 0; +fail: + free(temp); + free(wpath); + return -1; + } #else +static void discard_node(tree_node_t *root, tree_node_t *n) +{ + tree_node_t *it; + + if (n == root->data.dir.children) { + root->data.dir.children = n->next; + } else { + it = root->data.dir.children; + + while (it != NULL && it->next != n) + it = it->next; + + if (it != NULL) + it->next = n->next; + } + + free(n); +} + static int populate_dir(int dir_fd, fstree_t *fs, tree_node_t *root, dev_t devstart, scan_node_callback cb, void *user, unsigned int flags) |