aboutsummaryrefslogtreecommitdiff
path: root/nix-rust/src
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
parent6317f0f7a0663aed9d877031f7815fa292bd6b09 (diff)
Add NAR parser
Diffstat (limited to 'nix-rust/src')
-rw-r--r--nix-rust/src/c.rs14
-rw-r--r--nix-rust/src/error.rs18
-rw-r--r--nix-rust/src/lib.rs1
-rw-r--r--nix-rust/src/nar.rs126
4 files changed, 157 insertions, 2 deletions
diff --git a/nix-rust/src/c.rs b/nix-rust/src/c.rs
index 1abd34198..542df93d2 100644
--- a/nix-rust/src/c.rs
+++ b/nix-rust/src/c.rs
@@ -1,4 +1,8 @@
-use super::{foreign::{self, CBox}, error, util, store};
+use super::{
+ error,
+ foreign::{self, CBox},
+ util,
+};
#[no_mangle]
pub extern "C" fn unpack_tarfile(
@@ -10,7 +14,8 @@ pub extern "C" fn unpack_tarfile(
#[no_mangle]
pub extern "C" fn rust_test() {
- use crate::store::Store;
+ /*
+ use crate::store::{self, Store};
use futures::future::{FutureExt, TryFutureExt};
use std::path::Path;
@@ -39,4 +44,9 @@ pub extern "C" fn rust_test() {
};
tokio::run(fut.boxed().compat());
+ */
+
+ let file = std::fs::File::open("test.nar").unwrap();
+
+ crate::nar::parse(&mut std::io::BufReader::new(file)).unwrap();
}
diff --git a/nix-rust/src/error.rs b/nix-rust/src/error.rs
index 5717a7a47..4defc646d 100644
--- a/nix-rust/src/error.rs
+++ b/nix-rust/src/error.rs
@@ -8,6 +8,15 @@ pub enum Error {
BadBase32,
StorePathNameTooLong,
BadStorePathName,
+ NarSizeFieldTooBig,
+ BadNarString,
+ BadNarPadding,
+ BadNarVersionMagic,
+ MissingNarOpenTag,
+ MissingNarCloseTag,
+ MissingNarField,
+ BadNarField(String),
+ BadExecutableField,
IOError(std::io::Error),
HttpError(reqwest::Error),
Misc(String),
@@ -37,6 +46,15 @@ impl fmt::Display for Error {
write!(f, "store path name is longer than 211 characters")
}
Error::BadStorePathName => write!(f, "store path name contains forbidden character"),
+ Error::NarSizeFieldTooBig => write!(f, "size field in NAR is too big"),
+ Error::BadNarString => write!(f, "NAR string is not valid UTF-8"),
+ Error::BadNarPadding => write!(f, "NAR padding is not zero"),
+ Error::BadNarVersionMagic => write!(f, "unsupported NAR version"),
+ Error::MissingNarOpenTag => write!(f, "NAR open tag is missing"),
+ Error::MissingNarCloseTag => write!(f, "NAR close tag is missing"),
+ Error::MissingNarField => write!(f, "expected NAR field is missing"),
+ Error::BadNarField(s) => write!(f, "unrecognized NAR field '{}'", s),
+ Error::BadExecutableField => write!(f, "bad 'executable' field in NAR"),
Error::IOError(err) => write!(f, "I/O error: {}", err),
Error::HttpError(err) => write!(f, "HTTP error: {}", err),
Error::Foreign(_) => write!(f, "<C++ exception>"), // FIXME
diff --git a/nix-rust/src/lib.rs b/nix-rust/src/lib.rs
index 635c00aff..cda63d2c7 100644
--- a/nix-rust/src/lib.rs
+++ b/nix-rust/src/lib.rs
@@ -14,6 +14,7 @@ extern crate proptest;
mod c;
mod error;
mod foreign;
+mod nar;
mod store;
mod util;
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)?)
+ }
+}