22//! tarball in Unix operating systems.
33
44use std:: fs:: File ;
5- use std:: io:: { Read , Seek , SeekFrom } ;
5+ use std:: io:: Read ;
66use std:: path:: Path ;
77
88use super :: { Archive , ArchiveError , Origin } ;
99use attohttpc:: header:: HeaderMap ;
1010use flate2:: read:: GzDecoder ;
1111use fs_utils:: ensure_containing_dir_exists;
12- use headers:: { AcceptRanges , ContentLength , Header , HeaderMapExt , Range } ;
12+ use headers:: { ContentLength , Header , HeaderMapExt } ;
1313use progress_read:: ProgressRead ;
1414use tee:: TeeReader ;
1515
1616/// A Node installation tarball.
1717pub struct Tarball {
1818 compressed_size : u64 ,
19- // Some servers don't return the right data for byte range queries, so
20- // getting the uncompressed archive size for tarballs is an Option.
21- // If the uncompressed size is not available, the compressed size will be
22- // used for the download/unpack progress indicator, so that will be slightly off.
23- uncompressed_size : Option < u64 > ,
2419 data : Box < dyn Read > ,
2520 origin : Origin ,
2621}
@@ -36,11 +31,9 @@ fn content_length(headers: &HeaderMap) -> Result<u64, ArchiveError> {
3631
3732impl Tarball {
3833 /// Loads a tarball from the specified file.
39- pub fn load ( mut source : File ) -> Result < Box < dyn Archive > , ArchiveError > {
40- let uncompressed_size = load_uncompressed_size ( & mut source) ;
34+ pub fn load ( source : File ) -> Result < Box < dyn Archive > , ArchiveError > {
4135 let compressed_size = source. metadata ( ) ?. len ( ) ;
4236 Ok ( Box :: new ( Tarball {
43- uncompressed_size,
4437 compressed_size,
4538 data : Box :: new ( source) ,
4639 origin : Origin :: Local ,
@@ -58,18 +51,12 @@ impl Tarball {
5851 }
5952
6053 let compressed_size = content_length ( & headers) ?;
61- let uncompressed_size = if accepts_byte_ranges ( & headers) {
62- fetch_uncompressed_size ( url, compressed_size)
63- } else {
64- None
65- } ;
6654
6755 ensure_containing_dir_exists ( & cache_file) ?;
6856 let file = File :: create ( cache_file) ?;
6957 let data = Box :: new ( TeeReader :: new ( response, file) ) ;
7058
7159 Ok ( Box :: new ( Tarball {
72- uncompressed_size,
7360 compressed_size,
7461 data,
7562 origin : Origin :: Remote ,
@@ -81,16 +68,13 @@ impl Archive for Tarball {
8168 fn compressed_size ( & self ) -> u64 {
8269 self . compressed_size
8370 }
84- fn uncompressed_size ( & self ) -> Option < u64 > {
85- self . uncompressed_size
86- }
8771 fn unpack (
8872 self : Box < Self > ,
8973 dest : & Path ,
9074 progress : & mut dyn FnMut ( & ( ) , usize ) ,
9175 ) -> Result < ( ) , ArchiveError > {
92- let decoded = GzDecoder :: new ( self . data ) ;
93- let mut tarball = tar:: Archive :: new ( ProgressRead :: new ( decoded, ( ) , progress ) ) ;
76+ let decoded = GzDecoder :: new ( ProgressRead :: new ( self . data , ( ) , progress ) ) ;
77+ let mut tarball = tar:: Archive :: new ( decoded) ;
9478 tarball. unpack ( dest) ?;
9579 Ok ( ( ) )
9680 }
@@ -99,78 +83,6 @@ impl Archive for Tarball {
9983 }
10084}
10185
102- // From http://www.gzip.org/zlib/rfc-gzip.html#member-format
103- //
104- // 0 1 2 3 4 5 6 7
105- // +---+---+---+---+---+---+---+---+
106- // | CRC32 | ISIZE |
107- // +---+---+---+---+---+---+---+---+
108- //
109- // ISIZE (Input SIZE)
110- // This contains the size of the original (uncompressed) input data modulo 2^32.
111-
112- /// Fetches just the `isize` field (the field that indicates the uncompressed size)
113- /// of a gzip file from a URL. This makes two round-trips to the server but avoids
114- /// downloading the entire gzip file. For very small files it's unlikely to be
115- /// more efficient than simply downloading the entire file up front.
116- fn fetch_isize ( url : & str , len : u64 ) -> Result < [ u8 ; 4 ] , ArchiveError > {
117- let ( status, headers, mut response) = {
118- let mut request = attohttpc:: get ( url) ;
119- request
120- . headers_mut ( )
121- . typed_insert ( Range :: bytes ( len - 4 ..len) . unwrap ( ) ) ;
122- request. send ( ) ?. split ( )
123- } ;
124-
125- if !status. is_success ( ) {
126- return Err ( ArchiveError :: HttpError ( status) ) ;
127- }
128-
129- let actual_length = content_length ( & headers) ?;
130-
131- if actual_length != 4 {
132- return Err ( ArchiveError :: UnexpectedContentLengthError ( actual_length) ) ;
133- }
134-
135- let mut buf = [ 0 ; 4 ] ;
136- response. read_exact ( & mut buf) ?;
137- Ok ( buf)
138- }
139-
140- /// Loads the `isize` field (the field that indicates the uncompressed size)
141- /// of a gzip file from disk.
142- fn load_isize ( file : & mut File ) -> Result < [ u8 ; 4 ] , ArchiveError > {
143- file. seek ( SeekFrom :: End ( -4 ) ) ?;
144- let mut buf = [ 0 ; 4 ] ;
145- file. read_exact ( & mut buf) ?;
146- file. seek ( SeekFrom :: Start ( 0 ) ) ?;
147- Ok ( buf)
148- }
149-
150- fn accepts_byte_ranges ( headers : & HeaderMap ) -> bool {
151- headers
152- . typed_get :: < AcceptRanges > ( )
153- . is_some_and ( |v| v == AcceptRanges :: bytes ( ) )
154- }
155-
156- /// Determines the uncompressed size of a gzip file hosted at the specified
157- /// URL by fetching just the metadata associated with the file. This makes
158- /// an extra round-trip to the server, so it's only more efficient than just
159- /// downloading the file if the file is large enough that downloading it is
160- /// slower than the extra round trips.
161- fn fetch_uncompressed_size ( url : & str , len : u64 ) -> Option < u64 > {
162- // if there is an error, we ignore it and return None, instead of failing
163- fetch_isize ( url, len)
164- . ok ( )
165- . map ( |s| u32:: from_le_bytes ( s) as u64 )
166- }
167-
168- /// Determines the uncompressed size of the specified gzip file on disk.
169- fn load_uncompressed_size ( file : & mut File ) -> Option < u64 > {
170- // if there is an error, we ignore it and return None, instead of failing
171- load_isize ( file) . ok ( ) . map ( |s| u32:: from_le_bytes ( s) as u64 )
172- }
173-
17486#[ cfg( test) ]
17587pub mod tests {
17688
@@ -192,7 +104,6 @@ pub mod tests {
192104 let test_file = File :: open ( test_file_path) . expect ( "Couldn't open test file" ) ;
193105 let tarball = Tarball :: load ( test_file) . expect ( "Failed to load tarball" ) ;
194106
195- assert_eq ! ( tarball. uncompressed_size( ) , Some ( 10240 ) ) ;
196107 assert_eq ! ( tarball. compressed_size( ) , 402 ) ;
197108 }
198109}
0 commit comments