bsdiff/
patch.rs

1/*-
2 * Copyright 2003-2005 Colin Percival
3 * Copyright 2012 Matthew Endsley
4 * Modified 2017 Pieter-Jan Briers
5 * Modified 2021 Kornel Lesinski
6 * All rights reserved
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted providing that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30use std::io;
31use std::io::Read;
32
33/// Apply a patch to an "old" file, returning the "new" file.
34///
35/// `old` is the old file, `patch` will be read from with the patch,`new` is the buffer that will be written into.
36pub fn patch<T: Read>(old: &[u8], patch: &mut T, new: &mut Vec<u8>) -> io::Result<()> {
37    let mut oldpos: usize = 0;
38    loop {
39        // Read control data
40        let mut buf = [0; 24];
41        if read_or_eof(patch, &mut buf)? {
42            return Ok(());
43        }
44
45        // only seek can be negative
46        let mix_len = usize::try_from(u64::from_le_bytes(buf[0..8].try_into().unwrap())).map_err(|_| io::ErrorKind::InvalidData)?;
47        let copy_len = usize::try_from(u64::from_le_bytes(buf[8..16].try_into().unwrap())).map_err(|_| io::ErrorKind::InvalidData)?;
48        let seek_len = offtin(buf[16..24].try_into().unwrap());
49
50        // Read diff string and literal data at once
51        let to_read = copy_len
52            .checked_add(mix_len)
53            .ok_or(io::ErrorKind::InvalidData)?;
54        let mix_start = new.len();
55        let has_read = patch.take(to_read as u64).read_to_end(new)?;
56
57        // oldpos needs to be checked before the for loop to optimize it better
58        if has_read != to_read {
59            return Err(io::ErrorKind::UnexpectedEof.into());
60        }
61
62        let mix_end = mix_start.checked_add(mix_len).ok_or(io::ErrorKind::InvalidData)?;
63        let mix_slice = new.get_mut(mix_start..mix_end).ok_or(io::ErrorKind::UnexpectedEof)?;
64
65        let oldpos_end = oldpos.checked_add(mix_len).ok_or(io::ErrorKind::InvalidData)?;
66        let old_slice = old.get(oldpos ..oldpos_end).ok_or(io::ErrorKind::UnexpectedEof)?;
67
68        for (n, o) in mix_slice.iter_mut().zip(old_slice.iter().copied()) {
69            *n = n.wrapping_add(o);
70        }
71
72        // Adjust pointers
73        oldpos += mix_len;
74        oldpos = (oldpos as i64)
75            .checked_add(seek_len)
76            .and_then(|n| usize::try_from(n).ok())
77            .ok_or(io::ErrorKind::InvalidData)?;
78    }
79}
80
81/// It allows EOF only before the first byte.
82fn read_or_eof<T: Read>(reader: &mut T, buf: &mut [u8; 24]) -> io::Result<bool> {
83    let mut tmp = &mut buf[..];
84    loop {
85        match reader.read(tmp) {
86            Ok(0) => {
87                return if tmp.len() == 24 {
88                    Ok(true)
89                } else {
90                    Err(io::ErrorKind::UnexpectedEof.into())
91                }
92            }
93            Ok(n) => {
94                if n >= tmp.len() {
95                    return Ok(false);
96                }
97                tmp = &mut tmp[n..];
98            }
99            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
100            Err(e) => return Err(e),
101        }
102    }
103}
104
105/// Reads sign-magnitude i64 little-endian
106#[inline]
107fn offtin(buf: [u8; 8]) -> i64 {
108    let y = i64::from_le_bytes(buf);
109    if 0 == y & (1 << 63) {
110        y
111    } else {
112        -(y & !(1 << 63))
113    }
114}