]> wimlib.net Git - wimlib/blobdiff - src/unix_apply.c
unix_apply.c: Honor i_not_rpfixed
[wimlib] / src / unix_apply.c
index bf9c2911d57630ed2f847f68e4a35df9e00d92bb..077b49db9aa9e694dbe85f6536d3c3c9907e6cea 100644 (file)
@@ -31,6 +31,7 @@
 #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,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;
@@ -252,16 +256,17 @@ 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_has_unix_data(inode))
+           && inode_get_unix_data(inode, &unix_data))
        {
-               u32 uid = inode->i_unix_data.uid;
-               u32 gid = inode->i_unix_data.gid;
-               u32 mode = inode->i_unix_data.mode;
+               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) {
@@ -371,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;
@@ -388,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;
@@ -510,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:
@@ -592,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) {
@@ -707,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]);