+ {
+ struct timespec times[2];
+
+ times[0] = wim_timestamp_to_timespec(atime);
+ times[1] = wim_timestamp_to_timespec(mtime);
+
+ errno = ENOSYS;
+#ifdef HAVE_FUTIMENS
+ if (fd >= 0 && !futimens(fd, times))
+ return 0;
+#endif
+#ifdef HAVE_UTIMENSAT
+ if (fd < 0 && !utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW))
+ return 0;
+#endif
+ if (errno != ENOSYS)
+ return WIMLIB_ERR_SET_TIMESTAMPS;
+ }
+ {
+ struct timeval times[2];
+
+ times[0] = wim_timestamp_to_timeval(atime);
+ times[1] = wim_timestamp_to_timeval(mtime);
+
+ if (fd >= 0 && !futimes(fd, times))
+ return 0;
+ if (fd < 0 && !lutimes(path, times))
+ return 0;
+ return WIMLIB_ERR_SET_TIMESTAMPS;
+ }
+}
+
+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.
+ *
+ * @fd is an open file descriptor to the extracted file, or -1. @path is the
+ * path to the extracted file, or NULL. If valid, this function uses @fd.
+ * Otherwise, if valid, it uses @path. Otherwise, it calculates the path to one
+ * alias of the extracted file and uses it.
+ */
+static int
+unix_set_metadata(int fd, const struct wim_inode *inode,
+ const char *path, struct unix_apply_ctx *ctx)
+{
+ int ret;
+ struct wimlib_unix_data unix_data;
+
+ if (fd < 0 && !path)
+ path = unix_build_inode_extraction_path(inode, ctx);
+
+ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)
+ && inode_get_unix_data(inode, &unix_data))
+ {
+ u32 uid = unix_data.uid;
+ u32 gid = unix_data.gid;
+ u32 mode = 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);
+ if (ret) {
+ if (!path)
+ path = unix_build_inode_extraction_path(inode, ctx);
+ if (ctx->common.extract_flags &
+ WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS)
+ {
+ ERROR_WITH_ERRNO("Can't set timestamps on \"%s\"", path);
+ return ret;
+ } else {
+ WARNING_WITH_ERRNO("Can't set timestamps on \"%s\"", path);
+ }
+ }