repo: Add an API to read and parse directory metadata

The fact that the uid/gid/mode are big endian bit me when I was
trying to parse this "by hand" in ostree-rs-ext.

Let's add a footgun-free API for this.

(And yeah, we should probably do the same for the other variant types)
This commit is contained in:
Colin Walters 2021-12-15 13:57:54 -05:00
parent f01c847a68
commit d1731d0ea8
3 changed files with 44 additions and 1 deletions

View File

@ -19,3 +19,30 @@ pub type TreeVariantType = (Vec<(String, Vec<u8>)>, Vec<(String, Vec<u8>, Vec<u8
/// The type of a directory metadata object: `(uuua(ayay))` /// The type of a directory metadata object: `(uuua(ayay))`
pub type DirmetaVariantType = (u32, u32, u32, Vec<(Vec<u8>, Vec<u8>)>); pub type DirmetaVariantType = (u32, u32, u32, Vec<(Vec<u8>, Vec<u8>)>);
/// Parsed representation of directory metadata.
pub struct DirMetaParsed {
/// The user ID.
pub uid: u32,
/// The group ID.
pub gid: u32,
/// The Unix mode, including file type flag.
pub mode: u32,
/// Extended attributes.
pub xattrs: Vec<(Vec<u8>, Vec<u8>)>,
}
impl DirMetaParsed {
/// Parse a directory metadata variant; must be of type `(uuua(ayay))`.
pub fn from_variant(
v: &glib::Variant,
) -> Result<DirMetaParsed, glib::variant::VariantTypeMismatchError> {
let (uid, gid, mode, xattrs) = v.try_get::<crate::DirmetaVariantType>()?;
Ok(DirMetaParsed {
uid: u32::from_be(uid),
gid: u32::from_be(gid),
mode: u32::from_be(mode),
xattrs,
})
}
}

View File

@ -441,4 +441,13 @@ impl Repo {
); );
})) }))
} }
/// Load and parse directory metadata.
/// In particular, uid/gid/mode are stored in big-endian format; this function
/// converts them to host native endianness.
pub fn read_dirmeta(&self, checksum: &str) -> Result<crate::DirMetaParsed, glib::Error> {
let v = self.load_variant(crate::ObjectType::DirMeta, checksum)?;
// Safety: We know the variant type will match since we just passed it above
Ok(crate::DirMetaParsed::from_variant(&v).unwrap())
}
} }

View File

@ -27,7 +27,7 @@ fn should_commit_content_to_repo_and_list_refs_again() {
} }
#[test] #[test]
fn should_traverse_commit() { fn repo_traverse_and_read() {
let test_repo = TestRepo::new(); let test_repo = TestRepo::new();
let checksum = test_repo.test_commit("test"); let checksum = test_repo.test_commit("test");
@ -58,6 +58,13 @@ fn should_traverse_commit() {
), ),
objects objects
); );
let dirmeta = test_repo
.repo
.read_dirmeta("ad49a0f4e3bc165361b6d17e8a865d479b373ee67d89ac6f0ce871f27da1be6d")
.unwrap();
// Right now, the uid/gid are actually that of the test runner
assert_eq!(dirmeta.mode, 0o40750);
} }
#[test] #[test]