]> wimlib.net Git - wimlib/commitdiff
dump blobs dump_blobs
authorEric Biggers <ebiggers3@gmail.com>
Mon, 6 Feb 2017 01:43:15 +0000 (17:43 -0800)
committerEric Biggers <ebiggers3@gmail.com>
Mon, 6 Feb 2017 01:43:15 +0000 (17:43 -0800)
include/wimlib.h
programs/imagex.c
src/extract.c

index 135f5cb4bc91ec94026fd953aedcd062e73c662d..c0227bbd2d43965a51d6c4cddbcc6749804c0bd7 100644 (file)
@@ -1979,6 +1979,12 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
  * only allows the Administrator to create symbolic links.  */
 #define WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS             0x00008000
 
+/** wimlib_extract_blobs(): extract all data blobs.  */
+#define WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS             0x00010000
+
+/** wimlib_extract_blobs(): extract all metadata blobs.  */
+#define WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS         0x00020000
+
 /**
  * For wimlib_extract_paths() and wimlib_extract_pathlist() only:  Treat the
  * paths to extract as wildcard patterns ("globs") which may contain the
@@ -2776,6 +2782,50 @@ extern int
 wimlib_delete_path(WIMStruct *wim, int image,
                   const wimlib_tchar *path, int delete_flags);
 
+/**
+ * @ingroup G_extracting_wims
+ *
+ * Extract blobs identified by SHA-1 message digest.
+ *
+ * This is a special-purpose function usually used for debugging.  You may be
+ * looking for wimlib_extract_paths().
+ *
+ * @param wim
+ *     The ::WIMStruct for the WIM file from which to extract the blob.
+ *
+ * @param blob_sha1s
+ *     An array of SHA-1 message digests of blobs to extract.  Each SHA-1
+ *     message digest is 20 bytes.
+ *
+ * @param num_blobs
+ *     The number of blobs to extract --- that is, the number of SHA-1 message
+ *     digests specified in @p blob_sha1s.
+ *
+ * @param target
+ *     The directory to which to extract the blobs.  Each blob will be
+ *     extracted to a file in this directory named after the hexadecimal
+ *     representation of its SHA-1 message digest.
+ *
+ * @param extract_flags
+ *     Reserved; must be 0.
+ *
+ * @return 0 on success; nonzero on failure.  Some of the possible error codes
+ * are:
+ *
+ * @retval ::WIMLIB_ERR_DECOMPRESSION
+ *     The compressed data of one of the blobs was invalid.
+ * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH
+ *     One of the blobs was corrupted.
+ * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND
+ *     One of the specified blobs could not be found in the WIM.
+ * @retval ::WIMLIB_ERR_WRITE
+ *     Failed to write data to one of the files.
+ */
+extern int
+wimlib_extract_blobs(WIMStruct *wim,
+                    const uint8_t *blob_sha1s, size_t num_blobs,
+                    const wimlib_tchar *target, int extract_flags);
+
 /**
  * @ingroup G_modifying_wims
  *
index e62757b897153c3fbdb72d2d808b0d7de0ff9950..bfb57230412f0a3111afb869412618d689b43338 100644 (file)
@@ -155,6 +155,10 @@ enum {
        IMAGEX_DEREFERENCE_OPTION,
        IMAGEX_DEST_DIR_OPTION,
        IMAGEX_DETAILED_OPTION,
+       IMAGEX_DUMP_BLOB_OPTION,
+       IMAGEX_DUMP_DATA_BLOBS_OPTION,
+       IMAGEX_DUMP_METADATA_BLOBS_OPTION,
+       IMAGEX_DUMP_ALL_BLOBS_OPTION,
        IMAGEX_EXTRACT_XML_OPTION,
        IMAGEX_FLAGS_OPTION,
        IMAGEX_FORCE_OPTION,
@@ -309,6 +313,10 @@ static const struct option extract_options[] = {
        {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
        {T("wimboot"),     no_argument,       NULL, IMAGEX_WIMBOOT_OPTION},
        {T("compact"),     required_argument, NULL, IMAGEX_COMPACT_OPTION},
+       {T("dump-blob"), required_argument,     NULL, IMAGEX_DUMP_BLOB_OPTION},
+       {T("dump-data-blobs"), no_argument,     NULL, IMAGEX_DUMP_DATA_BLOBS_OPTION},
+       {T("dump-metadata-blobs"), no_argument, NULL, IMAGEX_DUMP_METADATA_BLOBS_OPTION},
+       {T("dump-all-blobs"), no_argument,      NULL, IMAGEX_DUMP_ALL_BLOBS_OPTION},
        {NULL, 0, NULL, 0},
 };
 
@@ -1079,6 +1087,41 @@ stdin_get_text_contents(size_t *num_tchars_ret)
        return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
 }
 
+static int
+parse_sha1(const tchar *hashstr, uint8_t hash[20])
+{
+       if (tstrlen(hashstr) != 40)
+               goto invalid;
+
+       for (int i = 0; i < 20; i++) {
+               tchar high = totlower(hashstr[i * 2 + 0]);
+               tchar low = totlower(hashstr[i * 2 + 1]);
+               uint8_t byte = 0;
+
+               if (high >= '0' && high <= '9')
+                       byte |= (high - '0') << 4;
+               else if (high >= 'a' && high <= 'f')
+                       byte |= (high - 'a' + 10) << 4;
+               else
+                       goto invalid;
+
+               if (low >= '0' && low <= '9')
+                       byte |= (low - '0');
+               else if (low >= 'a' && low <= 'f')
+                       byte |= (low - 'a' + 10);
+               else
+                       goto invalid;
+               hash[i] = byte;
+       }
+
+       return 0;
+
+invalid:
+       imagex_error("\"%"TS"\" is not a well-formed SHA-1 message digest!",
+                    hashstr);
+       return -1;
+}
+
 #define TO_PERCENT(numerator, denominator) \
        (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
 
@@ -3142,6 +3185,7 @@ imagex_extract(int argc, tchar **argv, int cmd)
                            WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
                            WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
        int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
+       const tchar *blob_to_dump = NULL;
 
        STRING_LIST(refglobs);
 
@@ -3202,6 +3246,19 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        if (ret)
                                goto out_free_refglobs;
                        break;
+               case IMAGEX_DUMP_DATA_BLOBS_OPTION:
+                       extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS;
+                       break;
+               case IMAGEX_DUMP_METADATA_BLOBS_OPTION:
+                       extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS;
+                       break;
+               case IMAGEX_DUMP_ALL_BLOBS_OPTION:
+                       extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS |
+                                        WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS;
+                       break;
+               case IMAGEX_DUMP_BLOB_OPTION:
+                       blob_to_dump = optarg;
+                       break;
                default:
                        goto out_usage;
                }
@@ -3209,6 +3266,36 @@ imagex_extract(int argc, tchar **argv, int cmd)
        argc -= optind;
        argv += optind;
 
+       if (blob_to_dump ||
+           (extract_flags & (WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS |
+                             WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS)))
+       {
+               uint8_t hash[20];
+               unsigned num_hashes = 0;
+
+               if (argc != 1) {
+                       imagex_error(T("Can't specify an image in combination "
+                                      "with any of the \"dump blobs\" "
+                                      "options"));
+                       goto out_err;
+               }
+
+               ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
+                                                   imagex_progress_func, NULL);
+               if (ret)
+                       goto out_free_refglobs;
+
+               if (blob_to_dump) {
+                       ret = parse_sha1(blob_to_dump, hash);
+                       if (ret)
+                               goto out_wimlib_free;
+                       num_hashes++;
+               }
+               ret = wimlib_extract_blobs(wim, hash, num_hashes, dest_dir,
+                                          extract_flags);
+               goto out_wimlib_free;
+       }
+
        if (argc < 2)
                goto out_usage;
 
index f59cffb55347acc014da0feb0aad58f42d2150b8..97656c4ab457e264fa445707ef06ad8caa2262e5 100644 (file)
@@ -80,6 +80,8 @@
         WIMLIB_EXTRACT_FLAG_TO_STDOUT                  |       \
         WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES  |       \
         WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS         |       \
+        WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS             |       \
+        WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS |       \
         WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS          |       \
         WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES         |       \
         WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS            |       \
@@ -567,6 +569,15 @@ extract_blob_list(struct apply_ctx *ctx, const struct read_blob_callbacks *cbs)
        }
 }
 
+static int
+extract_blob_to_stdout(struct blob_descriptor *blob)
+{
+       struct filedes _stdout;
+
+       filedes_init(&_stdout, STDOUT_FILENO);
+       return extract_blob_to_fd(blob, &_stdout);
+}
+
 /* Extract a WIM dentry to standard output.
  *
  * This obviously doesn't make sense in all cases.  We return an error if the
@@ -578,7 +589,6 @@ extract_dentry_to_stdout(struct wim_dentry *dentry,
 {
        struct wim_inode *inode = dentry->d_inode;
        struct blob_descriptor *blob;
-       struct filedes _stdout;
 
        if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
                                   FILE_ATTRIBUTE_DIRECTORY |
@@ -597,8 +607,7 @@ extract_dentry_to_stdout(struct wim_dentry *dentry,
                return 0;
        }
 
-       filedes_init(&_stdout, STDOUT_FILENO);
-       return extract_blob_to_fd(blob, &_stdout);
+       return extract_blob_to_stdout(blob);
 }
 
 static int
@@ -2077,3 +2086,163 @@ wimlib_extract_image(WIMStruct *wim, int image, const tchar *target,
                return WIMLIB_ERR_INVALID_PARAM;
        return do_wimlib_extract_image(wim, image, target, extract_flags);
 }
+
+/* Extract a list of blobs to the specified directory.
+ *
+ * This is somewhat of a hack: we generate a temporary WIM in memory so that we
+ * can just send it through the regular extraction code.  */
+static int
+do_wimlib_extract_blobs(struct list_head *blob_list, const tchar *target,
+                       wimlib_progress_func_t progfunc, void *progctx,
+                       int extract_flags)
+{
+       WIMStruct *tmp_wim = NULL;
+       int ret;
+       struct blob_descriptor *blob;
+       tchar name_buf[SHA1_HASH_SIZE * 2 + 1];
+       struct wim_dentry *root_dentry;
+       struct wim_dentry *dentry;
+       struct wim_dentry *existing;
+       struct wim_image_metadata *imd;
+       const tchar *root_path = WIMLIB_WIM_ROOT_PATH;
+
+       if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
+               list_for_each_entry(blob, blob_list, extraction_list) {
+                       ret = extract_blob_to_stdout(blob);
+                       if (ret)
+                               goto out;
+               }
+               return 0;
+       }
+
+       ret = wimlib_create_new_wim(WIMLIB_COMPRESSION_TYPE_NONE, &tmp_wim);
+       if (ret)
+               goto out;
+
+       wimlib_register_progress_function(tmp_wim, progfunc, progctx);
+
+       ret = wimlib_add_empty_image(tmp_wim, NULL, NULL);
+       if (ret)
+               goto out;
+
+       ret = select_wim_image(tmp_wim, 1);
+       if (ret)
+               goto out;
+
+       ret = new_filler_directory(&root_dentry);
+       if (ret)
+               goto out;
+
+       imd = wim_get_current_image_metadata(tmp_wim);
+
+       imd->root_dentry = root_dentry;
+
+       list_for_each_entry(blob, blob_list, extraction_list) {
+
+               sprint_hash(blob->hash, name_buf);
+
+               ret = new_dentry_with_new_inode(name_buf, true, &dentry);
+               if (ret)
+                       goto out;
+
+               ret = WIMLIB_ERR_NOMEM;
+               if (!inode_add_stream(dentry->d_inode, STREAM_TYPE_DATA,
+                                     NO_STREAM_NAME, blob))
+                       goto out;
+               hlist_add_head(&dentry->d_inode->i_hlist_node, &imd->inode_list);
+
+               existing = dentry_add_child(root_dentry, dentry);
+               wimlib_assert(!existing);
+       }
+
+       ret = do_wimlib_extract_paths(tmp_wim, 1, target, &root_path, 1,
+                                     extract_flags);
+out:
+       wimlib_free(tmp_wim);
+       return ret;
+}
+
+static struct blob_descriptor *
+lookup_data_or_metadata_blob(WIMStruct *wim, const u8 *hash)
+{
+       struct blob_descriptor *blob;
+
+       blob = lookup_blob(wim->blob_table, hash);
+       if (blob)
+               return blob;
+
+       if (wim_has_metadata(wim))
+               for (int i = 0; i < wim->hdr.image_count; i++)
+                       if (hashes_equal(hash, wim->image_metadata[i]-> metadata_blob->hash))
+                               return wim->image_metadata[i]->metadata_blob;
+       return NULL;
+}
+
+static int
+add_blob_to_list(struct blob_descriptor *blob, void *_list)
+{
+       list_add(&blob->extraction_list, (struct list_head *)_list);
+       return 0;
+}
+
+/* Extract the specified blobs from a WIM to a directory.  */
+WIMLIBAPI int
+wimlib_extract_blobs(WIMStruct *wim, const u8 *blob_sha1s, size_t num_blobs,
+                    const tchar *target, int extract_flags)
+{
+       const u8 *hashptr;
+       const u8 *hashend;
+       struct blob_descriptor *blob;
+
+       LIST_HEAD(blob_list);
+
+       if (!wim || !target || !*target)
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       if (num_blobs && !blob_sha1s)
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       if (extract_flags & ~WIMLIB_EXTRACT_MASK_PUBLIC)
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
+
+       if (extract_flags & (WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS |
+                            WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS))
+       {
+               /* Add all the data blobs.  */
+               if (extract_flags & WIMLIB_EXTRACT_FLAG_ALL_DATA_BLOBS)
+                       for_blob_in_table(wim->blob_table, add_blob_to_list,
+                                         &blob_list);
+
+               /* Also add the metadata blobs.  */
+               if ((extract_flags & WIMLIB_EXTRACT_FLAG_ALL_METADATA_BLOBS)
+                   && wim_has_metadata(wim))
+                       for (int i = 0; i < wim->hdr.image_count; i++)
+                               add_blob_to_list(wim->image_metadata[i]->metadata_blob,
+                                                &blob_list);
+       } else {
+               hashptr = blob_sha1s;
+               hashend = hashptr + (num_blobs * SHA1_HASH_SIZE);
+
+               /* Look up each blob requested.  */
+               for (; hashptr != hashend; hashptr += SHA1_HASH_SIZE) {
+
+                       blob = lookup_data_or_metadata_blob(wim, hashptr);
+                       if (!blob) {
+                               if (wimlib_print_errors) {
+                                       tchar hashstr[SHA1_HASH_SIZE * 2 + 1];
+                                       sprint_hash(hashptr, hashstr);
+                                       ERROR("Stream SHA1=%"TS" not found",
+                                             hashstr);
+                               }
+                               return WIMLIB_ERR_RESOURCE_NOT_FOUND;
+                       }
+                       add_blob_to_list(blob, &blob_list);
+               }
+       }
+
+       /* Extract the blobs.  */
+       return do_wimlib_extract_blobs(&blob_list, target, wim->progfunc,
+                                      wim->progctx, extract_flags);
+}