Skip to content

Commit 66f543f

Browse files
allisonkarlitskayacgwalters
authored andcommitted
image: replace Directory.entries Vec with BTreeMap
Instead of trying to maintain our own sorted array of directory entries, just use a BTreeMap. There might be some very minor performance implications here one way or another, but this removes a lot of pointless code. Closes #92 Signed-off-by: Allison Karlitskaya <[email protected]>
1 parent f76d01e commit 66f543f

File tree

5 files changed

+36
-96
lines changed

5 files changed

+36
-96
lines changed

src/dumpfile.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustix::fs::FileType;
1313

1414
use crate::{
1515
fsverity::Sha256HashValue,
16-
image::{DirEnt, Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
16+
image::{Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
1717
};
1818

1919
fn write_empty(writer: &mut impl fmt::Write) -> fmt::Result {
@@ -226,9 +226,9 @@ impl<'a, W: Write> DumpfileWriter<'a, W> {
226226
fn write_dir(&mut self, path: &mut PathBuf, dir: &Directory) -> Result<()> {
227227
// nlink is 2 + number of subdirectories
228228
// this is also true for the root dir since '..' is another self-ref
229-
let nlink = dir.entries.iter().fold(2, |count, ent| {
229+
let nlink = dir.entries.values().fold(2, |count, inode| {
230230
count + {
231-
match ent.inode {
231+
match inode {
232232
Inode::Directory(..) => 1,
233233
_ => 0,
234234
}
@@ -239,8 +239,8 @@ impl<'a, W: Write> DumpfileWriter<'a, W> {
239239
write_directory(fmt, path, &dir.stat, nlink)
240240
})?;
241241

242-
for DirEnt { name, inode } in dir.entries.iter() {
243-
path.push(name);
242+
for (name, inode) in &dir.entries {
243+
path.push(name.as_ref());
244244

245245
match inode {
246246
Inode::Directory(ref dir) => {

src/erofs/writer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -485,13 +485,13 @@ impl<'a> InodeCollector<'a> {
485485

486486
let mut entries = vec![];
487487

488-
for entry in &dir.entries {
489-
let child = match &entry.inode {
488+
for (name, inode) in &dir.entries {
489+
let child = match inode {
490490
image::Inode::Directory(dir) => self.collect_dir(dir, me),
491491
image::Inode::Leaf(leaf) => self.collect_leaf(leaf),
492492
};
493493
entries.push(DirEnt {
494-
name: entry.name.as_bytes(),
494+
name: name.as_bytes(),
495495
inode: child,
496496
file_type: self.inodes[child].file_type(),
497497
});

src/fs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use zerocopy::IntoBytes;
2525

2626
use crate::{
2727
fsverity::{digest::FsVerityHasher, Sha256HashValue},
28-
image::{DirEnt, Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
28+
image::{Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
2929
repository::Repository,
3030
selabel::selabel,
3131
util::proc_self_fd,
@@ -112,7 +112,7 @@ fn write_leaf(leaf: &Leaf, dirfd: &OwnedFd, name: &OsStr, repo: &Repository) ->
112112
}
113113

114114
fn write_directory_contents(dir: &Directory, fd: &OwnedFd, repo: &Repository) -> Result<()> {
115-
for DirEnt { name, inode } in &dir.entries {
115+
for (name, inode) in &dir.entries {
116116
match inode {
117117
Inode::Directory(ref dir) => write_directory(dir, fd, name, repo),
118118
Inode::Leaf(ref leaf) => write_leaf(leaf, fd, name, repo),

src/image.rs

Lines changed: 21 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::{
22
cell::RefCell,
3-
cmp::{Ord, Ordering},
43
collections::BTreeMap,
54
ffi::{OsStr, OsString},
65
path::Path,
@@ -45,7 +44,7 @@ pub struct Leaf {
4544
#[derive(Debug)]
4645
pub struct Directory {
4746
pub stat: Stat,
48-
pub entries: Vec<DirEnt>,
47+
pub entries: BTreeMap<Box<OsStr>, Inode>,
4948
}
5049

5150
#[derive(Debug)]
@@ -54,12 +53,6 @@ pub enum Inode {
5453
Leaf(Rc<Leaf>),
5554
}
5655

57-
#[derive(Debug)]
58-
pub struct DirEnt {
59-
pub name: OsString,
60-
pub inode: Inode,
61-
}
62-
6356
impl Inode {
6457
pub fn stat(&self) -> &Stat {
6558
match self {
@@ -73,99 +66,46 @@ impl Directory {
7366
pub fn new(stat: Stat) -> Self {
7467
Self {
7568
stat,
76-
entries: vec![],
77-
}
78-
}
79-
80-
pub fn find_entry(&self, name: &OsStr) -> Result<usize, usize> {
81-
// OCI layer tarballs are typically sorted, with the entries for a particular directory
82-
// written out immediately after that directory was created. That means that it's very
83-
// likely that the thing we're looking for is either the last entry or the insertion point
84-
// immediately following it. Fast-path those cases by essentially unrolling the first
85-
// iteration of the binary search.
86-
if let Some(last_entry) = self.entries.last() {
87-
match name.cmp(&last_entry.name) {
88-
Ordering::Equal => Ok(self.entries.len() - 1), // the last item, indeed
89-
Ordering::Greater => Err(self.entries.len()), // need to append
90-
Ordering::Less => self.entries.binary_search_by_key(&name, |e| &e.name),
91-
}
92-
} else {
93-
Err(0)
69+
entries: BTreeMap::new(),
9470
}
9571
}
9672

9773
pub fn recurse(&mut self, name: impl AsRef<OsStr>) -> Result<&mut Directory> {
98-
match self.find_entry(name.as_ref()) {
99-
Ok(idx) => match &mut self.entries[idx].inode {
100-
Inode::Directory(ref mut subdir) => Ok(subdir),
101-
_ => bail!("Parent directory is not a directory"),
102-
},
103-
_ => bail!("Unable to find parent directory {:?}", name.as_ref()),
74+
match self.entries.get_mut(name.as_ref()) {
75+
Some(Inode::Directory(subdir)) => Ok(subdir),
76+
Some(_) => bail!("Parent directory is not a directory"),
77+
None => bail!("Unable to find parent directory {:?}", name.as_ref()),
10478
}
10579
}
10680

10781
pub fn mkdir(&mut self, name: &OsStr, stat: Stat) {
108-
match self.find_entry(name) {
109-
Ok(idx) => match self.entries[idx].inode {
110-
// Entry already exists, is a dir
111-
Inode::Directory(ref mut dir) => {
112-
// update the stat, but don't drop the entries
113-
dir.stat = stat;
114-
}
115-
// Entry already exists, is not a dir
116-
Inode::Leaf(..) => {
117-
todo!("Trying to replace non-dir with dir!");
118-
}
119-
},
82+
match self.entries.get_mut(name) {
83+
// Entry already exists, is a dir. update the stat, but don't drop the entries
84+
Some(Inode::Directory(dir)) => dir.stat = stat,
85+
// Entry already exists, is not a dir
86+
Some(Inode::Leaf(..)) => todo!("Trying to replace non-dir with dir!"),
12087
// Entry doesn't exist yet
121-
Err(idx) => {
122-
self.entries.insert(
123-
idx,
124-
DirEnt {
125-
name: OsString::from(name),
126-
inode: Inode::Directory(Box::new(Directory::new(stat))),
127-
},
128-
);
88+
None => {
89+
self.entries
90+
.insert(name.into(), Inode::Directory(Directory::new(stat).into()));
12991
}
13092
}
13193
}
13294

13395
pub fn insert(&mut self, name: &OsStr, inode: Inode) {
134-
match self.find_entry(name) {
135-
Ok(idx) => {
136-
// found existing item
137-
self.entries[idx].inode = inode;
138-
}
139-
Err(idx) => {
140-
// need to add new item
141-
self.entries.insert(
142-
idx,
143-
DirEnt {
144-
name: OsString::from(name),
145-
inode,
146-
},
147-
);
148-
}
149-
}
96+
self.entries.insert(name.into(), inode);
15097
}
15198

15299
pub fn get_for_link(&self, name: &OsStr) -> Result<Rc<Leaf>> {
153-
match self.find_entry(name) {
154-
Ok(idx) => match self.entries[idx].inode {
155-
Inode::Leaf(ref leaf) => Ok(Rc::clone(leaf)),
156-
Inode::Directory(..) => bail!("Cannot hardlink to directory"),
157-
},
158-
_ => bail!("Attempt to hardlink to non-existent file"),
100+
match self.entries.get(name) {
101+
Some(Inode::Leaf(leaf)) => Ok(Rc::clone(leaf)),
102+
Some(Inode::Directory(..)) => bail!("Cannot hardlink to directory"),
103+
None => bail!("Attempt to hardlink to non-existent file"),
159104
}
160105
}
161106

162107
pub fn remove(&mut self, name: &OsStr) {
163-
match self.find_entry(name) {
164-
Ok(idx) => {
165-
self.entries.remove(idx);
166-
}
167-
_ => { /* not an error to remove an already-missing file */ }
168-
}
108+
self.entries.remove(name);
169109
}
170110

171111
pub fn remove_all(&mut self) {
@@ -174,7 +114,7 @@ impl Directory {
174114

175115
pub fn newest_file(&self) -> i64 {
176116
let mut newest = self.stat.st_mtim_sec;
177-
for DirEnt { inode, .. } in &self.entries {
117+
for inode in self.entries.values() {
178118
let mtime = match inode {
179119
Inode::Leaf(ref leaf) => leaf.stat.st_mtim_sec,
180120
Inode::Directory(ref dir) => dir.newest_file(),

src/selabel.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use anyhow::{bail, ensure, Context, Result};
1111
use regex_automata::{hybrid::dfa, util::syntax, Anchored, Input};
1212

1313
use crate::{
14-
image::{DirEnt, Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
14+
image::{Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
1515
repository::Repository,
1616
};
1717

@@ -110,11 +110,11 @@ pub fn openat<'a>(
110110
filename: impl AsRef<OsStr>,
111111
repo: &Repository,
112112
) -> Result<Option<Box<dyn Read + 'a>>> {
113-
let Ok(idx) = dir.find_entry(filename.as_ref()) else {
113+
let Some(inode) = dir.entries.get(filename.as_ref()) else {
114114
return Ok(None);
115115
};
116116

117-
match &dir.entries[idx].inode {
117+
match inode {
118118
Inode::Leaf(leaf) => match &leaf.content {
119119
LeafContent::Regular(RegularFile::Inline(data)) => Ok(Some(Box::new(&**data))),
120120
LeafContent::Regular(RegularFile::External(id, ..)) => {
@@ -231,8 +231,8 @@ fn relabel_inode(inode: &Inode, path: &mut PathBuf, policy: &mut Policy) {
231231
fn relabel_dir(dir: &Directory, path: &mut PathBuf, policy: &mut Policy) {
232232
relabel(&dir.stat, path, b'd', policy);
233233

234-
for DirEnt { name, inode } in dir.entries.iter() {
235-
path.push(name);
234+
for (name, inode) in &dir.entries {
235+
path.push(name.as_ref());
236236
match policy.check_aliased(path.as_os_str()) {
237237
Some(original) => relabel_inode(inode, &mut PathBuf::from(original), policy),
238238
None => relabel_inode(inode, path, policy),

0 commit comments

Comments
 (0)