From 71d3401258fb14dcfc921f53452ba972e27e8b8b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 21 May 2014 22:21:20 -0500 Subject: [PATCH] New format for UNIX data 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. --- NEWS | 5 +++ doc/man1/imagex-capture.1.in | 7 ++-- include/wimlib.h | 9 +++- include/wimlib/inode.h | 14 +++---- include/wimlib/unix_data.h | 49 ++++++---------------- programs/imagex.c | 5 +++ src/dentry.c | 27 +++++++++--- src/extract.c | 1 + src/inode.c | 81 ------------------------------------ src/iterate_dir.c | 5 +++ src/mount_image.c | 73 +++++++++++++++++--------------- src/unix_apply.c | 64 ++++++++++++++++++++++++++++ src/unix_capture.c | 10 ++--- 13 files changed, 176 insertions(+), 174 deletions(-) diff --git a/NEWS b/NEWS index 26dad0f3..3a79d47f 100644 --- 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. diff --git a/doc/man1/imagex-capture.1.in b/doc/man1/imagex-capture.1.in index 9f1e6a68..073e1cab 100644 --- a/doc/man1/imagex-capture.1.in +++ b/doc/man1/imagex-capture.1.in @@ -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 diff --git a/include/wimlib.h b/include/wimlib.h index 0ac092fd..a958bd0a 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -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 diff --git a/include/wimlib/inode.h b/include/wimlib/inode.h index c787a9d2..9a94ce2f 100644 --- a/include/wimlib/inode.h +++ b/include/wimlib/inode.h @@ -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? diff --git a/include/wimlib/unix_data.h b/include/wimlib/unix_data.h index bf0ee46c..186214b9 100644 --- a/include/wimlib/unix_data.h +++ b/include/wimlib/unix_data.h @@ -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 */ diff --git a/programs/imagex.c b/programs/imagex.c index 3501f4c8..7cb29890 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -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"), diff --git a/src/dentry.c b/src/dentry.c index 5435a648..42bc05ea 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -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); diff --git a/src/extract.c b/src/extract.c index 7b28dac7..05c3d16b 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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 | \ diff --git a/src/inode.c b/src/inode.c index a8214b3c..9eab0d7b 100644 --- a/src/inode.c +++ b/src/inode.c @@ -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. * diff --git a/src/iterate_dir.c b/src/iterate_dir.c index 693245e5..dc953c75 100644 --- a/src/iterate_dir.c +++ b/src/iterate_dir.c @@ -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); diff --git a/src/mount_image.c b/src/mount_image.c index 1578353f..06890dbc 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -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. */ diff --git a/src/unix_apply.c b/src/unix_apply.c index c4f5318b..bf9c2911 100644 --- a/src/unix_apply.c +++ b/src/unix_apply.c @@ -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); diff --git a/src/unix_capture.c b/src/unix_capture.c index c6619b00..281cbcb7 100644 --- a/src/unix_capture.c +++ b/src/unix_capture.c @@ -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) { -- 2.43.0