2 * Copyright 2003,2004 Colin Percival
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
27 * 2009-03-31 - Change to use Streams. Move CRC code to crc.{h,cc}
28 * --Stephen Adams <sra@chromium.org>
29 * 2013-04-10 - Add wrapper method to apply a patch to files directly.
30 * --Joshua Pawlicki <waffles@chromium.org>
33 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
34 // Use of this source code is governed by a BSD-style license that can be
35 // found in the LICENSE file.
37 #include "courgette/third_party/bsdiff.h"
39 #include "base/files/memory_mapped_file.h"
40 #include "courgette/crc.h"
41 #include "courgette/streams.h"
45 BSDiffStatus
MBS_ReadHeader(SourceStream
* stream
, MBSPatchHeader
* header
) {
46 if (!stream
->Read(header
->tag
, sizeof(header
->tag
))) return READ_ERROR
;
47 if (!stream
->ReadVarint32(&header
->slen
)) return READ_ERROR
;
48 if (!stream
->ReadVarint32(&header
->scrc32
)) return READ_ERROR
;
49 if (!stream
->ReadVarint32(&header
->dlen
)) return READ_ERROR
;
51 // The string will have a NUL terminator that we don't use, hence '-1'.
52 COMPILE_ASSERT(sizeof(MBS_PATCH_HEADER_TAG
) - 1 == sizeof(header
->tag
),
53 MBS_PATCH_HEADER_TAG_must_match_header_field_size
);
54 if (memcmp(header
->tag
, MBS_PATCH_HEADER_TAG
, 8) != 0)
55 return UNEXPECTED_ERROR
;
60 BSDiffStatus
MBS_ApplyPatch(const MBSPatchHeader
*header
,
61 SourceStream
* patch_stream
,
62 const uint8
* old_start
, size_t old_size
,
63 SinkStream
* new_stream
) {
64 const uint8
* old_end
= old_start
+ old_size
;
66 SourceStreamSet patch_streams
;
67 if (!patch_streams
.Init(patch_stream
))
70 SourceStream
* control_stream_copy_counts
= patch_streams
.stream(0);
71 SourceStream
* control_stream_extra_counts
= patch_streams
.stream(1);
72 SourceStream
* control_stream_seeks
= patch_streams
.stream(2);
73 SourceStream
* diff_skips
= patch_streams
.stream(3);
74 SourceStream
* diff_bytes
= patch_streams
.stream(4);
75 SourceStream
* extra_bytes
= patch_streams
.stream(5);
77 const uint8
* extra_start
= extra_bytes
->Buffer();
78 const uint8
* extra_end
= extra_start
+ extra_bytes
->Remaining();
79 const uint8
* extra_position
= extra_start
;
81 const uint8
* old_position
= old_start
;
83 if (header
->dlen
&& !new_stream
->Reserve(header
->dlen
))
86 uint32 pending_diff_zeros
= 0;
87 if (!diff_skips
->ReadVarint32(&pending_diff_zeros
))
88 return UNEXPECTED_ERROR
;
90 while (!control_stream_copy_counts
->Empty()) {
91 uint32 copy_count
, extra_count
;
92 int32 seek_adjustment
;
93 if (!control_stream_copy_counts
->ReadVarint32(©_count
))
94 return UNEXPECTED_ERROR
;
95 if (!control_stream_extra_counts
->ReadVarint32(&extra_count
))
96 return UNEXPECTED_ERROR
;
97 if (!control_stream_seeks
->ReadVarint32Signed(&seek_adjustment
))
98 return UNEXPECTED_ERROR
;
100 #ifdef DEBUG_bsmedberg
101 printf("Applying block: copy: %-8u extra: %-8u seek: %+i\n",
102 copy_count
, extra_count
, seek_adjustment
);
104 // Byte-wise arithmetically add bytes from old file to bytes from the diff
106 if (copy_count
> static_cast<size_t>(old_end
- old_position
))
107 return UNEXPECTED_ERROR
;
109 // Add together bytes from the 'old' file and the 'diff' stream.
110 for (size_t i
= 0; i
< copy_count
; ++i
) {
112 if (pending_diff_zeros
) {
113 --pending_diff_zeros
;
115 if (!diff_skips
->ReadVarint32(&pending_diff_zeros
))
116 return UNEXPECTED_ERROR
;
117 if (!diff_bytes
->Read(&diff_byte
, 1))
118 return UNEXPECTED_ERROR
;
120 uint8 byte
= old_position
[i
] + diff_byte
;
121 if (!new_stream
->Write(&byte
, 1))
124 old_position
+= copy_count
;
126 // Copy bytes from the extra block.
127 if (extra_count
> static_cast<size_t>(extra_end
- extra_position
))
128 return UNEXPECTED_ERROR
;
130 if (!new_stream
->Write(extra_position
, extra_count
))
133 extra_position
+= extra_count
;
135 // "seek" forwards (or backwards) in oldfile.
136 if (old_position
+ seek_adjustment
< old_start
||
137 old_position
+ seek_adjustment
> old_end
)
138 return UNEXPECTED_ERROR
;
140 old_position
+= seek_adjustment
;
143 if (!control_stream_copy_counts
->Empty() ||
144 !control_stream_extra_counts
->Empty() ||
145 !control_stream_seeks
->Empty() ||
146 !diff_skips
->Empty() ||
147 !diff_bytes
->Empty() ||
148 !extra_bytes
->Empty())
149 return UNEXPECTED_ERROR
;
154 BSDiffStatus
ApplyBinaryPatch(SourceStream
* old_stream
,
155 SourceStream
* patch_stream
,
156 SinkStream
* new_stream
) {
157 MBSPatchHeader header
;
158 BSDiffStatus ret
= MBS_ReadHeader(patch_stream
, &header
);
159 if (ret
!= OK
) return ret
;
161 const uint8
* old_start
= old_stream
->Buffer();
162 size_t old_size
= old_stream
->Remaining();
164 if (old_size
!= header
.slen
) return UNEXPECTED_ERROR
;
166 if (CalculateCrc(old_start
, old_size
) != header
.scrc32
)
169 MBS_ApplyPatch(&header
, patch_stream
, old_start
, old_size
, new_stream
);
174 BSDiffStatus
ApplyBinaryPatch(const base::FilePath
& old_file_path
,
175 const base::FilePath
& patch_file_path
,
176 const base::FilePath
& new_file_path
) {
177 // Set up the old stream.
178 base::MemoryMappedFile old_file
;
179 if (!old_file
.Initialize(old_file_path
)) {
182 SourceStream old_file_stream
;
183 old_file_stream
.Init(old_file
.data(), old_file
.length());
185 // Set up the patch stream.
186 base::MemoryMappedFile patch_file
;
187 if (!patch_file
.Initialize(patch_file_path
)) {
190 SourceStream patch_file_stream
;
191 patch_file_stream
.Init(patch_file
.data(), patch_file
.length());
193 // Set up the new stream and apply the patch.
194 SinkStream new_sink_stream
;
195 BSDiffStatus status
= ApplyBinaryPatch(&old_file_stream
,
202 // Write the stream to disk.
203 int written
= base::WriteFile(
205 reinterpret_cast<const char*>(new_sink_stream
.Buffer()),
206 static_cast<int>(new_sink_stream
.Length()));
207 if (written
!= static_cast<int>(new_sink_stream
.Length()))