New format for UNIX data
authorEric Biggers <ebiggers3@gmail.com>
Thu, 22 May 2014 03:21:20 +0000 (22:21 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 22 May 2014 03:55:23 +0000 (22:55 -0500)
This moves the UNIX data into reserved fields of the WIM dentry.

This is theoretically less extensible than the previous format, which was
to add a specially-named alternate data stream entry to each file with
UNIX data.  However, having the UNIX data present in the metadata
resource is simpler and avoids problems when doing sequential extraction.

For now, this also seems to maintain compatibility with the MS
implementation, since it seems simply ignore the reserve fields.

Also, use 32-bit uids, gids, and modes.

13 files changed:
NEWS
doc/man1/imagex-capture.1.in
include/wimlib.h
include/wimlib/inode.h
include/wimlib/unix_data.h
programs/imagex.c
src/dentry.c
src/extract.c
src/inode.c
src/iterate_dir.c
src/mount_image.c
src/unix_apply.c
src/unix_capture.c

diff --git a/NEWS b/NEWS
index 26dad0f..3a79d47 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,11 @@ Version 1.6.3-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.
+
        Removed the --hardlink and --symlink options to wimapply, since I don't
        think they are too useful and they got in the way of improving the code.
 
index 9f1e6a6..073e1ca 100644 (file)
@@ -347,8 +347,7 @@ files.  This is done by adding a special alternate data stream to each directory
 entry that contains this information.  Please note that this flag is for
 convenience only, in case you want to use \fB@IMAGEX_PROGNAME@\fR to archive
 files on UNIX.  Microsoft's software will not understand this special
-information.  You also may run into problems when applying an image with UNIX
-data from a pipable WIM.
+information.
 .TP
 \fB--no-acls\fR
 Do not capture files' security descriptors.
@@ -580,7 +579,9 @@ pointing out that Windows' own default filesystem, NTFS, supports these
 characters, although Windows does not!)
 .IP \[bu]
 WIMs captured with \fB--unix-data\fR should be assumed to be incompatible with
-Microsoft's software.
+Microsoft's software.  However, the UNIX data format used in wimlib v1.6.3 and
+later uses reserved fields in the WIM dentries which seem to be ignored by the
+Microsoft implementation as of Windows 8.1.
 .IP \[bu]
 Pipable WIMs are incompatible with Microsoft's software.  Pipable WIMs are
 created only if \fIWIMFILE\fR was specified as "-" (standard output) or if
