summaryrefslogtreecommitdiff
path: root/bin/gensquashfs/fstree_from_dir.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-11-22 14:45:32 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-11-22 14:45:32 +0100
commit168ef9be32ad754d7bcb38ed70787237fc12630d (patch)
tree39d567fa667b2f170783329a07481769dd538d43 /bin/gensquashfs/fstree_from_dir.c
parent61a0dd71c4b69c21ee4aacdc0459dba58504a24a (diff)
Move gensquashfs specific code from libfstree to gensquashfs
The "from dir" and from "from file" code, as well as the "sort file" code is specific to gensquashfs, so move them there and the test cases as well. The medium term idea is to reduce libfstree to a stub, merge it into the generic writer and ultimately hoist that into libsquashfs. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'bin/gensquashfs/fstree_from_dir.c')
-rw-r--r--bin/gensquashfs/fstree_from_dir.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/bin/gensquashfs/fstree_from_dir.c b/bin/gensquashfs/fstree_from_dir.c
new file mode 100644
index 0000000..5b3f003
--- /dev/null
+++ b/bin/gensquashfs/fstree_from_dir.c
@@ -0,0 +1,493 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * fstree_from_dir.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "mkfs.h"
+
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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)
+{
+ 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 {
+ if (flags & DIR_SCAN_NO_FILE) {
+ free(n);
+ return 0;
+ }
+
+ n->mode = S_IFREG | 0644;
+ }
+
+ if (cb != NULL) {
+ int ret = cb(user, fs, n);
+
+ if (ret != 0) {
+ free(n);
+ return ret < 0 ? ret : 0;
+ }
+ }
+
+ if (flags & DIR_SCAN_KEEP_TIME) {
+ n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime));
+ } else {
+ n->mod_time = fs->defaults.st_mtime;
+ }
+
+ fstree_insert_sorted(root, n);
+ return 0;
+}
+
+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)
+{
+ 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;
+}
+
+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);
+ 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)
+{
+ char *extra = NULL;
+ struct dirent *ent;
+ int ret, childfd;
+ struct stat sb;
+ tree_node_t *n;
+ DIR *dir;
+
+ dir = fdopendir(dir_fd);
+ if (dir == NULL) {
+ perror("fdopendir");
+ close(dir_fd);
+ return -1;
+ }
+
+ /* XXX: fdopendir can dup and close dir_fd internally
+ and still be compliant with the spec. */
+ dir_fd = dirfd(dir);
+
+ for (;;) {
+ errno = 0;
+ ent = readdir(dir);
+
+ if (ent == NULL) {
+ if (errno) {
+ perror("readdir");
+ goto fail;
+ }
+ break;
+ }
+
+ if (!strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "."))
+ continue;
+
+ if (fstatat(dir_fd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
+ perror(ent->d_name);
+ goto fail;
+ }
+
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFSOCK:
+ if (flags & DIR_SCAN_NO_SOCK)
+ continue;
+ break;
+ case S_IFLNK:
+ if (flags & DIR_SCAN_NO_SLINK)
+ continue;
+ break;
+ case S_IFREG:
+ if (flags & DIR_SCAN_NO_FILE)
+ continue;
+ break;
+ case S_IFBLK:
+ if (flags & DIR_SCAN_NO_BLK)
+ continue;
+ break;
+ case S_IFCHR:
+ if (flags & DIR_SCAN_NO_CHR)
+ continue;
+ break;
+ case S_IFIFO:
+ if (flags & DIR_SCAN_NO_FIFO)
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ if ((flags & DIR_SCAN_ONE_FILESYSTEM) && sb.st_dev != devstart)
+ continue;
+
+ if (S_ISLNK(sb.st_mode)) {
+ size_t size;
+
+ if ((sizeof(sb.st_size) > sizeof(size_t)) &&
+ sb.st_size > SIZE_MAX) {
+ errno = EOVERFLOW;
+ goto fail_rdlink;
+ }
+
+ if (SZ_ADD_OV((size_t)sb.st_size, 1, &size)) {
+ errno = EOVERFLOW;
+ goto fail_rdlink;
+ }
+
+ extra = calloc(1, size);
+ if (extra == NULL)
+ goto fail_rdlink;
+
+ if (readlinkat(dir_fd, ent->d_name,
+ extra, (size_t)sb.st_size) < 0) {
+ goto fail_rdlink;
+ }
+
+ extra[sb.st_size] = '\0';
+ }
+
+ if (!(flags & DIR_SCAN_KEEP_TIME))
+ sb.st_mtime = fs->defaults.st_mtime;
+
+ if (S_ISDIR(sb.st_mode) && (flags & DIR_SCAN_NO_DIR)) {
+ n = fstree_get_node_by_path(fs, root, ent->d_name,
+ false, false);
+ if (n == NULL)
+ continue;
+
+ ret = 0;
+ } else {
+ n = fstree_mknode(root, ent->d_name,
+ strlen(ent->d_name), extra, &sb);
+ if (n == NULL) {
+ perror("creating tree node");
+ goto fail;
+ }
+
+ ret = (cb == NULL) ? 0 : cb(user, fs, n);
+ }
+
+ free(extra);
+ extra = NULL;
+
+ if (ret < 0)
+ goto fail;
+
+ if (ret > 0) {
+ discard_node(root, n);
+ continue;
+ }
+
+ if (S_ISDIR(n->mode) && !(flags & DIR_SCAN_NO_RECURSION)) {
+ childfd = openat(dir_fd, n->name, O_DIRECTORY |
+ O_RDONLY | O_CLOEXEC);
+ if (childfd < 0) {
+ perror(n->name);
+ goto fail;
+ }
+
+ if (populate_dir(childfd, fs, n, devstart,
+ cb, user, flags)) {
+ goto fail;
+ }
+ }
+ }
+
+ closedir(dir);
+ return 0;
+fail_rdlink:
+ perror("readlink");
+fail:
+ closedir(dir);
+ free(extra);
+ 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)
+{
+ struct stat sb;
+ int fd, subfd;
+
+ if (!S_ISDIR(root->mode)) {
+ fprintf(stderr,
+ "scanning %s/%s into %s: target is not a directory\n",
+ path, subdir == NULL ? "" : subdir, root->name);
+ return -1;
+ }
+
+ fd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ perror(path);
+ return -1;
+ }
+
+ if (subdir != NULL) {
+ subfd = openat(fd, subdir, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+
+ if (subfd < 0) {
+ fprintf(stderr, "%s/%s: %s\n", path, subdir,
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ fd = subfd;
+ }
+
+ if (fstat(fd, &sb)) {
+ fprintf(stderr, "%s/%s: %s\n", path,
+ subdir == NULL ? "" : subdir,
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ return populate_dir(fd, fs, root, sb.st_dev, cb, user, flags);
+}
+
+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);
+}
+#endif