@@ -6,7 +6,7 @@ use std::fs::{create_dir_all, read_dir, read_to_string, remove_dir_all, write};
6
6
use std:: io;
7
7
use std:: path:: { Component :: Normal , Path , PathBuf } ;
8
8
use std:: { collections:: HashMap , fmt} ;
9
- use tar:: Archive ;
9
+ use tar:: { Archive , EntryType :: Directory } ;
10
10
11
11
const REGISTRY : & str = "https://registry.npmjs.org" ;
12
12
@@ -95,24 +95,42 @@ impl Package {
95
95
. expect ( "Tarball should be bytes" ) ;
96
96
97
97
/// Emulates `tar xzf archive --strip-components=1 --directory=P`.
98
- pub fn unpack < P , R > ( mut archive : Archive < R > , dst : P ) -> io:: Result < ( ) >
98
+ pub fn unpack < R > ( mut archive : Archive < R > , dst : & Path ) -> io:: Result < ( ) >
99
99
where
100
- P : AsRef < Path > ,
101
100
R : io:: Read ,
102
101
{
103
- if dst. as_ref ( ) . symlink_metadata ( ) . is_err ( ) {
102
+ if dst. symlink_metadata ( ) . is_err ( ) {
104
103
create_dir_all ( & dst) ?;
105
104
}
106
105
106
+ let dst = & dst. canonicalize ( ) . unwrap_or ( dst. to_path_buf ( ) ) ;
107
+
108
+ // Delay any directory entries until the end (they will be created if needed by
109
+ // descendants), to ensure that directory permissions do not interfer with descendant
110
+ // extraction.
111
+ let mut directories = Vec :: new ( ) ;
107
112
for entry in archive. entries ( ) ? {
108
- let mut entry = entry?;
109
- let path: PathBuf = entry
110
- . path ( ) ?
111
- . components ( )
112
- . skip ( 1 ) // strip top-level directory
113
- . filter ( |c| matches ! ( c, Normal ( _) ) ) // prevent traversal attacks
114
- . collect ( ) ;
115
- entry. unpack ( dst. as_ref ( ) . join ( path) ) ?;
113
+ let entry = entry?;
114
+ let mut entry = (
115
+ dst. join (
116
+ entry
117
+ . path ( ) ?
118
+ . components ( )
119
+ . skip ( 1 )
120
+ . filter ( |c| matches ! ( c, Normal ( _) ) )
121
+ . collect :: < PathBuf > ( ) ,
122
+ ) ,
123
+ entry,
124
+ ) ;
125
+ if entry. 1 . header ( ) . entry_type ( ) == Directory {
126
+ directories. push ( entry) ;
127
+ } else {
128
+ create_dir_all ( entry. 0 . parent ( ) . unwrap ( ) ) ?;
129
+ entry. 1 . unpack ( entry. 0 ) ?;
130
+ }
131
+ }
132
+ for mut dir in directories {
133
+ dir. 1 . unpack ( dir. 0 ) ?;
116
134
}
117
135
Ok ( ( ) )
118
136
}
0 commit comments