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.
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.
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.
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
/** 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
* 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
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?
#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 */
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"),
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
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);
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);
/* 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 | \
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.
*
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);
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);
}
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.
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;
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;
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;
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. */
}
}
+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.
*
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);
#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) {