summaryrefslogtreecommitdiff
path: root/bin/rdsquashfs/restore_fstree.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/rdsquashfs/restore_fstree.c')
-rw-r--r--bin/rdsquashfs/restore_fstree.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/bin/rdsquashfs/restore_fstree.c b/bin/rdsquashfs/restore_fstree.c
new file mode 100644
index 0000000..8f99439
--- /dev/null
+++ b/bin/rdsquashfs/restore_fstree.c
@@ -0,0 +1,320 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * restore_fstree.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "rdsquashfs.h"
+
+#ifdef _WIN32
+static int create_node(const sqfs_tree_node_t *n, const char *name)
+{
+ WCHAR *wpath;
+ HANDLE fh;
+
+ wpath = path_to_windows(name);
+ if (wpath == NULL)
+ return -1;
+
+ switch (n->inode->base.mode & S_IFMT) {
+ case S_IFDIR:
+ if (!CreateDirectoryW(wpath, NULL))
+ goto fail;
+ break;
+ case S_IFREG:
+ fh = CreateFileW(wpath, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_NEW, 0, NULL);
+
+ if (fh == INVALID_HANDLE_VALUE)
+ goto fail;
+
+ CloseHandle(fh);
+ break;
+ default:
+ break;
+ }
+
+ free(wpath);
+ return 0;
+fail:
+ fprintf(stderr, "Creating %s: %ld\n", name, GetLastError());
+ free(wpath);
+ return -1;
+}
+#else
+static int create_node(const sqfs_tree_node_t *n, const char *name)
+{
+ sqfs_u32 devno;
+ int fd;
+
+ switch (n->inode->base.mode & S_IFMT) {
+ case S_IFDIR:
+ if (mkdir(name, 0755) && errno != EEXIST) {
+ fprintf(stderr, "mkdir %s: %s\n",
+ name, strerror(errno));
+ return -1;
+ }
+ break;
+ case S_IFLNK:
+ if (symlink((const char *)n->inode->extra, name)) {
+ fprintf(stderr, "ln -s %s %s: %s\n",
+ (const char *)n->inode->extra, name,
+ strerror(errno));
+ return -1;
+ }
+ break;
+ case S_IFSOCK:
+ case S_IFIFO:
+ if (mknod(name, (n->inode->base.mode & S_IFMT) | 0700, 0)) {
+ fprintf(stderr, "creating %s: %s\n",
+ name, strerror(errno));
+ return -1;
+ }
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ if (n->inode->base.type == SQFS_INODE_EXT_BDEV ||
+ n->inode->base.type == SQFS_INODE_EXT_CDEV) {
+ devno = n->inode->data.dev_ext.devno;
+ } else {
+ devno = n->inode->data.dev.devno;
+ }
+
+ if (mknod(name, n->inode->base.mode & S_IFMT, devno)) {
+ fprintf(stderr, "creating device %s: %s\n",
+ name, strerror(errno));
+ return -1;
+ }
+ break;
+ case S_IFREG:
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0600);
+
+ if (fd < 0) {
+ fprintf(stderr, "creating %s: %s\n",
+ name, strerror(errno));
+ return -1;
+ }
+
+ close(fd);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+static int create_node_dfs(const sqfs_tree_node_t *n, int flags)
+{
+ const sqfs_tree_node_t *c;
+ char *name;
+ int ret;
+
+ if (!is_filename_sane((const char *)n->name, true)) {
+ fprintf(stderr, "Found an entry named '%s', skipping.\n",
+ n->name);
+ return 0;
+ }
+
+ name = sqfs_tree_node_get_path(n);
+ if (name == NULL) {
+ fprintf(stderr, "Constructing full path for '%s': %s\n",
+ (const char *)n->name, strerror(errno));
+ return -1;
+ }
+
+ ret = canonicalize_name(name);
+ assert(ret == 0);
+
+ if (!(flags & UNPACK_QUIET))
+ printf("creating %s\n", name);
+
+ ret = create_node(n, name);
+ free(name);
+ if (ret)
+ return -1;
+
+ if (S_ISDIR(n->inode->base.mode)) {
+ for (c = n->children; c != NULL; c = c->next) {
+ if (create_node_dfs(c, flags))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#ifdef HAVE_SYS_XATTR_H
+static int set_xattr(const char *path, sqfs_xattr_reader_t *xattr,
+ const sqfs_tree_node_t *n)
+{
+ sqfs_xattr_value_t *value;
+ sqfs_xattr_entry_t *key;
+ sqfs_xattr_id_t desc;
+ sqfs_u32 index;
+ size_t i;
+ int ret;
+
+ sqfs_inode_get_xattr_index(n->inode, &index);
+
+ if (index == 0xFFFFFFFF)
+ return 0;
+
+ if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) {
+ fputs("Error resolving xattr index\n", stderr);
+ return -1;
+ }
+
+ if (sqfs_xattr_reader_seek_kv(xattr, &desc)) {
+ fputs("Error locating xattr key-value pairs\n", stderr);
+ return -1;
+ }
+
+ for (i = 0; i < desc.count; ++i) {
+ if (sqfs_xattr_reader_read_key(xattr, &key)) {
+ fputs("Error reading xattr key\n", stderr);
+ return -1;
+ }
+
+ if (sqfs_xattr_reader_read_value(xattr, key, &value)) {
+ fputs("Error reading xattr value\n", stderr);
+ free(key);
+ return -1;
+ }
+
+ ret = lsetxattr(path, (const char *)key->key,
+ value->value, value->size, 0);
+ if (ret) {
+ fprintf(stderr, "setting xattr '%s' on %s: %s\n",
+ key->key, path, strerror(errno));
+ }
+
+ free(key);
+ free(value);
+ if (ret)
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int set_attribs(sqfs_xattr_reader_t *xattr,
+ const sqfs_tree_node_t *n, int flags)
+{
+ const sqfs_tree_node_t *c;
+ char *path;
+ int ret;
+
+ if (!is_filename_sane((const char *)n->name, true))
+ return 0;
+
+ if (S_ISDIR(n->inode->base.mode)) {
+ for (c = n->children; c != NULL; c = c->next) {
+ if (set_attribs(xattr, c, flags))
+ return -1;
+ }
+ }
+
+ path = sqfs_tree_node_get_path(n);
+ if (path == NULL) {
+ fprintf(stderr, "Reconstructing full path: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ ret = canonicalize_name(path);
+ assert(ret == 0);
+
+#ifdef HAVE_SYS_XATTR_H
+ if ((flags & UNPACK_SET_XATTR) && xattr != NULL) {
+ if (set_xattr(path, xattr, n))
+ goto fail;
+ }
+#endif
+
+#ifndef _WIN32
+ if (flags & UNPACK_SET_TIMES) {
+ struct timespec times[2];
+
+ memset(times, 0, sizeof(times));
+ times[0].tv_sec = n->inode->base.mod_time;
+ times[1].tv_sec = n->inode->base.mod_time;
+
+ if (utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW)) {
+ fprintf(stderr, "setting timestamp on %s: %s\n",
+ path, strerror(errno));
+ goto fail;
+ }
+ }
+#endif
+ if (flags & UNPACK_CHOWN) {
+ if (fchownat(AT_FDCWD, path, n->uid, n->gid,
+ AT_SYMLINK_NOFOLLOW)) {
+ fprintf(stderr, "chown %s: %s\n",
+ path, strerror(errno));
+ goto fail;
+ }
+ }
+
+ if (flags & UNPACK_CHMOD && !S_ISLNK(n->inode->base.mode)) {
+ if (fchmodat(AT_FDCWD, path,
+ n->inode->base.mode & ~S_IFMT, 0)) {
+ fprintf(stderr, "chmod %s: %s\n",
+ path, strerror(errno));
+ goto fail;
+ }
+ }
+
+ free(path);
+ return 0;
+fail:
+ free(path);
+ return -1;
+}
+
+int restore_fstree(sqfs_tree_node_t *root, int flags)
+{
+ sqfs_tree_node_t *n, *old_parent;
+
+ /* make sure fstree_get_path() stops at this node */
+ old_parent = root->parent;
+ root->parent = NULL;
+
+ if (S_ISDIR(root->inode->base.mode)) {
+ for (n = root->children; n != NULL; n = n->next) {
+ if (create_node_dfs(n, flags))
+ return -1;
+ }
+ } else {
+ if (create_node_dfs(root, flags))
+ return -1;
+ }
+
+ root->parent = old_parent;
+ return 0;
+}
+
+int update_tree_attribs(sqfs_xattr_reader_t *xattr,
+ const sqfs_tree_node_t *root, int flags)
+{
+ const sqfs_tree_node_t *n;
+
+ if ((flags & (UNPACK_CHOWN | UNPACK_CHMOD |
+ UNPACK_SET_TIMES | UNPACK_SET_XATTR)) == 0) {
+ return 0;
+ }
+
+ if (S_ISDIR(root->inode->base.mode)) {
+ for (n = root->children; n != NULL; n = n->next) {
+ if (set_attribs(xattr, n, flags))
+ return -1;
+ }
+ } else {
+ if (set_attribs(xattr, root, flags))
+ return -1;
+ }
+
+ return 0;
+}