index 0ac092f..a958bd0 100644 (file)
@@ -1208,7 +1208,14 @@ struct wimlib_dir_entry {
 
        /** Time this file was last accessed.  */
        struct timespec last_access_time;
-       uint64_t reserved[16];
+
+       /* UNIX data (wimlib extension), only valid if unix_mode != 0  */
+       uint32_t unix_uid;
+       uint32_t unix_gid;
+       uint32_t unix_mode;
+       uint32_t unix_reserved;
+
+       uint64_t reserved[14];
 
        /** Array of streams that make up this file.  The first entry will
         * always exist and will correspond to the unnamed data stream (default
index c787a9d..9a94ce2 100644 (file)
@@ -142,6 +142,9 @@ struct wim_inode {
         * wim_dentry_on_disk'.  */
        u64 i_ino;
 
+       /* UNIX data (wimlib extension)  */
+       struct wimlib_unix_data i_unix_data;
+
        union {
                /* Device number, used only during image capture, so we can
                 * identify hard linked files by the combination of inode number
@@ -336,18 +339,15 @@ inode_remove_ads(struct wim_inode *inode, u16 idx,
                 struct wim_lookup_table *lookup_table);
 
 static inline bool
-ads_entry_is_unix_data(const struct wim_ads_entry *entry)
+ads_entry_is_named_stream(const struct wim_ads_entry *entry)
 {
-       return (entry->stream_name_nbytes ==
-                       WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES) &&
-               !memcmp(entry->stream_name, WIMLIB_UNIX_DATA_TAG_UTF16LE,
-                       WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES);
+       return entry->stream_name_nbytes != 0;
 }
 
 static inline bool
-ads_entry_is_named_stream(const struct wim_ads_entry *entry)
+inode_has_unix_data(const struct wim_inode *inode)
 {
-       return entry->stream_name_nbytes != 0 && !ads_entry_is_unix_data(entry);
+       return inode->i_unix_data.mode != 0;
 }
 
 /* Is the inode a directory?
index bf0ee46..186214b 100644 (file)
@@ -2,44 +2,19 @@
 #define _WIMLIB_UNIX_DATA_H
 
 #include "wimlib/types.h"
-struct wim_inode;
-struct wim_lookup_table;
 
-#define WIMLIB_UNIX_DATA_TAG "$$__wimlib_UNIX_data"
-#define WIMLIB_UNIX_DATA_TAG_NBYTES (sizeof(WIMLIB_UNIX_DATA_TAG) - 1)
-
-#define WIMLIB_UNIX_DATA_TAG_UTF16LE "$\0$\0_\0_\0w\0i\0m\0l\0i\0b\0_\0U\0N\0I\0X\0_\0d\0a\0t\0a\0"
-#define WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES (sizeof(WIMLIB_UNIX_DATA_TAG_UTF16LE) - 1)
-
-extern bool
-inode_has_unix_data(const struct wim_inode *inode);
-
-#ifndef __WIN32__
-/* Format for special alternate data stream entries to store UNIX data for files
- * and directories (see: WIMLIB_ADD_FLAG_UNIX_DATA) */
 struct wimlib_unix_data {
-       u16 version; /* Must be 0 */
-       u16 uid;
-       u16 gid;
-       u16 mode;
-} _packed_attribute;
-
-#define NO_UNIX_DATA (-1)
-#define BAD_UNIX_DATA (-2)
-extern int
-inode_get_unix_data(const struct wim_inode *inode,
-                   struct wimlib_unix_data *unix_data,
-                   u16 *stream_idx_ret);
-
-#define UNIX_DATA_UID    0x1
-#define UNIX_DATA_GID    0x2
-#define UNIX_DATA_MODE   0x4
-#define UNIX_DATA_ALL    (UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE)
-#define UNIX_DATA_CREATE 0x8
-extern int
-inode_set_unix_data(struct wim_inode *inode, u16 uid, u16 gid, u16 mode,
-                   struct wim_lookup_table *lookup_table, int which);
-
-#endif /* __WIN32__  */
+       u32 uid;
+       u32 gid;
+       u32 mode;
+       u32 reserved;
+};
+
+struct wimlib_unix_data_disk {
+       le32 uid;
+       le32 gid;
+       le32 mode;
+       le32 reserved;
+};
 
 #endif /* _WIMLIB_UNIX_DATA_H  */
index 3501f4c..7cb2989 100644 (file)
@@ -2427,6 +2427,11 @@ print_dentry_detailed(const struct wimlib_dir_entry *dentry)
        tprintf(T("Link Group ID       = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
        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);
+       }
+
        for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
                if (dentry->streams[i].stream_name) {
                        tprintf(T("\tData stream \"%"TS"\":\n"),
index 5435a64..42bc05e 100644 (file)
@@ -81,9 +81,14 @@ struct wim_dentry_on_disk {
        le64 subdir_offset;
 
        /* Reserved fields */
-       le64 unused_1;
-       le64 unused_2;
-
+       /* As an extension, wimlib can store UNIX data here.  */
+       union {
+               struct {
+                       le64 unused_1;
+                       le64 unused_2;
+               };
+               struct wimlib_unix_data_disk unix_data;
+       };
 
        /* Creation time, last access time, and last write time, in
         * 100-nanosecond intervals since 12:00 a.m UTC January 1, 1601.  They
@@ -1207,6 +1212,12 @@ read_dentry(const u8 * restrict buf, size_t buf_len,
        inode->i_attributes = le32_to_cpu(disk_dentry->attributes);
        inode->i_security_id = le32_to_cpu(disk_dentry->security_id);
        dentry->subdir_offset = le64_to_cpu(disk_dentry->subdir_offset);
+
+       inode->i_unix_data.uid = le32_to_cpu(disk_dentry->unix_data.uid);
+       inode->i_unix_data.gid = le32_to_cpu(disk_dentry->unix_data.gid);
+       inode->i_unix_data.mode = le32_to_cpu(disk_dentry->unix_data.mode);
+       inode->i_unix_data.reserved = le32_to_cpu(disk_dentry->unix_data.reserved);
+
        inode->i_creation_time = le64_to_cpu(disk_dentry->creation_time);
        inode->i_last_access_time = le64_to_cpu(disk_dentry->last_access_time);
        inode->i_last_write_time = le64_to_cpu(disk_dentry->last_write_time);
@@ -1555,8 +1566,14 @@ write_dentry(const struct wim_dentry * restrict dentry, u8 * restrict p)
        disk_dentry->attributes = cpu_to_le32(inode->i_attributes);
        disk_dentry->security_id = cpu_to_le32(inode->i_security_id);
        disk_dentry->subdir_offset = cpu_to_le64(dentry->subdir_offset);
-       disk_dentry->unused_1 = cpu_to_le64(0);
-       disk_dentry->unused_2 = cpu_to_le64(0);
+
+       /* UNIX data uses the two 8-byte reserved fields.  So if no UNIX data
+        * exists, they get set to 0, just as we would do anyway.  */
+       disk_dentry->unix_data.uid = cpu_to_le32(inode->i_unix_data.uid);
+       disk_dentry->unix_data.gid = cpu_to_le32(inode->i_unix_data.gid);
+       disk_dentry->unix_data.mode = cpu_to_le32(inode->i_unix_data.mode);
+       disk_dentry->unix_data.reserved = cpu_to_le32(inode->i_unix_data.reserved);
+
        disk_dentry->creation_time = cpu_to_le64(inode->i_creation_time);
        disk_dentry->last_access_time = cpu_to_le64(inode->i_last_access_time);
        disk_dentry->last_write_time = cpu_to_le64(inode->i_last_write_time);
index 7b28dac..05c3d16 100644 (file)
@@ -73,6 +73,7 @@
 /* Keep in sync with wimlib.h  */
 #define WIMLIB_EXTRACT_MASK_PUBLIC                             \
        (WIMLIB_EXTRACT_FLAG_NTFS                       |       \
+        WIMLIB_EXTRACT_FLAG_UNIX_DATA                  |       \
         WIMLIB_EXTRACT_FLAG_NO_ACLS                    |       \
         WIMLIB_EXTRACT_FLAG_STRICT_ACLS                |       \
         WIMLIB_EXTRACT_FLAG_RPFIX                      |       \
index a8214b3..9eab0d7 100644 (file)
@@ -326,87 +326,6 @@ inode_remove_ads(struct wim_inode *inode, u16 idx,
        inode->i_num_ads--;
 }
 
-bool
-inode_has_unix_data(const struct wim_inode *inode)
-{
-       for (u16 i = 0; i < inode->i_num_ads; i++)
-               if (ads_entry_is_unix_data(&inode->i_ads_entries[i]))
-                       return true;
-       return false;
-}
-
-#ifndef __WIN32__
-int
-inode_get_unix_data(const struct wim_inode *inode,
-                   struct wimlib_unix_data *unix_data,
-                   u16 *stream_idx_ret)
-{
-       const struct wim_ads_entry *ads_entry;
-       const struct wim_lookup_table_entry *lte;
-       size_t size;
-       int ret;
-
-       wimlib_assert(inode->i_resolved);
-
-       ads_entry = inode_get_ads_entry((struct wim_inode*)inode,
-                                       WIMLIB_UNIX_DATA_TAG, NULL);
-       if (ads_entry == NULL)
-               return NO_UNIX_DATA;
-
-       if (stream_idx_ret)
-               *stream_idx_ret = ads_entry - inode->i_ads_entries;
-
-       lte = ads_entry->lte;
-       if (lte == NULL)
-               return NO_UNIX_DATA;
-
-       size = lte->size;
-       if (size != sizeof(struct wimlib_unix_data))
-               return BAD_UNIX_DATA;
-
-       ret = read_full_stream_into_buf(lte, unix_data);
-       if (ret)
-               return ret;
-
-       if (unix_data->version != 0)
-               return BAD_UNIX_DATA;
-       return 0;
-}
-
-int
-inode_set_unix_data(struct wim_inode *inode, u16 uid, u16 gid, u16 mode,
-                   struct wim_lookup_table *lookup_table, int which)
-{
-       struct wimlib_unix_data unix_data;
-       int ret;
-       bool have_good_unix_data = false;
-       bool have_unix_data = false;
-       u16 stream_idx;
-
-       if (!(which & UNIX_DATA_CREATE)) {
-               ret = inode_get_unix_data(inode, &unix_data, &stream_idx);
-               if (ret == 0 || ret == BAD_UNIX_DATA || ret > 0)
-                       have_unix_data = true;
-               if (ret == 0)
-                       have_good_unix_data = true;
-       }
-       unix_data.version = 0;
-       if (which & UNIX_DATA_UID || !have_good_unix_data)
-               unix_data.uid = uid;
-       if (which & UNIX_DATA_GID || !have_good_unix_data)
-               unix_data.gid = gid;
-       if (which & UNIX_DATA_MODE || !have_good_unix_data)
-               unix_data.mode = mode;
-       ret = inode_add_ads_with_data(inode, WIMLIB_UNIX_DATA_TAG,
-                                     &unix_data,
-                                     sizeof(struct wimlib_unix_data),
-                                     lookup_table);
-       if (ret == 0 && have_unix_data)
-               inode_remove_ads(inode, stream_idx, lookup_table);
-       return ret;
-}
-#endif /* __WIN32__  */
-
 /*
  * Resolve an inode's lookup table entries.
  *
index 693245e..dc953c7 100644 (file)
@@ -82,6 +82,11 @@ init_wimlib_dentry(struct wimlib_dir_entry *wdentry, struct wim_dentry *dentry,
        wdentry->last_write_time = wim_timestamp_to_timespec(inode->i_last_write_time);
        wdentry->last_access_time = wim_timestamp_to_timespec(inode->i_last_access_time);
 
+       wdentry->unix_uid = inode->i_unix_data.uid;
+       wdentry->unix_gid = inode->i_unix_data.gid;
+       wdentry->unix_mode = inode->i_unix_data.mode;
+       wdentry->unix_reserved = inode->i_unix_data.reserved;
+
        lte = inode_unnamed_lte(inode, wim->lookup_table);
        if (lte) {
                lte_to_wimlib_resource_entry(lte, &wdentry->streams[0].resource);
index 1578353..06890db 100644 (file)
@@ -341,16 +341,9 @@ 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),
-                                       wimfs_ctx->wim->lookup_table,
-                                       UNIX_DATA_ALL | UNIX_DATA_CREATE))
-               {
-                       free_dentry(new);
-                       return -ENOMEM;
-               }
+               new->d_inode->i_unix_data.uid = fuse_ctx->uid;
+               new->d_inode->i_unix_data.gid = fuse_ctx->gid;
+               new->d_inode->i_unix_data.mode = fuse_mask_mode(mode, fuse_ctx);
        }
        dentry_add_child(parent, new);
        list_add_tail(&new->d_inode->i_list, wimfs_ctx->image_inode_list);
@@ -389,14 +382,20 @@ remove_dentry(struct wim_dentry *dentry,
 }
 
 static mode_t
-inode_default_unix_mode(const struct wim_inode *inode)
+inode_unix_file_type(const struct wim_inode *inode)
 {
        if (inode_is_symlink(inode))
-               return S_IFLNK | 0777;
+               return S_IFLNK;
        else if (inode_is_directory(inode))
-               return S_IFDIR | 0777;
+               return S_IFDIR;
        else
-               return S_IFREG | 0777;
+               return S_IFREG;
+}
+
+static mode_t
+inode_default_unix_mode(const struct wim_inode *inode)
+{
+       return inode_unix_file_type(inode) | 0777;
 }
 
 /* Transfers file attributes from a struct wim_inode to a `stat' buffer.
@@ -412,16 +411,16 @@ inode_to_stbuf(const struct wim_inode *inode,
        const struct wimfs_context *ctx = wimfs_get_context();
 
        memset(stbuf, 0, sizeof(struct stat));
-       stbuf->st_mode = inode_default_unix_mode(inode);
-       stbuf->st_uid = ctx->default_uid;
-       stbuf->st_gid = ctx->default_gid;
-       if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
-               struct wimlib_unix_data unix_data;
-               if (inode_get_unix_data(inode, &unix_data, NULL) == 0) {
-                       stbuf->st_uid = unix_data.uid;
-                       stbuf->st_gid = unix_data.gid;
-                       stbuf->st_mode = unix_data.mode;
-               }
+       if ((ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) &&
+           inode_has_unix_data(inode))
+       {
+               stbuf->st_uid = inode->i_unix_data.uid;
+               stbuf->st_gid = inode->i_unix_data.gid;
+               stbuf->st_mode = inode->i_unix_data.mode;
+       } else {
+               stbuf->st_uid = ctx->default_uid;
+               stbuf->st_gid = ctx->default_gid;
+               stbuf->st_mode = inode_default_unix_mode(inode);
        }
        stbuf->st_ino = (ino_t)inode->i_ino;
        stbuf->st_nlink = inode->i_nlink;
@@ -1609,6 +1608,7 @@ static int
 wimfs_chmod(const char *path, mode_t mask)
 {
        struct wim_dentry *dentry;
+       struct wim_inode *inode;
        struct wimfs_context *ctx = wimfs_get_context();
        int ret;
 
@@ -1620,16 +1620,21 @@ wimfs_chmod(const char *path, mode_t mask)
        if (ret)
                return ret;
 
-       ret = inode_set_unix_data(dentry->d_inode, ctx->default_uid,
-                                 ctx->default_gid, mask,
-                                 ctx->wim->lookup_table, UNIX_DATA_MODE);
-       return ret ? -ENOMEM : 0;
+       inode = dentry->d_inode;
+
+       if (!inode_has_unix_data(inode)) {
+               inode->i_unix_data.uid = ctx->default_uid;
+               inode->i_unix_data.gid = ctx->default_gid;
+       }
+       inode->i_unix_data.mode = mask;
+       return 0;
 }
 
 static int
 wimfs_chown(const char *path, uid_t uid, gid_t gid)
 {
        struct wim_dentry *dentry;
+       struct wim_inode *inode;
        struct wimfs_context *ctx = wimfs_get_context();
        int ret;
 
@@ -1641,11 +1646,13 @@ wimfs_chown(const char *path, uid_t uid, gid_t gid)
        if (ret)
                return ret;
 
-       ret = inode_set_unix_data(dentry->d_inode, uid, gid,
-                                 inode_default_unix_mode(dentry->d_inode),
-                                 ctx->wim->lookup_table,
-                                 UNIX_DATA_UID | UNIX_DATA_GID);
-       return ret ? -ENOMEM : 0;
+       inode = dentry->d_inode;
+
+       if (!inode_has_unix_data(inode))
+               inode->i_unix_data.mode = inode_default_unix_mode(inode);
+       inode->i_unix_data.uid = uid;
+       inode->i_unix_data.gid = gid;
+       return 0;
 }
 
 /* Called when the filesystem is unmounted. */
index c4f5318..bf9c291 100644 (file)
@@ -219,6 +219,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.
  *
@@ -236,6 +256,50 @@ unix_set_metadata(int fd, const struct wim_inode *inode,
        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))
+       {
+               u32 uid = inode->i_unix_data.uid;
+               u32 gid = inode->i_unix_data.gid;
+               u32 mode = inode->i_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);
index c6619b0..281cbcb 100644 (file)
@@ -388,13 +388,9 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
 #endif
        inode->i_resolved = 1;
        if (params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) {
-               ret = inode_set_unix_data(inode, stbuf.st_uid,
-                                         stbuf.st_gid,
-                                         stbuf.st_mode,
-                                         params->lookup_table,
-                                         UNIX_DATA_ALL | UNIX_DATA_CREATE);
-               if (ret)
-                       goto out;
+               inode->i_unix_data.uid = stbuf.st_uid;
+               inode->i_unix_data.gid = stbuf.st_gid;
+               inode->i_unix_data.mode = stbuf.st_mode;
        }
 
        if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {