From 337df3674d64fff382890d1e8757dadf57d93b1f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 21 Apr 2013 21:43:23 -0500 Subject: [PATCH] rpfix extract on UNIX --- programs/imagex.c | 10 +++- src/extract_image.c | 55 +++++++++++++++---- src/symlink.c | 83 ++++++++++++++++++----------- src/wimlib.h | 19 +++++-- src/wimlib_internal.h | 2 + tests/test-imagex-capture_and_apply | 8 +++ 6 files changed, 133 insertions(+), 44 deletions(-) diff --git a/programs/imagex.c b/programs/imagex.c index afaf1102..c1f4217e 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -96,7 +96,7 @@ T( 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( @@ -202,6 +202,8 @@ static const struct option apply_options[] = { {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[] = { @@ -1184,6 +1186,12 @@ imagex_apply(int argc, tchar **argv) 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; diff --git a/src/extract_image.c b/src/extract_image.c index fffdcfd1..48a916ab 100644 --- a/src/extract_image.c +++ b/src/extract_image.c @@ -324,9 +324,13 @@ extract_symlink(struct wim_dentry *dentry, 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) { @@ -334,10 +338,20 @@ extract_symlink(struct wim_dentry *dentry, 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); @@ -351,7 +365,7 @@ extract_symlink(struct wim_dentry *dentry, 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); @@ -782,6 +796,7 @@ extract_single_image(WIMStruct *w, int image, struct apply_args args; const struct apply_operations *ops; + tchar *target_realpath; memset(&args, 0, sizeof(args)); @@ -863,10 +878,17 @@ extract_single_image(WIMStruct *w, int image, &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, @@ -877,12 +899,14 @@ extract_single_image(WIMStruct *w, int image, 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 */ @@ -1011,6 +1035,19 @@ wimlib_extract_image(WIMStruct *w, #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; diff --git a/src/symlink.c b/src/symlink.c index 9eaf6b4b..dc365e8c 100644 --- a/src/symlink.c +++ b/src/symlink.c @@ -57,9 +57,7 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, 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; @@ -67,15 +65,13 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, 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; } @@ -93,32 +89,57 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, 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); @@ -187,7 +208,7 @@ inode_readlink(const struct wim_inode *inode, char *buf, size_t buf_len, 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); diff --git a/src/wimlib.h b/src/wimlib.h index 479fa241..e7f89faa 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -697,10 +697,13 @@ struct wimlib_capture_config { /** 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 @@ -754,6 +757,16 @@ struct wimlib_capture_config { * 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_* * ******************************/ diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 10784571..1d3edcd7 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -540,6 +540,8 @@ write_metadata_resource(WIMStruct *w); 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; diff --git a/tests/test-imagex-capture_and_apply b/tests/test-imagex-capture_and_apply index d4577517..dfa240f8 100755 --- a/tests/test-imagex-capture_and_apply +++ b/tests/test-imagex-capture_and_apply @@ -142,6 +142,14 @@ if [[ `readlink out.dir/abslink` != "/file" ]] || [[ `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 " -- 2.43.0