2 * PatchAPI PA19 file handlers
4 * Copyright 2019 Conor McCarthy
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * - Normalization of 32-bit PE executable files and reversal of special
22 * processing of these executables is not implemented.
23 * Without normalization, old files cannot be validated for patching. The
24 * function NormalizeFileForPatchSignature() in Windows could be used to work
25 * out exactly how normalization works.
26 * Most/all of the special processing seems to be relocation of targets for
27 * some jump/call instructions to match more of the old file and improve
28 * compression. Patching of 64-bit exes works because mspatchc.dll does not
29 * implement special processing of them. In 32-bit patches, the variable
30 * named here 'unknown_count' seems to indicate presence of data related to
31 * reversing the processing. The changes that must be reversed occur at some,
32 * but not all, of the positions listed in the PE .reloc table.
40 #include "wine/heap.h"
41 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(mspatcha
);
50 #define PA19_FILE_MAGIC 0x39314150
51 #define PATCH_OPTION_EXTRA_FLAGS 0x80000000
53 static UINT32
compute_zero_crc32(UINT32 crc
, INT_PTR len
)
55 static const BYTE zero_buffer
[1024];
59 crc
= RtlComputeCrc32(crc
, zero_buffer
, min(len
, sizeof(zero_buffer
)));
60 len
-= min(len
, sizeof(zero_buffer
));
65 /***********************************************************************************
66 * PatchAPI PA19 file header
70 * UINT32 options_2; (present if PATCH_OPTION_EXTRA_FLAGS set)
71 * UINT32 timestamp; (if PATCH_OPTION_NO_TIMESTAMP is SET in options)
72 * UVLI rebase; (present if PATCH_OPTION_NO_REBASE is not set; used on 32-bit executables)
73 * UVLI unpatched_size;
74 * UINT32 crc32_patched;
75 * BYTE input_file_count;
77 * For each source file:
78 * SVLI (patched_size - unpatched_size);
79 * UINT32 crc32_unpatched;
80 * BYTE ignore_range_count;
81 * For each ignore range:
82 * SVLI OffsetInOldFile;
84 * BYTE retain_range_count;
85 * For each retain range:
86 * SVLI (OffsetInOldFile - (prevOffsetInOldFile + prevLengthInBytes));
87 * SVLI (OffsetInNewFile - OffsetInOldFile);
89 * UVLI unknown_count; (a count of pairs of values related to the reversal of special
90 * processing done to improve compression of 32-bit executables)
91 * UVLI interleave_count; (present only if PATCH_OPTION_INTERLEAVE_FILES is set in options)
92 * UVLI interleave_values[interleave_count * 3 - 1];
93 * UVLI lzxd_input_size;
95 * For each source file:
96 * UINT16 lzxd_block[lzxd_input_size / 2]; (NOT always 16-bit aligned)
98 * UINT32 crc_hack; (rounds out the entire file crc32 to 0)
102 #define MAX_RANGES 255
104 struct input_file_info
{
107 BYTE ignore_range_count
;
108 BYTE retain_range_count
;
109 PATCH_IGNORE_RANGE ignore_table
[MAX_RANGES
];
110 PATCH_RETAIN_RANGE retain_table
[MAX_RANGES
];
111 size_t unknown_count
;
113 const BYTE
*stream_start
;
118 struct patch_file_header
{
123 unsigned input_file_count
;
124 struct input_file_info
*file_table
;
131 /* Currently supported options. Some such as PATCH_OPTION_FAIL_IF_BIGGER don't
132 * affect decoding but can get recorded in the patch file anyway */
133 #define PATCH_OPTION_SUPPORTED_FLAGS ( \
134 PATCH_OPTION_USE_LZX_A \
135 | PATCH_OPTION_USE_LZX_B \
136 | PATCH_OPTION_USE_LZX_LARGE \
137 | PATCH_OPTION_NO_BINDFIX \
138 | PATCH_OPTION_NO_LOCKFIX \
139 | PATCH_OPTION_NO_REBASE \
140 | PATCH_OPTION_FAIL_IF_SAME_FILE \
141 | PATCH_OPTION_FAIL_IF_BIGGER \
142 | PATCH_OPTION_NO_CHECKSUM \
143 | PATCH_OPTION_NO_RESTIMEFIX \
144 | PATCH_OPTION_NO_TIMESTAMP \
145 | PATCH_OPTION_EXTRA_FLAGS)
148 /* read a byte-aligned little-endian UINT32 from input and set error if eof
150 static inline UINT32
read_raw_uint32(struct patch_file_header
*ph
)
152 const BYTE
*src
= ph
->src
;
155 if (ph
->src
> ph
->end
)
157 ph
->err
= ERROR_PATCH_CORRUPT
;
166 /* Read a variable-length integer from a sequence of bytes terminated by
167 * a value with bit 7 set. Set error if invalid or eof */
168 static UINT64
read_uvli(struct patch_file_header
*ph
)
170 const BYTE
*vli
= ph
->src
;
173 ptrdiff_t limit
= min(ph
->end
- vli
, 9);
175 if (ph
->src
>= ph
->end
)
177 ph
->err
= ERROR_PATCH_CORRUPT
;
182 for (i
= 1; i
< limit
&& vli
[i
- 1] < 0x80; ++i
)
183 n
+= (UINT64
)(vli
[i
] & 0x7F) << (7 * i
);
185 if (vli
[i
- 1] < 0x80)
187 TRACE("exceeded maximum vli size\n");
188 ph
->err
= ERROR_PATCH_CORRUPT
;
197 /* Signed variant of the above. First byte sign flag is 0x40.
199 static INT64
read_svli(struct patch_file_header
*ph
)
201 const BYTE
*vli
= ph
->src
;
204 ptrdiff_t limit
= min(ph
->end
- vli
, 9);
206 if (ph
->src
>= ph
->end
)
208 ph
->err
= ERROR_PATCH_CORRUPT
;
213 for (i
= 1; i
< limit
&& vli
[i
- 1] < 0x80; ++i
)
214 n
+= (INT64
)(vli
[i
] & 0x7F) << (7 * i
- 1);
216 if (vli
[i
- 1] < 0x80)
218 TRACE("exceeded maximum vli size\n");
219 ph
->err
= ERROR_PATCH_CORRUPT
;
231 static int __cdecl
compare_ignored_range(const void *a
, const void *b
)
233 LONG delta
= ((PATCH_IGNORE_RANGE
*)a
)->OffsetInOldFile
- ((PATCH_IGNORE_RANGE
*)b
)->OffsetInOldFile
;
241 static int __cdecl
compare_retained_range_old(const void *a
, const void *b
)
243 LONG delta
= ((PATCH_RETAIN_RANGE
*)a
)->OffsetInOldFile
- ((PATCH_RETAIN_RANGE
*)b
)->OffsetInOldFile
;
251 static int __cdecl
compare_retained_range_new(const void *a
, const void *b
)
253 LONG delta
= ((PATCH_RETAIN_RANGE
*)a
)->OffsetInNewFile
- ((PATCH_RETAIN_RANGE
*)b
)->OffsetInNewFile
;
261 static int read_header(struct patch_file_header
*ph
, const BYTE
*buf
, size_t size
)
266 ph
->end
= buf
+ size
;
268 ph
->file_table
= NULL
;
269 ph
->err
= ERROR_SUCCESS
;
271 if (read_raw_uint32(ph
) != PA19_FILE_MAGIC
)
273 TRACE("no PA19 signature\n");
274 ph
->err
= ERROR_PATCH_CORRUPT
;
278 ph
->flags
= read_raw_uint32(ph
);
279 if ((ph
->flags
& PATCH_OPTION_SUPPORTED_FLAGS
) != ph
->flags
)
281 FIXME("unsupported option flag(s): 0x%08lx\n", ph
->flags
& ~PATCH_OPTION_SUPPORTED_FLAGS
);
282 ph
->err
= ERROR_PATCH_PACKAGE_UNSUPPORTED
;
286 /* additional 32-bit flag field */
287 if (ph
->flags
& PATCH_OPTION_EXTRA_FLAGS
)
289 TRACE("skipping extra flag field\n");
290 (void)read_raw_uint32(ph
);
293 /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */
294 if(ph
->flags
& PATCH_OPTION_NO_TIMESTAMP
)
295 ph
->timestamp
= read_raw_uint32(ph
);
297 /* not sure what this value is for, but its absence seems to mean only that timestamps
298 * in the decompressed 32-bit exe are not modified */
299 if (!(ph
->flags
& PATCH_OPTION_NO_REBASE
))
301 TRACE("skipping rebase field\n");
305 ph
->patched_size
= (size_t)read_uvli(ph
);
306 TRACE("patched file size will be %u\n", (unsigned)ph
->patched_size
);
307 ph
->patched_crc32
= read_raw_uint32(ph
);
309 ph
->input_file_count
= *ph
->src
;
311 TRACE("patch supports %u old file(s)\n", ph
->input_file_count
);
312 /* if no old file used, input_file_count is still 1 */
313 if (ph
->input_file_count
== 0)
315 ph
->err
= ERROR_PATCH_CORRUPT
;
319 if (ph
->err
!= ERROR_SUCCESS
)
322 ph
->file_table
= heap_calloc(ph
->input_file_count
, sizeof(struct input_file_info
));
323 if (ph
->file_table
== NULL
)
325 ph
->err
= ERROR_OUTOFMEMORY
;
329 for (fileno
= 0; fileno
< ph
->input_file_count
; ++fileno
) {
330 struct input_file_info
*fi
= ph
->file_table
+ fileno
;
334 delta
= (ptrdiff_t)read_svli(ph
);
335 fi
->input_size
= ph
->patched_size
+ delta
;
337 fi
->crc32
= read_raw_uint32(ph
);
339 fi
->ignore_range_count
= *ph
->src
;
341 TRACE("found %u range(s) to ignore\n", fi
->ignore_range_count
);
343 for (i
= 0; i
< fi
->ignore_range_count
; ++i
) {
344 PATCH_IGNORE_RANGE
*ir
= fi
->ignore_table
+ i
;
346 ir
->OffsetInOldFile
= (LONG
)read_svli(ph
);
347 ir
->LengthInBytes
= (ULONG
)read_uvli(ph
);
351 ir
->OffsetInOldFile
+= fi
->ignore_table
[i
- 1].OffsetInOldFile
352 + fi
->ignore_table
[i
- 1].LengthInBytes
;
354 if (ir
->OffsetInOldFile
> fi
->input_size
355 || ir
->OffsetInOldFile
+ ir
->LengthInBytes
> fi
->input_size
356 || ir
->LengthInBytes
> fi
->input_size
)
358 ph
->err
= ERROR_PATCH_CORRUPT
;
363 fi
->retain_range_count
= *ph
->src
;
365 TRACE("found %u range(s) to retain\n", fi
->retain_range_count
);
367 for (i
= 0; i
< fi
->retain_range_count
; ++i
) {
368 PATCH_RETAIN_RANGE
*rr
= fi
->retain_table
+ i
;
370 rr
->OffsetInOldFile
= (LONG
)read_svli(ph
);
372 rr
->OffsetInOldFile
+=
373 fi
->retain_table
[i
- 1].OffsetInOldFile
+ fi
->retain_table
[i
- 1].LengthInBytes
;
375 rr
->OffsetInNewFile
= rr
->OffsetInOldFile
+ (LONG
)read_svli(ph
);
376 rr
->LengthInBytes
= (ULONG
)read_uvli(ph
);
378 if (rr
->OffsetInOldFile
> fi
->input_size
379 || rr
->OffsetInOldFile
+ rr
->LengthInBytes
> fi
->input_size
380 || rr
->OffsetInNewFile
> ph
->patched_size
381 || rr
->OffsetInNewFile
+ rr
->LengthInBytes
> ph
->patched_size
382 || rr
->LengthInBytes
> ph
->patched_size
)
384 ph
->err
= ERROR_PATCH_CORRUPT
;
388 /* ranges in new file must be equal and in the same order for all source files */
391 PATCH_RETAIN_RANGE
*rr_0
= ph
->file_table
[0].retain_table
+ i
;
392 if (rr
->OffsetInNewFile
!= rr_0
->OffsetInNewFile
393 || rr
->LengthInBytes
!= rr_0
->LengthInBytes
)
395 ph
->err
= ERROR_PATCH_CORRUPT
;
401 fi
->unknown_count
= (size_t)read_uvli(ph
);
402 if (fi
->unknown_count
)
404 FIXME("special processing of 32-bit executables not implemented.\n");
405 ph
->err
= ERROR_PATCH_PACKAGE_UNSUPPORTED
;
408 fi
->stream_size
= (size_t)read_uvli(ph
);
411 for (fileno
= 0; fileno
< ph
->input_file_count
; ++fileno
)
413 struct input_file_info
*fi
= ph
->file_table
+ fileno
;
415 qsort(fi
->ignore_table
, fi
->ignore_range_count
, sizeof(fi
->ignore_table
[0]), compare_ignored_range
);
416 qsort(fi
->retain_table
, fi
->retain_range_count
, sizeof(fi
->retain_table
[0]), compare_retained_range_old
);
418 fi
->stream_start
= ph
->src
;
419 ph
->src
+= fi
->stream_size
;
422 /* skip the crc adjustment field */
423 ph
->src
= min(ph
->src
+ 4, ph
->end
);
426 UINT32 crc
= RtlComputeCrc32(0, buf
, ph
->src
- buf
) ^ 0xFFFFFFFF;
429 TRACE("patch file crc32 failed\n");
430 if (ph
->src
< ph
->end
)
431 FIXME("probable header parsing error\n");
432 ph
->err
= ERROR_PATCH_CORRUPT
;
436 return (ph
->err
== ERROR_SUCCESS
) ? 0 : -1;
439 static void free_header(struct patch_file_header
*ph
)
441 heap_free(ph
->file_table
);
444 #define TICKS_PER_SEC 10000000
445 #define SEC_TO_UNIX_EPOCH ((369 * 365 + 89) * (ULONGLONG)86400)
447 static void posix_time_to_file_time(ULONG timestamp
, FILETIME
*ft
)
449 UINT64 ticks
= ((UINT64
)timestamp
+ SEC_TO_UNIX_EPOCH
) * TICKS_PER_SEC
;
450 ft
->dwLowDateTime
= (DWORD
)ticks
;
451 ft
->dwHighDateTime
= (DWORD
)(ticks
>> 32);
454 /* Get the next range to ignore in the old file.
455 * fi->next_i must be initialized before use */
456 static ULONG
next_ignored_range(const struct input_file_info
*fi
, size_t index
, ULONG old_file_size
, ULONG
*end
)
458 ULONG start
= old_file_size
;
459 *end
= old_file_size
;
460 /* if patching is unnecessary, the ignored ranges are skipped during crc calc */
461 if (fi
->next_i
< fi
->ignore_range_count
&& fi
->stream_size
!= 0)
463 start
= fi
->ignore_table
[fi
->next_i
].OffsetInOldFile
;
464 *end
= max(start
+ fi
->ignore_table
[fi
->next_i
].LengthInBytes
, index
);
465 start
= max(start
, index
);
470 /* Get the next range to retain from the old file.
471 * fi->next_r must be initialized before use */
472 static ULONG
next_retained_range_old(const struct input_file_info
*fi
, size_t index
, ULONG old_file_size
, ULONG
*end
)
474 ULONG start
= old_file_size
;
475 *end
= old_file_size
;
476 if (fi
->next_r
< fi
->retain_range_count
)
478 start
= fi
->retain_table
[fi
->next_r
].OffsetInOldFile
;
479 *end
= max(start
+ fi
->retain_table
[fi
->next_r
].LengthInBytes
, index
);
480 start
= max(start
, index
);
485 /* Get the next range to retain in the new file.
486 * fi->next_r must be initialized before use */
487 static ULONG
next_retained_range_new(const struct input_file_info
*fi
, size_t index
, ULONG new_file_size
, ULONG
*end
)
489 ULONG start
= new_file_size
;
490 *end
= new_file_size
;
491 if (fi
->next_r
< fi
->retain_range_count
)
493 start
= fi
->retain_table
[fi
->next_r
].OffsetInNewFile
;
494 *end
= max(start
+ fi
->retain_table
[fi
->next_r
].LengthInBytes
, index
);
495 start
= max(start
, index
);
500 /* Find the next range in the old file which must be assumed zero-filled during crc32 calc
502 static ULONG
next_zeroed_range(struct input_file_info
*fi
, size_t index
, ULONG old_file_size
, ULONG
*end
)
504 ULONG start
= old_file_size
;
510 *end
= old_file_size
;
512 start_i
= next_ignored_range(fi
, index
, old_file_size
, &end_i
);
513 start_r
= next_retained_range_old(fi
, index
, old_file_size
, &end_r
);
515 if (start_i
< start_r
)
530 /* Use the crc32 of the input file to match the file with an entry in the patch file table
532 struct input_file_info
*find_matching_old_file(const struct patch_file_header
*ph
, const BYTE
*old_file_view
, ULONG old_file_size
)
536 for (i
= 0; i
< ph
->input_file_count
; ++i
)
541 if (ph
->file_table
[i
].input_size
!= old_file_size
)
544 ph
->file_table
[i
].next_i
= 0;
545 for (index
= 0; index
< old_file_size
; )
548 ULONG start
= next_zeroed_range(ph
->file_table
+ i
, index
, old_file_size
, &end
);
549 crc32
= RtlComputeCrc32(crc32
, old_file_view
+ index
, start
- index
);
550 crc32
= compute_zero_crc32(crc32
, end
- start
);
553 if (ph
->file_table
[i
].crc32
== crc32
)
554 return ph
->file_table
+ i
;
559 /* Zero-fill ignored ranges in the old file data for decoder matching
561 static void zero_fill_ignored_ranges(BYTE
*old_file_buf
, const struct input_file_info
*fi
)
564 for (i
= 0; i
< fi
->ignore_range_count
; ++i
)
566 memset(old_file_buf
+ fi
->ignore_table
[i
].OffsetInOldFile
,
568 fi
->ignore_table
[i
].LengthInBytes
);
572 /* Zero-fill retained ranges in the old file data for decoder matching
574 static void zero_fill_retained_ranges(BYTE
*old_file_buf
, BYTE
*new_file_buf
, const struct input_file_info
*fi
)
577 for (i
= 0; i
< fi
->retain_range_count
; ++i
)
579 memset(old_file_buf
+ fi
->retain_table
[i
].OffsetInOldFile
,
581 fi
->retain_table
[i
].LengthInBytes
);
585 /* Copy the retained ranges to the new file buffer
587 static void apply_retained_ranges(const BYTE
*old_file_buf
, BYTE
*new_file_buf
, const struct input_file_info
*fi
)
591 if (old_file_buf
== NULL
)
594 for (i
= 0; i
< fi
->retain_range_count
; ++i
)
596 memcpy(new_file_buf
+ fi
->retain_table
[i
].OffsetInNewFile
,
597 old_file_buf
+ fi
->retain_table
[i
].OffsetInOldFile
,
598 fi
->retain_table
[i
].LengthInBytes
);
602 /* Compute the crc32 for the new file, assuming zero for the retained ranges
604 static DWORD
compute_target_crc32(struct input_file_info
*fi
, const BYTE
*new_file_buf
, ULONG new_file_size
)
609 qsort(fi
->retain_table
, fi
->retain_range_count
, sizeof(fi
->retain_table
[0]), compare_retained_range_new
);
612 for (index
= 0; index
< new_file_size
; )
615 ULONG start
= next_retained_range_new(fi
, index
, new_file_size
, &end
);
617 crc32
= RtlComputeCrc32(crc32
, new_file_buf
+ index
, start
- index
);
618 crc32
= compute_zero_crc32(crc32
, end
- start
);
624 DWORD
apply_patch_to_file_by_buffers(const BYTE
*patch_file_view
, const ULONG patch_file_size
,
625 const BYTE
*old_file_view
, ULONG old_file_size
,
626 BYTE
**pnew_file_buf
, const ULONG new_file_buf_size
, ULONG
*new_file_size
,
627 FILETIME
*new_file_time
,
628 const ULONG apply_option_flags
,
629 PATCH_PROGRESS_CALLBACK
*progress_fn
, void *progress_ctx
,
630 const BOOL test_header_only
)
632 DWORD err
= ERROR_SUCCESS
;
633 struct input_file_info
*file_info
;
634 struct patch_file_header ph
;
636 BYTE
*new_file_buf
= NULL
;
637 BYTE
*decode_buf
= NULL
;
639 if (pnew_file_buf
== NULL
)
641 if (!test_header_only
&& !(apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
642 return ERROR_INVALID_PARAMETER
;
646 new_file_buf
= *pnew_file_buf
;
649 if (old_file_view
== NULL
)
652 if (read_header(&ph
, patch_file_view
, patch_file_size
))
655 goto free_patch_header
;
658 if (new_file_size
!= NULL
)
659 *new_file_size
= (ULONG
)ph
.patched_size
;
661 if (new_file_buf
!= NULL
&& new_file_buf_size
< ph
.patched_size
)
663 err
= ERROR_INSUFFICIENT_BUFFER
;
664 goto free_patch_header
;
667 file_info
= find_matching_old_file(&ph
, old_file_view
, old_file_size
);
668 if (file_info
== NULL
)
670 err
= ERROR_PATCH_WRONG_FILE
;
671 goto free_patch_header
;
673 if (file_info
->input_size
!= old_file_size
)
675 err
= ERROR_PATCH_CORRUPT
;
676 goto free_patch_header
;
678 if (file_info
->stream_size
== 0 && (apply_option_flags
& APPLY_OPTION_FAIL_IF_EXACT
))
680 err
= ERROR_PATCH_NOT_NECESSARY
;
681 goto free_patch_header
;
683 if (file_info
->stream_size
!= 0
684 && file_info
->input_size
> ((ph
.flags
& PATCH_OPTION_USE_LZX_LARGE
) ? MAX_LARGE_WINDOW
: MAX_NORMAL_WINDOW
))
686 /* interleaved by default but not the same as PATCH_OPTION_INTERLEAVE_FILES */
687 FIXME("interleaved LZXD decompression is not supported.\n");
688 err
= ERROR_PATCH_PACKAGE_UNSUPPORTED
;
689 goto free_patch_header
;
692 if (test_header_only
)
693 goto free_patch_header
;
695 /* missing lzxd stream means it's a header test extract */
696 if (file_info
->stream_start
+ file_info
->stream_size
> ph
.end
)
698 err
= ERROR_PATCH_NOT_AVAILABLE
;
699 goto free_patch_header
;
702 buf_size
= old_file_size
+ ph
.patched_size
;
703 decode_buf
= new_file_buf
;
704 if (new_file_buf
== NULL
|| new_file_buf_size
< buf_size
)
706 /* decode_buf must have room for both files, so allocate a new buffer if
707 * necessary. This will be returned to the caller if new_file_buf == NULL */
708 decode_buf
= VirtualAlloc(NULL
, buf_size
, MEM_COMMIT
, PAGE_READWRITE
);
709 if (decode_buf
== NULL
)
711 err
= GetLastError();
712 goto free_patch_header
;
716 if (old_file_view
!= NULL
)
717 memcpy(decode_buf
, old_file_view
, file_info
->input_size
);
719 zero_fill_ignored_ranges(decode_buf
, file_info
);
720 zero_fill_retained_ranges(decode_buf
, decode_buf
+ file_info
->input_size
, file_info
);
722 if (file_info
->stream_size
!= 0)
724 err
= decode_lzxd_stream(file_info
->stream_start
, file_info
->stream_size
,
725 decode_buf
, ph
.patched_size
, file_info
->input_size
,
726 ph
.flags
& PATCH_OPTION_USE_LZX_LARGE
,
727 progress_fn
, progress_ctx
);
729 else if (file_info
->input_size
== ph
.patched_size
)
731 /* files are identical so copy old to new. copying is avoidable but rare */
732 memcpy(decode_buf
+ file_info
->input_size
, decode_buf
, ph
.patched_size
);
736 err
= ERROR_PATCH_CORRUPT
;
737 goto free_decode_buf
;
740 if(err
!= ERROR_SUCCESS
)
742 if (err
== ERROR_PATCH_DECODE_FAILURE
)
743 FIXME("decode failure: data corruption or bug.\n");
744 goto free_decode_buf
;
747 apply_retained_ranges(old_file_view
, decode_buf
+ file_info
->input_size
, file_info
);
749 if (ph
.patched_crc32
!= compute_target_crc32(file_info
, decode_buf
+ file_info
->input_size
, ph
.patched_size
))
751 err
= ERROR_PATCH_CORRUPT
;
752 goto free_decode_buf
;
755 /* retained ranges must be ignored for this test */
756 if ((apply_option_flags
& APPLY_OPTION_FAIL_IF_EXACT
)
757 && file_info
->input_size
== ph
.patched_size
758 && memcmp(decode_buf
, decode_buf
+ file_info
->input_size
, ph
.patched_size
) == 0)
760 err
= ERROR_PATCH_NOT_NECESSARY
;
761 goto free_decode_buf
;
764 if (!(apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
766 if (new_file_buf
== NULL
)
768 /* caller will VirtualFree the buffer */
769 new_file_buf
= decode_buf
;
770 *pnew_file_buf
= new_file_buf
;
772 memmove(new_file_buf
, decode_buf
+ old_file_size
, ph
.patched_size
);
775 if (new_file_time
!= NULL
)
777 new_file_time
->dwLowDateTime
= 0;
778 new_file_time
->dwHighDateTime
= 0;
780 /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */
781 if (ph
.flags
& PATCH_OPTION_NO_TIMESTAMP
)
782 posix_time_to_file_time(ph
.timestamp
, new_file_time
);
786 if(decode_buf
!= NULL
&& decode_buf
!= new_file_buf
)
787 VirtualFree(decode_buf
, 0, MEM_RELEASE
);
795 BOOL
apply_patch_to_file_by_handles(HANDLE patch_file_hndl
, HANDLE old_file_hndl
, HANDLE new_file_hndl
,
796 const ULONG apply_option_flags
,
797 PATCH_PROGRESS_CALLBACK
*progress_fn
, void *progress_ctx
,
798 const BOOL test_header_only
)
800 LARGE_INTEGER patch_size
;
801 LARGE_INTEGER old_size
;
803 HANDLE old_map
= NULL
;
805 const BYTE
*old_buf
= NULL
;
806 BYTE
*new_buf
= NULL
;
810 DWORD err
= ERROR_SUCCESS
;
812 /* truncate the output file if required, or set the handle to invalid */
813 if (test_header_only
|| (apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
815 new_file_hndl
= INVALID_HANDLE_VALUE
;
817 else if (SetFilePointer(new_file_hndl
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
818 || !SetEndOfFile(new_file_hndl
))
820 err
= GetLastError();
824 if (patch_file_hndl
== INVALID_HANDLE_VALUE
)
826 SetLastError(ERROR_INVALID_HANDLE
);
830 old_size
.QuadPart
= 0;
831 if (!GetFileSizeEx(patch_file_hndl
, &patch_size
)
832 || (old_file_hndl
!= INVALID_HANDLE_VALUE
&& !GetFileSizeEx(old_file_hndl
, &old_size
)))
834 /* Last error set by API */
838 patch_map
= CreateFileMappingW(patch_file_hndl
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
839 if (patch_map
== NULL
)
841 /* Last error set by API */
845 if (old_file_hndl
!= INVALID_HANDLE_VALUE
)
847 old_map
= CreateFileMappingW(old_file_hndl
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
850 err
= GetLastError();
851 goto close_patch_map
;
855 patch_buf
= MapViewOfFile(patch_map
, FILE_MAP_READ
, 0, 0, (SIZE_T
)patch_size
.QuadPart
);
856 if (patch_buf
== NULL
)
858 err
= GetLastError();
862 if (old_size
.QuadPart
)
864 old_buf
= MapViewOfFile(old_map
, FILE_MAP_READ
, 0, 0, (SIZE_T
)old_size
.QuadPart
);
867 err
= GetLastError();
868 goto unmap_patch_buf
;
872 err
= apply_patch_to_file_by_buffers(patch_buf
, (ULONG
)patch_size
.QuadPart
,
873 old_buf
, (ULONG
)old_size
.QuadPart
,
874 &new_buf
, 0, &new_size
,
876 apply_option_flags
, progress_fn
, progress_ctx
,
884 if(new_file_hndl
!= INVALID_HANDLE_VALUE
)
887 res
= WriteFile(new_file_hndl
, new_buf
, new_size
, &Written
, NULL
);
890 err
= GetLastError();
891 else if (new_time
.dwLowDateTime
|| new_time
.dwHighDateTime
)
892 SetFileTime(new_file_hndl
, &new_time
, NULL
, &new_time
);
897 VirtualFree(new_buf
, 0, MEM_RELEASE
);
900 UnmapViewOfFile(old_buf
);
903 UnmapViewOfFile(patch_buf
);
907 CloseHandle(old_map
);
910 CloseHandle(patch_map
);
917 BOOL
apply_patch_to_file(LPCWSTR patch_file_name
, LPCWSTR old_file_name
, LPCWSTR new_file_name
,
918 const ULONG apply_option_flags
,
919 PATCH_PROGRESS_CALLBACK
*progress_fn
, void *progress_ctx
,
920 const BOOL test_header_only
)
923 HANDLE old_hndl
= INVALID_HANDLE_VALUE
;
924 HANDLE new_hndl
= INVALID_HANDLE_VALUE
;
926 DWORD err
= ERROR_SUCCESS
;
928 patch_hndl
= CreateFileW(patch_file_name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
929 if (patch_hndl
== INVALID_HANDLE_VALUE
)
931 /* last error set by CreateFileW */
935 if (old_file_name
!= NULL
)
937 old_hndl
= CreateFileW(old_file_name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
938 if (old_hndl
== INVALID_HANDLE_VALUE
)
940 err
= GetLastError();
941 goto close_patch_file
;
945 if (!test_header_only
&& !(apply_option_flags
& APPLY_OPTION_TEST_ONLY
))
947 new_hndl
= CreateFileW(new_file_name
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
948 if (new_hndl
== INVALID_HANDLE_VALUE
)
950 err
= GetLastError();
955 res
= apply_patch_to_file_by_handles(patch_hndl
, old_hndl
, new_hndl
, apply_option_flags
, progress_fn
, progress_ctx
, test_header_only
);
957 err
= GetLastError();
959 if (new_hndl
!= INVALID_HANDLE_VALUE
)
961 CloseHandle(new_hndl
);
963 DeleteFileW(new_file_name
);
967 if (old_hndl
!= INVALID_HANDLE_VALUE
)
968 CloseHandle(old_hndl
);
971 CloseHandle(patch_hndl
);
973 /* set last error even on success as per windows */