]> wimlib.net Git - wimlib/blobdiff - src/unix_apply.c
win32_capture.c: Fix exclusion when capture path ends in slash
[wimlib] / src / unix_apply.c
index c4f5318b23b0694c0e6737c19128e0dab9cc6798..a683b4da1d2ce194a3f7ccafba96e6377897b7b9 100644 (file)
 #endif
 
 #include "wimlib/apply.h"
+#include "wimlib/assert.h"
 #include "wimlib/dentry.h"
 #include "wimlib/error.h"
 #include "wimlib/file_io.h"
 #include "wimlib/reparse.h"
 #include "wimlib/timestamp.h"
+#include "wimlib/unix_data.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -63,8 +65,6 @@ 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
-                            identical streams exceeds this number.  */
 
 struct unix_apply_ctx {
        /* Extract flags, the pointer to the WIMStruct, etc.  */
@@ -77,7 +77,7 @@ struct unix_apply_ctx {
        unsigned which_pathbuf;
 
        /* Currently open file descriptors for extraction  */
-       struct filedes open_fds[MAX_OPEN_FDS];
+       struct filedes open_fds[MAX_OPEN_STREAMS];
 
        /* Number of currently open file descriptors in open_fds, starting from
         * the beginning of the array.  */
@@ -95,6 +95,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 +112,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 +160,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 +222,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 +255,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 +375,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 +392,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 +542,9 @@ unix_begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
                return 0;
        }
 
+       /* This should be ensured by extract_stream_list()  */
+       wimlib_assert(ctx->num_open_fds < MAX_OPEN_STREAMS);
+
        first_dentry = inode_first_extraction_dentry(inode);
        first_path = unix_build_extraction_path(first_dentry, ctx);
 retry_create:
@@ -462,8 +561,7 @@ retry_create:
 
 /* Called when starting to read a single-instance stream for extraction  */
 static int
-unix_begin_extract_stream(struct wim_lookup_table_entry *stream,
-                         u32 flags, void *_ctx)
+unix_begin_extract_stream(struct wim_lookup_table_entry *stream, void *_ctx)
 {
        struct unix_apply_ctx *ctx = _ctx;
        const struct stream_owner *owners = stream_owners(stream);
@@ -528,13 +626,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 +745,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]);