@@ -138,7 +138,11 @@ impl CopyCmd {
138
138
) ;
139
139
}
140
140
Err ( e) if e. kind ( ) == ErrorKind :: NotFound => {
141
- dst_op. create_dir ( & final_dst_path) . await ?;
141
+ let mut path_to_create = final_dst_path. clone ( ) ;
142
+ if !path_to_create. ends_with ( '/' ) {
143
+ path_to_create. push ( '/' ) ;
144
+ }
145
+ dst_op. create_dir ( & path_to_create) . await ?;
142
146
}
143
147
Err ( e) => {
144
148
// Another error occurred trying to stat the base destination.
@@ -149,43 +153,60 @@ impl CopyCmd {
149
153
// Proceed with recursive copy logic. dst_root is the target directory.
150
154
let dst_root = Path :: new ( & final_dst_path) ;
151
155
let mut ds = src_op. lister_with ( & src_path) . recursive ( true ) . await ?;
152
- let prefix = src_path. strip_prefix ( '/' ) . unwrap_or ( src_path. as_str ( ) ) ;
153
156
154
157
while let Some ( de) = ds. try_next ( ) . await ? {
155
158
let meta = de. metadata ( ) ;
156
159
let depath = de. path ( ) ;
157
160
158
- // Calculate relative path from the source root
159
- let relative_path = depath. strip_prefix ( prefix) . with_context ( || {
160
- anyhow:: anyhow!(
161
- "Internal error: Failed to strip prefix '{}' from path '{}'" ,
162
- prefix,
163
- depath
161
+ // Calculate relative path using Path::strip_prefix
162
+ let src_root_path = Path :: new ( & src_path) ;
163
+ let entry_path = Path :: new ( depath) ;
164
+ let relative_path = entry_path. strip_prefix ( src_root_path) . with_context ( || {
165
+ format ! (
166
+ "Internal error: Lister path '{}' does not start with source path '{}'" ,
167
+ depath, src_path
164
168
)
165
- } ) ?;
166
- let relative_path = relative_path. strip_prefix ( '/' ) . unwrap_or ( relative_path) ;
169
+ } ) ?; // relative_path is a &Path
167
170
168
- let current_dst_path = dst_root. join ( relative_path) . to_string_lossy ( ) . to_string ( ) ;
171
+ let current_dst_path_path = dst_root. join ( relative_path) ;
172
+ let current_dst_path = current_dst_path_path. to_string_lossy ( ) . to_string ( ) ;
169
173
170
174
if meta. mode ( ) . is_dir ( ) {
171
- dst_op. create_dir ( & current_dst_path) . await ?;
175
+ let mut dir_path_to_create = current_dst_path. clone ( ) ;
176
+ if !dir_path_to_create. ends_with ( '/' ) {
177
+ dir_path_to_create. push ( '/' ) ;
178
+ }
179
+ dst_op. create_dir ( & dir_path_to_create) . await ?;
172
180
continue ;
173
181
}
174
182
175
- if let Some ( parent) = Path :: new ( & current_dst_path) . parent ( ) {
176
- if parent != dst_root {
177
- dst_op. create_dir ( & parent. to_string_lossy ( ) ) . await ?;
183
+ // Explicitly stat the source file to get fresh metadata
184
+ let fresh_meta = src_op. stat ( depath) . await . with_context ( || {
185
+ format ! (
186
+ "Failed to stat source file '{}' before recursive copy" ,
187
+ depath
188
+ )
189
+ } ) ?;
190
+
191
+ // Use the Path object `current_dst_path_path` to check parent
192
+ if let Some ( parent_path) = current_dst_path_path. parent ( ) {
193
+ if parent_path != dst_root {
194
+ let mut parent_dir_string = parent_path. to_string_lossy ( ) . into_owned ( ) ;
195
+ if !parent_dir_string. ends_with ( '/' ) {
196
+ parent_dir_string. push ( '/' ) ;
197
+ }
198
+ dst_op. create_dir ( & parent_dir_string) . await ?;
178
199
}
179
200
}
180
201
181
- let reader = src_op. reader_with ( de . path ( ) ) . chunk ( 8 * 1024 * 1024 ) . await ?;
202
+ let reader = src_op. reader_with ( depath ) . chunk ( 8 * 1024 * 1024 ) . await ?;
182
203
let buf_reader = reader
183
- . into_futures_async_read ( 0 ..meta . content_length ( ) )
204
+ . into_futures_async_read ( 0 ..fresh_meta . content_length ( ) )
184
205
. await ?;
185
206
186
- let copy_progress = CopyProgress :: new ( meta , de . path ( ) . to_string ( ) ) ;
207
+ let copy_progress = CopyProgress :: new ( & fresh_meta , depath . to_string ( ) ) ;
187
208
let mut writer = dst_op
188
- . writer ( & current_dst_path) // Use the calculated path
209
+ . writer ( & current_dst_path)
189
210
. await ?
190
211
. into_futures_async_write ( ) ;
191
212
0 commit comments