4
4
5
5
use libarchive3_sys:: ffi:: * ;
6
6
use mime:: Mime ;
7
+ use std:: alloc:: { alloc, Layout } ;
7
8
use std:: ffi:: { c_void, CString , OsString } ;
8
- use std:: fs:: File ;
9
- use std:: io;
9
+ use std:: fs:: { File , OpenOptions } ;
10
10
use std:: io:: prelude:: * ;
11
+ use std:: io:: { self , Read } ;
11
12
use std:: os:: unix:: ffi:: OsStrExt ;
13
+ use std:: os:: unix:: fs:: OpenOptionsExt ;
12
14
use std:: sync:: { Arc , Mutex } ;
13
15
use std:: time:: Instant ;
14
16
17
+ const BLOCK_SIZE : usize = 1024 * 1024 ;
18
+
15
19
pub struct Drive {
16
20
pub name : OsString ,
17
21
pub model : String ,
@@ -52,8 +56,8 @@ pub fn list(all_drives: bool) -> Vec<Drive> {
52
56
53
57
#[ derive( Default ) ]
54
58
pub struct Progress {
55
- pub copied : u64 ,
56
59
pub size : u64 ,
60
+ pub done : u64 ,
57
61
pub secs : u64 ,
58
62
pub finished : bool ,
59
63
}
@@ -63,7 +67,7 @@ impl Progress {
63
67
if self . size == 0 {
64
68
0.0
65
69
} else {
66
- self . copied as f64 / self . size as f64
70
+ self . done as f64 / self . size as f64
67
71
}
68
72
}
69
73
}
@@ -73,90 +77,108 @@ pub struct Path {
73
77
pub size : Option < u64 > ,
74
78
}
75
79
76
- pub fn copy ( src : & Path , dest : & Path , from_drive : bool , image_mime_type : Mime ,
77
- progress_mutex : & Arc < Mutex < Progress > > ) -> io:: Result < ( ) > {
80
+ fn libarchive_open_for_reading ( path : & Path )
81
+ -> io:: Result < ( * mut Struct_archive , * mut Struct_archive_entry ) > {
78
82
79
- if !from_drive {
80
- if let ( Some ( ssize) , Some ( dsize) ) = ( src. size , dest. size ) {
81
- if ssize > dsize {
82
- return Err ( io:: Error :: other ( "File too large (os error 27)" ) ) ;
83
- }
83
+ let filename = CString :: new ( path. path . as_bytes ( ) ) . unwrap ( ) ;
84
+
85
+ unsafe {
86
+ let a = archive_read_new ( ) ;
87
+ archive_read_support_filter_all ( a) ;
88
+ archive_read_support_format_raw ( a) ;
89
+
90
+ let rc = archive_read_open_filename ( a, filename. as_ptr ( ) , BLOCK_SIZE ) ;
91
+ if rc != ARCHIVE_OK {
92
+ return Err ( io:: Error :: other ( "Cannot open file for reading" ) ) ;
93
+ }
94
+
95
+ let ae = archive_entry_new2 ( a) ;
96
+ let ae_ptr = ae as * mut * mut Struct_archive_entry ;
97
+ let rc = archive_read_next_header ( a, ae_ptr) ;
98
+ if rc != ARCHIVE_OK {
99
+ return Err ( io:: Error :: other ( "Cannot read file header" ) ) ;
84
100
}
101
+
102
+ Ok ( ( a, ae) )
85
103
}
104
+ }
86
105
87
- let mut srcfile = File :: open ( & src. path ) ?;
106
+ fn libarchive_open_for_writing ( path : & Path , image_mime_type : Mime )
107
+ -> io:: Result < ( * mut Struct_archive , * mut Struct_archive_entry ) > {
88
108
89
- let a = unsafe {
90
- match from_drive {
91
- false => {
92
- let a = archive_read_new ( ) ;
93
- archive_read_support_filter_all ( a) ;
94
- archive_read_support_format_raw ( a) ;
95
- a
96
- } ,
97
- true => {
98
- let a = archive_write_new ( ) ;
99
- let filter = match image_mime_type. essence_str ( ) {
100
- "application/gzip" => 1 ,
101
- "application/x-bzip2" => 2 ,
102
- "application/x-xz" => 6 ,
103
- "application/zstd" => 14 ,
104
- _ => 0 ,
105
- } ;
106
- archive_write_add_filter ( a, filter) ;
107
- archive_write_set_format ( a, 0x90000 ) ; // RAW
108
- a
109
- } ,
110
- }
111
- } ;
109
+ let filename = CString :: new ( path. path . as_bytes ( ) ) . unwrap ( ) ;
112
110
113
- let ae = if !from_drive {
114
- unsafe {
115
- let srcfilename = CString :: new ( src. path . as_bytes ( ) ) . unwrap ( ) ;
116
- let rc = archive_read_open_filename ( a, srcfilename. as_ptr ( ) , 1024 * 1024 ) ;
117
- if rc != ARCHIVE_OK {
118
- return Err ( io:: Error :: other ( "Cannot open file for reading" ) ) ;
119
- }
111
+ unsafe {
112
+ let a = archive_write_new ( ) ;
113
+ let filter = match image_mime_type. essence_str ( ) {
114
+ "application/gzip" => 1 ,
115
+ "application/x-bzip2" => 2 ,
116
+ "application/x-xz" => 6 ,
117
+ "application/zstd" => 14 ,
118
+ _ => 0 ,
119
+ } ;
120
+ archive_write_add_filter ( a, filter) ;
121
+ archive_write_set_format ( a, 0x90000 ) ; // RAW
120
122
121
- let ae = archive_entry_new2 ( a) ;
122
- let ae_ptr = ae as * mut * mut Struct_archive_entry ;
123
- let rc = archive_read_next_header ( a, ae_ptr) ;
124
- if rc != ARCHIVE_OK {
125
- return Err ( io:: Error :: other ( "Cannot read file header" ) ) ;
126
- }
127
- ae
123
+ let rc = archive_write_open_filename ( a, filename. as_ptr ( ) ) ;
124
+ if rc != ARCHIVE_OK {
125
+ return Err ( io:: Error :: other ( "Cannot open file for writing" ) ) ;
128
126
}
129
- } else {
130
- unsafe {
131
- let destfilename = CString :: new ( dest. path . as_bytes ( ) ) . unwrap ( ) ;
132
- let rc = archive_write_open_filename ( a, destfilename. as_ptr ( ) ) ;
133
- if rc != ARCHIVE_OK {
134
- return Err ( io:: Error :: other ( "Cannot open file for writing" ) ) ;
135
- }
136
127
137
- let ae = archive_entry_new2 ( a) ;
138
- archive_entry_set_filetype ( ae, AE_IFREG ) ;
139
- let rc = archive_write_header ( a, ae) ;
140
- if rc != ARCHIVE_OK {
141
- return Err ( io:: Error :: other ( "Cannot write file header" ) ) ;
142
- }
143
- ae
128
+ let ae = archive_entry_new2 ( a) ;
129
+ archive_entry_set_filetype ( ae, AE_IFREG ) ;
130
+ let rc = archive_write_header ( a, ae) ;
131
+ if rc != ARCHIVE_OK {
132
+ return Err ( io:: Error :: other ( "Cannot write file header" ) ) ;
133
+ }
134
+
135
+ Ok ( ( a, ae) )
136
+ }
137
+ }
138
+
139
+ fn libarchive_close_for_reading ( a : * mut Struct_archive ) {
140
+ unsafe {
141
+ archive_read_close ( a) ;
142
+ archive_read_free ( a) ;
143
+ }
144
+ }
145
+
146
+ fn libarchive_close_for_writing ( a : * mut Struct_archive , ae : * mut Struct_archive_entry ) {
147
+ unsafe {
148
+ archive_entry_free ( ae) ;
149
+ archive_write_close ( a) ;
150
+ archive_write_free ( a) ;
151
+ }
152
+ }
153
+
154
+ pub fn copy ( src : & Path , dest : & Path , from_drive : bool , image_mime_type : Mime ,
155
+ progress_mutex : & Arc < Mutex < Progress > > ) -> io:: Result < ( ) > {
156
+
157
+ if let ( Some ( ssize) , Some ( dsize) ) = ( src. size , dest. size ) {
158
+ if !from_drive && ssize > dsize {
159
+ return Err ( io:: Error :: other ( "File too large (os error 27)" ) ) ;
144
160
}
161
+ }
162
+
163
+ let mut srcfile = File :: open ( & src. path ) ?;
164
+ let mut destfile = OpenOptions :: new ( )
165
+ . create ( true ) . write ( true ) . truncate ( true )
166
+ . custom_flags ( libc:: O_DSYNC ) . open ( & dest. path ) ?;
167
+
168
+ let ( a, ae) = match from_drive {
169
+ false => libarchive_open_for_reading ( & src) ?,
170
+ true => libarchive_open_for_writing ( & dest, image_mime_type. clone ( ) ) ?,
145
171
} ;
146
172
147
- let mut destfile = File :: create ( & dest. path ) ?;
148
- let mut buffer = [ 0 ; 1024 * 1024 ] ;
173
+ let mut buffer = [ 0 ; BLOCK_SIZE ] ;
149
174
let buffer_ptr = buffer. as_mut_ptr ( ) as * mut c_void ;
150
- let timer = Instant :: now ( ) ;
151
175
152
- let mut progress = progress_mutex. lock ( ) . unwrap ( ) ;
153
- progress. size = src. size . unwrap_or_default ( ) ;
154
- drop ( progress) ;
176
+ let timer = Instant :: now ( ) ;
155
177
156
178
loop {
157
179
let len = match from_drive {
158
180
false => unsafe {
159
- archive_read_data ( a, buffer_ptr, 1024 * 1024 )
181
+ archive_read_data ( a, buffer_ptr, BLOCK_SIZE )
160
182
} ,
161
183
true => srcfile. read ( & mut buffer) ? as isize ,
162
184
} ;
@@ -170,26 +192,78 @@ pub fn copy(src: &Path, dest: &Path, from_drive: bool, image_mime_type: Mime,
170
192
}
171
193
} else {
172
194
destfile. write_all ( & buffer[ ..( len as usize ) ] ) ?;
173
- destfile. sync_data ( ) ?;
174
195
}
175
196
176
197
let mut progress = progress_mutex. lock ( ) . unwrap ( ) ;
177
- progress. copied += len as u64 ;
198
+ progress. done += len as u64 ;
178
199
}
179
200
180
- unsafe {
181
- if !from_drive {
182
- archive_read_close ( a) ;
183
- archive_read_free ( a) ;
184
- } else {
185
- archive_entry_free ( ae) ;
186
- archive_write_close ( a) ;
187
- archive_write_free ( a) ;
201
+ match from_drive {
202
+ false => libarchive_close_for_reading ( a) ,
203
+ true => libarchive_close_for_writing ( a, ae) ,
204
+ } ;
205
+
206
+ let mut progress = progress_mutex. lock ( ) . unwrap ( ) ;
207
+ progress. secs = timer. elapsed ( ) . as_secs ( ) ;
208
+ progress. finished = true ;
209
+
210
+ Ok ( ( ) )
211
+ }
212
+
213
+ pub fn verify ( src : & Path , dest : & Path , from_drive : bool ,
214
+ progress_mutex : & Arc < Mutex < Progress > > ) -> io:: Result < ( ) > {
215
+
216
+ let mut srcfile = File :: open ( & src. path ) ?;
217
+ let mut destfile = OpenOptions :: new ( ) . read ( true )
218
+ . custom_flags ( libc:: O_DIRECT ) . open ( & dest. path ) ?;
219
+
220
+ let ( a, _ae) = match from_drive {
221
+ false => libarchive_open_for_reading ( & src) ?,
222
+ true => libarchive_open_for_reading ( & dest) ?,
223
+ } ;
224
+
225
+
226
+ let mut srcbuffer = [ 0 ; BLOCK_SIZE ] ;
227
+ let srcbuffer_ptr = srcbuffer. as_mut_ptr ( ) as * mut c_void ;
228
+ let destbuffer_ptr = unsafe {
229
+ alloc ( Layout :: from_size_align ( BLOCK_SIZE , BLOCK_SIZE ) . unwrap ( ) )
230
+ } ;
231
+ let mut destbuffer = unsafe {
232
+ std:: slice:: from_raw_parts_mut ( destbuffer_ptr, BLOCK_SIZE )
233
+ } ;
234
+
235
+ let timer = Instant :: now ( ) ;
236
+
237
+ loop {
238
+ let len = match from_drive {
239
+ false => unsafe {
240
+ archive_read_data ( a, srcbuffer_ptr, BLOCK_SIZE )
241
+ } ,
242
+ true => srcfile. read ( & mut srcbuffer) ? as isize ,
243
+ } ;
244
+ if len <= 0 {
245
+ break ;
188
246
}
247
+
248
+ match from_drive {
249
+ false => destfile. read ( & mut destbuffer) ? as isize ,
250
+ true => unsafe {
251
+ archive_read_data ( a, destbuffer_ptr as * mut c_void , BLOCK_SIZE )
252
+ } ,
253
+ } ;
254
+
255
+ if & srcbuffer[ ..len as usize ] != & destbuffer[ ..len as usize ] {
256
+ return Err ( io:: Error :: other ( "Verification failed" ) ) ;
257
+ }
258
+
259
+ let mut progress = progress_mutex. lock ( ) . unwrap ( ) ;
260
+ progress. done += len as u64 ;
189
261
}
190
262
263
+ libarchive_close_for_reading ( a) ;
264
+
191
265
let mut progress = progress_mutex. lock ( ) . unwrap ( ) ;
192
- progress. secs = timer. elapsed ( ) . as_secs ( ) ;
266
+ progress. secs + = timer. elapsed ( ) . as_secs ( ) ;
193
267
progress. finished = true ;
194
268
195
269
Ok ( ( ) )
0 commit comments