IMAGEX_PROGNAME" apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
" (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
" [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n"
-" [--no-acls] [--strict-acls]\n"
+" [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
),
[CAPTURE] =
T(
{T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
{T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
{T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
+ {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
+ {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
{NULL, 0, NULL, 0},
};
static const struct option capture_or_append_options[] = {
case IMAGEX_STRICT_ACLS_OPTION:
extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
break;
+ case IMAGEX_NORPFIX_OPTION:
+ extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
+ break;
+ case IMAGEX_RPFIX_OPTION:
+ extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
+ break;
default:
usage(APPLY);
return -1;
struct apply_args *args,
const char *output_path)
{
- char target[4096];
- ssize_t ret = inode_readlink(dentry->d_inode, target,
- sizeof(target), args->w, false);
+ char target[4096 + args->target_realpath_len];
+ char *fixed_target;
+
+ ssize_t ret = inode_readlink(dentry->d_inode,
+ target + args->target_realpath_len,
+ sizeof(target) - args->target_realpath_len - 1,
+ args->w, false);
struct wim_lookup_table_entry *lte;
if (ret <= 0) {
dentry->_full_path);
return WIMLIB_ERR_INVALID_DENTRY;
}
- ret = symlink(target, output_path);
- if (ret != 0) {
+ target[args->target_realpath_len + ret] = '\0';
+ if (target[args->target_realpath_len] == '/' &&
+ args->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
+ {
+ memcpy(target, args->target_realpath,
+ args->target_realpath_len);
+ fixed_target = target;
+ } else {
+ fixed_target = target + args->target_realpath_len;
+ }
+ ret = symlink(fixed_target, output_path);
+ if (ret) {
ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'",
- output_path, target);
+ output_path, fixed_target);
return WIMLIB_ERR_LINK;
}
lte = inode_unnamed_lte_resolved(dentry->d_inode);
ret = 0;
else
ret = symlink_apply_unix_data(output_path, &unix_data);
- if (ret != 0)
+ if (ret)
return ret;
}
args->progress.extract.completed_bytes += wim_resource_size(lte);
struct apply_args args;
const struct apply_operations *ops;
+ tchar *target_realpath;
memset(&args, 0, sizeof(args));
&args.progress);
}
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
+ args.target_realpath = realpath(target, NULL);
+ if (!args.target_realpath)
+ return WIMLIB_ERR_NOMEM;
+ args.target_realpath_len = tstrlen(args.target_realpath);
+ }
+
/* Extract non-empty files */
ret = apply_stream_list(&stream_list, &args, ops, progress_func);
if (ret)
- goto out;
+ goto out_free_target_realpath;
if (progress_func) {
progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
ret = for_dentry_in_tree_depth(wim_root_dentry(w),
ops->apply_dentry_timestamps, &args);
if (ret)
- goto out;
+ goto out_free_target_realpath;
if (progress_func) {
progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
&args.progress);
}
+out_free_target_realpath:
+ FREE(args.target_realpath);
out:
#ifdef WITH_NTFS_3G
/* Unmount the NTFS volume */
#endif
}
+ if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
+ WIMLIB_EXTRACT_FLAG_RPFIX)) ==
+ (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
+ {
+ ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
+ return WIMLIB_ERR_INVALID_PARAM;
+ }
+
+ if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
+ WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
+ if (w->hdr.flags & WIM_HDR_FLAG_RP_FIX)
+ extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
+
ret = verify_swm_set(w, additional_swms, num_additional_swms);
if (ret)
return ret;
size_t link_target_len;
ssize_t ret;
unsigned header_size;
- char *translated_target;
- bool is_absolute;
- u32 flags;
+ bool translate_slashes;
if (resource_len < 12)
return -EIO;
p = get_u16(p, &substitute_name_len);
p = get_u16(p, &print_name_offset);
p = get_u16(p, &print_name_len);
- get_u32(p, &flags);
wimlib_assert(reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
- if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) {
+ if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)
header_size = 8;
- } else {
- is_absolute = (flags & 1) ? false : true;
+ else {
header_size = 12;
p += 4;
}
goto out;
}
- translated_target = link_target;
- if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT || is_absolute) {
- if (link_target_len < 7
- || memcmp(translated_target, "\\??\\", 4) != 0
- || translated_target[4] == '\0'
- || translated_target[5] != ':'
- || translated_target[6] != '\\') {
- ret = -EIO;
- goto out;
- }
- translated_target += 4;
- link_target_len -= 4;
- /* There's a drive letter, so just leave the backslashes since
- * it won't go anyhwere on UNIX anyway...
- *
- * XXX
- * NTFS-3g tries to re-map these links to actually point to
- * something, so maybe we could do something like that here
- * XXX*/
+ DEBUG("Interpeting substitute name \"%s\" (ReparseTag=0x%x)",
+ link_target, reparse_tag);
+ translate_slashes = true;
+ if (link_target_len >= 7 &&
+ link_target[0] == '\\' &&
+ link_target[1] == '?' &&
+ link_target[2] == '?' &&
+ link_target[3] == '\\' &&
+ link_target[4] != '\0' &&
+ link_target[5] == ':' &&
+ link_target[6] == '\\')
+ {
+ /* "Full" symlink or junction (\??\x:\ prefixed path) */
+ link_target += 6;
+ link_target_len -= 6;
+ } else if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT &&
+ link_target_len >= 12 &&
+ memcmp(link_target, "\\\\?\\Volume{", 11) == 0 &&
+ link_target[link_target_len - 1] == '\\')
+ {
+ /* Volume junction. Can't really do anything with it. */
+ translate_slashes = false;
+ } else if (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK &&
+ link_target_len >= 3 &&
+ link_target[0] != '\0' &&
+ link_target[1] == ':' &&
+ link_target[2] == '/')
+ {
+ /* "Absolute" symlink, with drive letter */
+ link_target += 2;
+ link_target_len -= 2;
+ } else if (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK &&
+ link_target_len >= 1)
+ {
+ if (link_target[0] == '/')
+ /* "Absolute" symlink, without drive letter */
+ ;
+ else
+ /* "Relative" symlink, without drive letter */
+ ;
} else {
- for (size_t i = 0; i < link_target_len; i++)
- if (translated_target[i] == '\\')
- translated_target[i] = '/';
+ ERROR("Invalid reparse point: \"%s\"", link_target);
+ ret = -EIO;
+ goto out;
}
- memcpy(buf, translated_target, link_target_len + 1);
+ if (translate_slashes)
+ for (size_t i = 0; i < link_target_len; i++)
+ if (link_target[i] == '\\')
+ link_target[i] = '/';
+ memcpy(buf, link_target, link_target_len + 1);
ret = link_target_len;
out:
FREE(link_target);
u8 res_buf[wim_resource_size(lte)];
ret = read_full_resource_into_buf(lte, res_buf, threadsafe);
- if (ret != 0)
+ if (ret)
return -EIO;
return get_symlink_name(res_buf, wim_resource_size(lte), buf,
buf_len, inode->i_reparse_tag);
/** Reparse-point fixups: Modify absolute symbolic links (or junction points,
* in the case of Windows) that point inside the directory being captured to
* instead be absolute relative to the directory being captured, rather than the
- * current root.
+ * current root; also exclude absolute symbolic links that point outside the
+ * directory tree being captured.
*
- * Without this flag, the default is to do the reparse-point fixups if
- * WIM_HDR_FLAG_RP_FIX is set in the WIM header. */
+ * Without this flag, the default is to do this only if WIM_HDR_FLAG_RP_FIX is
+ * set in the WIM header. WIM_HDR_FLAG_RP_FIX is set if the first image in a
+ * WIM is captured with WIMLIB_ADD_IMAGE_FLAG_RPFIX enabled and currently cannot
+ * be changed. */
#define WIMLIB_ADD_IMAGE_FLAG_RPFIX 0x00000100
/* Don't do reparse point fixups. Without this flag, the default is to do
* not have permission to set the desired one. */
#define WIMLIB_EXTRACT_FLAG_STRICT_ACLS 0x00000080
+/* Extract equivalent to ::WIMLIB_ADD_IMAGE_FLAG_RPFIX; force reparse-point
+ * fixups on, so absolute symbolic links are junction points will be fixed to be
+ * absolute relative to the actual extraction root. Done by default if
+ * WIM_HDR_FLAG_RP_FIX is set in the WIM header. */
+#define WIMLIB_EXTRACT_FLAG_RPFIX 0x00000100
+
+/** Force reparse-point fixups on extraction off, regardless of the state of the
+ * WIM_HDR_FLAG_RP_FIX flag in the WIM header. */
+#define WIMLIB_EXTRACT_FLAG_NORPFIX 0x00000200
+
/******************************
* WIMLIB_MOUNT_FLAG_* *
******************************/
struct apply_args {
WIMStruct *w;
const tchar *target;
+ tchar *target_realpath;
+ unsigned target_realpath_len;
int extract_flags;
union wimlib_progress_info progress;
wimlib_progress_func_t progress_func;
[[ `readlink out.dir/abslinkslashes` != "/file///" ]]; then
error "imagex capture --rpfix did fix absolute link properly"
fi
+rm -rf out.dir
+
+imagex apply test.wim out.dir
+if [[ $(get_inode_number $(readlink out.dir/absrootlink)) != \
+ $(get_inode_number out.dir) ]];
+then
+ error "imagex apply failed to apply fixed absolute symlinks"
+fi
echo "**********************************************************"
echo " imagex capture/apply tests passed "