]> wimlib.net Git - wimlib/blobdiff - src/extract_image.c
refactor reparse point code; Win32: working extract rpfix
[wimlib] / src / extract_image.c
index b18270dfb899ef1a5bc702c908930ebed23d807a..0d83a182c6d042fa2ffec32cf898a0ec10b58bfa 100644 (file)
@@ -111,10 +111,8 @@ extract_regular_file_linked(struct wim_dentry *dentry,
                const char *p2;
                size_t i;
 
-               num_path_components =
-                       get_num_path_components(dentry_full_path(dentry)) - 1;
-               num_output_dir_path_components =
-                       get_num_path_components(args->target);
+               num_path_components = get_num_path_components(dentry->_full_path) - 1;
+               num_output_dir_path_components = get_num_path_components(args->target);
 
                if (args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) {
                        num_path_components++;
@@ -283,7 +281,7 @@ out_extract_unix_data:
                        ret = 0;
                else
                        ret = fd_apply_unix_data(out_fd, &unix_data);
-               if (ret != 0)
+               if (ret)
                        goto out;
        }
        if (lte)
@@ -326,36 +324,53 @@ 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;
+       const struct wim_inode *inode = dentry->d_inode;
+
+       ssize_t ret = wim_inode_readlink(inode,
+                                        target + args->target_realpath_len,
+                                        sizeof(target) - args->target_realpath_len - 1);
        struct wim_lookup_table_entry *lte;
 
        if (ret <= 0) {
                ERROR("Could not read the symbolic link from dentry `%s'",
-                     dentry_full_path(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)
+       {
+               /* Fix absolute symbolic link target to point into the actual
+                * extraction destination */
+               memcpy(target, args->target_realpath,
+                      args->target_realpath_len);
+               fixed_target = target;
+       } else {
+               /* Keep same link target */
+               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);
-       wimlib_assert(lte != NULL);
        if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
                struct wimlib_unix_data unix_data;
-               ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL);
+               ret = inode_get_unix_data(inode, &unix_data, NULL);
                if (ret > 0)
                        ;
                else if (ret < 0)
                        ret = 0;
                else
                        ret = symlink_apply_unix_data(output_path, &unix_data);
-               if (ret != 0)
+               if (ret)
                        return ret;
        }
+       lte = inode_unnamed_lte_resolved(inode);
+       wimlib_assert(lte != NULL);
        args->progress.extract.completed_bytes += wim_resource_size(lte);
        return 0;
 }
@@ -409,10 +424,9 @@ dir_exists:
 }
 
 #ifndef __WIN32__
-static int unix_do_apply_dentry(const char *output_path,
-                               size_t output_path_len,
-                               struct wim_dentry *dentry,
-                               struct apply_args *args)
+static int
+unix_do_apply_dentry(const char *output_path, size_t output_path_len,
+                    struct wim_dentry *dentry, struct apply_args *args)
 {
        const struct wim_inode *inode = dentry->d_inode;
 
@@ -492,15 +506,13 @@ static int
 apply_dentry_normal(struct wim_dentry *dentry, void *arg)
 {
        struct apply_args *args = arg;
-       tchar *output_path;
        size_t len;
+       tchar *output_path;
 
        len = tstrlen(args->target);
        if (dentry_is_root(dentry)) {
                output_path = (tchar*)args->target;
        } else {
-               if (!dentry_full_path(dentry))
-                       return WIMLIB_ERR_NOMEM;
                output_path = alloca(len * sizeof(tchar) + dentry->full_path_nbytes +
                                     sizeof(tchar));
                memcpy(output_path, args->target, len * sizeof(tchar));
@@ -528,8 +540,6 @@ apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
        if (dentry_is_root(dentry)) {
                output_path = (tchar*)args->target;
        } else {
-               if (!dentry_full_path(dentry))
-                       return WIMLIB_ERR_NOMEM;
                output_path = alloca(len * sizeof(tchar) + dentry->full_path_nbytes +
                                     sizeof(tchar));
                memcpy(output_path, args->target, len * sizeof(tchar));
@@ -538,7 +548,6 @@ apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
                output_path[len] = T('\0');
        }
 
-
 #ifdef __WIN32__
        return win32_do_apply_dentry_timestamps(output_path, len, dentry, args);
 #else
@@ -557,16 +566,13 @@ maybe_apply_dentry(struct wim_dentry *dentry, void *arg)
        if (dentry->is_extracted)
                return 0;
 
-       if (!dentry_full_path(dentry))
-               return WIMLIB_ERR_NOMEM;
-
        if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS)
                if (inode_unnamed_lte_resolved(dentry->d_inode))
                        return 0;
 
        if ((args->extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
             args->progress_func) {
-               args->progress.extract.cur_path = dentry_full_path(dentry);
+               args->progress.extract.cur_path = dentry->_full_path;
                args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
                                    &args->progress);
        }
@@ -632,8 +638,21 @@ inode_find_streams_for_extraction(struct wim_inode *inode,
                list_add_tail(&inode->i_lte_inode_list, &lte->inode_list);
                inode_added = true;
        }
-#ifdef WITH_NTFS_3G
-       if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
+
+       /* Determine whether to include alternate data stream entries or not.
+        *
+        * UNIX:  Include them if extracting using NTFS-3g.
+        *
+        * Windows: Include them undconditionally, although if the filesystem is
+        * not NTFS we won't actually be able to extract them. */
+#if defined(WITH_NTFS_3G)
+       if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
+#elif defined(__WIN32__)
+       if (1)
+#else
+       if (0)
+#endif
+       {
                for (unsigned i = 0; i < inode->i_num_ads; i++) {
                        if (inode->i_ads_entries[i].stream_name_nbytes != 0) {
                                lte = inode->i_ads_entries[i].lte;
@@ -649,7 +668,6 @@ inode_find_streams_for_extraction(struct wim_inode *inode,
                        }
                }
        }
-#endif
 }
 
 static void
@@ -720,7 +738,7 @@ apply_stream_list(struct list_head *stream_list,
                                /* Extract the dentry if it was not already
                                 * extracted */
                                ret = maybe_apply_dentry(dentry, args);
-                               if (ret != 0)
+                               if (ret)
                                        return ret;
                                if (progress_func &&
                                    args->progress.extract.completed_bytes >= next_progress)
@@ -855,12 +873,16 @@ extract_single_image(WIMStruct *w, int image,
                              &args.progress);
        }
 
+       ret = calculate_dentry_tree_full_paths(wim_root_dentry(w));
+       if (ret)
+               goto out;
+
        /* Make the directory structure and extract empty files */
        args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
        args.apply_dentry = ops->apply_dentry;
        ret = for_dentry_in_tree(wim_root_dentry(w), maybe_apply_dentry, &args);
        args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
-       if (ret != 0)
+       if (ret)
                goto out;
 
        if (progress_func) {
@@ -868,10 +890,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 != 0)
-               goto out;
+       if (ret)
+               goto out_free_target_realpath;
 
        if (progress_func) {
                progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
@@ -881,13 +910,15 @@ extract_single_image(WIMStruct *w, int image,
        /* Apply timestamps */
        ret = for_dentry_in_tree_depth(wim_root_dentry(w),
                                       ops->apply_dentry_timestamps, &args);
-       if (ret != 0)
-               goto out;
+       if (ret)
+               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 */
@@ -903,7 +934,7 @@ out:
        return ret;
 }
 
-static const tchar *filename_forbidden_chars =
+static const tchar * const filename_forbidden_chars =
 T(
 #ifdef __WIN32__
 "<>:\"/\\|?*"
@@ -1016,6 +1047,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;