aboutsummaryrefslogtreecommitdiff
path: root/nix-rust/src/nar.rs
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-09-18 00:31:51 +0200
committerEelco Dolstra <edolstra@gmail.com>2019-12-10 13:37:23 +0100
commit7f08975050e19b3e3ed002743ed19022789c7ccc (patch)
treeabfbadd19848490618684c892caf21afc182a766 /nix-rust/src/nar.rs
parent6317f0f7a0663aed9d877031f7815fa292bd6b09 (diff)
Add NAR parser
Diffstat (limited to 'nix-rust/src/nar.rs')
-rw-r--r--nix-rust/src/nar.rs126
1 files changed, 126 insertions, 0 deletions
diff --git a/nix-rust/src/nar.rs b/nix-rust/src/nar.rs
new file mode 100644
index 000000000..aa05d815b
--- /dev/null
+++ b/nix-rust/src/nar.rs
@@ -0,0 +1,126 @@
+use crate::Error;
+use byteorder::{LittleEndian, ReadBytesExt};
+use std::convert::TryFrom;
+use std::io::Read;
+
+pub fn parse<R: Read>(input: &mut R) -> Result<(), Error> {
+ if String::read(input)? != NAR_VERSION_MAGIC {
+ return Err(Error::BadNarVersionMagic);
+ }
+
+ parse_file(input)
+}
+
+const NAR_VERSION_MAGIC: &str = "nix-archive-1";
+
+fn parse_file<R: Read>(input: &mut R) -> Result<(), Error> {
+ if String::read(input)? != "(" {
+ return Err(Error::MissingNarOpenTag);
+ }
+
+ if String::read(input)? != "type" {
+ return Err(Error::MissingNarField);
+ }
+
+ match String::read(input)?.as_ref() {
+ "regular" => {
+ let mut executable = false;
+ let mut tag = String::read(input)?;
+ if tag == "executable" {
+ executable = true;
+ if String::read(input)? != "" {
+ return Err(Error::BadExecutableField);
+ }
+ tag = String::read(input)?;
+ }
+ if tag != "contents" {
+ return Err(Error::MissingNarField);
+ }
+ let contents = Vec::<u8>::read(input)?;
+ if String::read(input)? != ")" {
+ return Err(Error::MissingNarCloseTag);
+ }
+ }
+ "directory" => loop {
+ match String::read(input)?.as_ref() {
+ "entry" => {
+ if String::read(input)? != "(" {
+ return Err(Error::MissingNarOpenTag);
+ }
+ if String::read(input)? != "name" {
+ return Err(Error::MissingNarField);
+ }
+ let name = String::read(input)?;
+ if String::read(input)? != "node" {
+ return Err(Error::MissingNarField);
+ }
+ parse_file(input)?;
+ let tag = String::read(input)?;
+ if tag != ")" {
+ return Err(Error::MissingNarCloseTag);
+ }
+ }
+ ")" => break,
+ s => return Err(Error::BadNarField(s.into())),
+ }
+ },
+ "symlink" => {
+ if String::read(input)? != "target" {
+ return Err(Error::MissingNarField);
+ }
+ let target = String::read(input)?;
+ if String::read(input)? != ")" {
+ return Err(Error::MissingNarCloseTag);
+ }
+ }
+ s => return Err(Error::BadNarField(s.into())),
+ }
+
+ Ok(())
+}
+
+trait Deserialize: Sized {
+ fn read<R: Read>(input: &mut R) -> Result<Self, Error>;
+}
+
+impl Deserialize for String {
+ fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
+ let buf = Deserialize::read(input)?;
+ Ok(String::from_utf8(buf).map_err(|_| Error::BadNarString)?)
+ }
+}
+
+impl Deserialize for Vec<u8> {
+ fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
+ let n: usize = Deserialize::read(input)?;
+ let mut buf = vec![0; n];
+ input.read_exact(&mut buf)?;
+ skip_padding(input, n)?;
+ Ok(buf)
+ }
+}
+
+fn skip_padding<R: Read>(input: &mut R, len: usize) -> Result<(), Error> {
+ if len % 8 != 0 {
+ let mut buf = [0; 8];
+ let buf = &mut buf[0..8 - (len % 8)];
+ input.read_exact(buf)?;
+ if !buf.iter().all(|b| *b == 0) {
+ return Err(Error::BadNarPadding);
+ }
+ }
+ Ok(())
+}
+
+impl Deserialize for u64 {
+ fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
+ Ok(input.read_u64::<LittleEndian>()?)
+ }
+}
+
+impl Deserialize for usize {
+ fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
+ let n: u64 = Deserialize::read(input)?;
+ Ok(usize::try_from(n).map_err(|_| Error::NarSizeFieldTooBig)?)
+ }
+}