#include "wimlib/file_io.h"
#include "wimlib/reparse.h"
#include "wimlib/timestamp.h"
+#include "wimlib/unix_data.h"
#include <errno.h>
#include <fcntl.h>
}
#define NUM_PATHBUFS 2 /* We need 2 when creating hard links */
-#define MAX_OPEN_FDS 1024 /* TODO: Add special case for when the number of
+#define MAX_OPEN_FDS 1000 /* TODO: Add special case for when the number of
identical streams exceeds this number. */
struct unix_apply_ctx {
/* Number of characters in target_abspath. */
size_t target_abspath_nchars;
+
+ /* Number of special files we couldn't create due to EPERM */
+ unsigned long num_special_files_ignored;
};
/* Returns the number of characters needed to represent the path to the
d = dentry;
do {
len += d->d_extraction_name_nchars + 1;
- d = d->parent;
+ d = d->d_parent;
} while (!dentry_is_root(d) && will_extract_dentry(d));
return len;
p -= d->d_extraction_name_nchars;
memcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
*--p = '/';
- d = d->parent;
+ d = d->d_parent;
} while (!dentry_is_root(d) && will_extract_dentry(d));
return pathbuf;
}
}
+static int
+unix_set_owner_and_group(int fd, const char *path, uid_t uid, gid_t gid)
+{
+ if (fd >= 0 && !fchown(fd, uid, gid))
+ return 0;
+ if (fd < 0 && !lchown(path, uid, gid))
+ return 0;
+ return WIMLIB_ERR_SET_SECURITY;
+}
+
+static int
+unix_set_mode(int fd, const char *path, mode_t mode)
+{
+ if (fd >= 0 && !fchmod(fd, mode))
+ return 0;
+ if (fd < 0 && !chmod(path, mode))
+ return 0;
+ return WIMLIB_ERR_SET_SECURITY;
+}
+
/*
* Set metadata on an extracted file.
*
const char *path, struct unix_apply_ctx *ctx)
{
int ret;
+ struct wimlib_unix_data unix_data;
if (fd < 0 && !path)
path = unix_build_inode_extraction_path(inode, ctx);
+ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)
+ && inode_get_unix_data(inode, &unix_data))
+ {
+ u32 uid = unix_data.uid;
+ u32 gid = unix_data.gid;
+ u32 mode = unix_data.mode;
+
+ ret = unix_set_owner_and_group(fd, path, uid, gid);
+ if (ret) {
+ if (!path)
+ path = unix_build_inode_extraction_path(inode, ctx);
+ if (ctx->common.extract_flags &
+ WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
+ {
+ ERROR_WITH_ERRNO("Can't set uid=%"PRIu32" and "
+ "gid=%"PRIu32" on \"%s\"",
+ uid, gid, path);
+ return ret;
+ } else {
+ WARNING_WITH_ERRNO("Can't set uid=%"PRIu32" and "
+ "gid=%"PRIu32" on \"%s\"",
+ uid, gid, path);
+ }
+ }
+
+ ret = 0;
+ if (!inode_is_symlink(inode))
+ ret = unix_set_mode(fd, path, mode);
+ if (ret) {
+ if (!path)
+ path = unix_build_inode_extraction_path(inode, ctx);
+ if (ctx->common.extract_flags &
+ WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
+ {
+ ERROR_WITH_ERRNO("Can't set mode=0%"PRIo32" "
+ "on \"%s\"", mode, path);
+ return ret;
+ } else {
+ WARNING_WITH_ERRNO("Can't set mode=0%"PRIo32" "
+ "on \"%s\"", mode, path);
+ }
+ }
+ }
+
ret = unix_set_timestamps(fd, path,
inode->i_last_access_time,
inode->i_last_write_time);
return 0;
}
-/* If @dentry represents an empty regular file, create it, set its metadata, and
- * create any needed hard links. */
+/* If @dentry represents an empty regular file or a special file, create it, set
+ * its metadata, and create any needed hard links. */
static int
unix_extract_if_empty_file(const struct wim_dentry *dentry,
struct unix_apply_ctx *ctx)
{
const struct wim_inode *inode;
+ struct wimlib_unix_data unix_data;
const char *path;
- int fd;
int ret;
inode = dentry->d_inode;
if (dentry != inode_first_extraction_dentry(inode))
return 0;
- /* Not an empty regular file? */
+ /* Is this a directory, a symbolic link, or any type of nonempty file?
+ */
if (inode_is_directory(inode) || inode_is_symlink(inode) ||
inode_unnamed_lte_resolved(inode))
return 0;
- path = unix_build_extraction_path(dentry, ctx);
-retry_create:
- fd = open(path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
- if (fd < 0) {
- if (errno == EEXIST && !unlink(path))
- goto retry_create;
- ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path);
- return WIMLIB_ERR_OPEN;
- }
- /* On empty files, we can set timestamps immediately because we don't
- * need to write any data to them. */
- ret = unix_set_metadata(fd, inode, path, ctx);
- if (close(fd) && !ret) {
- ERROR_WITH_ERRNO("Error closing \"%s\"", path);
- ret = WIMLIB_ERR_WRITE;
+ /* Recognize special files in UNIX_DATA mode */
+ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
+ inode_get_unix_data(inode, &unix_data) &&
+ !S_ISREG(unix_data.mode))
+ {
+ path = unix_build_extraction_path(dentry, ctx);
+ retry_mknod:
+ if (mknod(path, unix_data.mode, unix_data.rdev)) {
+ if (errno == EPERM) {
+ WARNING_WITH_ERRNO("Can't create special "
+ "file \"%s\"", path);
+ ctx->num_special_files_ignored++;
+ return 0;
+ }
+ if (errno == EEXIST && !unlink(path))
+ goto retry_mknod;
+ ERROR_WITH_ERRNO("Can't create special file \"%s\"",
+ path);
+ return WIMLIB_ERR_MKNOD;
+ }
+ /* On special files, we can set timestamps immediately because
+ * we don't need to write any data to them. */
+ ret = unix_set_metadata(-1, inode, path, ctx);
+ } else {
+ int fd;
+
+ path = unix_build_extraction_path(dentry, ctx);
+ retry_create:
+ fd = open(path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
+ if (fd < 0) {
+ if (errno == EEXIST && !unlink(path))
+ goto retry_create;
+ ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path);
+ return WIMLIB_ERR_OPEN;
+ }
+ /* On empty files, we can set timestamps immediately because we
+ * don't need to write any data to them. */
+ ret = unix_set_metadata(fd, inode, path, ctx);
+ if (close(fd) && !ret) {
+ ERROR_WITH_ERRNO("Error closing \"%s\"", path);
+ ret = WIMLIB_ERR_WRITE;
+ }
}
if (ret)
return ret;
return 0;
}
+ if (ctx->num_open_fds == MAX_OPEN_FDS) {
+ ERROR("Can't extract data: too many open files!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
first_dentry = inode_first_extraction_dentry(inode);
first_path = unix_build_extraction_path(first_dentry, ctx);
retry_create:
/* We finally have the symlink data, so we can create
* the symlink. */
const char *path;
+ bool rpfix;
+
+ rpfix = (ctx->common.extract_flags &
+ WIMLIB_EXTRACT_FLAG_RPFIX) &&
+ !inode->i_not_rpfixed;
path = unix_build_inode_extraction_path(inode, ctx);
ret = unix_create_symlink(inode, path,
ctx->reparse_data,
stream->size,
- (ctx->common.extract_flags &
- WIMLIB_EXTRACT_FLAG_RPFIX),
+ rpfix,
ctx->target_abspath,
ctx->target_abspath_nchars);
if (ret) {
/* Set directory metadata. We do this last so that we get the right
* directory timestamps. */
ret = unix_set_dir_metadata(dentry_list, ctx);
+ if (ret)
+ goto out;
+ if (ctx->num_special_files_ignored) {
+ WARNING("%lu special files were not extracted due to EPERM!",
+ ctx->num_special_files_ignored);
+ }
out:
for (unsigned i = 0; i < NUM_PATHBUFS; i++)
FREE(ctx->pathbufs[i]);