@@ -112,27 +112,26 @@ where
112
112
// first by ourselves when there are odd paths like including `..` or /
113
113
// for our clearer pattern matching reasoning:
114
114
// https://docs.rs/tar/0.4.26/src/tar/entry.rs.html#371
115
- let parts = path. components ( ) . map ( |p| match p {
116
- CurDir => Some ( "." ) ,
117
- Normal ( c) => c. to_str ( ) ,
118
- _ => None , // Prefix (for Windows) and RootDir are forbidden
119
- } ) ;
115
+ let parts = path
116
+ . components ( )
117
+ . map ( |p| match p {
118
+ CurDir => Ok ( "." ) ,
119
+ Normal ( c) => c. to_str ( ) . ok_or ( ( ) ) ,
120
+ _ => Err ( ( ) ) , // Prefix (for Windows) and RootDir are forbidden
121
+ } )
122
+ . collect :: < std:: result:: Result < Vec < _ > , _ > > ( ) ;
120
123
121
124
// Reject old-style BSD directory entries that aren't explicitly tagged as directories
122
125
let legacy_dir_entry =
123
126
entry. header ( ) . as_ustar ( ) . is_none ( ) && entry. path_bytes ( ) . ends_with ( b"/" ) ;
124
127
let kind = entry. header ( ) . entry_type ( ) ;
125
128
let reject_legacy_dir_entry = legacy_dir_entry && ( kind != Directory ) ;
126
-
127
- if parts. clone ( ) . any ( |p| p. is_none ( ) ) || reject_legacy_dir_entry {
129
+ let ( Ok ( parts) , false ) = ( parts, reject_legacy_dir_entry) else {
128
130
return Err ( UnpackError :: Archive ( format ! (
129
131
"invalid path found: {path_str:?}"
130
132
) ) ) ;
131
- }
133
+ } ;
132
134
133
- let parts: Vec < _ > = parts. map ( |p| p. unwrap ( ) ) . collect ( ) ;
134
- let account_filename =
135
- ( parts. len ( ) == 2 && parts[ 0 ] == "accounts" ) . then ( || PathBuf :: from ( parts[ 1 ] ) ) ;
136
135
let unpack_dir = match entry_checker ( parts. as_slice ( ) , kind) {
137
136
UnpackPath :: Invalid => {
138
137
return Err ( UnpackError :: Archive ( format ! (
@@ -159,30 +158,32 @@ where
159
158
) ?;
160
159
total_count = checked_total_count_increment ( total_count, limit_count) ?;
161
160
162
- let target = sanitize_path ( & entry. path ( ) ?, unpack_dir) ?; // ? handles file system errors
163
- if target. is_none ( ) {
161
+ let account_filename = match parts. as_slice ( ) {
162
+ [ "accounts" , account_filename] => Some ( PathBuf :: from ( account_filename) ) ,
163
+ _ => None ,
164
+ } ;
165
+ let entry_path = if let Some ( account) = account_filename {
166
+ // Special case account files. We're unpacking an account entry inside one of the
167
+ // account_paths returned by `entry_checker`. We want to unpack into
168
+ // account_path/<account> instead of account_path/accounts/<account> so we strip the
169
+ // accounts/ prefix.
170
+ sanitize_path ( & account, unpack_dir)
171
+ } else {
172
+ sanitize_path ( & path, unpack_dir)
173
+ } ?; // ? handles file system errors
174
+ let Some ( entry_path) = entry_path else {
164
175
continue ; // skip it
165
- }
166
- let target = target. unwrap ( ) ;
176
+ } ;
167
177
168
- let unpack = entry. unpack ( target ) ;
178
+ let unpack = entry. unpack ( & entry_path ) ;
169
179
check_unpack_result ( unpack. map ( |_unpack| true ) ?, path_str) ?;
170
180
171
181
// Sanitize permissions.
172
182
let mode = match entry. header ( ) . entry_type ( ) {
173
183
GNUSparse | Regular => 0o644 ,
174
184
_ => 0o755 ,
175
185
} ;
176
- let entry_path_buf = unpack_dir. join ( entry. path ( ) ?) ;
177
- set_perms ( & entry_path_buf, mode) ?;
178
-
179
- let entry_path = if let Some ( account_filename) = account_filename {
180
- let stripped_path = unpack_dir. join ( account_filename) ; // strip away "accounts"
181
- fs:: rename ( & entry_path_buf, & stripped_path) ?;
182
- stripped_path
183
- } else {
184
- entry_path_buf
185
- } ;
186
+ set_perms ( & entry_path, mode) ?;
186
187
187
188
// Process entry after setting permissions
188
189
entry_processor ( entry_path) ;
@@ -1029,4 +1030,27 @@ mod tests {
1029
1030
if message == "too many files in snapshot: 1000000000000"
1030
1031
) ;
1031
1032
}
1033
+
1034
+ #[ test]
1035
+ fn test_archive_unpack_account_path ( ) {
1036
+ let mut header = Header :: new_gnu ( ) ;
1037
+ header. set_path ( "accounts/123.456" ) . unwrap ( ) ;
1038
+ header. set_size ( 4 ) ;
1039
+ header. set_cksum ( ) ;
1040
+ let data: & [ u8 ] = & [ 1 , 2 , 3 , 4 ] ;
1041
+
1042
+ let mut archive = Builder :: new ( Vec :: new ( ) ) ;
1043
+ archive. append ( & header, data) . unwrap ( ) ;
1044
+ let result = with_finalize_and_unpack ( archive, |ar, tmp| {
1045
+ unpack_snapshot_with_processors (
1046
+ ar,
1047
+ tmp,
1048
+ & [ tmp. join ( "accounts_dest" ) ] ,
1049
+ None ,
1050
+ |_, _| { } ,
1051
+ |path| assert_eq ! ( path, tmp. join( "accounts_dest/123.456" ) ) ,
1052
+ )
1053
+ } ) ;
1054
+ assert_matches ! ( result, Ok ( ( ) ) ) ;
1055
+ }
1032
1056
}
0 commit comments