]> wimlib.net Git - wimlib/commitdiff
Add support for special files on UNIX
authorEric Biggers <ebiggers3@gmail.com>
Fri, 23 May 2014 14:58:19 +0000 (09:58 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Fri, 23 May 2014 14:58:19 +0000 (09:58 -0500)
13 files changed:
NEWS
doc/man1/imagex-apply.1.in
doc/man1/imagex-capture.1.in
doc/man1/imagex-mount.1.in
include/wimlib.h
include/wimlib/unix_data.h
programs/imagex.c
src/iterate_dir.c
src/mount_image.c
src/tagged_items.c
src/unix_apply.c
src/unix_capture.c
src/util.c

diff --git a/NEWS b/NEWS
index f4074bd36e6d9bae2160cf747da522e7f18ad1c5..4fef76cd30c707b376ac37e96a051ec89a124e59 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,10 +13,14 @@ Version 1.7.0-BETA:
        documentation for the new '--wimboot' option to wimcapture and wimapply
        for more information.
 
-       The format of UNIX data has been changed.  If you captured any WIMs with
-       the --unix-data option, to upgrade them you'll need to apply them with
-       --unix-data using wimlib v1.6.2, then re-capture them with --unix-data
-       using this version.
+       On UNIX-like systems, you can now backup and restore device nodes, named
+       pipes, and sockets.  In addition, 32-bit user and group IDs are now
+       supported.
+
+       The way that UNIX data is stored in WIM files has been changed.  If you
+       captured any WIMs with the --unix-data option, to upgrade them you'll
+       need to apply them with --unix-data using wimlib-imagex v1.6.2, then
+       re-capture them with --unix-data using this version.
 
        wimlib now understands tagged metadata items, such as object IDs, that
        can be stored in WIM directory entries.
index 5aec9b3fb30cc7b9450d782fea7a32c3531db99f..e3d04381bc435e432ace12381e3316998cec78bc 100644 (file)
@@ -331,15 +331,12 @@ Reparse point fixups are never done in the NTFS volume extraction mode on
 UNIX-like systems.
 .TP
 \fB--unix-data\fR
-(UNIX-like systems only)  By default, in the directory extraction mode on UNIX,
-\fB@IMAGEX_PROGNAME@ apply\fR will ignore both Windows-style security
-descriptors and UNIX-specific file owners, groups, and modes set when using
-\fB@IMAGEX_PROGNAME@ capture\fR with the \fB--unix-data\fR flag.  By passing
-\fB--unix-data\fR to \fB@IMAGEX_PROGNAME@ apply\fR instead, this causes this
-UNIX-specific data to be restored when available.  However, by default, if
-\fB@IMAGEX_PROGNAME@\fR does not have permission to set the UNIX owner, group or
-file mode on an extracted file, a warning will be printed and it will not be
-considered an error condition; use \fB--strict-acls\fR to get stricter behavior.
+(UNIX-like systems only)  Restore UNIX owners, groups, modes, and device IDs
+(major and minor numbers) that were captured by \fB@IMAGEX_PROGNAME@ capture\fR
+with the \fB--unix-data\fR option.  As of wimlib v1.7.0, you can backup and
+restore not only the standard UNIX file permission information, but also
+character device nodes, block device nodes, named pipes (FIFOs), and UNIX domain
+sockets.
 .TP
 \fB--no-acls\fR
 Do not restore security descriptors on extracted files and directories.
index b1f726752ea1794db6911f697d46a5c2074a316b..a1b6ff2b62503e2b562c851f8a35242bf9573bae 100644 (file)
@@ -57,10 +57,10 @@ the WIM format was designed for Windows, so it cannot store all possible
 metadata from filesystems used on UNIX-like systems.  The main information that
 will \fInot\fR be stored is:
 .IP \[bu] 4
-UNIX file owners, groups, and modes.  (Exception: see the \fB--unix-data\fR
-option.)  As a result, file permissions will not be stored, and files that are
-neither regular files, directories, nor symbolic links, such as device files and
-FIFOs, cannot be captured and will be excluded by default.
+UNIX file owners, groups, modes, and device IDs (major and minor numbers),
+unless the \fB--unix-data\fR option is specified.  By default (without
+\fB--unix-data\fR), files that are neither regular files, directories, nor
+symbolic links, such as device nodes and FIFOs, will be excluded.
 .IP \[bu]
 Extended attributes.  This mainly includes extensions to the traditional UNIX
 security model, such as SELinux security labels, POSIX ACLs, and capabilities
@@ -342,11 +342,18 @@ saved in the WIM image as Windows/System32/WimBootCompress.ini, overriding any
 that may be present on the filesystem.
 .TP
 \fB--unix-data\fR
-(UNIX-like systems only) Store the UNIX owner, group, and mode of all captured
-files.  This is done by adding a special tagged metadata item to each directory
-entry that contains this information.  This information should be ignored by the
-Microsoft implementation.  (Note: the way that UNIX data is stored was changed
-in wimlib v1.7.0 and is not backwards or forward compatible.)
+(UNIX-like systems only) Store the UNIX owner, group, mode, and device ID (major
+and minor number) of each captured file.  As of wimlib v1.7.0, you can backup
+and restore not only the standard UNIX file permission information, but also
+character device nodes, block device nodes, named pipes (FIFOs), and UNIX domain
+sockets.
+.IP
+wimlib stores UNIX data by adding a special tagged metadata item to each
+directory entry of each file that contains this information.  This extra
+information is ignored by the Microsoft implementation.  Note: UNIX data stored
+by wimlib before v1.7.0 used a different format that is no longer supported.  If
+you have old WIM files with UNIX data, apply them with v1.6.2 and recapture them
+with v1.7.0 or later.
 .TP
 \fB--no-acls\fR
 Do not capture files' security descriptors.
index 7ebf0598cefa42aba43b09a3c4df8a8dff2447c4..f475527c3d79c312b9d079fc9562e73d30062bea 100644 (file)
@@ -123,17 +123,20 @@ Store temporary staging files in a subdirectory of the directory \fIDIR\fR.
 Only valid for \fB@IMAGEX_PROGNAME@ mountrw\fR.
 .TP
 \fB--unix-data\fR
-By default, \fB@IMAGEX_PROGNAME@ mount\fR and \fB@IMAGEX_PROGNAME@ mountrw\fR will ignore both
-Windows-style security descriptors (which may have been set either from Windows or by
-\fB@IMAGEX_PROGNAME@ capture\fR from an NTFS-volume) and UNIX-specific data (which is from using
-\fB@IMAGEX_PROGNAME@ capture\fR with the \fB--unix-data\fR flag).  In this default mode,
-all files will simply be owned by the user running \fB@IMAGEX_PROGNAME@\fR and will have mode 0777.
-(Note: they will still not be accessible to other users unless you also specify
-\fB--allow-other\fR.)  If you instead provide the \fB--unix-data\fR flag, these
-default permissions will be overridden on a per-file basis with the
-UNIX-specific data when available, and in the case of \fB@IMAGEX_PROGNAME@ mountrw\fR it
-will be possible to change the UNIX permissions using the standard UNIX
-tools and functions.
+Honor UNIX-specific metadata that was captured by \fB@IMAGEX_PROGNAME@
+capture\fR with the \fB--unix-data option\fR.  By default, \fB@IMAGEX_PROGNAME@
+mount\fR and \fB@IMAGEX_PROGNAME@ mountrw\fR will ignore both Windows-style
+security descriptors (which may have been set either from Windows or by
+\fB@IMAGEX_PROGNAME@ capture\fR from an NTFS-volume) and UNIX-specific metadata.
+In this default mode, all files will simply be owned by the user running
+\fB@IMAGEX_PROGNAME@\fR and will have mode 0777.  (Note: they will still not be
+accessible to other users unless you also specify \fB--allow-other\fR.)  If you
+instead provide the \fB--unix-data\fR option, these default permissions will be
+overridden on a per-file basis with the UNIX-specific data when available, and
+in the case of \fB@IMAGEX_PROGNAME@ mountrw\fR it will be possible to change the
+UNIX permissions using the standard UNIX tools and functions.  In addition, with
+wimlib v1.7.0 and later, you can create device nodes, named pipes, and sockets
+on the mounted filesystem and have them stored in the WIM image.
 .TP
 \fB--allow-other\fR
 Pass the \fBallow_other\fR option to the FUSE mount.  See \fBmount.fuse\fR (8).
index f28e971fc26c9df26ec890c4d6b78b91b8f1a692..702c3a63e69ed01c9147c3b6bdef9eecd7aa2c49 100644 (file)
@@ -1256,7 +1256,7 @@ struct wimlib_dir_entry {
        uint32_t unix_uid;
        uint32_t unix_gid;
        uint32_t unix_mode;
-       uint32_t unix_reserved;
+       uint32_t unix_rdev;
 
        uint64_t reserved[14];
 
@@ -1321,10 +1321,9 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
  * wimlib_update_image().  */
 #define WIMLIB_ADD_FLAG_BOOT                   0x00000008
 
-/** Store the UNIX owner, group, and mode.  This is done by adding a special
- * alternate data stream to each regular file, symbolic link, and directory to
- * contain this information.  Please note that this flag is for convenience
- * only; Microsoft's implementation will not understand this special
+/** UNIX-like systems only: Store the UNIX owner, group, mode, and device ID
+ * (major and minor number) of each file.  See the documentation for the
+ * <b>--unix-data</b> option to <b>wimlib-imagex capture</b> for more
  * information.  */
 #define WIMLIB_ADD_FLAG_UNIX_DATA              0x00000010
 
@@ -2068,6 +2067,7 @@ enum wimlib_error_code {
        WIMLIB_ERR_WIMBOOT,
        WIMLIB_ERR_ABORTED_BY_PROGRESS,
        WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS,
+       WIMLIB_ERR_MKNOD,
 };
 
 
index 6432369533acab7ee0c023bf313d0a0ea5fa99e2..ede0fdcec3cf1cc5c46091083db7eeb06c55b36f 100644 (file)
@@ -7,6 +7,7 @@ struct wimlib_unix_data {
        u32 uid;
        u32 gid;
        u32 mode;
+       u32 rdev;
 };
 
 struct wim_inode;
@@ -21,11 +22,12 @@ inode_get_unix_data(const struct wim_inode *inode,
 #define UNIX_DATA_UID  0x1
 #define UNIX_DATA_GID  0x2
 #define UNIX_DATA_MODE 0x4
+#define UNIX_DATA_RDEV 0x8
 
-#define UNIX_DATA_ALL  (UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE)
+#define UNIX_DATA_ALL  0xF
 
 extern bool
-inode_set_unix_data(struct wim_inode *inode, u32 uid, u32 gid, u32 mode,
-                   int which);
+inode_set_unix_data(struct wim_inode *inode,
+                   struct wimlib_unix_data *unix_data, int which);
 
 #endif /* _WIMLIB_UNIX_DATA_H  */
index 41ab646dbacb138557d63659bb5cf11dadf33e11..059e6d9b4b275f752dcbbb3e9a63cc653ab934db 100644 (file)
@@ -2430,8 +2430,10 @@ print_dentry_detailed(const struct wimlib_dir_entry *dentry)
        tprintf(T("Link Count          = %"PRIu32"\n"), dentry->num_links);
 
        if (dentry->unix_mode != 0) {
-               tprintf(T("UNIX Data           = uid:%"PRIu32" gid:%"PRIu32" mode:0%"PRIo32"\n"),
-                       dentry->unix_uid, dentry->unix_gid, dentry->unix_mode);
+               tprintf(T("UNIX Data           = uid:%"PRIu32" gid:%"PRIu32" "
+                         "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
+                       dentry->unix_uid, dentry->unix_gid,
+                       dentry->unix_mode, dentry->unix_rdev);
        }
 
        for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
index c295dd1858f03af0829583988082bca7e246a349..4e286b157f105c502d9bc85aae29f2e1185965bd 100644 (file)
@@ -87,6 +87,7 @@ init_wimlib_dentry(struct wimlib_dir_entry *wdentry, struct wim_dentry *dentry,
                wdentry->unix_uid = unix_data.uid;
                wdentry->unix_gid = unix_data.gid;
                wdentry->unix_mode = unix_data.mode;
+               wdentry->unix_rdev = unix_data.rdev;
        }
 
        lte = inode_unnamed_lte(inode, wim->lookup_table);
index 169f3d3bf967c66c5ab44630feecec2373274530..168ee8523bc9bc038a9c678bdbf9c2f50fce62e9 100644 (file)
@@ -315,7 +315,8 @@ fuse_mask_mode(mode_t mode, struct fuse_context *fuse_ctx)
  */
 static int
 create_dentry(struct fuse_context *fuse_ctx, const char *path,
-             mode_t mode, int attributes, struct wim_dentry **dentry_ret)
+             mode_t mode, dev_t rdev, int attributes,
+             struct wim_dentry **dentry_ret)
 {
        struct wim_dentry *parent;
        struct wim_dentry *new;
@@ -343,10 +344,14 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path,
        new->d_inode->i_attributes = attributes;
 
        if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
-               if (!inode_set_unix_data(new->d_inode,
-                                        fuse_ctx->uid,
-                                        fuse_ctx->gid,
-                                        fuse_mask_mode(mode, fuse_ctx),
+               struct wimlib_unix_data unix_data;
+
+               unix_data.uid = fuse_ctx->uid;
+               unix_data.gid = fuse_ctx->gid;
+               unix_data.mode = fuse_mask_mode(mode, fuse_ctx);
+               unix_data.rdev = rdev;
+
+               if (!inode_set_unix_data(new->d_inode, &unix_data,
                                         UNIX_DATA_ALL))
                {
                        free_dentry(new);
@@ -426,10 +431,12 @@ inode_to_stbuf(const struct wim_inode *inode,
                stbuf->st_uid = unix_data.uid;
                stbuf->st_gid = unix_data.gid;
                stbuf->st_mode = unix_data.mode;
+               stbuf->st_rdev = unix_data.rdev;
        } else {
                stbuf->st_uid = ctx->default_uid;
                stbuf->st_gid = ctx->default_gid;
                stbuf->st_mode = inode_default_unix_mode(inode);
+               stbuf->st_rdev = 0;
        }
        stbuf->st_ino = (ino_t)inode->i_ino;
        stbuf->st_nlink = inode->i_nlink;
@@ -1617,6 +1624,7 @@ wimfs_chmod(const char *path, mode_t mask)
        struct wim_dentry *dentry;
        struct wim_inode *inode;
        struct wimfs_context *ctx = wimfs_get_context();
+       struct wimlib_unix_data unix_data;
        int ret;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
@@ -1629,8 +1637,11 @@ wimfs_chmod(const char *path, mode_t mask)
 
        inode = dentry->d_inode;
 
-       if (!inode_set_unix_data(inode, ctx->default_uid,
-                                ctx->default_gid, mask, UNIX_DATA_MODE))
+       unix_data.uid = ctx->default_uid;
+       unix_data.gid = ctx->default_gid;
+       unix_data.mode = mask;
+       unix_data.rdev = 0;
+       if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_MODE))
                return -ENOMEM;
 
        return 0;
@@ -1643,6 +1654,7 @@ wimfs_chown(const char *path, uid_t uid, gid_t gid)
        struct wim_inode *inode;
        struct wimfs_context *ctx = wimfs_get_context();
        int which;
+       struct wimlib_unix_data unix_data;
        int ret;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
@@ -1668,8 +1680,11 @@ wimfs_chown(const char *path, uid_t uid, gid_t gid)
                gid = ctx->default_gid;
 
 
-       if (!inode_set_unix_data(inode, uid, gid,
-                                inode_default_unix_mode(inode), which))
+       unix_data.uid = uid;
+       unix_data.gid = gid;
+       unix_data.mode = inode_default_unix_mode(inode);
+       unix_data.rdev = 0;
+       if (!inode_set_unix_data(inode, &unix_data, which))
                return -ENOMEM;
 
        return 0;
@@ -1880,11 +1895,12 @@ wimfs_listxattr(const char *path, char *list, size_t size)
 static int
 wimfs_mkdir(const char *path, mode_t mode)
 {
-       return create_dentry(fuse_get_context(), path, mode | S_IFDIR,
+       return create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
                             FILE_ATTRIBUTE_DIRECTORY, NULL);
 }
 
-/* Create a regular file or alternate data stream in the WIM image. */
+/* Create a non-directory, non-symbolic-link file or alternate data stream in
+ * the WIM image.  */
 static int
 wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
 {
@@ -1892,11 +1908,12 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
        struct fuse_context *fuse_ctx = fuse_get_context();
        struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
 
-       if (!S_ISREG(mode))
-               return -EPERM;
-
        if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
             && (stream_name = path_stream_name(path))) {
+
+               if (!S_ISREG(mode))
+                       return -EPERM;
+
                /* Make an alternate data stream */
                struct wim_ads_entry *new_entry;
                struct wim_inode *inode;
@@ -1917,8 +1934,12 @@ wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                        return -ENOMEM;
                return 0;
        } else {
-               /* Make a normal file (not an alternate data stream) */
-               return create_dentry(fuse_ctx, path, mode | S_IFREG,
+               if (!S_ISREG(mode) &&
+                   !(wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
+                       return -EPERM;
+
+               /* Make a regular file, device node, named pipe, or socket.  */
+               return create_dentry(fuse_ctx, path, mode, rdev,
                                     FILE_ATTRIBUTE_NORMAL, NULL);
        }
 }
@@ -2255,7 +2276,7 @@ wimfs_symlink(const char *to, const char *from)
        struct wim_dentry *dentry;
        int ret;
 
-       ret = create_dentry(fuse_ctx, from, S_IFLNK | 0777,
+       ret = create_dentry(fuse_ctx, from, S_IFLNK | 0777, 0,
                            FILE_ATTRIBUTE_REPARSE_POINT, &dentry);
        if (ret == 0) {
                dentry->d_inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
index 3a12974ae5b6b8ad3ae95efe375c91160ae8db37..6cd897fa7ae1bf167c7c4f6e21088b670d1a394c 100644 (file)
@@ -69,7 +69,7 @@ struct wimlib_unix_data_disk {
        le32 uid;
        le32 gid;
        le32 mode;
-       le32 reserved;
+       le32 rdev;
 };
 
 /* Retrieves the first tagged item with the specified tag and minimum length
@@ -178,19 +178,20 @@ inode_get_unix_data(const struct wim_inode *inode,
        unix_data->uid = le32_to_cpu(p->uid);
        unix_data->gid = le32_to_cpu(p->gid);
        unix_data->mode = le32_to_cpu(p->mode);
+       unix_data->rdev = le32_to_cpu(p->rdev);
        return true;
 }
 
 /* Sets UNIX data on the specified WIM inode.
  * This is a wimlib extension.
  *
- * Callers must specify all of @uid, @gid, and @mode.  If the inode does not yet
+ * Callers must specify all members in @unix_data.  If the inode does not yet
  * have UNIX data, it is given these values.  Otherwise, only the values that
  * also have the corresponding flags in @which set are changed.
  *
  * Returns %true if successful, %false if failed (out of memory).  */
 bool
-inode_set_unix_data(struct wim_inode *inode, u32 uid, u32 gid, u32 mode,
+inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data,
                    int which)
 {
        struct wimlib_unix_data_disk *p;
@@ -200,14 +201,15 @@ inode_set_unix_data(struct wim_inode *inode, u32 uid, u32 gid, u32 mode,
                p = inode_add_unix_data_disk(inode);
                if (!p)
                        return false;
-               p->reserved = cpu_to_le32(0);
-               which = UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE;
+               which = UNIX_DATA_ALL;
        }
        if (which & UNIX_DATA_UID)
-               p->uid = cpu_to_le32(uid);
+               p->uid = cpu_to_le32(unix_data->uid);
        if (which & UNIX_DATA_GID)
-               p->gid = cpu_to_le32(gid);
+               p->gid = cpu_to_le32(unix_data->gid);
        if (which & UNIX_DATA_MODE)
-               p->mode = cpu_to_le32(mode);
+               p->mode = cpu_to_le32(unix_data->mode);
+       if (which & UNIX_DATA_RDEV)
+               p->rdev = cpu_to_le32(unix_data->rdev);
        return true;
 }
index 40dc9edf701a1a9285632f24504be666103055de..8b8d9bf5bf0480a1640d95ff1aaf76e7ac1bf049 100644 (file)
@@ -96,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
@@ -373,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;
@@ -390,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;
@@ -709,6 +740,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]);
index a72d3ad3fc8f033ab5910aeb4bf80ec495451be2..ba540c98237abbb41b1c9678c7c75ded74f0a470 100644 (file)
@@ -347,19 +347,25 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
                goto out;
        }
 
-       if (unlikely(!S_ISREG(stbuf.st_mode) &&
-                    !S_ISDIR(stbuf.st_mode) &&
-                    !S_ISLNK(stbuf.st_mode)))
-       {
-               if (params->add_flags & WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
+       if (!(params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA)) {
+               if (unlikely(!S_ISREG(stbuf.st_mode) &&
+                            !S_ISDIR(stbuf.st_mode) &&
+                            !S_ISLNK(stbuf.st_mode)))
                {
-                       ERROR("\"%s\": File type is unsupported", full_path);
-                       ret = WIMLIB_ERR_UNSUPPORTED_FILE;
+                       if (params->add_flags &
+                           WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
+                       {
+                               ERROR("\"%s\": File type is unsupported",
+                                     full_path);
+                               ret = WIMLIB_ERR_UNSUPPORTED_FILE;
+                               goto out;
+                       }
+                       params->progress.scan.cur_path = full_path;
+                       ret = do_capture_progress(params,
+                                                 WIMLIB_SCAN_DENTRY_UNSUPPORTED,
+                                                 NULL);
                        goto out;
                }
-               params->progress.scan.cur_path = full_path;
-               ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL);
-               goto out;
        }
 
        ret = inode_table_new_dentry(params->inode_table, relpath,
@@ -387,9 +393,13 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
 #endif
        inode->i_resolved = 1;
        if (params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) {
-               if (!inode_set_unix_data(inode, stbuf.st_uid, stbuf.st_gid,
-                                        stbuf.st_mode, UNIX_DATA_ALL))
-               {
+               struct wimlib_unix_data unix_data;
+
+               unix_data.uid = stbuf.st_uid;
+               unix_data.gid = stbuf.st_gid;
+               unix_data.mode = stbuf.st_mode;
+               unix_data.rdev = stbuf.st_rdev;
+               if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_ALL)) {
                        ret = WIMLIB_ERR_NOMEM;
                        goto out;
                }
@@ -407,7 +417,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
        } else if (S_ISDIR(stbuf.st_mode)) {
                ret = unix_scan_directory(tree, full_path, full_path_len,
                                          dirfd, relpath, params);
-       } else {
+       } else if (S_ISLNK(stbuf.st_mode)) {
                ret = unix_scan_symlink(&tree, full_path, dirfd, relpath,
                                        inode, params);
                if (!tree)
index 02017d030e39ea1d2504a610e7a3272c9bf0a82d..7916f40a77b8a0b333af90c163e4b6d2ca8458a5 100644 (file)
@@ -350,6 +350,8 @@ static const tchar *error_strings[] = {
                = T("The operation was aborted by the library user"),
        [WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS]
                = T("The user-provided progress function returned an unrecognized value"),
+       [WIMLIB_ERR_MKNOD]
+               = T("Unable to create a special file (e.g. device node or socket)"),
 };
 
 /* API function documented in wimlib.h  */