]>
Commit | Line | Data |
---|---|---|
606ce64b | 1 | use failure::*; |
afb4cd28 | 2 | use std::io::{Seek, SeekFrom}; |
0a51fe00 | 3 | use std::convert::TryInto; |
606ce64b | 4 | |
22968600 | 5 | use crate::tools; |
7bc1d727 | 6 | use super::IndexFile; |
7e336555 | 7 | use super::chunk_stat::*; |
606ce64b DM |
8 | use super::chunk_store::*; |
9 | ||
150f1bd8 | 10 | use std::sync::Arc; |
b526bd14 | 11 | use std::io::Write; |
10eea49d | 12 | use std::fs::File; |
606ce64b DM |
13 | use std::path::{Path, PathBuf}; |
14 | use std::os::unix::io::AsRawFd; | |
d13e3745 | 15 | use uuid::Uuid; |
4818c8b6 | 16 | use chrono::{Local, TimeZone}; |
afb4cd28 | 17 | |
f98ac774 | 18 | use super::ChunkInfo; |
afb4cd28 | 19 | use super::read_chunk::*; |
606ce64b | 20 | |
4dc79bb1 WB |
21 | use proxmox::tools::io::ReadExt; |
22 | ||
8e39232a | 23 | /// Header format definition for fixed index files (`.fidx`) |
d13e3745 | 24 | #[repr(C)] |
91a905b6 | 25 | pub struct FixedIndexHeader { |
a7dd4830 | 26 | pub magic: [u8; 8], |
d13e3745 | 27 | pub uuid: [u8; 16], |
5e5b7f1c | 28 | pub ctime: u64, |
9335d74e DM |
29 | /// Sha256 over the index ``SHA256(digest1||digest2||...)`` |
30 | pub index_csum: [u8; 32], | |
a7dd4830 DM |
31 | pub size: u64, |
32 | pub chunk_size: u64, | |
33 | reserved: [u8; 4016], // overall size is one page (4096 bytes) | |
d13e3745 | 34 | } |
ccdf3ad1 | 35 | proxmox::tools::static_assert_size!(FixedIndexHeader, 4096); |
606ce64b DM |
36 | |
37 | // split image into fixed size chunks | |
38 | ||
91a905b6 | 39 | pub struct FixedIndexReader { |
10eea49d | 40 | _file: File, |
29ae5c86 | 41 | pub chunk_size: usize, |
b46c3fad | 42 | pub size: u64, |
e1225de4 | 43 | index_length: usize, |
4818c8b6 | 44 | index: *mut u8, |
9f49fe1d DM |
45 | pub uuid: [u8; 16], |
46 | pub ctime: u64, | |
9335d74e | 47 | pub index_csum: [u8; 32], |
4818c8b6 DM |
48 | } |
49 | ||
5be4065b WB |
50 | // `index` is mmap()ed which cannot be thread-local so should be sendable |
51 | unsafe impl Send for FixedIndexReader {} | |
5c1130df | 52 | unsafe impl Sync for FixedIndexReader {} |
5be4065b | 53 | |
91a905b6 | 54 | impl Drop for FixedIndexReader { |
4818c8b6 DM |
55 | |
56 | fn drop(&mut self) { | |
57 | if let Err(err) = self.unmap() { | |
a7c72ad9 | 58 | eprintln!("Unable to unmap file - {}", err); |
4818c8b6 DM |
59 | } |
60 | } | |
61 | } | |
62 | ||
91a905b6 | 63 | impl FixedIndexReader { |
4818c8b6 | 64 | |
a7c72ad9 | 65 | pub fn open(path: &Path) -> Result<Self, Error> { |
4818c8b6 | 66 | |
a7c72ad9 DM |
67 | File::open(path) |
68 | .map_err(Error::from) | |
69 | .and_then(|file| Self::new(file)) | |
70 | .map_err(|err| format_err!("Unable to open fixed index {:?} - {}", path, err)) | |
71 | } | |
4818c8b6 | 72 | |
a7c72ad9 | 73 | pub fn new(mut file: std::fs::File) -> Result<Self, Error> { |
4818c8b6 | 74 | |
c597a92c | 75 | if let Err(err) = nix::fcntl::flock(file.as_raw_fd(), nix::fcntl::FlockArg::LockSharedNonblock) { |
a7c72ad9 | 76 | bail!("unable to get shared lock - {}", err); |
c597a92c DM |
77 | } |
78 | ||
afb4cd28 DM |
79 | file.seek(SeekFrom::Start(0))?; |
80 | ||
91a905b6 | 81 | let header_size = std::mem::size_of::<FixedIndexHeader>(); |
4dc79bb1 | 82 | let header: Box<FixedIndexHeader> = unsafe { file.read_host_value_boxed()? }; |
4818c8b6 | 83 | |
a7dd4830 | 84 | if header.magic != super::FIXED_SIZED_CHUNK_INDEX_1_0 { |
a7c72ad9 | 85 | bail!("got unknown magic number"); |
a360f6fa DM |
86 | } |
87 | ||
b46c3fad | 88 | let size = u64::from_le(header.size); |
48d0d356 | 89 | let ctime = u64::from_le(header.ctime); |
b46c3fad | 90 | let chunk_size = u64::from_le(header.chunk_size); |
4818c8b6 | 91 | |
b46c3fad | 92 | let index_length = ((size + chunk_size - 1)/chunk_size) as usize; |
e1225de4 | 93 | let index_size = index_length*32; |
4818c8b6 | 94 | |
0b8e75ed DM |
95 | let rawfd = file.as_raw_fd(); |
96 | ||
97 | let stat = match nix::sys::stat::fstat(rawfd) { | |
98 | Ok(stat) => stat, | |
a7c72ad9 | 99 | Err(err) => bail!("fstat failed - {}", err), |
0b8e75ed DM |
100 | }; |
101 | ||
ddbdf80d | 102 | let expected_index_size = (stat.st_size as usize) - header_size; |
0b8e75ed | 103 | if index_size != expected_index_size { |
a7c72ad9 | 104 | bail!("got unexpected file size ({} != {})", index_size, expected_index_size); |
0b8e75ed | 105 | } |
4818c8b6 DM |
106 | |
107 | let data = unsafe { nix::sys::mman::mmap( | |
108 | std::ptr::null_mut(), | |
109 | index_size, | |
110 | nix::sys::mman::ProtFlags::PROT_READ, | |
111 | nix::sys::mman::MapFlags::MAP_PRIVATE, | |
112 | file.as_raw_fd(), | |
113 | header_size as i64) }? as *mut u8; | |
114 | ||
115 | Ok(Self { | |
10eea49d | 116 | _file: file, |
b46c3fad | 117 | chunk_size: chunk_size as usize, |
4818c8b6 | 118 | size, |
e1225de4 | 119 | index_length, |
4818c8b6 DM |
120 | index: data, |
121 | ctime, | |
122 | uuid: header.uuid, | |
9335d74e | 123 | index_csum: header.index_csum, |
4818c8b6 DM |
124 | }) |
125 | } | |
126 | ||
127 | fn unmap(&mut self) -> Result<(), Error> { | |
128 | ||
129 | if self.index == std::ptr::null_mut() { return Ok(()); } | |
130 | ||
e1225de4 | 131 | let index_size = self.index_length*32; |
4818c8b6 DM |
132 | |
133 | if let Err(err) = unsafe { nix::sys::mman::munmap(self.index as *mut std::ffi::c_void, index_size) } { | |
a7c72ad9 | 134 | bail!("unmap file failed - {}", err); |
4818c8b6 DM |
135 | } |
136 | ||
137 | self.index = std::ptr::null_mut(); | |
138 | ||
139 | Ok(()) | |
140 | } | |
141 | ||
afb4cd28 DM |
142 | pub fn chunk_info(&self, pos: usize) -> Result<(u64, u64, [u8; 32]), Error> { |
143 | ||
144 | if pos >= self.index_length { | |
145 | bail!("chunk index out of range"); | |
146 | } | |
147 | let start = (pos * self.chunk_size) as u64; | |
148 | let mut end = start + self.chunk_size as u64; | |
149 | ||
150 | if end > self.size { | |
151 | end = self.size; | |
152 | } | |
153 | ||
5e58e1bb WB |
154 | let mut digest = std::mem::MaybeUninit::<[u8; 32]>::uninit(); |
155 | unsafe { | |
156 | std::ptr::copy_nonoverlapping( | |
157 | self.index.add(pos*32), | |
158 | (*digest.as_mut_ptr()).as_mut_ptr(), | |
159 | 32, | |
160 | ); | |
161 | } | |
afb4cd28 | 162 | |
5e58e1bb | 163 | Ok((start, end, unsafe { digest.assume_init() })) |
afb4cd28 DM |
164 | } |
165 | ||
0a51fe00 DM |
166 | #[inline] |
167 | fn chunk_digest(&self, pos: usize) -> &[u8; 32] { | |
168 | if pos >= self.index_length { | |
169 | panic!("chunk index out of range"); | |
170 | } | |
171 | let slice = unsafe { std::slice::from_raw_parts(self.index.add(pos*32), 32) }; | |
172 | slice.try_into().unwrap() | |
173 | } | |
174 | ||
afb4cd28 DM |
175 | #[inline] |
176 | fn chunk_end(&self, pos: usize) -> u64 { | |
177 | if pos >= self.index_length { | |
178 | panic!("chunk index out of range"); | |
179 | } | |
180 | ||
181 | let end = ((pos+1) * self.chunk_size) as u64; | |
182 | if end > self.size { | |
183 | self.size | |
184 | } else { | |
185 | end | |
186 | } | |
187 | } | |
188 | ||
0a51fe00 DM |
189 | /// Compute checksum and data size |
190 | pub fn compute_csum(&self) -> ([u8; 32], u64) { | |
191 | ||
192 | let mut csum = openssl::sha::Sha256::new(); | |
193 | let mut chunk_end = 0; | |
194 | for pos in 0..self.index_length { | |
195 | chunk_end = ((pos+1) * self.chunk_size) as u64; | |
196 | let digest = self.chunk_digest(pos); | |
0a51fe00 DM |
197 | csum.update(digest); |
198 | } | |
199 | let csum = csum.finish(); | |
200 | ||
201 | (csum, chunk_end) | |
202 | } | |
203 | ||
4818c8b6 | 204 | pub fn print_info(&self) { |
4818c8b6 DM |
205 | println!("Size: {}", self.size); |
206 | println!("ChunkSize: {}", self.chunk_size); | |
207 | println!("CTime: {}", Local.timestamp(self.ctime as i64, 0).format("%c")); | |
208 | println!("UUID: {:?}", self.uuid); | |
209 | } | |
210 | } | |
211 | ||
7bc1d727 WB |
212 | impl IndexFile for FixedIndexReader { |
213 | fn index_count(&self) -> usize { | |
e1225de4 | 214 | self.index_length |
7bc1d727 WB |
215 | } |
216 | ||
217 | fn index_digest(&self, pos: usize) -> Option<&[u8; 32]> { | |
e1225de4 | 218 | if pos >= self.index_length { |
7bc1d727 WB |
219 | None |
220 | } else { | |
221 | Some(unsafe { std::mem::transmute(self.index.add(pos*32)) }) | |
222 | } | |
223 | } | |
a660978c DM |
224 | |
225 | fn index_bytes(&self) -> u64 { | |
b46c3fad | 226 | self.size |
a660978c | 227 | } |
7bc1d727 WB |
228 | } |
229 | ||
91a905b6 | 230 | pub struct FixedIndexWriter { |
150f1bd8 | 231 | store: Arc<ChunkStore>, |
9335d74e | 232 | file: File, |
43b13033 | 233 | _lock: tools::ProcessLockSharedGuard, |
4fbb72a8 DM |
234 | filename: PathBuf, |
235 | tmp_filename: PathBuf, | |
606ce64b DM |
236 | chunk_size: usize, |
237 | size: usize, | |
e1225de4 | 238 | index_length: usize, |
606ce64b | 239 | index: *mut u8, |
9f49fe1d DM |
240 | pub uuid: [u8; 16], |
241 | pub ctime: u64, | |
606ce64b DM |
242 | } |
243 | ||
c3bb97e5 WB |
244 | // `index` is mmap()ed which cannot be thread-local so should be sendable |
245 | unsafe impl Send for FixedIndexWriter {} | |
246 | ||
91a905b6 | 247 | impl Drop for FixedIndexWriter { |
4fbb72a8 DM |
248 | |
249 | fn drop(&mut self) { | |
250 | let _ = std::fs::remove_file(&self.tmp_filename); // ignore errors | |
251 | if let Err(err) = self.unmap() { | |
0cd9d420 | 252 | eprintln!("Unable to unmap file {:?} - {}", self.tmp_filename, err); |
4fbb72a8 DM |
253 | } |
254 | } | |
255 | } | |
256 | ||
91a905b6 | 257 | impl FixedIndexWriter { |
606ce64b | 258 | |
150f1bd8 | 259 | pub fn create(store: Arc<ChunkStore>, path: &Path, size: usize, chunk_size: usize) -> Result<Self, Error> { |
606ce64b | 260 | |
43b13033 DM |
261 | let shared_lock = store.try_shared_lock()?; |
262 | ||
606ce64b | 263 | let full_path = store.relative_path(path); |
4fbb72a8 | 264 | let mut tmp_path = full_path.clone(); |
91a905b6 | 265 | tmp_path.set_extension("tmp_fidx"); |
606ce64b DM |
266 | |
267 | let mut file = std::fs::OpenOptions::new() | |
d13e3745 | 268 | .create(true).truncate(true) |
606ce64b DM |
269 | .read(true) |
270 | .write(true) | |
4fbb72a8 | 271 | .open(&tmp_path)?; |
606ce64b | 272 | |
91a905b6 | 273 | let header_size = std::mem::size_of::<FixedIndexHeader>(); |
d13e3745 DM |
274 | |
275 | // todo: use static assertion when available in rust | |
276 | if header_size != 4096 { panic!("got unexpected header size"); } | |
277 | ||
278 | let ctime = std::time::SystemTime::now().duration_since( | |
5e5b7f1c | 279 | std::time::SystemTime::UNIX_EPOCH)?.as_secs(); |
d13e3745 DM |
280 | |
281 | let uuid = Uuid::new_v4(); | |
282 | ||
0cd9d420 | 283 | let buffer = vec![0u8; header_size]; |
91a905b6 | 284 | let header = unsafe { &mut * (buffer.as_ptr() as *mut FixedIndexHeader) }; |
d13e3745 | 285 | |
a7dd4830 | 286 | header.magic = super::FIXED_SIZED_CHUNK_INDEX_1_0; |
48d0d356 DM |
287 | header.ctime = u64::to_le(ctime); |
288 | header.size = u64::to_le(size as u64); | |
289 | header.chunk_size = u64::to_le(chunk_size as u64); | |
d13e3745 DM |
290 | header.uuid = *uuid.as_bytes(); |
291 | ||
9335d74e DM |
292 | header.index_csum = [0u8; 32]; |
293 | ||
5e5b7f1c | 294 | file.write_all(&buffer)?; |
d13e3745 | 295 | |
e1225de4 DM |
296 | let index_length = (size + chunk_size - 1)/chunk_size; |
297 | let index_size = index_length*32; | |
d13e3745 DM |
298 | nix::unistd::ftruncate(file.as_raw_fd(), (header_size + index_size) as i64)?; |
299 | ||
606ce64b DM |
300 | let data = unsafe { nix::sys::mman::mmap( |
301 | std::ptr::null_mut(), | |
302 | index_size, | |
303 | nix::sys::mman::ProtFlags::PROT_READ | nix::sys::mman::ProtFlags::PROT_WRITE, | |
304 | nix::sys::mman::MapFlags::MAP_SHARED, | |
305 | file.as_raw_fd(), | |
d13e3745 DM |
306 | header_size as i64) }? as *mut u8; |
307 | ||
606ce64b DM |
308 | Ok(Self { |
309 | store, | |
9335d74e | 310 | file, |
43b13033 | 311 | _lock: shared_lock, |
4fbb72a8 DM |
312 | filename: full_path, |
313 | tmp_filename: tmp_path, | |
606ce64b DM |
314 | chunk_size, |
315 | size, | |
e1225de4 | 316 | index_length, |
606ce64b | 317 | index: data, |
d13e3745 DM |
318 | ctime, |
319 | uuid: *uuid.as_bytes(), | |
606ce64b DM |
320 | }) |
321 | } | |
322 | ||
006f3ff4 DM |
323 | pub fn index_length(&self) -> usize { |
324 | self.index_length | |
325 | } | |
326 | ||
4fbb72a8 DM |
327 | fn unmap(&mut self) -> Result<(), Error> { |
328 | ||
329 | if self.index == std::ptr::null_mut() { return Ok(()); } | |
330 | ||
e1225de4 | 331 | let index_size = self.index_length*32; |
4fbb72a8 DM |
332 | |
333 | if let Err(err) = unsafe { nix::sys::mman::munmap(self.index as *mut std::ffi::c_void, index_size) } { | |
0cd9d420 | 334 | bail!("unmap file {:?} failed - {}", self.tmp_filename, err); |
4fbb72a8 DM |
335 | } |
336 | ||
337 | self.index = std::ptr::null_mut(); | |
338 | ||
339 | Ok(()) | |
340 | } | |
341 | ||
9335d74e | 342 | pub fn close(&mut self) -> Result<[u8; 32], Error> { |
4fbb72a8 DM |
343 | |
344 | if self.index == std::ptr::null_mut() { bail!("cannot close already closed index file."); } | |
345 | ||
9335d74e DM |
346 | let index_size = self.index_length*32; |
347 | let data = unsafe { std::slice::from_raw_parts(self.index, index_size) }; | |
348 | let index_csum = openssl::sha::sha256(data); | |
349 | ||
4fbb72a8 DM |
350 | self.unmap()?; |
351 | ||
9335d74e | 352 | let csum_offset = proxmox::tools::offsetof!(FixedIndexHeader, index_csum); |
afb4cd28 | 353 | self.file.seek(SeekFrom::Start(csum_offset as u64))?; |
9335d74e DM |
354 | self.file.write_all(&index_csum)?; |
355 | self.file.flush()?; | |
356 | ||
4fbb72a8 DM |
357 | if let Err(err) = std::fs::rename(&self.tmp_filename, &self.filename) { |
358 | bail!("Atomic rename file {:?} failed - {}", self.filename, err); | |
359 | } | |
360 | ||
9335d74e | 361 | Ok(index_csum) |
4fbb72a8 DM |
362 | } |
363 | ||
5e04ec70 | 364 | pub fn check_chunk_alignment(&self, offset: usize, chunk_len: usize) -> Result<usize, Error> { |
f98ac774 | 365 | |
5e04ec70 DM |
366 | if offset < chunk_len { |
367 | bail!("got chunk with small offset ({} < {}", offset, chunk_len); | |
f98ac774 DM |
368 | } |
369 | ||
5e04ec70 | 370 | let pos = offset - chunk_len; |
606ce64b | 371 | |
5e04ec70 DM |
372 | if offset > self.size { |
373 | bail!("chunk data exceeds size ({} >= {})", offset, self.size); | |
606ce64b DM |
374 | } |
375 | ||
376 | // last chunk can be smaller | |
5e04ec70 | 377 | if ((offset != self.size) && (chunk_len != self.chunk_size)) || |
f98ac774 | 378 | (chunk_len > self.chunk_size) || (chunk_len == 0) { |
5e04ec70 | 379 | bail!("chunk with unexpected length ({} != {}", chunk_len, self.chunk_size); |
606ce64b DM |
380 | } |
381 | ||
5e04ec70 DM |
382 | if pos & (self.chunk_size-1) != 0 { |
383 | bail!("got unaligned chunk (pos = {})", pos); | |
f98ac774 DM |
384 | } |
385 | ||
5e04ec70 DM |
386 | Ok(pos / self.chunk_size) |
387 | } | |
388 | ||
389 | // Note: We want to add data out of order, so do not assume any order here. | |
390 | pub fn add_chunk(&mut self, chunk_info: &ChunkInfo, stat: &mut ChunkStat) -> Result<(), Error> { | |
391 | ||
392 | let chunk_len = chunk_info.chunk_len as usize; | |
393 | let offset = chunk_info.offset as usize; // end of chunk | |
394 | ||
395 | let idx = self.check_chunk_alignment(offset, chunk_len)?; | |
396 | ||
f98ac774 | 397 | let (is_duplicate, compressed_size) = self.store.insert_chunk(&chunk_info.chunk)?; |
798f7fa0 | 398 | |
cb0708dd DM |
399 | stat.chunk_count += 1; |
400 | stat.compressed_size += compressed_size; | |
606ce64b | 401 | |
f98ac774 DM |
402 | let digest = chunk_info.chunk.digest(); |
403 | ||
5e04ec70 | 404 | println!("ADD CHUNK {} {} {}% {} {}", idx, chunk_len, |
bffd40d6 | 405 | (compressed_size*100)/(chunk_len as u64), is_duplicate, proxmox::tools::digest_to_hex(digest)); |
798f7fa0 DM |
406 | |
407 | if is_duplicate { | |
cb0708dd | 408 | stat.duplicate_chunks += 1; |
798f7fa0 | 409 | } else { |
cb0708dd | 410 | stat.disk_size += compressed_size; |
798f7fa0 | 411 | } |
606ce64b | 412 | |
5e04ec70 | 413 | self.add_digest(idx, digest) |
e3062f87 WB |
414 | } |
415 | ||
416 | pub fn add_digest(&mut self, index: usize, digest: &[u8; 32]) -> Result<(), Error> { | |
01af11f3 | 417 | |
fc14b849 DM |
418 | if index >= self.index_length { |
419 | bail!("add digest failed - index out of range ({} >= {})", index, self.index_length); | |
420 | } | |
421 | ||
01af11f3 DM |
422 | if self.index == std::ptr::null_mut() { bail!("cannot write to closed index file."); } |
423 | ||
e3062f87 | 424 | let index_pos = index*32; |
606ce64b DM |
425 | unsafe { |
426 | let dst = self.index.add(index_pos); | |
427 | dst.copy_from_nonoverlapping(digest.as_ptr(), 32); | |
428 | } | |
429 | ||
430 | Ok(()) | |
431 | } | |
432 | } | |
afb4cd28 DM |
433 | |
434 | pub struct BufferedFixedReader<S> { | |
435 | store: S, | |
436 | index: FixedIndexReader, | |
437 | archive_size: u64, | |
438 | read_buffer: Vec<u8>, | |
439 | buffered_chunk_idx: usize, | |
440 | buffered_chunk_start: u64, | |
441 | read_offset: u64, | |
442 | } | |
443 | ||
444 | impl <S: ReadChunk> BufferedFixedReader<S> { | |
445 | ||
446 | pub fn new(index: FixedIndexReader, store: S) -> Self { | |
447 | ||
448 | let archive_size = index.size; | |
449 | Self { | |
450 | store, | |
653b1ca1 WB |
451 | index, |
452 | archive_size, | |
afb4cd28 DM |
453 | read_buffer: Vec::with_capacity(1024*1024), |
454 | buffered_chunk_idx: 0, | |
455 | buffered_chunk_start: 0, | |
456 | read_offset: 0, | |
457 | } | |
458 | } | |
459 | ||
460 | pub fn archive_size(&self) -> u64 { self.archive_size } | |
461 | ||
462 | fn buffer_chunk(&mut self, idx: usize) -> Result<(), Error> { | |
463 | ||
464 | let index = &self.index; | |
465 | let (start, end, digest) = index.chunk_info(idx)?; | |
466 | ||
467 | // fixme: avoid copy | |
468 | ||
469 | let data = self.store.read_chunk(&digest)?; | |
470 | ||
471 | if (end - start) != data.len() as u64 { | |
472 | bail!("read chunk with wrong size ({} != {}", (end - start), data.len()); | |
473 | } | |
474 | ||
475 | self.read_buffer.clear(); | |
476 | self.read_buffer.extend_from_slice(&data); | |
477 | ||
478 | self.buffered_chunk_idx = idx; | |
479 | ||
480 | self.buffered_chunk_start = start as u64; | |
481 | //println!("BUFFER {} {}", self.buffered_chunk_start, end); | |
482 | Ok(()) | |
483 | } | |
484 | } | |
485 | ||
486 | impl <S: ReadChunk> crate::tools::BufferedRead for BufferedFixedReader<S> { | |
487 | ||
488 | fn buffered_read(&mut self, offset: u64) -> Result<&[u8], Error> { | |
489 | ||
490 | if offset == self.archive_size { return Ok(&self.read_buffer[0..0]); } | |
491 | ||
492 | let buffer_len = self.read_buffer.len(); | |
493 | let index = &self.index; | |
494 | ||
495 | // optimization for sequential read | |
496 | if buffer_len > 0 && | |
497 | ((self.buffered_chunk_idx + 1) < index.index_length) && | |
498 | (offset >= (self.buffered_chunk_start + (self.read_buffer.len() as u64))) | |
499 | { | |
500 | let next_idx = self.buffered_chunk_idx + 1; | |
501 | let next_end = index.chunk_end(next_idx); | |
502 | if offset < next_end { | |
503 | self.buffer_chunk(next_idx)?; | |
504 | let buffer_offset = (offset - self.buffered_chunk_start) as usize; | |
505 | return Ok(&self.read_buffer[buffer_offset..]); | |
506 | } | |
507 | } | |
508 | ||
509 | if (buffer_len == 0) || | |
510 | (offset < self.buffered_chunk_start) || | |
511 | (offset >= (self.buffered_chunk_start + (self.read_buffer.len() as u64))) | |
512 | { | |
513 | let idx = (offset / index.chunk_size as u64) as usize; | |
514 | self.buffer_chunk(idx)?; | |
515 | } | |
516 | ||
517 | let buffer_offset = (offset - self.buffered_chunk_start) as usize; | |
518 | Ok(&self.read_buffer[buffer_offset..]) | |
519 | } | |
520 | } | |
521 | ||
522 | impl <S: ReadChunk> std::io::Read for BufferedFixedReader<S> { | |
523 | ||
524 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> { | |
525 | ||
526 | use std::io::{Error, ErrorKind}; | |
527 | use crate::tools::BufferedRead; | |
528 | ||
529 | let data = match self.buffered_read(self.read_offset) { | |
530 | Ok(v) => v, | |
531 | Err(err) => return Err(Error::new(ErrorKind::Other, err.to_string())), | |
532 | }; | |
533 | ||
534 | let n = if data.len() > buf.len() { buf.len() } else { data.len() }; | |
535 | ||
536 | unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf.as_mut_ptr(), n); } | |
537 | ||
538 | self.read_offset += n as u64; | |
539 | ||
540 | return Ok(n); | |
541 | } | |
542 | } | |
543 | ||
544 | impl <S: ReadChunk> Seek for BufferedFixedReader<S> { | |
545 | ||
546 | fn seek(&mut self, pos: SeekFrom) -> Result<u64, std::io::Error> { | |
547 | ||
548 | let new_offset = match pos { | |
549 | SeekFrom::Start(start_offset) => start_offset as i64, | |
550 | SeekFrom::End(end_offset) => (self.archive_size as i64)+ end_offset, | |
551 | SeekFrom::Current(offset) => (self.read_offset as i64) + offset, | |
552 | }; | |
553 | ||
554 | use std::io::{Error, ErrorKind}; | |
555 | if (new_offset < 0) || (new_offset > (self.archive_size as i64)) { | |
556 | return Err(Error::new( | |
557 | ErrorKind::Other, | |
558 | format!("seek is out of range {} ([0..{}])", new_offset, self.archive_size))); | |
559 | } | |
560 | self.read_offset = new_offset as u64; | |
561 | ||
562 | Ok(self.read_offset) | |
563 | } | |
564 | } |