1#[cfg(test)]
24mod tests;
25
26use log::{debug, error, warn};
27use nuts_backend::Backend;
28use std::cmp;
29use std::convert::{TryFrom, TryInto};
30
31use crate::entry::{populate_mode_api, populate_tstamp_api, Inner};
32use crate::error::{ArchiveResult, Error};
33use crate::id::Id;
34use crate::pager::Pager;
35use crate::tree::Tree;
36
37pub enum Entry<'a, B: Backend> {
45 File(FileEntry<'a, B>),
47
48 Directory(DirectoryEntry<'a, B>),
50
51 Symlink(SymlinkEntry<'a, B>),
53}
54
55impl<'a, B: Backend> Entry<'a, B> {
56 pub fn next(self) -> Option<ArchiveResult<Entry<'a, B>, B>> {
61 match self.into_inner_entry().next() {
62 Some(Ok(entry)) => Some(entry.try_into()),
63 Some(Err(err)) => Some(Err(err)),
64 None => None,
65 }
66 }
67
68 pub fn name(&self) -> &str {
70 &self.inner_entry().inner.name
71 }
72
73 pub fn size(&self) -> u64 {
75 self.inner_entry().inner.size
76 }
77
78 populate_mode_api!();
79 populate_tstamp_api!();
80
81 pub fn is_file(&self) -> bool {
83 match self {
84 Self::File(_) => true,
85 Self::Directory(_) => false,
86 Self::Symlink(_) => false,
87 }
88 }
89
90 pub fn as_file(&self) -> Option<&FileEntry<'a, B>> {
97 match self {
98 Self::File(value) => Some(value),
99 Self::Directory(_) => None,
100 Self::Symlink(_) => None,
101 }
102 }
103
104 pub fn as_file_mut(&mut self) -> Option<&mut FileEntry<'a, B>> {
111 match self {
112 Entry::File(value) => Some(value),
113 Entry::Directory(_) => None,
114 Entry::Symlink(_) => None,
115 }
116 }
117
118 pub fn into_file(self) -> Option<FileEntry<'a, B>> {
124 match self {
125 Self::File(value) => Some(value),
126 Self::Directory(_) => None,
127 Self::Symlink(_) => None,
128 }
129 }
130
131 pub fn is_directory(&self) -> bool {
133 match self {
134 Self::File(_) => false,
135 Self::Directory(_) => true,
136 Self::Symlink(_) => false,
137 }
138 }
139
140 pub fn as_directory(&self) -> Option<&DirectoryEntry<'a, B>> {
147 match self {
148 Self::File(_) => None,
149 Self::Directory(value) => Some(value),
150 Self::Symlink(_) => None,
151 }
152 }
153
154 pub fn into_directory(self) -> Option<DirectoryEntry<'a, B>> {
161 match self {
162 Self::File(_) => None,
163 Self::Directory(value) => Some(value),
164 Self::Symlink(_) => None,
165 }
166 }
167
168 pub fn is_symlink(&self) -> bool {
170 match self {
171 Self::File(_) => false,
172 Self::Directory(_) => false,
173 Self::Symlink(_) => true,
174 }
175 }
176
177 pub fn as_symlink(&self) -> Option<&SymlinkEntry<'a, B>> {
184 match self {
185 Self::File(_) => None,
186 Self::Directory(_) => None,
187 Self::Symlink(value) => Some(value),
188 }
189 }
190
191 pub fn into_symlink(self) -> Option<SymlinkEntry<'a, B>> {
197 match self {
198 Self::File(_) => None,
199 Self::Directory(_) => None,
200 Self::Symlink(value) => Some(value),
201 }
202 }
203
204 fn inner_entry(&'a self) -> &InnerEntry<'a, B> {
205 match self {
206 Self::File(inner) => &inner.0,
207 Self::Directory(inner) => &inner.0,
208 Self::Symlink(inner) => &inner.shared,
209 }
210 }
211
212 fn into_inner_entry(self) -> InnerEntry<'a, B> {
213 match self {
214 Self::File(inner) => inner.0,
215 Self::Directory(inner) => inner.0,
216 Self::Symlink(inner) => inner.shared,
217 }
218 }
219
220 fn inner(&self) -> &Inner {
221 &self.inner_entry().inner
222 }
223}
224
225impl<'a, B: Backend> TryFrom<InnerEntry<'a, B>> for Entry<'a, B> {
226 type Error = Error<B>;
227
228 fn try_from(src: InnerEntry<'a, B>) -> ArchiveResult<Self, B> {
229 if src.inner.mode.is_file() {
230 Ok(Self::File(FileEntry(src)))
231 } else if src.inner.mode.is_directory() {
232 Ok(Self::Directory(DirectoryEntry(src)))
233 } else if src.inner.mode.is_symlink() {
234 Ok(Self::Symlink(SymlinkEntry::new(src)?))
235 } else {
236 error!(
237 "could not detect entry type at {} from mode {:?}",
238 src.idx, src.inner.mode
239 );
240
241 match src.tree.lookup(src.pager, src.idx) {
242 Some(Ok(id)) => Err(Error::InvalidType(Some(id.as_ref().clone()))),
243 Some(Err(err)) => {
244 error!("id lookup failed for idx {}", src.idx);
245 Err(err)
246 }
247 None => {
248 warn!("no id found at idx {}", src.idx);
249 Err(Error::InvalidType(None))
250 }
251 }
252 }
253 }
254}
255
256pub struct FileEntry<'a, B: Backend>(InnerEntry<'a, B>);
263
264impl<'a, B: Backend> FileEntry<'a, B> {
265 pub fn name(&self) -> &str {
267 &self.0.inner.name
268 }
269
270 pub fn size(&self) -> u64 {
272 self.0.inner.size
273 }
274
275 populate_mode_api!();
276 populate_tstamp_api!();
277
278 pub fn read(&mut self, buf: &mut [u8]) -> ArchiveResult<usize, B> {
287 self.0.read(buf)
288 }
289
290 pub fn read_all(&mut self, mut buf: &mut [u8]) -> ArchiveResult<(), B> {
301 while !buf.is_empty() {
302 match self.read(buf) {
303 Ok(0) => break,
304 Ok(n) => {
305 let tmp = buf;
306 buf = &mut tmp[n..];
307 }
308 Err(e) => return Err(e),
309 }
310 }
311
312 if !buf.is_empty() {
313 Err(Error::UnexpectedEof)
314 } else {
315 Ok(())
316 }
317 }
318
319 pub fn read_vec(&mut self) -> ArchiveResult<Vec<u8>, B> {
322 let mut vec = vec![0; self.0.inner.size as usize];
323 self.read_all(&mut vec).map(|()| vec)
324 }
325
326 fn inner(&self) -> &Inner {
327 &self.0.inner
328 }
329}
330
331pub struct DirectoryEntry<'a, B: Backend>(InnerEntry<'a, B>);
336
337impl<'a, B: Backend> DirectoryEntry<'a, B> {
338 pub fn name(&self) -> &str {
340 &self.0.inner.name
341 }
342
343 populate_mode_api!();
344 populate_tstamp_api!();
345
346 fn inner(&self) -> &Inner {
347 &self.0.inner
348 }
349}
350
351pub struct SymlinkEntry<'a, B: Backend> {
356 shared: InnerEntry<'a, B>,
357 target: String,
358}
359
360impl<'a, B: Backend> SymlinkEntry<'a, B> {
361 fn new(mut shared: InnerEntry<'a, B>) -> ArchiveResult<SymlinkEntry<'a, B>, B> {
362 let target = Self::read_target(&mut shared)?;
363
364 Ok(SymlinkEntry { shared, target })
365 }
366
367 pub fn name(&self) -> &str {
369 &self.shared.inner.name
370 }
371
372 pub fn target(&self) -> &str {
376 &self.target
377 }
378
379 populate_mode_api!();
380 populate_tstamp_api!();
381
382 fn read_target(shared: &mut InnerEntry<'a, B>) -> ArchiveResult<String, B> {
383 const CHUNK: usize = 64;
384 let mut vec = vec![];
385 let mut nbytes = 0;
386
387 loop {
388 vec.resize(vec.len() + CHUNK, 0);
389
390 let n = shared.read(&mut vec[nbytes..nbytes + CHUNK])?;
391 nbytes += n;
392
393 vec.resize(nbytes, 0);
394
395 if n == 0 {
396 break;
397 }
398 }
399
400 Ok(String::from_utf8_lossy(&vec).to_string())
401 }
402
403 fn inner(&self) -> &Inner {
404 &self.shared.inner
405 }
406}
407
408pub struct InnerEntry<'a, B: Backend> {
409 pager: &'a mut Pager<B>,
410 tree: &'a mut Tree<B>,
411 inner: Inner,
412 idx: usize,
413 rcache: Vec<u8>,
414 ridx: usize,
415}
416
417impl<'a, B: Backend> InnerEntry<'a, B> {
418 pub fn load(
419 pager: &'a mut Pager<B>,
420 tree: &'a mut Tree<B>,
421 idx: usize,
422 id: &Id<B>,
423 ) -> ArchiveResult<InnerEntry<'a, B>, B> {
424 let inner = Inner::load(pager, id)?;
425
426 Ok(InnerEntry {
427 pager,
428 tree,
429 inner,
430 idx,
431 rcache: vec![],
432 ridx: 0,
433 })
434 }
435
436 pub fn first(
437 pager: &'a mut Pager<B>,
438 tree: &'a mut Tree<B>,
439 ) -> Option<ArchiveResult<InnerEntry<'a, B>, B>> {
440 match tree.lookup(pager, 0) {
441 Some(Ok(id)) => {
442 debug!("lookup first at {}: {}", 0, id);
443 let id = id.clone();
444 Some(Self::load(pager, tree, 0, &id))
445 }
446 Some(Err(err)) => {
447 error!("lookup first at {}: {}", 0, err);
448 Some(Err(err))
449 }
450 None => {
451 debug!("lookup first at {}: none", 0);
452 None
453 }
454 }
455 }
456
457 fn next(self) -> Option<ArchiveResult<InnerEntry<'a, B>, B>> {
458 let content_blocks = self.content_blocks() as usize;
459 let next_idx = self.idx + content_blocks + 1;
460
461 debug!(
462 "next_idx={} (idx={}, size={}, content_blocks={})",
463 next_idx, self.idx, self.inner.size, content_blocks
464 );
465
466 match self.tree.lookup(self.pager, next_idx) {
467 Some(Ok(id)) => {
468 debug!("lookup next at {}: {}", next_idx, id);
469
470 let id = id.clone();
471 Some(Self::load(self.pager, self.tree, next_idx, &id))
472 }
473 Some(Err(err)) => {
474 error!("lookup next at {}: {}", next_idx, err);
475 Some(Err(err))
476 }
477 None => {
478 debug!("lookup next at {}: none", next_idx);
479 None
480 }
481 }
482 }
483
484 fn read(&mut self, buf: &mut [u8]) -> ArchiveResult<usize, B> {
485 if self.rcache.is_empty() {
486 let blocks = self.content_blocks();
487
488 debug!("fill cache: idx={}, blocks={}", self.ridx, blocks);
489
490 if self.ridx >= blocks as usize {
491 return Ok(0);
492 }
493
494 let block_size = self.pager.block_size() as usize;
495 let remaining = self.inner.size as usize - self.ridx * block_size;
496 let cache_size = cmp::min(remaining, block_size);
497
498 debug!(
499 "fill cache: remaining={}, cache_size={}",
500 remaining, cache_size
501 );
502
503 self.rcache.resize(cache_size, 0);
504
505 let idx = self.idx + self.ridx + 1;
506
507 match self.tree.lookup(self.pager, idx) {
508 Some(Ok(id)) => {
509 let n = self.pager.read(id, self.rcache.as_mut_slice())?;
510
511 assert_eq!(n, cache_size);
512
513 self.ridx += 1;
514 }
515 Some(Err(err)) => return Err(err),
516 None => {
517 warn!("premature end of archive, no block at {}", idx);
518 return Ok(0);
519 }
520 };
521 }
522
523 let len = cmp::min(self.rcache.len(), buf.len());
524
525 self.rcache
526 .drain(..len)
527 .enumerate()
528 .for_each(|(i, n)| buf[i] = n);
529
530 Ok(len)
531 }
532
533 fn content_blocks(&self) -> u64 {
534 let block_size = self.pager.block_size() as u64;
535
536 if self.inner.size % block_size == 0 {
537 self.inner.size / block_size
538 } else {
539 self.inner.size / block_size + 1
540 }
541 }
542}