* extract.c
*
* Support for extracting WIM files.
- *
+ */
+
+/*
* Copyright (C) 2010 Carl Thijssen
* Copyright (C) 2012 Eric Biggers
*
- * wimlib - Library for working with WIM files
+ * This file is part of wimlib, a library for working with WIM files.
*
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option) any
- * later version.
+ * wimlib is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
*
- * This library is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
*
- * You should have received a copy of the GNU Lesser General Public License along
- * with this library; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
*/
#include "wimlib_internal.h"
#include <string.h>
#include <errno.h>
+#ifdef WITH_NTFS_3G
+#include <ntfs-3g/volume.h>
+#include <ntfs-3g/security.h>
+#endif
+
+/* Sets and creates the directory to which files are to be extracted when
+ * extracting files from the WIM. */
+static int set_output_dir(WIMStruct *w, const char *dir)
+{
+ char *p;
+ DEBUG("Setting output directory to `%s'", dir);
+
+ p = STRDUP(dir);
+ if (!p) {
+ ERROR("Out of memory");
+ return WIMLIB_ERR_NOMEM;
+ }
+
+ if (mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
+ if (errno == EEXIST) {
+ DEBUG("`%s' already exists", dir);
+ goto done;
+ }
+ ERROR_WITH_ERRNO("Cannot create directory `%s'", dir);
+ FREE(p);
+ return WIMLIB_ERR_MKDIR;
+ } else {
+ DEBUG("Created directory `%s'", dir);
+ }
+done:
+ FREE(w->output_dir);
+ w->output_dir = p;
+ return 0;
+}
/*
* Extracts a regular file from the WIM archive.
*/
static int extract_regular_file(WIMStruct *w,
const struct dentry *dentry,
- const char *output_path)
+ const char *output_path,
+ int extract_flags)
{
- struct lookup_table *lookup_table;
- int link_type;
- bool is_multi_image_extraction;
struct lookup_table_entry *lte;
int ret;
int out_fd;
const struct resource_entry *res_entry;
- lookup_table = w->lookup_table;
- link_type = w->link_type;
- is_multi_image_extraction = w->is_multi_image_extraction;
- lte = lookup_resource(lookup_table, dentry->hash);
+ lte = __lookup_resource(w->lookup_table, dentry_hash(dentry));
/* If we already extracted the same file or a hard link copy of it, we
* may be able to simply create a link. The exact action is specified
* by the current @link_type. */
- if (link_type != WIM_LINK_TYPE_NONE && lte && lte->out_refcnt != 0) {
+ if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) &&
+ lte && lte->out_refcnt != 0)
+ {
wimlib_assert(lte->file_on_disk);
- if (link_type == WIM_LINK_TYPE_HARD) {
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) {
if (link(lte->file_on_disk, output_path) != 0) {
- ERROR("Failed to hard link `%s' to `%s': %m\n",
- output_path, lte->file_on_disk);
+ ERROR_WITH_ERRNO("Failed to hard link "
+ "`%s' to `%s'",
+ output_path, lte->file_on_disk);
return WIMLIB_ERR_LINK;
}
} else {
num_output_dir_path_components =
get_num_path_components(w->output_dir);
- if (is_multi_image_extraction) {
+ if (w->is_multi_image_extraction) {
num_path_components++;
num_output_dir_path_components--;
}
p2 = path_next_part(p2, NULL);
strcpy(p, p2);
if (symlink(buf, output_path) != 0) {
- ERROR("Failed to symlink `%s' to `%s': %m\n",
- buf, lte->file_on_disk);
+ ERROR_WITH_ERRNO("Failed to symlink `%s' to "
+ "`%s'",
+ buf, lte->file_on_disk);
return WIMLIB_ERR_LINK;
}
out_fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (out_fd == -1) {
- ERROR("Failed to open the file `%s' for writing: "
- "%m\n", output_path);
+ ERROR_WITH_ERRNO("Failed to open the file `%s' for writing",
+ output_path);
return WIMLIB_ERR_OPEN;
}
/* Extract empty file, with no lookup table entry... */
if (!lte) {
- DEBUG("Empty file `%s'\n", output_path);
+ DEBUG("Empty file `%s'.", output_path);
ret = 0;
goto done;
}
res_entry->original_size);
if (ret != 0) {
- ERROR("Failed to extract resource to `%s'!\n", output_path);
+ ERROR("Failed to extract resource to `%s'", output_path);
goto done;
}
return ret;
}
+static int extract_symlink(const struct dentry *dentry, const char *output_path,
+ const WIMStruct *w)
+{
+ char target[4096];
+ ssize_t ret = dentry_readlink(dentry, target, sizeof(target), w);
+ if (ret <= 0) {
+ ERROR("Could not read the symbolic link from dentry `%s'",
+ dentry->full_path_utf8);
+ return WIMLIB_ERR_INVALID_DENTRY;
+ }
+ ret = symlink(target, output_path);
+ if (ret != 0) {
+ ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'",
+ output_path, target);
+ return WIMLIB_ERR_LINK;
+ }
+ return 0;
+}
+
/*
* Extracts a directory from the WIM archive.
*
itself. */
return 0;
default:
- ERROR("Cannot create directory `%s': %m\n",
- output_path);
+ ERROR_WITH_ERRNO("Cannot create directory `%s'",
+ output_path);
return WIMLIB_ERR_MKDIR;
}
}
return 0;
}
+struct extract_args {
+ WIMStruct *w;
+ int extract_flags;
+#ifdef WITH_NTFS_3G
+ struct SECURITY_API *scapi;
+#endif
+};
/*
* Extracts a file or directory from the WIM archive. For use in
* @dentry: The dentry to extract.
* @arg: A pointer to the WIMStruct for the WIM file.
*/
-static int extract_regular_file_or_directory(struct dentry *dentry, void *arg)
+static int extract_dentry(struct dentry *dentry, void *arg)
{
- WIMStruct *w = (WIMStruct*)arg;
+ struct extract_args *args = arg;
+ WIMStruct *w = args->w;
+ int extract_flags = args->extract_flags;
size_t len = strlen(w->output_dir);
char output_path[len + dentry->full_path_utf8_len + 1];
+ int ret = 0;
- if (w->verbose)
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)
puts(dentry->full_path_utf8);
memcpy(output_path, w->output_dir, len);
memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len);
output_path[len + dentry->full_path_utf8_len] = '\0';
-
- if (dentry_is_regular_file(dentry)) {
- return extract_regular_file(w, dentry, output_path);
+ if (dentry_is_symlink(dentry)) {
+ ret = extract_symlink(dentry, output_path, w);
+ } else if (dentry_is_directory(dentry)) {
+ if (!dentry_is_root(dentry)) /* Root doesn't need to be extracted. */
+ ret = extract_directory(dentry, output_path);
} else {
- if (dentry_is_root(dentry)) /* Root doesn't need to be extracted. */
- return 0;
- else
- return extract_directory(dentry, output_path);
+ ret = extract_regular_file(w, dentry, output_path, extract_flags);
}
}
-static int extract_single_image(WIMStruct *w, int image)
+
+static int extract_single_image(WIMStruct *w, int image, int extract_flags)
{
- DEBUG("Extracting image %d\n", image);
+ DEBUG("Extracting image %d", image);
int ret;
ret = wimlib_select_image(w, image);
if (ret != 0)
return ret;
- return for_dentry_in_tree(wim_root_dentry(w),
- extract_regular_file_or_directory, w);
+ struct extract_args args = {
+ .w = w,
+ .extract_flags = extract_flags,
+ #ifdef WITH_NTFS_3G
+ .scapi = NULL
+ #endif
+ };
+
+ return for_dentry_in_tree(wim_root_dentry(w), extract_dentry, &args);
}
/* Extracts all images from the WIM to w->output_dir, with the images placed in
* subdirectories named by their image names. */
-static int extract_all_images(WIMStruct *w)
+static int extract_all_images(WIMStruct *w, int extract_flags)
{
- size_t image_name_max_len = xml_get_max_image_name_len(w);
+ size_t image_name_max_len = max(xml_get_max_image_name_len(w), 20);
size_t output_path_len = strlen(w->output_dir);
char buf[output_path_len + 1 + image_name_max_len + 1];
int ret;
int image;
+ const char *image_name;
- DEBUG("Attempting to extract all images from `%s'\n", w->filename);
+ DEBUG("Attempting to extract all images from `%s'", w->filename);
memcpy(buf, w->output_dir, output_path_len);
buf[output_path_len] = '/';
for (image = 1; image <= w->hdr.image_count; image++) {
- buf[output_path_len + 1] = '\0';
- strncat(buf + output_path_len + 1, wimlib_get_image_name(w, image),
- image_name_max_len);
- ret = wimlib_set_output_dir(w, buf);
+
+ image_name = wimlib_get_image_name(w, image);
+ if (*image_name) {
+ strcpy(buf + output_path_len + 1, image_name);
+ } else {
+ /* Image name is empty. Use image number instead */
+ sprintf(buf + output_path_len + 1, "%d", image);
+ }
+ ret = set_output_dir(w, buf);
if (ret != 0)
goto done;
- ret = extract_single_image(w, image);
+ ret = extract_single_image(w, image, extract_flags);
if (ret != 0)
goto done;
}
- ret = 0;
done:
+ /* Restore original output directory */
buf[output_path_len + 1] = '\0';
- wimlib_set_output_dir(w, buf);
- return ret;
+ return 0;
}
/* Extracts a single image or all images from a WIM file. */
-WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image)
+WIMLIBAPI int wimlib_extract_image(WIMStruct *w, int image,
+ const char *output_dir, int flags)
{
- if (!w->output_dir) {
- ERROR("No output directory selected.\n");
- return WIMLIB_ERR_NOTDIR;
+ int ret;
+ if (!output_dir)
+ return WIMLIB_ERR_INVALID_PARAM;
+ if ((flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
+ == (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ ret = set_output_dir(w, output_dir);
+ if (ret != 0)
+ return ret;
+
+ if ((flags & WIMLIB_EXTRACT_FLAG_NTFS)) {
+ #ifdef WITH_NTFS_3G
+ unsigned long mnt_flags;
+ ret = ntfs_check_if_mounted(output_dir, &mnt_flags);
+ if (ret != 0) {
+ ERROR_WITH_ERRNO("NTFS-3g: Cannot determine if `%s' "
+ "is mounted", output_dir);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ if (!(mnt_flags & NTFS_MF_MOUNTED)) {
+ ERROR("NTFS-3g: Filesystem on `%s' is not mounted ",
+ output_dir);
+ }
+ if (mnt_flags & NTFS_MF_READONLY) {
+ ERROR("NTFS-3g: Filesystem on `%s' is mounted "
+ "read-only", output_dir);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ #else
+ ERROR("wimlib was compiled without support for NTFS-3g, so");
+ ERROR("we cannot extract a WIM image while preserving NTFS-");
+ ERROR("specific information");
+ return WIMLIB_ERR_UNSUPPORTED;
+ #endif
}
if (image == WIM_ALL_IMAGES) {
w->is_multi_image_extraction = true;
- return extract_all_images(w);
+ ret = extract_all_images(w, flags);
} else {
w->is_multi_image_extraction = false;
- return extract_single_image(w, image);
- }
-
-}
-
-/* Set the output directory for WIM extraction. The directory is created using
- * mkdir(). Fails if directory cannot be created or already exists. */
-WIMLIBAPI int wimlib_set_output_dir(WIMStruct *w, const char *dir)
-{
- char *p;
- DEBUG("Setting output directory to `%s'\n", dir);
-
- if (!dir) {
- ERROR("Must specify a directory!\n");
- return WIMLIB_ERR_INVALID_PARAM;
- }
- p = STRDUP(dir);
- if (!p) {
- ERROR("Out of memory!\n");
- return WIMLIB_ERR_NOMEM;
+ ret = extract_single_image(w, image, flags);
}
+ return ret;
- if (mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
- if (errno == EEXIST) {
- DEBUG("`%s' already exists\n", dir);
- goto done;
- }
- ERROR("Cannot create directory `%s': %m\n", dir);
- FREE(p);
- return WIMLIB_ERR_MKDIR;
- } else {
- DEBUG("Created directory `%s'\n", dir);
- }
-done:
- FREE(w->output_dir);
- w->output_dir = p;
- return 0;
-}
-
-WIMLIBAPI int wimlib_set_link_type(WIMStruct *w, int link_type)
-{
- switch (link_type) {
- case WIM_LINK_TYPE_NONE:
- case WIM_LINK_TYPE_HARD:
- case WIM_LINK_TYPE_SYMBOLIC:
- w->link_type = link_type;
- return 0;
- default:
- return WIMLIB_ERR_INVALID_PARAM;
- }
}
-