summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/fstree/fstree_from_dir.c271
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)