X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Funix_apply.c;h=077b49db9aa9e694dbe85f6536d3c3c9907e6cea;hb=9e3ef4570081d0d9113dde0c0a1760c64498c0e4;hp=c4f5318b23b0694c0e6737c19128e0dab9cc6798;hpb=cc7b6ee47d4037ae8fa11b4c2d5154091d543704;p=wimlib diff --git a/src/unix_apply.c b/src/unix_apply.c index c4f5318b..077b49db 100644 --- a/src/unix_apply.c +++ b/src/unix_apply.c @@ -31,6 +31,7 @@ #include "wimlib/file_io.h" #include "wimlib/reparse.h" #include "wimlib/timestamp.h" +#include "wimlib/unix_data.h" #include #include @@ -63,7 +64,7 @@ unix_get_supported_features(const char *target, } #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 { @@ -95,6 +96,9 @@ 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 @@ -109,7 +113,7 @@ unix_dentry_path_length(const struct wim_dentry *dentry) 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; @@ -157,7 +161,7 @@ unix_build_extraction_path(const struct wim_dentry *dentry, 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; @@ -219,6 +223,26 @@ unix_set_timestamps(int fd, const char *path, u64 atime, u64 mtime) } } +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. * @@ -232,10 +256,55 @@ unix_set_metadata(int fd, const struct wim_inode *inode, 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); @@ -307,15 +376,15 @@ unix_create_if_directory(const struct wim_dentry *dentry, 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; @@ -324,26 +393,54 @@ unix_extract_if_empty_file(const struct wim_dentry *dentry, 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; @@ -446,6 +543,11 @@ unix_begin_extract_stream_instance(const struct wim_lookup_table_entry *stream, 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: @@ -528,13 +630,17 @@ unix_end_extract_stream(struct wim_lookup_table_entry *stream, int status, /* 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) { @@ -643,6 +749,12 @@ unix_extract(struct list_head *dentry_list, struct apply_ctx *_ctx) /* 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]);