diff --git a/src/builder.rs b/src/builder.rs index a26eb31d..4a581ce7 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -120,8 +120,9 @@ impl Builder { /// header will be modified. /// /// Then it will append the header, followed by contents of the stream - /// specified by `data`. To produce a valid archive the `size` field of - /// `header` must be the same as the length of the stream that's being + /// specified by `data`, with the `size` field of `header` enforced as an + /// upper limit. To produce a valid archive the `size` field of `header` + /// must be less than or equal to the length of the stream that's being /// written. /// /// Note that this will not attempt to seek the archive to a valid position, @@ -406,9 +407,13 @@ impl Builder { } } -fn append(mut dst: &mut dyn Write, header: &Header, mut data: &mut dyn Read) -> io::Result<()> { +fn append(mut dst: &mut dyn Write, header: &Header, data: &mut dyn Read) -> io::Result<()> { dst.write_all(header.as_bytes())?; - let len = io::copy(&mut data, &mut dst)?; + // Guards against there being more data than the header indicates in the case where contents + // have been appended to the file since the header was created. + let size = header.size()?; + let mut limited = data.take(size); + let len = io::copy(&mut limited, &mut dst)?; // Pad with zeros if necessary. let buf = [0; 512]; diff --git a/tests/all.rs b/tests/all.rs index 11103bd6..a5fc97c6 100644 --- a/tests/all.rs +++ b/tests/all.rs @@ -1279,10 +1279,12 @@ fn append_long_multibyte() { let mut x = tar::Builder::new(Vec::new()); let mut name = String::new(); let data: &[u8] = &[]; + let mut header = Header::new_gnu(); + header.set_size(data.len() as u64); for _ in 0..512 { name.push('a'); name.push('ð‘¢®'); - x.append_data(&mut Header::new_gnu(), &name, data).unwrap(); + x.append_data(&mut header, &name, data).unwrap(); name.pop(); } } @@ -1385,3 +1387,31 @@ fn header_size_overflow() { err ); } + +#[test] +fn file_contents_appended_after_header_creation() { + let mut data = String::from("Hello"); + + let mut header = Header::new_gnu(); + header.set_size(data.len() as u64); + header.set_cksum(); + + // Additional data is appended after header creation + data.push_str(", World!"); + + let mut ar = Builder::new(Vec::new()); + t!(ar.append_data(&mut header, "test2", data.as_bytes())); + + let raw = t!(ar.into_inner()); + let mut ar = Archive::new(Cursor::new(raw)); + let mut entries = t!(ar.entries()); + let entry = t!(entries.next().unwrap()); + let offset = entry.raw_file_position(); + let mut raw = ar.into_inner(); + + let mut s = String::new(); + raw.set_position(offset); + t!(raw.read_to_string(&mut s)); + let s = s.trim_end_matches(char::from(0)); + assert_eq!(s, "Hello"); +}