nix_remote/
nar.rs

1//! The Nar archive format.
2//!
3//! The [`Nar`] struct represents a nar archive (essentially a directory tree) in memory.
4//! Since these can be large, it is often preferred to avoid buffering an entire nar in
5//! memory; the `stream` function allows for streaming a `Nar` (represented in the nix wire
6//! format) from a `std::io::Read` to a `std::io::Write`.
7
8use serde::{de::SeqAccess, ser::SerializeTuple, Deserialize, Serialize};
9use serde_bytes::ByteBuf;
10
11use crate::{
12    serialize::{NixDeserializer, Tee},
13    NixString,
14};
15
16#[derive(Clone, Debug, Default)]
17pub struct NarFile {
18    pub contents: NixString,
19    pub executable: bool,
20}
21
22#[derive(Clone, Debug)]
23pub enum Nar {
24    Contents(NarFile),
25    Target(NixString),
26    Directory(Vec<NarDirectoryEntry>),
27}
28
29impl Default for Nar {
30    fn default() -> Nar {
31        Nar::Contents(NarFile::default())
32    }
33}
34
35// TODO: if tagged_serde supported tagging with arbitrary ser/de types,
36// we could use it here
37#[derive(Clone, Debug)]
38pub struct NarDirectoryEntry {
39    pub name: NixString,
40    pub node: Nar,
41}
42
43pub trait EntrySink<'a>: 'a {
44    type DirectorySink: DirectorySink<'a>;
45    type FileSink: FileSink;
46
47    fn become_directory(self) -> Self::DirectorySink;
48    fn become_file(self) -> Self::FileSink;
49    fn become_symlink(self, target: NixString);
50}
51
52// The workaround for
53// https://github.com/rust-lang/rust/issues/87479
54pub trait DirectorySinkSuper {
55    type EntrySink<'b>: EntrySink<'b>;
56}
57
58pub trait DirectorySink<'a>: DirectorySinkSuper {
59    fn create_entry<'b>(&'b mut self, name: NixString) -> Self::EntrySink<'b>
60    where
61        'a: 'b;
62}
63
64pub trait FileSink: std::io::Write {
65    fn set_executable(&mut self, executable: bool);
66    fn add_contents(&mut self, contents: &[u8]);
67}
68
69impl<'a> EntrySink<'a> for &'a mut Nar {
70    type DirectorySink = &'a mut Vec<NarDirectoryEntry>;
71    type FileSink = &'a mut NarFile;
72
73    fn become_directory(self) -> Self::DirectorySink {
74        *self = Nar::Directory(Vec::new());
75        let Nar::Directory(dir) = self else {
76            unreachable!()
77        };
78        dir
79    }
80
81    fn become_file(self) -> Self::FileSink {
82        *self = Nar::Contents(NarFile {
83            executable: false,
84            contents: NixString::default(),
85        });
86        // TODO: can we express this better?
87        let Nar::Contents(contents) = self else {
88            unreachable!()
89        };
90        contents
91    }
92
93    fn become_symlink(self, target: NixString) {
94        *self = Nar::Target(target);
95    }
96}
97
98impl<'a> DirectorySinkSuper for &'a mut Vec<NarDirectoryEntry> {
99    type EntrySink<'b> = &'b mut Nar;
100}
101
102impl<'a> DirectorySink<'a> for &'a mut Vec<NarDirectoryEntry> {
103    fn create_entry<'b>(&'b mut self, name: NixString) -> Self::EntrySink<'b>
104    where
105        'a: 'b,
106    {
107        self.push(NarDirectoryEntry {
108            name,
109            node: Nar::Contents(NarFile {
110                contents: NixString::default(),
111                executable: false,
112            }),
113        });
114        &mut self.last_mut().unwrap().node
115    }
116}
117
118impl<'a> std::io::Write for &'a mut NarFile {
119    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
120        self.add_contents(buf);
121        Ok(buf.len())
122    }
123
124    fn flush(&mut self) -> std::io::Result<()> {
125        Ok(())
126    }
127}
128
129impl<'a> FileSink for &'a mut NarFile {
130    fn set_executable(&mut self, executable: bool) {
131        self.executable = executable;
132    }
133
134    fn add_contents(&mut self, contents: &[u8]) {
135        self.contents.0.extend_from_slice(contents);
136    }
137}
138
139#[derive(Default)]
140struct Null;
141
142impl<'a> std::io::Write for &'a mut Null {
143    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
144        Ok(buf.len())
145    }
146
147    fn flush(&mut self) -> std::io::Result<()> {
148        Ok(())
149    }
150}
151
152impl<'a> FileSink for &'a mut Null {
153    fn set_executable(&mut self, _executable: bool) {}
154
155    fn add_contents(&mut self, _contents: &[u8]) {}
156}
157
158impl<'a> EntrySink<'a> for &'a mut Null {
159    type DirectorySink = &'a mut Null;
160    type FileSink = &'a mut Null;
161
162    fn become_directory(self) -> Self::DirectorySink {
163        self
164    }
165
166    fn become_file(self) -> Self::FileSink {
167        self
168    }
169
170    fn become_symlink(self, _target: NixString) {}
171}
172
173impl<'a> DirectorySinkSuper for &'a mut Null {
174    type EntrySink<'b> = &'b mut Null;
175}
176
177impl<'a> DirectorySink<'a> for &'a mut Null {
178    fn create_entry<'b>(&'b mut self, _name: NixString) -> Self::EntrySink<'b>
179    where
180        'a: 'b,
181    {
182        self
183    }
184}
185
186trait SerializeTupleExt: SerializeTuple {
187    fn serialize_buf(&mut self, s: impl AsRef<[u8]>) -> Result<(), Self::Error> {
188        self.serialize_element(&ByteBuf::from(s.as_ref()))
189    }
190}
191
192impl<S: SerializeTuple> SerializeTupleExt for S {}
193
194// A trait that lets you read strings one-by-one in the Nix wire format.
195trait StringReader<'a> {
196    type Error: serde::de::Error;
197
198    fn expect_string(&mut self) -> Result<NixString, Self::Error>;
199
200    fn expect_tag(&mut self, s: &str) -> Result<(), Self::Error> {
201        let tag = self.expect_string()?;
202        if tag.0 != s.as_bytes() {
203            Err(serde::de::Error::custom(format!(
204                "got {tag:?} instead of `{s}`"
205            )))
206        } else {
207            Ok(())
208        }
209    }
210
211    // A "streaming" version of `expect_string` that might be optimized for long strings.
212    //
213    // The default impl doesn't do any streaming, it just reads the string into memory using
214    // `expect_string` and then writes it out again.
215    fn write_string(&mut self, mut write: impl std::io::Write) -> Result<(), Self::Error> {
216        write
217            .write_all(&self.expect_string()?.0)
218            .map_err(|e| serde::de::Error::custom(format!("io error: {e}")))
219    }
220}
221
222impl<'v, A: SeqAccess<'v>> StringReader<'v> for A {
223    type Error = A::Error;
224
225    fn expect_string(&mut self) -> Result<NixString, Self::Error> {
226        self.next_element()
227            .transpose()
228            .unwrap_or_else(|| Err(serde::de::Error::custom("unexpected end")))
229    }
230}
231
232fn read_entry<'v, 's, A: StringReader<'v>, S: EntrySink<'s> + 's>(
233    seq: &mut A,
234    sink: S,
235) -> Result<(), A::Error> {
236    seq.expect_tag("(")?;
237    seq.expect_tag("type")?;
238    let ty = seq.expect_string()?;
239    match ty.0.as_slice() {
240        b"regular" => {
241            let mut file = sink.become_file();
242            // This probably doesn't happen, but the nix source allows multiple settings of "executable"
243            let mut tag = seq.expect_string()?;
244            while tag.0 == b"executable" {
245                // Nix expects an empty string
246                seq.expect_tag("")?;
247                file.set_executable(true);
248                tag = seq.expect_string()?
249            }
250
251            if tag.0 == "contents" {
252                seq.write_string(file)?;
253                seq.expect_tag(")")?;
254                Ok(())
255            } else if tag.0 == ")" {
256                Ok(())
257            } else {
258                Err(serde::de::Error::custom(format!(
259                    "expected contents, got {tag:?}"
260                )))
261            }
262        }
263        b"symlink" => {
264            seq.expect_tag("target")?;
265            let target = seq.expect_string()?;
266            seq.expect_tag(")")?;
267            sink.become_symlink(target);
268            Ok(())
269        }
270        b"directory" => {
271            let mut dir = sink.become_directory();
272            loop {
273                let tag = seq.expect_string()?;
274                if tag.0 == ")" {
275                    break Ok(());
276                } else if tag.0 == "entry" {
277                    seq.expect_tag("(")?;
278                    seq.expect_tag("name")?;
279                    let name = seq.expect_string()?;
280                    let entry = dir.create_entry(name);
281                    seq.expect_tag("node")?;
282                    read_entry(seq, entry)?;
283                    seq.expect_tag(")")?;
284                } else {
285                    break Err(serde::de::Error::custom(format!(
286                        "expected entry, got {tag:?}"
287                    )));
288                }
289            }
290        }
291        v => Err(serde::de::Error::custom(format!(
292            "unknown file type `{v:?}`"
293        ))),
294    }
295}
296
297impl<'v> StringReader<'v> for NixDeserializer<'v> {
298    type Error = crate::serialize::Error;
299
300    fn expect_string(&mut self) -> Result<NixString, Self::Error> {
301        NixString::deserialize(self)
302    }
303
304    fn write_string(
305        &mut self,
306        mut write: impl std::io::Write,
307    ) -> Result<(), crate::serialize::Error> {
308        let len = self.read_u64()? as usize;
309        let mut buf = [0; 4096];
310        let mut remaining = len;
311        while remaining > 0 {
312            let max_len = buf.len().min(remaining);
313            let written = self.read.read(&mut buf[0..max_len])?;
314            write.write_all(&buf[0..written])?;
315
316            remaining -= written;
317        }
318
319        if len % 8 > 0 {
320            let padding = 8 - len % 8;
321            self.read.read_exact(&mut buf[..padding])?;
322        }
323        Ok(())
324    }
325}
326
327/// Stream a Nar from a reader to a writer.
328///
329// The tricky part is that a Nar isn't framed; in order to know when it ends,
330// we actually have to parse the thing. But we don't want to parse and then
331// re-serialize it, because we don't want to hold the whole thing in memory. So
332// what we do is to parse it into a dummy `EntrySink` (just so we know when the
333// Nar ends) while using a `Tee` to simultaneously write the consumed input into
334// the output.
335pub fn stream<R: std::io::Read, W: std::io::Write>(
336    read: R,
337    write: W,
338) -> Result<(), crate::serialize::Error> {
339    let mut tee = Tee::new(read, write);
340    let mut de = NixDeserializer { read: &mut tee };
341    de.expect_tag("nix-archive-1")?;
342    read_entry(&mut de, &mut Null)?;
343    Ok(())
344}
345
346impl<'de> Deserialize<'de> for Nar {
347    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
348    where
349        D: serde::Deserializer<'de>,
350    {
351        struct Visitor;
352
353        impl<'v> serde::de::Visitor<'v> for Visitor {
354            type Value = Nar;
355
356            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
357                formatter.write_str("Nar")
358            }
359
360            fn visit_seq<A: SeqAccess<'v>>(self, mut seq: A) -> Result<Nar, A::Error> {
361                seq.expect_tag("nix-archive-1")?;
362                let mut entry = Nar::default();
363                read_entry(&mut seq, &mut entry)?;
364                Ok(entry)
365            }
366        }
367
368        deserializer.deserialize_tuple(usize::MAX, Visitor)
369    }
370}
371
372impl Serialize for Nar {
373    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
374    where
375        S: serde::Serializer,
376    {
377        let mut tup = serializer.serialize_tuple(usize::MAX)?;
378        tup.serialize_buf(b"nix-archive-1")?;
379        tup.serialize_element(&Untagged(self))?;
380        tup.end()
381    }
382}
383
384struct Untagged<T>(T);
385
386impl<'a> Serialize for Untagged<&'a Nar> {
387    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
388    where
389        S: serde::Serializer,
390    {
391        let mut tup = serializer.serialize_tuple(usize::MAX)?;
392        tup.serialize_buf(b"(")?;
393        tup.serialize_buf(b"type")?;
394        match self.0 {
395            Nar::Contents(NarFile {
396                contents,
397                executable,
398            }) => {
399                tup.serialize_buf(b"regular")?;
400                if *executable {
401                    tup.serialize_buf(b"executable")?;
402                    tup.serialize_buf(b"")?;
403                }
404                tup.serialize_buf(b"contents")?;
405                tup.serialize_element(&contents)?;
406            }
407            Nar::Target(s) => {
408                tup.serialize_buf(b"symlink")?;
409                tup.serialize_buf(b"target")?;
410                tup.serialize_element(s)?;
411            }
412            Nar::Directory(entries) => {
413                tup.serialize_buf(b"directory")?;
414                for entry in entries {
415                    tup.serialize_buf(b"entry")?;
416                    tup.serialize_buf(b"(")?;
417                    tup.serialize_buf(b"name")?;
418                    tup.serialize_element(&entry.name)?;
419                    tup.serialize_buf(b"node")?;
420                    tup.serialize_element(&Untagged(&entry.node))?;
421                    tup.serialize_buf(b")")?;
422                }
423            }
424        }
425        tup.serialize_buf(b")")?;
426        tup.end()
427    }
428}