]> 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.
 
        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.
 
        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.
 .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.
 .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
 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
 .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
 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.
 .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
 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).
 .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_uid;
        uint32_t unix_gid;
        uint32_t unix_mode;
-       uint32_t unix_reserved;
+       uint32_t unix_rdev;
 
        uint64_t reserved[14];
 
 
        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
 
  * 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
 
  * 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_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 uid;
        u32 gid;
        u32 mode;
+       u32 rdev;
 };
 
 struct wim_inode;
 };
 
 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_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
 
 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  */
 
 #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("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++) {
        }
 
        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_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);
        }
 
        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,
  */
 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;
 {
        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) {
        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);
                                         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_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);
        } 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;
        }
        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 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))
        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;
 
 
        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;
                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 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))
        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;
 
 
                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;
                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)
 {
 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);
 }
 
                             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)
 {
 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);
 
        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 ((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;
                /* 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 {
                        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);
        }
 }
                                     FILE_ATTRIBUTE_NORMAL, NULL);
        }
 }
@@ -2255,7 +2276,7 @@ wimfs_symlink(const char *to, const char *from)
        struct wim_dentry *dentry;
        int ret;
 
        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;
                            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 uid;
        le32 gid;
        le32 mode;
-       le32 reserved;
+       le32 rdev;
 };
 
 /* Retrieves the first tagged item with the specified tag and minimum length
 };
 
 /* 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->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.
  *
        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
  * 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;
                    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 = 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)
        }
        if (which & UNIX_DATA_UID)
-               p->uid = cpu_to_le32(uid);
+               p->uid = cpu_to_le32(unix_data->uid);
        if (which & UNIX_DATA_GID)
        if (which & UNIX_DATA_GID)
-               p->gid = cpu_to_le32(gid);
+               p->gid = cpu_to_le32(unix_data->gid);
        if (which & UNIX_DATA_MODE)
        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;
 }
        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 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
 };
 
 /* 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;
 }
 
        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;
 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;
        const char *path;
-       int fd;
        int ret;
 
        inode = dentry->d_inode;
        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;
 
        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;
 
        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;
        }
        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);
        /* 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]);
 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;
        }
 
                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;
                }
                        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,
        }
 
        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) {
 #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;
                }
                        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 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)
                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"),
                = 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  */
 };
 
 /* API function documented in wimlib.h  */