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.
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.
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
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.
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).
uint32_t unix_uid;
uint32_t unix_gid;
uint32_t unix_mode;
- uint32_t unix_reserved;
+ uint32_t unix_rdev;
uint64_t reserved[14];
* 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
WIMLIB_ERR_WIMBOOT,
WIMLIB_ERR_ABORTED_BY_PROGRESS,
WIMLIB_ERR_UNKNOWN_PROGRESS_STATUS,
+ WIMLIB_ERR_MKNOD,
};
u32 uid;
u32 gid;
u32 mode;
+ u32 rdev;
};
struct wim_inode;
#define UNIX_DATA_UID 0x1
#define UNIX_DATA_GID 0x2
#define UNIX_DATA_MODE 0x4
+#define UNIX_DATA_RDEV 0x8
-#define UNIX_DATA_ALL (UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE)
+#define UNIX_DATA_ALL 0xF
extern bool
-inode_set_unix_data(struct wim_inode *inode, u32 uid, u32 gid, u32 mode,
- int which);
+inode_set_unix_data(struct wim_inode *inode,
+ struct wimlib_unix_data *unix_data, int which);
#endif /* _WIMLIB_UNIX_DATA_H */
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++) {
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);
*/
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;
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);
stbuf->st_uid = unix_data.uid;
stbuf->st_gid = unix_data.gid;
stbuf->st_mode = unix_data.mode;
+ stbuf->st_rdev = unix_data.rdev;
} else {
stbuf->st_uid = ctx->default_uid;
stbuf->st_gid = ctx->default_gid;
stbuf->st_mode = inode_default_unix_mode(inode);
+ stbuf->st_rdev = 0;
}
stbuf->st_ino = (ino_t)inode->i_ino;
stbuf->st_nlink = inode->i_nlink;
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))
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;
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))
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;
static int
wimfs_mkdir(const char *path, mode_t mode)
{
- return create_dentry(fuse_get_context(), path, mode | S_IFDIR,
+ return create_dentry(fuse_get_context(), path, mode | S_IFDIR, 0,
FILE_ATTRIBUTE_DIRECTORY, NULL);
}
-/* Create a regular file or alternate data stream in the WIM image. */
+/* Create a non-directory, non-symbolic-link file or alternate data stream in
+ * the WIM image. */
static int
wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
{
struct fuse_context *fuse_ctx = fuse_get_context();
struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
- if (!S_ISREG(mode))
- return -EPERM;
-
if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
&& (stream_name = path_stream_name(path))) {
+
+ if (!S_ISREG(mode))
+ return -EPERM;
+
/* Make an alternate data stream */
struct wim_ads_entry *new_entry;
struct wim_inode *inode;
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);
}
}
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;
le32 uid;
le32 gid;
le32 mode;
- le32 reserved;
+ le32 rdev;
};
/* Retrieves the first tagged item with the specified tag and minimum length
unix_data->uid = le32_to_cpu(p->uid);
unix_data->gid = le32_to_cpu(p->gid);
unix_data->mode = le32_to_cpu(p->mode);
+ unix_data->rdev = le32_to_cpu(p->rdev);
return true;
}
/* Sets UNIX data on the specified WIM inode.
* This is a wimlib extension.
*
- * Callers must specify all of @uid, @gid, and @mode. If the inode does not yet
+ * Callers must specify all members in @unix_data. If the inode does not yet
* have UNIX data, it is given these values. Otherwise, only the values that
* also have the corresponding flags in @which set are changed.
*
* Returns %true if successful, %false if failed (out of memory). */
bool
-inode_set_unix_data(struct wim_inode *inode, u32 uid, u32 gid, u32 mode,
+inode_set_unix_data(struct wim_inode *inode, struct wimlib_unix_data *unix_data,
int which)
{
struct wimlib_unix_data_disk *p;
p = inode_add_unix_data_disk(inode);
if (!p)
return false;
- p->reserved = cpu_to_le32(0);
- which = UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE;
+ which = UNIX_DATA_ALL;
}
if (which & UNIX_DATA_UID)
- p->uid = cpu_to_le32(uid);
+ p->uid = cpu_to_le32(unix_data->uid);
if (which & UNIX_DATA_GID)
- p->gid = cpu_to_le32(gid);
+ p->gid = cpu_to_le32(unix_data->gid);
if (which & UNIX_DATA_MODE)
- p->mode = cpu_to_le32(mode);
+ p->mode = cpu_to_le32(unix_data->mode);
+ if (which & UNIX_DATA_RDEV)
+ p->rdev = cpu_to_le32(unix_data->rdev);
return true;
}
/* 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
return 0;
}
-/* If @dentry represents an empty regular file, create it, set its metadata, and
- * create any needed hard links. */
+/* If @dentry represents an empty regular file or a special file, create it, set
+ * its metadata, and create any needed hard links. */
static int
unix_extract_if_empty_file(const struct wim_dentry *dentry,
struct unix_apply_ctx *ctx)
{
const struct wim_inode *inode;
+ struct wimlib_unix_data unix_data;
const char *path;
- int fd;
int ret;
inode = dentry->d_inode;
if (dentry != inode_first_extraction_dentry(inode))
return 0;
- /* Not an empty regular file? */
+ /* Is this a directory, a symbolic link, or any type of nonempty file?
+ */
if (inode_is_directory(inode) || inode_is_symlink(inode) ||
inode_unnamed_lte_resolved(inode))
return 0;
- path = unix_build_extraction_path(dentry, ctx);
-retry_create:
- fd = open(path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
- if (fd < 0) {
- if (errno == EEXIST && !unlink(path))
- goto retry_create;
- ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path);
- return WIMLIB_ERR_OPEN;
- }
- /* On empty files, we can set timestamps immediately because we don't
- * need to write any data to them. */
- ret = unix_set_metadata(fd, inode, path, ctx);
- if (close(fd) && !ret) {
- ERROR_WITH_ERRNO("Error closing \"%s\"", path);
- ret = WIMLIB_ERR_WRITE;
+ /* Recognize special files in UNIX_DATA mode */
+ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
+ inode_get_unix_data(inode, &unix_data) &&
+ !S_ISREG(unix_data.mode))
+ {
+ path = unix_build_extraction_path(dentry, ctx);
+ retry_mknod:
+ if (mknod(path, unix_data.mode, unix_data.rdev)) {
+ if (errno == EPERM) {
+ WARNING_WITH_ERRNO("Can't create special "
+ "file \"%s\"", path);
+ ctx->num_special_files_ignored++;
+ return 0;
+ }
+ if (errno == EEXIST && !unlink(path))
+ goto retry_mknod;
+ ERROR_WITH_ERRNO("Can't create special file \"%s\"",
+ path);
+ return WIMLIB_ERR_MKNOD;
+ }
+ /* On special files, we can set timestamps immediately because
+ * we don't need to write any data to them. */
+ ret = unix_set_metadata(-1, inode, path, ctx);
+ } else {
+ int fd;
+
+ path = unix_build_extraction_path(dentry, ctx);
+ retry_create:
+ fd = open(path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
+ if (fd < 0) {
+ if (errno == EEXIST && !unlink(path))
+ goto retry_create;
+ ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path);
+ return WIMLIB_ERR_OPEN;
+ }
+ /* On empty files, we can set timestamps immediately because we
+ * don't need to write any data to them. */
+ ret = unix_set_metadata(fd, inode, path, ctx);
+ if (close(fd) && !ret) {
+ ERROR_WITH_ERRNO("Error closing \"%s\"", path);
+ ret = WIMLIB_ERR_WRITE;
+ }
}
if (ret)
return ret;
/* 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]);
goto out;
}
- if (unlikely(!S_ISREG(stbuf.st_mode) &&
- !S_ISDIR(stbuf.st_mode) &&
- !S_ISLNK(stbuf.st_mode)))
- {
- if (params->add_flags & WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
+ if (!(params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA)) {
+ if (unlikely(!S_ISREG(stbuf.st_mode) &&
+ !S_ISDIR(stbuf.st_mode) &&
+ !S_ISLNK(stbuf.st_mode)))
{
- ERROR("\"%s\": File type is unsupported", full_path);
- ret = WIMLIB_ERR_UNSUPPORTED_FILE;
+ if (params->add_flags &
+ WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
+ {
+ ERROR("\"%s\": File type is unsupported",
+ full_path);
+ ret = WIMLIB_ERR_UNSUPPORTED_FILE;
+ goto out;
+ }
+ params->progress.scan.cur_path = full_path;
+ ret = do_capture_progress(params,
+ WIMLIB_SCAN_DENTRY_UNSUPPORTED,
+ NULL);
goto out;
}
- params->progress.scan.cur_path = full_path;
- ret = do_capture_progress(params, WIMLIB_SCAN_DENTRY_UNSUPPORTED, NULL);
- goto out;
}
ret = inode_table_new_dentry(params->inode_table, relpath,
#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;
}
} 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)
= 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 */