X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fextract_image.c;h=7955417f88c0798255f5de04f6e8a72821995802;hb=406df77b8a9c2bb3318cf7737702eac27041e41e;hp=c2021563d11289edcc04ba8eb5ddaf54757e9b4c;hpb=80224de6c2ff870ea36ff98b491223c674e4d57a;p=wimlib diff --git a/src/extract_image.c b/src/extract_image.c index c2021563..7955417f 100644 --- a/src/extract_image.c +++ b/src/extract_image.c @@ -2,15 +2,10 @@ * extract_image.c * * Support for extracting WIM files. - * - * This code does NOT contain any filesystem-specific features. In particular, - * security information (i.e. file permissions) and alternate data streams are - * ignored, except possibly to read an alternate data stream that contains - * symbolic link data. */ /* - * Copyright (C) 2012 Eric Biggers + * Copyright (C) 2012, 2013 Eric Biggers * * This file is part of wimlib, a library for working with WIM files. * @@ -31,29 +26,39 @@ #include "config.h" #include + +#ifdef __WIN32__ +# include "win32.h" +#else +# ifdef HAVE_UTIME_H +# include +# endif +# include "timestamp.h" +# include +#endif + #include #include +#include #include #include -#include -#include - -#ifdef HAVE_UTIME_H -#include -#endif #include #include "dentry.h" #include "lookup_table.h" -#include "timestamp.h" #include "wimlib_internal.h" #include "xml.h" #ifdef WITH_NTFS_3G -#include +# include +#endif + +#ifdef HAVE_ALLOCA_H +# include #endif +#ifndef __WIN32__ static int extract_regular_file_linked(struct wim_dentry *dentry, const char *output_path, struct apply_args *args, @@ -114,6 +119,63 @@ static int extract_regular_file_linked(struct wim_dentry *dentry, return 0; } +static int symlink_apply_unix_data(const char *link, + const struct wimlib_unix_data *unix_data) +{ + if (lchown(link, unix_data->uid, unix_data->gid)) { + if (errno == EPERM) { + /* Ignore */ + WARNING_WITH_ERRNO("failed to set symlink UNIX owner/group"); + } else { + ERROR_WITH_ERRNO("failed to set symlink UNIX owner/group"); + return WIMLIB_ERR_INVALID_DENTRY; + } + } + return 0; +} + +static int fd_apply_unix_data(int fd, const struct wimlib_unix_data *unix_data) +{ + if (fchown(fd, unix_data->uid, unix_data->gid)) { + if (errno == EPERM) { + WARNING_WITH_ERRNO("failed to set file UNIX owner/group"); + /* Ignore? */ + } else { + ERROR_WITH_ERRNO("failed to set file UNIX owner/group"); + return WIMLIB_ERR_INVALID_DENTRY; + } + } + + if (fchmod(fd, unix_data->mode)) { + if (errno == EPERM) { + WARNING_WITH_ERRNO("failed to set UNIX file mode"); + /* Ignore? */ + } else { + ERROR_WITH_ERRNO("failed to set UNIX file mode"); + return WIMLIB_ERR_INVALID_DENTRY; + } + } + return 0; +} + +static int dir_apply_unix_data(const char *dir, + const struct wimlib_unix_data *unix_data) +{ + int dfd = open(dir, O_RDONLY); + int ret; + if (dfd >= 0) { + ret = fd_apply_unix_data(dfd, unix_data); + if (close(dfd)) { + ERROR_WITH_ERRNO("can't close directory `%s'", dir); + ret = WIMLIB_ERR_MKDIR; + } + } else { + ERROR_WITH_ERRNO("can't open directory `%s'", dir); + ret = WIMLIB_ERR_MKDIR; + } + return ret; +} + static int extract_regular_file_unlinked(struct wim_dentry *dentry, struct apply_args *args, const char *output_path, @@ -171,7 +233,7 @@ static int extract_regular_file_unlinked(struct wim_dentry *dentry, /* Empty file with no lookup table entry */ DEBUG("Empty file `%s'.", output_path); ret = 0; - goto out; + goto out_extract_unix_data; } ret = extract_wim_resource_to_fd(lte, out_fd, wim_resource_size(lte)); @@ -179,11 +241,27 @@ static int extract_regular_file_unlinked(struct wim_dentry *dentry, ERROR("Failed to extract resource to `%s'", output_path); goto out; } - args->progress.extract.completed_bytes += wim_resource_size(lte); + +out_extract_unix_data: + if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { + struct wimlib_unix_data unix_data; + ret = inode_get_unix_data(inode, &unix_data, NULL); + if (ret > 0) + ; + else if (ret < 0) + ret = 0; + else + ret = fd_apply_unix_data(out_fd, &unix_data); + if (ret != 0) + goto out; + } + if (lte) + args->progress.extract.completed_bytes += wim_resource_size(lte); out: if (close(out_fd) != 0) { ERROR_WITH_ERRNO("Failed to close file `%s'", output_path); - ret = WIMLIB_ERR_WRITE; + if (ret == 0) + ret = WIMLIB_ERR_WRITE; } return ret; } @@ -232,20 +310,37 @@ static int extract_symlink(struct wim_dentry *dentry, 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); + if (ret > 0) + ; + else if (ret < 0) + ret = 0; + else + ret = symlink_apply_unix_data(output_path, &unix_data); + if (ret != 0) + return ret; + } args->progress.extract.completed_bytes += wim_resource_size(lte); return 0; } -static int extract_directory(const char *output_path, bool is_root) +#endif /* !__WIN32__ */ + +static int extract_directory(struct wim_dentry *dentry, + const char *output_path, bool is_root) { int ret; struct stat stbuf; + ret = stat(output_path, &stbuf); if (ret == 0) { if (S_ISDIR(stbuf.st_mode)) { /*if (!is_root)*/ /*WARNING("`%s' already exists", output_path);*/ - return 0; + goto dir_exists; } else { ERROR("`%s' is not a directory", output_path); return WIMLIB_ERR_MKDIR; @@ -262,43 +357,47 @@ static int extract_directory(const char *output_path, bool is_root) output_path); return WIMLIB_ERR_MKDIR; } - return 0; +dir_exists: + ret = 0; +#ifndef __WIN32__ + if (dentry) { + struct wimlib_unix_data unix_data; + ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL); + if (ret > 0) + ; + else if (ret < 0) + ret = 0; + else + ret = dir_apply_unix_data(output_path, &unix_data); + } +#endif + return ret; } -/* Extracts a file, directory, or symbolic link from the WIM archive. */ -static int apply_dentry_normal(struct wim_dentry *dentry, void *arg) +#ifndef __WIN32__ +static int unix_apply_dentry(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + const struct apply_args *args) { - struct apply_args *args = arg; - struct wim_inode *inode = dentry->d_inode; - size_t len; - - len = strlen(args->target); - char output_path[len + dentry->full_path_utf8_len + 1]; - memcpy(output_path, args->target, len); - memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); - output_path[len + dentry->full_path_utf8_len] = '\0'; + const struct wim_inode *inode = dentry->d_inode; if (inode_is_symlink(inode)) return extract_symlink(dentry, args, output_path); else if (inode_is_directory(inode)) - return extract_directory(output_path, false); + return extract_directory((args->extract_flags & + WIMLIB_EXTRACT_FLAG_UNIX_DATA) ? dentry : NULL, + output_path, false); else return extract_regular_file(dentry, args, output_path); } -/* Apply timestamps to an extracted file or directory */ -static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg) +static int unix_apply_dentry_timestamps(const char *output_path, + size_t output_path_len, + const struct wim_dentry *dentry, + struct apply_args *args) { - struct apply_args *args = arg; - size_t len = strlen(args->target); - char output_path[len + dentry->full_path_utf8_len + 1]; - const struct wim_inode *inode = dentry->d_inode; int ret; - - memcpy(output_path, args->target, len); - memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); - output_path[len + dentry->full_path_utf8_len] = '\0'; - /* Convert the WIM timestamps, which are accurate to 100 nanoseconds, * into struct timeval's. */ struct timeval tv[2]; @@ -328,6 +427,57 @@ static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg) } return 0; } +#endif /* !__WIN32__ */ + +/* Extracts a file, directory, or symbolic link from the WIM archive. */ +static int apply_dentry_normal(struct wim_dentry *dentry, void *arg) +{ + struct apply_args *args = arg; + struct wim_inode *inode = dentry->d_inode; + size_t len; + char *output_path; + + len = strlen(args->target); + if (dentry_is_root(dentry)) { + output_path = (char*)args->target; + } else { + output_path = alloca(len + dentry->full_path_utf8_len + 1); + memcpy(output_path, args->target, len); + memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); + output_path[len + dentry->full_path_utf8_len] = '\0'; + len += dentry->full_path_utf8_len; + } +#ifdef __WIN32__ + return win32_apply_dentry(output_path, len, dentry, args); +#else + return unix_apply_dentry(output_path, len, dentry, args); +#endif +} + + +/* Apply timestamps to an extracted file or directory */ +static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg) +{ + struct apply_args *args = arg; + size_t len; + char *output_path; + + len = strlen(args->target); + if (dentry_is_root(dentry)) { + output_path = (char*)args->target; + } else { + output_path = alloca(len + dentry->full_path_utf8_len + 1); + memcpy(output_path, args->target, len); + memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len); + output_path[len + dentry->full_path_utf8_len] = '\0'; + len += dentry->full_path_utf8_len; + } +#ifdef __WIN32__ + return win32_apply_dentry_timestamps(output_path, len, dentry, args); +#else + return unix_apply_dentry_timestamps(output_path, len, dentry, args); +#endif +} /* Extract a dentry if it hasn't already been extracted, and either the dentry * has no streams or WIMLIB_EXTRACT_FLAG_NO_STREAMS is not specified. */ @@ -702,7 +852,7 @@ static int extract_all_images(WIMStruct *w, const char *target, int image; const char *image_name; - ret = extract_directory(target, true); + ret = extract_directory(NULL, target, true); if (ret != 0) return ret; @@ -746,6 +896,17 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, == (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) return WIMLIB_ERR_INVALID_PARAM; +#ifdef __WIN32__ + if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { + ERROR("Extracting UNIX data is not supported on Windows"); + return WIMLIB_ERR_INVALID_PARAM; + } + if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) { + ERROR("Linked extraction modes are not supported on Windows"); + return WIMLIB_ERR_INVALID_PARAM; + } +#endif + if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { #ifdef WITH_NTFS_3G if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))) { @@ -758,6 +919,10 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, "directly to a NTFS volume"); return WIMLIB_ERR_INVALID_PARAM; } + if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) { + ERROR("Cannot restore UNIX-specific data in the NTFS extraction mode"); + return WIMLIB_ERR_INVALID_PARAM; + } #else ERROR("wimlib was compiled without support for NTFS-3g, so"); ERROR("we cannot apply a WIM image directly to a NTFS volume"); @@ -778,6 +943,9 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, w->lookup_table = joined_tab; } +#ifdef __WIN32__ + win32_acquire_restore_privileges(); +#endif if (image == WIMLIB_ALL_IMAGES) { extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE; ret = extract_all_images(w, target, extract_flags, @@ -787,6 +955,9 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w, ret = extract_single_image(w, image, target, extract_flags, progress_func); } +#ifdef __WIN32__ + win32_release_restore_privileges(); +#endif if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))