X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwrite.c;h=ccad7f07caa5c3f4b7af309995ab5974d2742aa0;hp=9835dba03f67b70e15e5e9a812532774bc702cdd;hb=062465170ddb4520ca6fbc64a0f6e27e786de101;hpb=9e2571b03cd9c71d11b3dad9ea5dcfa43f50deb4 diff --git a/src/write.c b/src/write.c index 9835dba0..ccad7f07 100644 --- a/src/write.c +++ b/src/write.c @@ -187,9 +187,9 @@ get_compress_func(int out_ctype) * * @chunk: Uncompressed data of the chunk. * @chunk_size: Size of the chunk (<= WIM_CHUNK_SIZE) - * @out_fp: FILE * to write tho chunk to. - * @out_ctype: Compression type to use when writing the chunk (ignored if no - * chunk table provided) + * @out_fp: FILE * to write the chunk to. + * @compress: Compression function to use (NULL if writing uncompressed + * data). * @chunk_tab: Pointer to chunk table being created. It is updated with the * offset of the chunk we write. * @@ -202,7 +202,7 @@ write_wim_resource_chunk(const void *chunk, unsigned chunk_size, { const u8 *out_chunk; unsigned out_chunk_size; - if (chunk_tab) { + if (compress) { u8 *compressed_chunk = alloca(chunk_size); out_chunk_size = compress(chunk, chunk_size, compressed_chunk); @@ -378,12 +378,12 @@ write_wim_resource(struct wim_lookup_table_entry *lte, ret = read_resource_prefix(lte, wim_resource_size(lte), write_resource_cb, &write_ctx, 0); - /* Verify SHA1 message digest of the resource, Or, if the hash we had - * before is all 0's, just re-set it to be the new hash. */ + /* Verify SHA1 message digest of the resource, or set the hash for the + * first time. */ if (write_ctx.doing_sha) { u8 md[SHA1_HASH_SIZE]; sha1_final(md, &write_ctx.sha_ctx); - if (is_zero_hash(lte->hash)) { + if (lte->unhashed) { copy_hash(lte->hash, md); } else if (!hashes_equal(md, lte->hash)) { ERROR("WIM resource has incorrect hash!"); @@ -404,7 +404,6 @@ write_wim_resource(struct wim_lookup_table_entry *lte, /* Doing a raw write: The new compressed size is the same as * the compressed size in the other WIM. */ new_size = lte->resource_entry.size; - out_res_entry->flags = lte->resource_entry.flags; } else if (out_ctype == WIMLIB_COMPRESSION_TYPE_NONE) { /* Using WIMLIB_COMPRESSION_TYPE_NONE: The new compressed size * is the original size. */ @@ -599,8 +598,35 @@ do_write_streams_progress(union wimlib_progress_info *progress, } } +static int +sha1_chunk(const void *buf, size_t len, void *ctx) +{ + sha1_update(ctx, buf, len); + return 0; +} + +static int +sha1_resource(struct wim_lookup_table_entry *lte) +{ + int ret; + SHA_CTX sha_ctx; + + sha1_init(&sha_ctx); + ret = read_resource_prefix(lte, wim_resource_size(lte), + sha1_chunk, &sha_ctx, 0); + if (ret == 0) + sha1_final(lte->hash, &sha_ctx); + return ret; +} + +enum { + STREAMS_MERGED = 0, + STREAMS_NOT_MERGED = 1, +}; + static int do_write_stream_list(struct list_head *my_resources, + struct wim_lookup_table *lookup_table, FILE *out_fp, int out_ctype, wimlib_progress_func_t progress_func, @@ -608,18 +634,61 @@ do_write_stream_list(struct list_head *my_resources, int write_resource_flags) { int ret; - struct wim_lookup_table_entry *lte, *tmp; + struct wim_lookup_table_entry *lte; + + while (!list_empty(my_resources)) { + lte = container_of(my_resources->next, + struct wim_lookup_table_entry, + write_streams_list); + list_del(<e->write_streams_list); + if (lte->unhashed && !lte->unique_size) { + struct wim_lookup_table_entry *duplicate_lte; + struct wim_lookup_table_entry **my_ptr; + + my_ptr = lte->my_ptr; + ret = sha1_resource(lte); + if (ret) + return ret; + duplicate_lte = __lookup_resource(lookup_table, lte->hash); + if (duplicate_lte) { + bool new_stream = (duplicate_lte->out_refcnt == 0); + duplicate_lte->refcnt += lte->refcnt; + duplicate_lte->out_refcnt += lte->refcnt; + *my_ptr = duplicate_lte; + free_lookup_table_entry(lte); + + if (new_stream) { + lte = duplicate_lte; + DEBUG("Stream of length %"PRIu64" is duplicate " + "with one already in WIM", + wim_resource_size(duplicate_lte)); + } else { + DEBUG("Discarding duplicate stream of length %"PRIu64, + wim_resource_size(duplicate_lte)); + goto skip_to_progress; + } + + } else { + lookup_table_insert(lookup_table, lte); + lte->out_refcnt = lte->refcnt; + lte->unhashed = 0; + } + } + + wimlib_assert(lte->out_refcnt != 0); - list_for_each_entry_safe(lte, tmp, my_resources, staging_list) { ret = write_wim_resource(lte, out_fp, out_ctype, <e->output_resource_entry, write_resource_flags); - if (ret != 0) + if (ret) return ret; - list_del(<e->staging_list); - + if (lte->unhashed) { + lookup_table_insert(lookup_table, lte); + lte->unhashed = 0; + } + skip_to_progress: do_write_streams_progress(progress, progress_func, wim_resource_size(lte)); @@ -629,6 +698,7 @@ do_write_stream_list(struct list_head *my_resources, static int write_stream_list_serial(struct list_head *stream_list, + struct wim_lookup_table *lookup_table, FILE *out_fp, int out_ctype, int write_flags, @@ -644,7 +714,9 @@ write_stream_list_serial(struct list_head *stream_list, progress->write_streams.num_threads = 1; if (progress_func) progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress); - return do_write_stream_list(stream_list, out_fp, + return do_write_stream_list(stream_list, + lookup_table, + out_fp, out_ctype, progress_func, progress, write_resource_flags); } @@ -1112,6 +1184,7 @@ get_default_num_threads() static int write_stream_list_parallel(struct list_head *stream_list, + struct wim_lookup_table *lookup_table, FILE *out_fp, int out_ctype, int write_flags, @@ -1206,6 +1279,7 @@ out_destroy_res_to_compress_queue: out_serial: WARNING("Falling back to single-threaded compression"); return write_stream_list_serial(stream_list, + lookup_table, out_fp, out_ctype, write_flags, @@ -1220,18 +1294,19 @@ out_serial: * @out_ctype and up to @num_threads compressor threads. */ static int -write_stream_list(struct list_head *stream_list, FILE *out_fp, - int out_ctype, int write_flags, - unsigned num_threads, - wimlib_progress_func_t progress_func) +write_stream_list(struct list_head *stream_list, + struct wim_lookup_table *lookup_table, + FILE *out_fp, int out_ctype, int write_flags, + unsigned num_threads, wimlib_progress_func_t progress_func) { struct wim_lookup_table_entry *lte; size_t num_streams = 0; u64 total_bytes = 0; u64 total_compression_bytes = 0; union wimlib_progress_info progress; + int ret; - list_for_each_entry(lte, stream_list, staging_list) { + list_for_each_entry(lte, stream_list, write_streams_list) { num_streams++; total_bytes += wim_resource_size(lte); if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE @@ -1251,21 +1326,24 @@ write_stream_list(struct list_head *stream_list, FILE *out_fp, #ifdef ENABLE_MULTITHREADED_COMPRESSION if (total_compression_bytes >= 1000000 && num_threads != 1) - return write_stream_list_parallel(stream_list, - out_fp, - out_ctype, - write_flags, - num_threads, - progress_func, - &progress); + ret = write_stream_list_parallel(stream_list, + lookup_table, + out_fp, + out_ctype, + write_flags, + num_threads, + progress_func, + &progress); else #endif - return write_stream_list_serial(stream_list, - out_fp, - out_ctype, - write_flags, - progress_func, - &progress); + ret = write_stream_list_serial(stream_list, + lookup_table, + out_fp, + out_ctype, + write_flags, + progress_func, + &progress); + return ret; } struct lte_overwrite_prepare_args { @@ -1311,7 +1389,7 @@ wim_prepare_streams(WIMStruct *wim, off_t end_offset, int ret; for (int i = 0; i < wim->hdr.image_count; i++) { - ret = lte_overwrite_prepare(wim->image_metadata[i].metadata_lte, + ret = lte_overwrite_prepare(wim->image_metadata[i]->metadata_lte, &args); if (ret) return ret; @@ -1320,17 +1398,73 @@ wim_prepare_streams(WIMStruct *wim, off_t end_offset, lte_overwrite_prepare, &args); } +struct stream_size_table { + struct hlist_head *array; + size_t num_entries; + size_t capacity; +}; + +static int +init_stream_size_table(struct stream_size_table *tab, size_t capacity) +{ + tab->array = CALLOC(capacity, sizeof(tab->array[0])); + if (!tab->array) + return WIMLIB_ERR_NOMEM; + tab->num_entries = 0; + tab->capacity = capacity; + return 0; +} + +static void +destroy_stream_size_table(struct stream_size_table *tab) +{ + FREE(tab->array); +} + +static int +stream_size_table_insert(struct wim_lookup_table_entry *lte, void *_tab) +{ + struct stream_size_table *tab = _tab; + size_t pos; + struct wim_lookup_table_entry *hashed_lte; + struct hlist_node *tmp; + + pos = hash_u64(wim_resource_size(lte)) % tab->capacity; + lte->unique_size = 1; + hlist_for_each_entry(hashed_lte, tmp, &tab->array[pos], hash_list_2) { + if (wim_resource_size(hashed_lte) == wim_resource_size(lte)) { + lte->unique_size = 0; + hashed_lte->unique_size = 0; + break; + } + } + + hlist_add_head(<e->hash_list_2, &tab->array[pos]); + tab->num_entries++; + return 0; +} + + +struct find_streams_ctx { + struct list_head stream_list; + struct stream_size_table stream_size_tab; +}; + static int inode_find_streams_to_write(struct wim_inode *inode, struct wim_lookup_table *table, - struct list_head *stream_list) + struct list_head *stream_list, + struct stream_size_table *tab) { struct wim_lookup_table_entry *lte; for (unsigned i = 0; i <= inode->i_num_ads; i++) { lte = inode_stream_lte(inode, i, table); if (lte) { - if (lte->out_refcnt == 0) - list_add_tail(<e->staging_list, stream_list); + if (lte->out_refcnt == 0) { + if (lte->unhashed) + stream_size_table_insert(lte, tab); + list_add_tail(<e->write_streams_list, stream_list); + } lte->out_refcnt += inode->i_nlink; } } @@ -1340,31 +1474,85 @@ inode_find_streams_to_write(struct wim_inode *inode, static int image_find_streams_to_write(WIMStruct *w) { + struct wim_image_metadata *imd; + struct find_streams_ctx *ctx; struct wim_inode *inode; - struct hlist_node *cur; - struct hlist_head *inode_list; + struct wim_lookup_table_entry *lte; + + ctx = w->private; + imd = wim_get_current_image_metadata(w); + + image_for_each_unhashed_stream(lte, imd) { + lte->out_refcnt = 0; + wimlib_assert(lte->unhashed); + wimlib_assert(lte->my_ptr != NULL); + } - inode_list = &wim_get_current_image_metadata(w)->inode_list; - hlist_for_each_entry(inode, cur, inode_list, i_hlist) { + /* Go through this image's inodes to find any streams that have not been + * found yet. */ + image_for_each_inode(inode, imd) { inode_find_streams_to_write(inode, w->lookup_table, - (struct list_head*)w->private); + &ctx->stream_list, + &ctx->stream_size_tab); } return 0; } +/* Given a WIM that from which one or all of the images is being written, build + * the list of unique streams ('struct wim_lookup_table_entry's) that must be + * written, plus any unhashed streams that need to be written but may be + * identical to other hashed or unhashed streams being written. These unhashed + * streams are checksummed while the streams are being written. To aid this + * process, the member @unique_size is set to 1 on streams that have a unique + * size and therefore must be written. + * + * The out_refcnt member of each 'struct wim_lookup_table_entry' is set to + * indicate the number of times the stream is referenced in only the streams + * that are being written; this may still be adjusted later when unhashed + * streams are being resolved. + */ +static int +prepare_stream_list(WIMStruct *wim, int image, struct list_head *stream_list) +{ + int ret; + struct find_streams_ctx ctx; + + for_lookup_table_entry(wim->lookup_table, lte_zero_out_refcnt, NULL); + ret = init_stream_size_table(&ctx.stream_size_tab, 9001); + if (ret) + return ret; + for_lookup_table_entry(wim->lookup_table, stream_size_table_insert, + &ctx.stream_size_tab); + INIT_LIST_HEAD(&ctx.stream_list); + wim->private = &ctx; + for_image(wim, image, image_find_streams_to_write); + destroy_stream_size_table(&ctx.stream_size_tab); + + INIT_LIST_HEAD(stream_list); + list_splice(&ctx.stream_list, stream_list); + return 0; +} + +/* Writes the streams for the specified @image in @wim to @wim->out_fp. + */ static int -write_wim_streams(WIMStruct *w, int image, int write_flags, - unsigned num_threads, - wimlib_progress_func_t progress_func) +write_wim_streams(WIMStruct *wim, int image, int write_flags, + unsigned num_threads, + wimlib_progress_func_t progress_func) { + int ret; + struct list_head stream_list; - for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, NULL); - LIST_HEAD(stream_list); - w->private = &stream_list; - for_image(w, image, image_find_streams_to_write); - return write_stream_list(&stream_list, w->out_fp, - wimlib_get_compression_type(w), write_flags, - num_threads, progress_func); + ret = prepare_stream_list(wim, image, &stream_list); + if (ret) + return ret; + return write_stream_list(&stream_list, + wim->lookup_table, + wim->out_fp, + wimlib_get_compression_type(wim), + write_flags, + num_threads, + progress_func); } /* @@ -1497,7 +1685,7 @@ finish_write(WIMStruct *w, int image, int write_flags, } else { memcpy(&hdr.boot_metadata_res_entry, &w->image_metadata[ - hdr.boot_idx - 1].metadata_lte->output_resource_entry, + hdr.boot_idx - 1]->metadata_lte->output_resource_entry, sizeof(struct resource_entry)); } @@ -1509,7 +1697,7 @@ finish_write(WIMStruct *w, int image, int write_flags, } ret = write_header(&hdr, out); - if (ret != 0) + if (ret) goto out; if (write_flags & WIMLIB_WRITE_FLAG_FSYNC) { @@ -1627,19 +1815,19 @@ wimlib_write(WIMStruct *w, const tchar *path, } ret = begin_write(w, path, write_flags); - if (ret != 0) + if (ret) goto out; ret = write_wim_streams(w, image, write_flags, num_threads, progress_func); - if (ret != 0) + if (ret) goto out; if (progress_func) progress_func(WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN, NULL); ret = for_image(w, image, write_metadata_resource); - if (ret != 0) + if (ret) goto out; if (progress_func) @@ -1656,7 +1844,7 @@ static bool any_images_modified(WIMStruct *w) { for (int i = 0; i < w->hdr.image_count; i++) - if (w->image_metadata[i].modified) + if (w->image_metadata[i]->modified) return true; return false; } @@ -1786,7 +1974,9 @@ overwrite_wim_inplace(WIMStruct *w, int write_flags, if (!list_empty(&stream_list)) { DEBUG("Writing newly added streams (offset = %"PRIu64")", old_wim_end); - ret = write_stream_list(&stream_list, w->out_fp, + ret = write_stream_list(&stream_list, + w->lookup_table, + w->out_fp, wimlib_get_compression_type(w), write_flags, num_threads, progress_func); @@ -1797,10 +1987,10 @@ overwrite_wim_inplace(WIMStruct *w, int write_flags, } for (int i = 0; i < w->hdr.image_count; i++) { - if (w->image_metadata[i].modified) { + if (w->image_metadata[i]->modified) { select_wim_image(w, i + 1); ret = write_metadata_resource(w); - if (ret != 0) + if (ret) goto out_ftruncate; } }