1use capnp::{message, serialize};
2use memmap2::{Mmap, MmapOptions};
3use nix::errno::Errno;
4use nix::sys::stat;
5use std::backtrace::Backtrace;
6use std::collections::BTreeMap;
7use std::ffi::OsStr;
8use std::ffi::OsString;
9use std::fmt;
10use std::fs;
11use std::io;
12use std::os::unix::ffi::OsStrExt;
13use std::os::unix::ffi::OsStringExt;
14use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
15use std::path::Path;
16
17use serde::de::Error as SerdeError;
18use serde::de::Visitor;
19use serde::{Deserialize, Deserializer, Serialize, Serializer};
20
21use super::error::{Result, WireFormatError};
22use hex::FromHexError;
23
24pub const DEFAULT_FILE_PERMISSIONS: u16 = 0o644;
25pub const SHA256_BLOCK_SIZE: usize = 32;
26pub type VerityData = BTreeMap<[u8; SHA256_BLOCK_SIZE], [u8; SHA256_BLOCK_SIZE]>;
29
30#[derive(Debug)]
31pub struct Rootfs {
32 pub metadatas: Vec<Vec<Inode>>,
33 pub fs_verity_data: VerityData,
34 pub manifest_version: u64,
35}
36
37impl TryFrom<RootfsReader> for Rootfs {
38 type Error = WireFormatError;
39 fn try_from(rootfs_reader: RootfsReader) -> Result<Self> {
40 Rootfs::from_capnp(rootfs_reader.reader.get()?)
41 }
42}
43
44impl Rootfs {
45 pub fn from_capnp(reader: crate::metadata_capnp::rootfs::Reader<'_>) -> Result<Self> {
46 let metadata_vec = reader
47 .get_metadatas()?
48 .iter()
49 .map(InodeVector::from_capnp)
50 .collect::<Result<Vec<Vec<_>>>>()?;
51
52 let capnp_verities = reader.get_fs_verity_data()?;
53 let mut fs_verity_data = VerityData::new();
54
55 for capnp_verity in capnp_verities {
56 let digest = capnp_verity.get_digest()?.try_into()?;
57 let verity = capnp_verity.get_verity()?.try_into()?;
58 fs_verity_data.insert(digest, verity);
59 }
60
61 Ok(Rootfs {
62 metadatas: metadata_vec,
63 fs_verity_data,
64 manifest_version: reader.get_manifest_version(),
65 })
66 }
67
68 pub fn fill_capnp(
69 &self,
70 builder: &mut crate::metadata_capnp::rootfs::Builder<'_>,
71 ) -> Result<()> {
72 builder.set_manifest_version(self.manifest_version);
73
74 let metadatas_len = self.metadatas.len().try_into()?;
75 let mut capnp_metadatas = builder.reborrow().init_metadatas(metadatas_len);
76
77 for (i, metadata) in self.metadatas.iter().enumerate() {
78 let mut capnp_metadata = capnp_metadatas.reborrow().get(i as u32);
80 InodeVector::fill_capnp(metadata, &mut capnp_metadata)?;
81 }
82
83 let verity_data_len = self.fs_verity_data.len().try_into()?;
84 let mut capnp_verities = builder.reborrow().init_fs_verity_data(verity_data_len);
85
86 for (i, (digest, verity)) in self.fs_verity_data.iter().enumerate() {
87 let mut capnp_verity = capnp_verities.reborrow().get(i as u32);
89 capnp_verity.set_digest(digest);
90 capnp_verity.set_verity(verity);
91 }
92
93 Ok(())
94 }
95}
96
97pub struct RootfsReader {
98 reader: message::TypedReader<
99 ::capnp::serialize::BufferSegments<Mmap>,
100 crate::metadata_capnp::rootfs::Owned,
101 >,
102}
103
104impl RootfsReader {
105 pub fn open(f: cap_std::fs::File) -> Result<Self> {
106 let unlimited_reads = message::ReaderOptions {
108 traversal_limit_in_words: None,
109 nesting_limit: 64,
110 };
111 let mmapped_region = unsafe { MmapOptions::new().map_copy_read_only(&f)? };
112 let segments = serialize::BufferSegments::new(mmapped_region, unlimited_reads)?;
113 let reader = message::Reader::new(segments, unlimited_reads).into_typed();
114
115 Ok(Self { reader })
116 }
117
118 pub fn get_manifest_version(&self) -> Result<u64> {
119 Ok(self.reader.get()?.get_manifest_version())
120 }
121
122 pub fn get_verity_data(&self) -> Result<VerityData> {
123 let mut fs_verity_data = VerityData::new();
124
125 let capnp_verities = self.reader.get()?.get_fs_verity_data()?;
126 for capnp_verity in capnp_verities {
127 let digest = capnp_verity.get_digest()?.try_into()?;
128 let verity = capnp_verity.get_verity()?.try_into()?;
129 fs_verity_data.insert(digest, verity);
130 }
131 Ok(fs_verity_data)
132 }
133
134 pub fn find_inode(&self, ino: u64) -> Result<Inode> {
135 for layer in self.reader.get()?.get_metadatas()?.iter() {
136 let inode_vector = InodeVector { reader: layer };
137
138 if let Some(inode) = inode_vector.find_inode(ino)? {
139 let inode = Inode::from_capnp(inode)?;
140 if let InodeMode::Wht = inode.mode {
141 return Err(WireFormatError::from_errno(Errno::ENOENT));
143 }
144 return Ok(inode);
145 }
146 }
147
148 Err(WireFormatError::from_errno(Errno::ENOENT))
149 }
150
151 pub fn max_inode(&self) -> Result<Ino> {
152 let mut max: Ino = 1;
153 for layer in self.reader.get()?.get_metadatas()?.iter() {
154 let inode_vector = InodeVector { reader: layer };
155 if let Some(ino) = inode_vector.max_ino()? {
156 max = std::cmp::max(ino, max)
157 }
158 }
159 Ok(max)
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub struct BlobRef {
166 pub digest: [u8; SHA256_BLOCK_SIZE],
167 pub offset: u64,
168 pub compressed: bool,
169}
170
171impl BlobRef {
172 pub fn from_capnp(reader: crate::metadata_capnp::blob_ref::Reader<'_>) -> Result<Self> {
173 let digest = reader.get_digest()?;
174 Ok(BlobRef {
175 digest: digest.try_into()?,
176 offset: reader.get_offset(),
177 compressed: reader.get_compressed(),
178 })
179 }
180 pub fn fill_capnp(&self, builder: &mut crate::metadata_capnp::blob_ref::Builder<'_>) {
181 builder.set_digest(&self.digest);
182 builder.set_offset(self.offset);
183 builder.set_compressed(self.compressed);
184 }
185}
186
187#[derive(Debug, PartialEq, Eq)]
188pub struct DirEnt {
189 pub ino: Ino,
190 pub name: Vec<u8>,
191}
192
193#[derive(Debug, PartialEq, Eq)]
194pub struct DirList {
195 pub look_below: bool,
197 pub entries: Vec<DirEnt>,
198}
199
200#[derive(Debug)]
201pub struct FileChunkList {
202 pub chunks: Vec<FileChunk>,
203}
204
205#[derive(Debug, PartialEq, Eq)]
206pub struct FileChunk {
207 pub blob: BlobRef,
208 pub len: u64,
209}
210
211pub type Ino = u64;
212
213impl FileChunk {
214 pub fn from_capnp(reader: crate::metadata_capnp::file_chunk::Reader<'_>) -> Result<Self> {
215 let len = reader.get_len();
216 let blob = BlobRef::from_capnp(reader.get_blob()?)?;
217
218 Ok(FileChunk { blob, len })
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 const DEFAULT_DIRECTORY_PERMISSIONS: u16 = 0o755;
227
228 fn blobref_roundtrip(original: BlobRef) {
229 let mut message = ::capnp::message::Builder::new_default();
230 let mut capnp_blob_ref =
231 message.init_root::<crate::metadata_capnp::blob_ref::Builder<'_>>();
232
233 original.fill_capnp(&mut capnp_blob_ref);
234
235 let mut buf = Vec::new();
236 ::capnp::serialize::write_message(&mut buf, &message)
237 .expect("capnp::serialize::write_message failed");
238
239 let message_reader = serialize::read_message_from_flat_slice(
240 &mut &buf[..],
241 ::capnp::message::ReaderOptions::new(),
242 )
243 .expect("read_message_from_flat_slice failed");
244 let blobref_reader = message_reader
245 .get_root::<crate::metadata_capnp::blob_ref::Reader<'_>>()
246 .expect("message_reader.get_root failed");
247 let deserialized = BlobRef::from_capnp(blobref_reader).expect("BlobRef::from_capnp failed");
248
249 assert_eq!(original, deserialized);
250 }
251
252 #[test]
253 fn test_blobref_serialization() {
254 let local = BlobRef {
255 offset: 42,
256 digest: [
257 0xb7, 0x2e, 0x68, 0x50, 0x82, 0xd1, 0xdd, 0xfe, 0xb6, 0xcc, 0x31, 0xa5, 0x35, 0x29,
258 0x12, 0xFE, 0x3f, 0x51, 0x14, 0x65, 0xf5, 0x27, 0xa5, 0x1a, 0xb3, 0xff, 0xd3, 0xb8,
259 0xAA, 0x3C, 0x25, 0xDD,
260 ],
261 compressed: true,
262 };
263 blobref_roundtrip(local)
264 }
265
266 #[test]
267 fn test_inode_is_constant_serialized_size() {
268 let testcases = vec![
270 Inode {
271 ino: 0,
272 mode: InodeMode::Unknown,
273 uid: 0,
274 gid: 0,
275 permissions: 0,
276 additional: None,
277 },
278 Inode {
279 ino: 0,
280 mode: InodeMode::Lnk,
281 uid: 0,
282 gid: 0,
283 permissions: 0,
284 additional: None,
285 },
286 Inode {
287 ino: 0,
288 mode: InodeMode::File {
289 chunks: vec![FileChunk {
290 blob: BlobRef {
291 digest: [
292 0x12, 0x44, 0xFE, 0xDD, 0x13, 0x39, 0x88, 0x12, 0x48, 0xA8, 0xF8,
293 0xE4, 0x22, 0x12, 0x15, 0x16, 0x12, 0x44, 0xFE, 0xDD, 0x31, 0x93,
294 0x88, 0x21, 0x84, 0x8A, 0xF8, 0x4E, 0x22, 0x12, 0x51, 0x16,
295 ],
296 offset: 100,
297 compressed: true,
298 },
299 len: 100,
300 }],
301 },
302 uid: 0,
303 gid: 0,
304 permissions: DEFAULT_FILE_PERMISSIONS,
305 additional: None,
306 },
307 Inode {
308 ino: 65343,
309 mode: InodeMode::Chr {
310 major: 64,
311 minor: 65536,
312 },
313 uid: 10,
314 gid: 10000,
315 permissions: DEFAULT_DIRECTORY_PERMISSIONS,
316 additional: None,
317 },
318 Inode {
319 ino: 0,
320 mode: InodeMode::Lnk,
321 uid: 0,
322 gid: 0,
323 permissions: 0xFFFF,
324 additional: Some(InodeAdditional {
325 xattrs: vec![Xattr {
326 key: b"some extended attribute".to_vec(),
327 val: b"with some value".to_vec(),
328 }],
329 symlink_target: Some(b"some/other/path".to_vec()),
330 }),
331 },
332 ];
333
334 for test in testcases {
335 let wire = test.to_wire().unwrap();
336 let message_reader = serialize::read_message_from_flat_slice(
337 &mut &wire[..],
338 ::capnp::message::ReaderOptions::new(),
339 )
340 .expect("read_message_from_flat_slice failed");
341 let inode_reader = message_reader
342 .get_root::<crate::metadata_capnp::inode::Reader<'_>>()
343 .expect("message_reader.get_root failed");
344 let after = Inode::from_capnp(inode_reader).expect("BlobRef::from_capnp failed");
345 assert_eq!(test, after);
346 }
347 }
348}
349
350#[derive(Debug, PartialEq, Eq)]
351pub struct Inode {
352 pub ino: Ino,
353 pub mode: InodeMode,
354 pub uid: u32,
355 pub gid: u32,
356 pub permissions: u16,
357 pub additional: Option<InodeAdditional>,
358}
359
360impl Inode {
361 pub fn from_capnp(reader: crate::metadata_capnp::inode::Reader<'_>) -> Result<Self> {
362 Ok(Inode {
363 ino: reader.get_ino(),
364 mode: InodeMode::from_capnp(reader.get_mode())?,
365 uid: reader.get_uid(),
366 gid: reader.get_gid(),
367 permissions: reader.get_permissions(),
368 additional: InodeAdditional::from_capnp(reader.get_additional()?)?,
369 })
370 }
371
372 pub fn fill_capnp(
373 &self,
374 builder: &mut crate::metadata_capnp::inode::Builder<'_>,
375 ) -> Result<()> {
376 builder.set_ino(self.ino);
377
378 let mut mode_builder = builder.reborrow().init_mode();
379 self.mode.fill_capnp(&mut mode_builder)?;
380
381 builder.set_uid(self.uid);
382 builder.set_gid(self.gid);
383 builder.set_permissions(self.permissions);
384
385 if let Some(additional) = &self.additional {
386 let mut additional_builder = builder.reborrow().init_additional();
387 additional.fill_capnp(&mut additional_builder)?;
388 }
389
390 Ok(())
391 }
392
393 pub fn new_dir(
394 ino: Ino,
395 md: &fs::Metadata,
396 dir_list: DirList,
397 additional: Option<InodeAdditional>,
398 ) -> io::Result<Self> {
399 if !md.is_dir() {
400 return Err(io::Error::new(
401 io::ErrorKind::Other,
402 format!("{ino} is a dir"),
403 ));
404 }
405
406 let mode = InodeMode::Dir { dir_list };
407 Ok(Self::new_inode(ino, md, mode, additional))
408 }
409
410 pub fn new_file(
411 ino: Ino,
412 md: &fs::Metadata,
413 file_chunks: Vec<FileChunk>,
414 additional: Option<InodeAdditional>,
415 ) -> io::Result<Self> {
416 if !md.is_file() {
417 return Err(io::Error::new(
418 io::ErrorKind::Other,
419 format!("{ino} is a file"),
420 ));
421 }
422
423 let mode = InodeMode::File {
424 chunks: file_chunks,
425 };
426 Ok(Self::new_inode(ino, md, mode, additional))
427 }
428
429 pub fn new_other(
430 ino: Ino,
431 md: &fs::Metadata,
432 additional: Option<InodeAdditional>,
433 ) -> io::Result<Self> {
434 let file_type = md.file_type();
435 let mode = if file_type.is_fifo() {
436 InodeMode::Fifo
437 } else if file_type.is_char_device() {
438 let major = stat::major(md.rdev());
439 let minor = stat::minor(md.rdev());
440 InodeMode::Chr { major, minor }
441 } else if file_type.is_dir() {
442 return Err(io::Error::new(
443 io::ErrorKind::Other,
444 format!("{ino} is a dir"),
445 ));
446 } else if file_type.is_block_device() {
447 let major = stat::major(md.rdev());
448 let minor = stat::minor(md.rdev());
449 InodeMode::Blk { major, minor }
450 } else if file_type.is_file() {
451 return Err(io::Error::new(
452 io::ErrorKind::Other,
453 format!("{ino} is a file"),
454 ));
455 } else if file_type.is_symlink() {
456 InodeMode::Lnk
457 } else if file_type.is_socket() {
458 InodeMode::Sock
459 } else {
460 InodeMode::Unknown
461 };
462
463 Ok(Self::new_inode(ino, md, mode, additional))
464 }
465
466 pub fn new_whiteout(ino: Ino) -> Self {
467 Inode {
468 ino,
469 mode: InodeMode::Wht,
470 uid: 0,
471 gid: 0,
472 permissions: DEFAULT_FILE_PERMISSIONS,
473 additional: None,
474 }
475 }
476
477 fn new_inode(
478 ino: Ino,
479 md: &fs::Metadata,
480 mode: InodeMode,
481 additional: Option<InodeAdditional>,
482 ) -> Self {
483 Inode {
484 ino,
485 mode,
486 uid: md.uid(),
487 gid: md.gid(),
488 permissions: (md.permissions().mode() & 0xFFF) as u16,
490 additional,
491 }
492 }
493
494 pub fn dir_entries(&self) -> Result<&Vec<DirEnt>> {
495 match &self.mode {
496 InodeMode::Dir { dir_list } => Ok(&dir_list.entries),
497 _ => Err(WireFormatError::from_errno(Errno::ENOTDIR)),
498 }
499 }
500
501 pub fn dir_lookup(&self, name: &[u8]) -> Result<u64> {
502 let entries = self.dir_entries()?;
503 entries
504 .iter()
505 .find(|dir_ent| dir_ent.name == name)
506 .map(|dir_ent| dir_ent.ino)
507 .ok_or_else(|| WireFormatError::from_errno(Errno::ENOENT))
508 }
509
510 pub fn file_len(&self) -> Result<u64> {
511 let chunks = match &self.mode {
512 InodeMode::File { chunks } => chunks,
513 _ => return Err(WireFormatError::from_errno(Errno::ENOTDIR)),
514 };
515 Ok(chunks.iter().map(|c| c.len).sum())
516 }
517
518 pub fn symlink_target(&self) -> Result<&OsStr> {
519 self.additional
520 .as_ref()
521 .and_then(|a| {
522 a.symlink_target
523 .as_ref()
524 .map(|x| OsStr::from_bytes(x.as_slice()))
525 })
526 .ok_or_else(|| WireFormatError::from_errno(Errno::ENOENT))
527 }
528
529 #[cfg(test)]
530 fn to_wire(&self) -> Result<Vec<u8>> {
531 let mut message = ::capnp::message::Builder::new_default();
532 let mut capnp_inode = message.init_root::<crate::metadata_capnp::inode::Builder<'_>>();
533
534 self.fill_capnp(&mut capnp_inode)?;
535
536 let mut buf = Vec::new();
537 ::capnp::serialize::write_message(&mut buf, &message)?;
538 Ok(buf)
539 }
540}
541
542#[derive(Debug, PartialEq, Eq)]
543pub enum InodeMode {
544 Unknown,
545 Fifo,
546 Chr { major: u64, minor: u64 },
547 Dir { dir_list: DirList },
548 Blk { major: u64, minor: u64 },
549 File { chunks: Vec<FileChunk> },
550 Lnk,
551 Sock,
552 Wht,
553}
554
555impl InodeMode {
556 fn from_capnp(reader: crate::metadata_capnp::inode::mode::Reader<'_>) -> Result<Self> {
557 match reader.which() {
558 Ok(crate::metadata_capnp::inode::mode::Unknown(())) => Ok(InodeMode::Unknown),
559 Ok(crate::metadata_capnp::inode::mode::Fifo(())) => Ok(InodeMode::Fifo),
560 Ok(crate::metadata_capnp::inode::mode::Lnk(())) => Ok(InodeMode::Lnk),
561 Ok(crate::metadata_capnp::inode::mode::Sock(())) => Ok(InodeMode::Sock),
562 Ok(crate::metadata_capnp::inode::mode::Wht(())) => Ok(InodeMode::Wht),
563 Ok(crate::metadata_capnp::inode::mode::Chr(reader)) => {
564 let r = reader?;
565 Ok(InodeMode::Chr {
566 major: r.get_major(),
567 minor: r.get_minor(),
568 })
569 }
570 Ok(crate::metadata_capnp::inode::mode::Blk(reader)) => {
571 let r = reader?;
572 Ok(InodeMode::Blk {
573 major: r.get_major(),
574 minor: r.get_minor(),
575 })
576 }
577 Ok(crate::metadata_capnp::inode::mode::File(reader)) => {
578 let r = reader?;
579 let chunks = r
580 .iter()
581 .map(FileChunk::from_capnp)
582 .collect::<Result<Vec<FileChunk>>>()?;
583 Ok(InodeMode::File { chunks })
584 }
585 Ok(crate::metadata_capnp::inode::mode::Dir(reader)) => {
586 let r = reader?;
587 let entries = r
588 .get_entries()?
589 .iter()
590 .map(|entry| {
591 let ino = entry.get_ino();
592 let dir_entry = entry.get_name().map(Vec::from);
593 match dir_entry {
594 Ok(d) => Ok(DirEnt { ino, name: d }),
595 Err(e) => Err(WireFormatError::from(e)),
596 }
597 })
598 .collect::<Result<Vec<DirEnt>>>()?;
599 let look_below = r.get_look_below();
600 Ok(InodeMode::Dir {
601 dir_list: DirList {
602 look_below,
603 entries,
604 },
605 })
606 }
607 Err(::capnp::NotInSchema(_e)) => {
608 Err(WireFormatError::InvalidSerializedData(Backtrace::capture()))
609 }
610 }
611 }
612
613 fn fill_capnp(
614 &self,
615 builder: &mut crate::metadata_capnp::inode::mode::Builder<'_>,
616 ) -> Result<()> {
617 match &self {
618 Self::Unknown => builder.set_unknown(()),
619 Self::Fifo => builder.set_fifo(()),
620 Self::Chr { major, minor } => {
621 let mut chr_builder = builder.reborrow().init_chr();
622 chr_builder.set_minor(*minor);
623 chr_builder.set_major(*major);
624 }
625 Self::Dir { dir_list } => {
626 let mut dir_builder = builder.reborrow().init_dir();
627 dir_builder.set_look_below(dir_list.look_below);
628 let entries_len = dir_list.entries.len().try_into()?;
629 let mut entries_builder = dir_builder.reborrow().init_entries(entries_len);
630
631 for (i, entry) in dir_list.entries.iter().enumerate() {
632 let mut dir_entry_builder = entries_builder.reborrow().get(i as u32);
634 dir_entry_builder.set_ino(entry.ino);
635 dir_entry_builder.set_name(&entry.name);
636 }
637 }
638 Self::Blk { major, minor } => {
639 let mut blk_builder = builder.reborrow().init_blk();
640 blk_builder.set_minor(*minor);
641 blk_builder.set_major(*major);
642 }
643 Self::File { chunks } => {
644 let chunks_len = chunks.len().try_into()?;
645 let mut chunks_builder = builder.reborrow().init_file(chunks_len);
646
647 for (i, chunk) in chunks.iter().enumerate() {
648 let mut chunk_builder = chunks_builder.reborrow().get(i as u32);
650 chunk_builder.set_len(chunk.len);
651 let mut blob_ref_builder = chunk_builder.init_blob();
652 chunk.blob.fill_capnp(&mut blob_ref_builder);
653 }
654 }
655 Self::Lnk => builder.set_lnk(()),
656 Self::Sock => builder.set_sock(()),
657 Self::Wht => builder.set_wht(()),
658 }
659 Ok(())
660 }
661}
662
663#[derive(Debug, PartialEq, Eq)]
664pub struct InodeAdditional {
665 pub xattrs: Vec<Xattr>,
666 pub symlink_target: Option<Vec<u8>>,
667}
668
669impl InodeAdditional {
670 pub fn from_capnp(
671 reader: crate::metadata_capnp::inode_additional::Reader<'_>,
672 ) -> Result<Option<Self>> {
673 if !(reader.has_xattrs() || reader.has_symlink_target()) {
674 return Ok(None);
675 }
676
677 let mut xattrs = Vec::new();
678 if reader.has_xattrs() {
679 for capnp_xattr in reader.get_xattrs()? {
680 let xattr = Xattr::from_capnp(capnp_xattr)?;
681 xattrs.push(xattr);
682 }
683 }
684
685 let symlink_target = if reader.has_symlink_target() {
686 Some(reader.get_symlink_target()?.to_vec())
687 } else {
688 None
689 };
690
691 Ok(Some(InodeAdditional {
692 xattrs,
693 symlink_target,
694 }))
695 }
696
697 pub fn fill_capnp(
698 &self,
699 builder: &mut crate::metadata_capnp::inode_additional::Builder<'_>,
700 ) -> Result<()> {
701 let xattrs_len = self.xattrs.len().try_into()?;
702 let mut xattrs_builder = builder.reborrow().init_xattrs(xattrs_len);
703
704 for (i, xattr) in self.xattrs.iter().enumerate() {
705 let mut xattr_builder = xattrs_builder.reborrow().get(i as u32);
707 xattr.fill_capnp(&mut xattr_builder);
708 }
709
710 if let Some(symlink_target) = &self.symlink_target {
711 builder.set_symlink_target(symlink_target);
712 }
713
714 Ok(())
715 }
716
717 pub fn new(p: &Path, md: &fs::Metadata) -> io::Result<Option<Self>> {
718 let symlink_target = if md.file_type().is_symlink() {
719 let t = fs::read_link(p)?;
720 Some(OsString::from(t).into_vec())
721 } else {
722 None
723 };
724 let xattrs = Self::get_xattrs(p)?;
725 if symlink_target.is_none() && xattrs.is_empty() {
726 Ok(None)
727 } else {
728 Ok(Some(InodeAdditional {
729 xattrs,
730 symlink_target,
731 }))
732 }
733 }
734
735 fn get_xattrs(p: &Path) -> io::Result<Vec<Xattr>> {
736 xattr::list(p)?
737 .map(|xa| {
738 let value = xattr::get(p, &xa)?;
739 Ok(Xattr {
740 key: xa.into_vec(),
741 val: value.unwrap(),
742 })
743 })
744 .collect()
745 }
746}
747
748#[derive(Debug, PartialEq, Eq)]
749pub struct Xattr {
750 pub key: Vec<u8>,
751 pub val: Vec<u8>,
752}
753
754impl Xattr {
755 pub fn from_capnp(reader: crate::metadata_capnp::xattr::Reader<'_>) -> Result<Self> {
756 let key = reader.get_key()?.to_vec();
757 let val = reader.get_val()?.to_vec();
758 Ok(Xattr { key, val })
759 }
760
761 pub fn fill_capnp(&self, builder: &mut crate::metadata_capnp::xattr::Builder<'_>) {
762 builder.set_val(&self.val);
763 builder.set_key(&self.key);
764 }
765}
766
767pub struct InodeVector<'a> {
768 reader: crate::metadata_capnp::inode_vector::Reader<'a>,
769}
770
771impl<'a> InodeVector<'a> {
772 pub fn get_inode_vector(
773 &self,
774 ) -> ::capnp::Result<::capnp::struct_list::Reader<'_, crate::metadata_capnp::inode::Owned>>
775 {
776 self.reader.get_inodes()
777 }
778
779 pub fn find_inode(&self, ino: Ino) -> Result<Option<crate::metadata_capnp::inode::Reader<'_>>> {
780 let mut left = 0;
781 let inodes = self.get_inode_vector()?;
782 let mut right = inodes.len() - 1;
783
784 while left <= right {
785 let mid = left + (right - left) / 2;
786 let i = inodes.get(mid);
787
788 if i.get_ino() == ino {
789 return Ok(Some(i));
790 }
791
792 if i.get_ino() < ino {
793 left = mid + 1;
794 } else {
795 if mid == 0 {
797 break;
798 }
799 right = mid - 1;
800 };
801 }
802
803 Ok(None)
804 }
805
806 pub fn max_ino(&self) -> Result<Option<Ino>> {
807 let inodes = self.get_inode_vector()?;
808 let last_index = inodes.len() - 1;
809 Ok(Some(inodes.get(last_index).get_ino()))
810 }
811
812 pub fn from_capnp(
813 reader: crate::metadata_capnp::inode_vector::Reader<'a>,
814 ) -> Result<Vec<Inode>> {
815 reader
816 .get_inodes()?
817 .iter()
818 .map(|inode| Inode::from_capnp(inode))
819 .collect()
820 }
821
822 fn fill_capnp(
823 inodes: &[Inode],
824 builder: &mut crate::metadata_capnp::inode_vector::Builder<'_>,
825 ) -> Result<()> {
826 let inodes_len = inodes.len().try_into()?;
827 let mut capnp_inodes = builder.reborrow().init_inodes(inodes_len);
828
829 for (i, inode) in inodes.iter().enumerate() {
830 let mut capnp_inode = capnp_inodes.reborrow().get(i as u32);
832 inode.fill_capnp(&mut capnp_inode)?;
833 }
834
835 Ok(())
836 }
837}
838
839#[derive(Debug, Clone, PartialEq, Eq)]
840pub struct Digest([u8; SHA256_BLOCK_SIZE]);
841
842impl Digest {
843 pub fn new(digest: &[u8; SHA256_BLOCK_SIZE]) -> Self {
844 Self(*digest)
845 }
846 pub fn underlying(&self) -> [u8; SHA256_BLOCK_SIZE] {
847 let mut dest = [0_u8; SHA256_BLOCK_SIZE];
848 dest.copy_from_slice(&self.0);
849 dest
850 }
851}
852
853impl fmt::Display for Digest {
854 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
855 write!(f, "{}", hex::encode(self.0))
856 }
857}
858
859impl Serialize for Digest {
860 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
861 where
862 S: Serializer,
863 {
864 let val = format!("sha256:{}", hex::encode(self.0));
865 serializer.serialize_str(&val)
866 }
867}
868
869impl TryFrom<&str> for Digest {
870 type Error = FromHexError;
871 fn try_from(s: &str) -> std::result::Result<Self, Self::Error> {
872 let mut digest: [u8; SHA256_BLOCK_SIZE] = [0; SHA256_BLOCK_SIZE];
873 hex::decode_to_slice(s, &mut digest)?;
874 Ok(Digest(digest))
875 }
876}
877
878impl TryFrom<BlobRef> for Digest {
879 type Error = WireFormatError;
880 fn try_from(v: BlobRef) -> std::result::Result<Self, Self::Error> {
881 Ok(Digest(v.digest))
882 }
883}
884
885impl TryFrom<&BlobRef> for Digest {
886 type Error = WireFormatError;
887 fn try_from(v: &BlobRef) -> std::result::Result<Self, Self::Error> {
888 Ok(Digest(v.digest))
889 }
890}
891
892impl<'de> Deserialize<'de> for Digest {
893 fn deserialize<D>(deserializer: D) -> std::result::Result<Digest, D::Error>
894 where
895 D: Deserializer<'de>,
896 {
897 struct DigestVisitor;
898
899 impl<'de> Visitor<'de> for DigestVisitor {
900 type Value = Digest;
901
902 fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
903 formatter.write_fmt(format_args!("expected 'sha256:<hex encoded hash>'"))
904 }
905
906 fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
907 where
908 E: SerdeError,
909 {
910 let parts: Vec<&str> = s.split(':').collect();
911 if parts.len() != 2 {
912 return Err(SerdeError::custom(format!("bad digest {s}")));
913 }
914
915 match parts[0] {
916 "sha256" => {
917 let buf =
918 hex::decode(parts[1]).map_err(|e| SerdeError::custom(e.to_string()))?;
919
920 let len = buf.len();
921 let digest: [u8; SHA256_BLOCK_SIZE] = buf.try_into().map_err(|_| {
922 SerdeError::custom(format!("invalid sha256 block length {len}"))
923 })?;
924 Ok(Digest(digest))
925 }
926 _ => Err(SerdeError::custom(format!(
927 "unknown digest type {}",
928 parts[0]
929 ))),
930 }
931 }
932 }
933
934 deserializer.deserialize_str(DigestVisitor)
935 }
936}