Skip to main content

pijul_core/
fs.rs

1//! Manipulating the internal representation of files and directories
2//! tracked by Pijul (i.e. adding files, removing files, getting file
3//! names…).
4//!
5//! Pijul tracks files in two different ways: one is the *graph*,
6//! where changes are applied. The other one is the *working copy*,
7//! where some filesystem changes are not yet recorded. The purpose of
8//! this double representation is to be able to compare a file from
9//! the graph with its version in the working copy, even if its name
10//! has changed in the working copy.
11//!
12//! The functions of this module work at exactly one of these two
13//! levels. Changing the graph is done by recording and applying a
14//! change, and changing the working copy is done either by some of the
15//! functions in this module, or by outputting the graph to the
16//! working copy (using the [output module](../output/index.html)).
17use crate::HashSet;
18use crate::changestore::*;
19use crate::pristine::*;
20use crate::small_string::*;
21use std::iter::Iterator;
22
23#[derive(Error)]
24pub enum FsError<T: TreeTxnT> {
25    #[error("Inode not found")]
26    InodeNotFound(Inode),
27    #[error(transparent)]
28    NotFound(#[from] FsNotFound),
29    #[error("File already in repository: {0}")]
30    AlreadyInRepo(String),
31    #[error(transparent)]
32    Tree(#[from] TreeErr<T::TreeError>),
33    #[error("Invalid path: {0}")]
34    InvalidPath(String),
35    #[error(transparent)]
36    SmallString(#[from] crate::small_string::Error),
37}
38
39impl<T: TreeTxnT> std::fmt::Debug for FsError<T> {
40    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
41        match self {
42            FsError::InodeNotFound(inode) => write!(fmt, "Inode not found: {}", inode.to_base32()),
43            FsError::NotFound(e) => std::fmt::Debug::fmt(e, fmt),
44            FsError::AlreadyInRepo(e) => write!(fmt, "File already in repository: {}", e),
45            FsError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
46            FsError::InvalidPath(e) => write!(fmt, "Invalid path: {}", e),
47            FsError::SmallString(e) => write!(fmt, "{}", e),
48        }
49    }
50}
51
52#[derive(Error)]
53pub enum FsErrorC<C: std::error::Error + 'static, T: GraphTxnT> {
54    #[error(transparent)]
55    Txn(#[from] TxnErr<T::GraphError>),
56    #[error(transparent)]
57    Changestore(C),
58    #[error(transparent)]
59    NotFound(#[from] FsNotFound),
60}
61
62impl<C: std::error::Error + 'static, T: GraphTxnT> std::fmt::Debug for FsErrorC<C, T> {
63    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
64        match self {
65            FsErrorC::Txn(e) => std::fmt::Debug::fmt(e, fmt),
66            FsErrorC::Changestore(e) => std::fmt::Debug::fmt(e, fmt),
67            FsErrorC::NotFound(e) => std::fmt::Debug::fmt(e, fmt),
68        }
69    }
70}
71
72#[derive(Debug, Error)]
73#[error("Path not found: {0}")]
74pub struct FsNotFound(String);
75
76pub fn create_new_inode<T: TreeMutTxnT>(
77    txn: &mut T,
78    parent_id: &PathId,
79    salt: u64,
80) -> Result<Inode, TreeErr<T::TreeError>> {
81    use std::hash::BuildHasher;
82    let mut i: u64 = crate::Hasher::default().hash_one((parent_id, salt));
83    let mut inode = Inode(L64(i.to_le()));
84    while txn.get_revtree(&inode, None)?.is_some() {
85        i += 1;
86        inode = Inode(L64(i.to_le()));
87    }
88    Ok(inode)
89}
90
91/// Test whether `inode` is the inode of a directory (as opposed to a
92/// file).
93pub fn is_directory<T: TreeTxnT>(txn: &T, inode: Inode) -> Result<bool, TreeErr<T::TreeError>> {
94    if inode == Inode::ROOT {
95        return Ok(true);
96    }
97    for x in txn.iter_tree(&OwnedPathId::inode(inode), None)? {
98        let (pid, _) = x?;
99        if pid.parent_inode < inode {
100            continue;
101        } else if pid.parent_inode > inode {
102            break;
103        }
104        return Ok(true);
105    }
106    Ok(false)
107}
108
109fn closest_in_repo_ancestor<'a, T: TreeTxnT>(
110    txn: &T,
111    path: &'a str,
112) -> Result<(Inode, std::iter::Peekable<crate::path::Components<'a>>), TreeErr<T::TreeError>> {
113    let mut components = crate::path::components(path).peekable();
114    let mut fileid = OwnedPathId::inode(Inode::ROOT);
115    while let Some(c) = components.peek() {
116        trace!("component {:?}", c);
117        fileid.basename.clone_from_str(c);
118        trace!("{:?}", fileid);
119        let mut found = false;
120        for x in txn.iter_tree(&fileid, None)? {
121            let (id, inode) = x?;
122            trace!(
123                "id = {:?}, inode = {:?}, cmp = {:?}",
124                id,
125                inode,
126                id > &fileid
127            );
128            if id > &fileid {
129                break;
130            } else if id < &fileid {
131                continue;
132            }
133            found = true;
134            fileid.parent_inode = *inode;
135            break;
136        }
137        if found {
138            components.next();
139        } else {
140            break;
141        }
142    }
143    Ok((fileid.parent_inode, components))
144}
145
146/// Find the inode corresponding to that path, if it exists.
147pub fn find_inode<T: TreeTxnT>(txn: &T, path: &str) -> Result<Inode, FsError<T>> {
148    debug!("find_inode");
149    let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
150    debug!("/find_inode");
151    if let Some(c) = remaining_path_components.next() {
152        debug!("c = {:?}", c);
153        Err(FsNotFound(path.to_string()).into())
154    } else {
155        Ok(inode)
156    }
157}
158
159/// Returns whether a path is registered in the working copy.
160pub fn is_tracked<T: TreeTxnT>(txn: &T, path: &str) -> Result<bool, TreeErr<T::TreeError>> {
161    debug!("is_tracked {:?}", path);
162    let (_, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
163    debug!("/is_tracked {:?}", path);
164    Ok(remaining_path_components.next().is_none())
165}
166
167pub struct GetVertex {
168    pub remaining: bool,
169    pub result: Option<Position<ChangeId>>,
170}
171
172/// Returns whether a path is registered in the working copy.
173pub fn get_vertex<T: TreeTxnT>(txn: &T, path: &str) -> Result<GetVertex, TreeErr<T::TreeError>> {
174    debug!("is_tracked {:?}", path);
175    let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
176    debug!("/is_tracked {:?}", path);
177    if remaining_path_components.next().is_none() {
178        Ok(GetVertex {
179            remaining: true,
180            result: txn.get_inodes(&inode, None)?.cloned(),
181        })
182    } else {
183        Ok(GetVertex {
184            remaining: false,
185            result: None,
186        })
187    }
188}
189
190/// Find the filename leading from the root to `inode`.
191pub fn inode_filename<T: TreeTxnT>(
192    txn: &T,
193    inode: Inode,
194) -> Result<Option<String>, TreeErr<T::TreeError>> {
195    debug!("inode_filename {:?}", inode);
196    let mut components = Vec::new();
197    let mut current = inode;
198    loop {
199        match txn.get_revtree(&current, None)? {
200            Some(v) => {
201                components.push(v.basename.to_owned());
202                current = v.parent_inode;
203                if current == Inode::ROOT {
204                    break;
205                }
206            }
207            None => {
208                debug!("filename_of_inode: not in tree");
209                return Ok(None);
210            }
211        }
212    }
213
214    let mut path = String::new();
215    for c in components.iter().rev() {
216        if !path.is_empty() {
217            path.push('/')
218        }
219        path.push_str(c.as_str());
220    }
221    debug!("inode_filename = {:?}", path);
222    Ok(Some(path))
223}
224
225/// Record the information that `parent_inode` is now a parent of
226/// file `filename`, and `filename` has inode `child_inode`.
227pub fn make_new_child<T: TreeMutTxnT>(
228    txn: &mut T,
229    parent_inode: Inode,
230    filename: &str,
231    is_dir: bool,
232    child_inode: Option<Inode>,
233    salt: u64,
234) -> Result<Inode, FsError<T>> {
235    let parent_id = OwnedPathId {
236        parent_inode,
237        basename: filename.parse()?,
238    };
239
240    if let Some(&inode) = txn.get_tree(&parent_id, None)? {
241        debug!("inode = {:?}", inode);
242        if let Some(child) = child_inode {
243            if child == inode {
244                // No need to do anything.
245                Ok(inode)
246            } else {
247                del_tree_with_rev(txn, &parent_id, &inode)?;
248                if let Some(&vertex) = txn.get_inodes(&inode, None)? {
249                    del_inodes_with_rev(txn, &inode, &vertex)?;
250                }
251                put_tree_with_rev(txn, &parent_id, &child)?;
252                Ok(child)
253            }
254        } else {
255            Err(FsError::AlreadyInRepo(filename.to_string()))
256        }
257    } else {
258        let child_inode = match child_inode {
259            None => create_new_inode(txn, &parent_id, salt)?,
260            Some(i) => i,
261        };
262        debug!("make_new_child: {:?} {:?}", parent_id, child_inode);
263        put_tree_with_rev(txn, &parent_id, &child_inode)?;
264
265        if is_dir {
266            let dir_id = OwnedPathId {
267                parent_inode: child_inode,
268                basename: SmallString::new(),
269            };
270            txn.put_tree(&dir_id, &child_inode)?;
271        };
272        Ok(child_inode)
273    }
274}
275
276pub fn add_inode<T: TreeMutTxnT>(
277    txn: &mut T,
278    inode: Option<Inode>,
279    path: &str,
280    is_dir: bool,
281    salt: u64,
282) -> Result<Inode, FsError<T>> {
283    debug!("add_inode");
284    if let Some(parent) = crate::path::parent(path) {
285        let (current_inode, unrecorded_path) = closest_in_repo_ancestor(txn, parent)?;
286        let mut current_inode = current_inode;
287        debug!("add_inode: closest = {:?}", current_inode);
288        for c in unrecorded_path {
289            debug!("unrecorded: {:?}", c);
290            current_inode = make_new_child(txn, current_inode, c, true, None, salt)?;
291        }
292        let file_name = crate::path::file_name(path).unwrap();
293        debug!("add_inode: file_name = {:?}", file_name);
294        current_inode = make_new_child(txn, current_inode, file_name, is_dir, inode, salt)?;
295        Ok(current_inode)
296    } else {
297        Err(FsError::InvalidPath(path.to_string()))
298    }
299}
300
301/// Move an inode (file or directory) from `origin` to `destination`,
302/// (in the working copy).
303///
304/// **Warning**: both `origin` and `destination` must be full paths to
305/// the inode being moved (unlike e.g. in the `mv` Unix command).
306pub fn move_file<T: TreeMutTxnT>(
307    txn: &mut T,
308    origin: &str,
309    destination: &str,
310    salt: u64,
311) -> Result<(), FsError<T>> {
312    debug!("move_file: {},{}", origin, destination);
313    move_file_by_inode(txn, find_inode(txn, origin)?, destination, salt)?;
314    Ok(())
315}
316
317pub fn move_file_by_inode<T: TreeMutTxnT>(
318    txn: &mut T,
319    inode: Inode,
320    destination: &str,
321    salt: u64,
322) -> Result<(), FsError<T>> {
323    debug!("inode = {:?}", inode);
324    let fileref = if let Some(inode) = txn.get_revtree(&inode, None)? {
325        inode.to_owned()
326    } else {
327        return Err(FsError::InodeNotFound(inode));
328    };
329    debug!("fileref = {:?}", fileref);
330    del_tree_with_rev(txn, &fileref, &inode)?;
331    debug!("inode={:?} destination={}", inode, destination);
332    let is_dir = txn
333        .get_tree(
334            &OwnedPathId {
335                parent_inode: inode,
336                basename: SmallString::new(),
337            },
338            None,
339        )?
340        .is_some();
341    add_inode(txn, Some(inode), destination, is_dir, salt)?;
342    Ok(())
343}
344
345pub(crate) fn rec_delete<T: TreeMutTxnT>(
346    txn: &mut T,
347    parent: &PathId,
348    inode: Inode,
349    delete_inodes: bool,
350) -> Result<(), FsError<T>> {
351    let file_id = OwnedPathId {
352        parent_inode: inode,
353        basename: SmallString::new(),
354    };
355    let mut children = Vec::new();
356    let mut is_dir = false;
357    for x in txn.iter_tree(&file_id, None)? {
358        let (k, inode) = x?;
359        assert!(k.parent_inode >= file_id.parent_inode);
360        if k.parent_inode > file_id.parent_inode {
361            break;
362        }
363        debug!("iter_tree: {:?} {:?}", k, inode);
364        is_dir = true;
365        if !k.basename.is_empty() {
366            children.push((k.to_owned(), *inode))
367        }
368    }
369    for (k, inode_) in children {
370        assert_ne!(inode, inode_);
371        rec_delete(txn, &k, inode_, delete_inodes)?;
372    }
373    debug!(
374        "rec_delete: {:?}, {:?}, {:?}, {:?}",
375        parent, file_id, inode, is_dir
376    );
377    if is_dir {
378        assert!(inode.is_root() || txn.del_tree(&file_id, Some(&inode))?);
379    }
380    if !inode.is_root() && del_tree_with_rev(txn, parent, &inode)? {
381        if delete_inodes && let Some(&vertex) = txn.get_inodes(&inode, None)? {
382            del_inodes_with_rev(txn, &inode, &vertex)?;
383        }
384    } else {
385        debug!("rec_delete: {:?} {:?} not present", parent, inode);
386    }
387    Ok(())
388}
389
390/// Removes a file from the repository.
391pub fn remove_file<T: TreeMutTxnT>(txn: &mut T, path: &str) -> Result<(), FsError<T>> {
392    debug!("remove file {:?}", path);
393    let inode = find_inode(txn, path)?;
394    let parent = if inode.is_root() {
395        OwnedPathId {
396            parent_inode: Inode::ROOT,
397            basename: SmallString::new(),
398        }
399    } else {
400        txn.get_revtree(&inode, None)?.unwrap().to_owned()
401    };
402    debug!("remove file {:?} {:?}", parent, inode);
403    rec_delete(txn, &parent, inode, false)?;
404    Ok(())
405}
406
407/// An iterator over the children (i.e. one level down) of an inode in
408/// the working copy.
409///
410/// Constructed using
411/// [`working_copy_children`](fn.working_copy_children.html).
412pub struct WorkingCopyChildren<'txn, T: TreeTxnT> {
413    iter: crate::pristine::Cursor<T, &'txn T, T::TreeCursor, PathId, Inode>,
414    inode: Inode,
415}
416
417impl<'txn, T: TreeTxnT> Iterator for WorkingCopyChildren<'txn, T> {
418    type Item = Result<(SmallString, Inode), T::TreeError>;
419    fn next(&mut self) -> Option<Self::Item> {
420        loop {
421            match self.iter.next() {
422                Some(Ok((k, v))) => {
423                    if k.parent_inode == self.inode {
424                        if !k.basename.is_empty() {
425                            return Some(Ok((k.basename.to_owned(), *v)));
426                        }
427                    } else if k.parent_inode > self.inode {
428                        return None;
429                    }
430                }
431                None => return None,
432                Some(Err(e)) => return Some(Err(e.0)),
433            }
434        }
435    }
436}
437
438/// Returns a list of the children of an inode, in the working copy.
439pub fn working_copy_children<'a, T: TreeTxnT>(
440    txn: &'a T,
441    inode: Inode,
442) -> Result<WorkingCopyChildren<'a, T>, T::TreeError> {
443    Ok(WorkingCopyChildren {
444        iter: txn
445            .iter_tree(
446                &OwnedPathId {
447                    parent_inode: inode,
448                    basename: SmallString::new(),
449                },
450                None,
451            )
452            .map_err(|e| e.0)?,
453        inode,
454    })
455}
456
457/// An iterator over all the paths in the working copy.
458///
459/// Constructed using [`iter_working_copy`](fn.iter_working_copy.html).
460pub struct WorkingCopyIterator<'txn, T: TreeTxnT> {
461    stack: Vec<(Inode, String)>,
462    txn: &'txn T,
463}
464
465impl<'txn, T: TreeTxnT> Iterator for WorkingCopyIterator<'txn, T> {
466    type Item = Result<(Inode, String, bool), T::TreeError>;
467    fn next(&mut self) -> Option<Self::Item> {
468        loop {
469            if let Some((inode, name)) = self.stack.pop() {
470                let fileid = OwnedPathId {
471                    parent_inode: inode,
472                    basename: SmallString::default(),
473                };
474                let len = self.stack.len();
475                let iter = match self.txn.iter_tree(&fileid, None) {
476                    Ok(iter) => iter,
477                    Err(e) => return Some(Err(e.0)),
478                };
479                let mut is_folder = false;
480                for x in iter {
481                    let (k, v) = match x {
482                        Ok(x) => x,
483                        Err(e) => return Some(Err(e.0)),
484                    };
485                    if k.parent_inode < inode {
486                        continue;
487                    } else if k.parent_inode > inode {
488                        break;
489                    }
490                    is_folder = true;
491                    if !k.basename.is_empty() {
492                        let mut name = name.clone();
493                        crate::path::push(&mut name, k.basename.as_str());
494                        self.stack.push((*v, name))
495                    }
496                }
497                (self.stack[len..]).reverse();
498                if !name.is_empty() {
499                    return Some(Ok((inode, name, is_folder)));
500                }
501            } else {
502                return None;
503            }
504        }
505    }
506}
507
508/// Returns an iterator over all the files in the working copy.
509pub fn iter_working_copy<'a, T: TreeTxnT>(txn: &'a T, root: Inode) -> WorkingCopyIterator<'a, T> {
510    WorkingCopyIterator {
511        stack: vec![(root, String::new())],
512        txn,
513    }
514}
515
516/// An iterator over the descendants of an
517/// inode key in the graph.
518///
519/// Constructed using
520/// [`iter_graph_descendants`](fn.iter_graph_descendants.html).
521pub struct GraphDescendants<'txn, T: GraphTxnT> {
522    txn: &'txn T,
523    channel: &'txn T::Graph,
524    stack: Vec<AdjacentIterator<'txn, T>>,
525    visited: HashSet<Position<ChangeId>>,
526}
527
528impl<'txn, T: GraphTxnT> Iterator for GraphDescendants<'txn, T> {
529    type Item = Result<Position<ChangeId>, T::GraphError>;
530    fn next(&mut self) -> Option<Self::Item> {
531        while let Some(mut adj) = self.stack.pop() {
532            match adj.next() {
533                Some(Ok(child)) => {
534                    self.stack.push(adj);
535                    let dest = match self.txn.find_block(self.channel, child.dest()) {
536                        Ok(dest) => dest,
537                        Err(BlockError::Txn(t)) => return Some(Err(t)),
538                        Err(e) => panic!("{}", e),
539                    };
540                    let grandchild = match iter_adjacent(
541                        self.txn,
542                        self.channel,
543                        *dest,
544                        EdgeFlags::FOLDER,
545                        EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
546                    ) {
547                        Ok(mut x) => match x.next().unwrap() {
548                            Ok(x) => x,
549                            Err(e) => return Some(Err(e.0)),
550                        },
551                        Err(e) => return Some(Err(e.0)),
552                    };
553
554                    if self.visited.insert(grandchild.dest()) {
555                        match iter_adjacent(
556                            self.txn,
557                            self.channel,
558                            grandchild.dest().inode_vertex(),
559                            EdgeFlags::FOLDER,
560                            EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
561                        ) {
562                            Ok(adj) => self.stack.push(adj),
563                            Err(e) => return Some(Err(e.0)),
564                        }
565                    }
566                    return Some(Ok(grandchild.dest()));
567                }
568                Some(Err(e)) => return Some(Err(e.0)),
569                None => {
570                    // No child left, actually pop.
571                }
572            }
573        }
574        None
575    }
576}
577
578/// Returns a list of files under the given key.  The root key is
579/// [`pristine::Vertex::ROOT`](../pristine/constant.Vertex::ROOT.html).
580pub fn iter_graph_descendants<'txn, T: GraphTxnT>(
581    txn: &'txn T,
582    channel: &'txn T::Graph,
583    key: Position<ChangeId>,
584) -> Result<GraphDescendants<'txn, T>, T::GraphError> {
585    Ok(GraphDescendants {
586        stack: vec![
587            iter_adjacent(
588                txn,
589                channel,
590                key.inode_vertex(),
591                EdgeFlags::FOLDER,
592                EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
593            )
594            .map_err(|e| e.0)?,
595        ],
596        visited: HashSet::default(),
597        txn,
598        channel,
599    })
600}
601
602/// An iterator over the children (i.e. a single level down) of an
603/// inode key in the graph.
604///
605/// Constructed using
606/// [`iter_graph_children`](fn.iter_graph_children.html).
607pub struct GraphChildren<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
608    txn: &'txn T,
609    channel: &'txn T::Graph,
610    adj: AdjacentIterator<'txn, T>,
611    adj2: Option<AdjacentIterator<'txn, T>>,
612    changes: &'changes P,
613    buf: Vec<u8>,
614}
615
616impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
617    for GraphChildren<'txn, 'changes, T, P>
618{
619    type Item = Result<(Position<ChangeId>, ChangeId, InodeMetadata, String), T::GraphError>;
620    fn next(&mut self) -> Option<Self::Item> {
621        let dest = loop {
622            debug!("adj2 = {:?}", self.adj2.is_some());
623            if let Some(mut adj2) = self.adj2.take() {
624                match adj2.next() {
625                    None => {}
626                    Some(Ok(ch)) => {
627                        self.adj2 = Some(adj2);
628                        break self.txn.find_block(self.channel, ch.dest()).unwrap();
629                    }
630                    Some(Err(e)) => return Some(Err(e.0)),
631                }
632            }
633            match self.adj.next() {
634                Some(Ok(child)) => {
635                    let d = self.txn.find_block(self.channel, child.dest()).unwrap();
636                    if d.start == d.end {
637                        match iter_adjacent(
638                            self.txn,
639                            self.channel,
640                            *d,
641                            EdgeFlags::FOLDER,
642                            EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
643                        )
644                        .and_then(|mut it| it.next().unwrap())
645                        .and_then(|x| {
646                            iter_adjacent(
647                                self.txn,
648                                self.channel,
649                                x.dest().inode_vertex(),
650                                EdgeFlags::FOLDER,
651                                EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
652                            )
653                        }) {
654                            Ok(adj) => self.adj2 = Some(adj),
655                            Err(e) => return Some(Err(e.0)),
656                        }
657                    } else {
658                        break d;
659                    }
660                }
661                Some(Err(e)) => return Some(Err(e.0)),
662                None => return None,
663            }
664        };
665        debug!("dest = {:?}", dest);
666
667        let mut buf = std::mem::take(&mut self.buf);
668        buf.resize(dest.end - dest.start, 0);
669        let FileMetadata {
670            basename,
671            metadata: perms,
672            ..
673        } = self
674            .changes
675            .get_file_meta(
676                |p| self.txn.get_external(&p).ok().map(|x| x.into()),
677                *dest,
678                &mut buf,
679            )
680            .unwrap();
681        let basename = basename.to_string();
682        self.buf = buf;
683
684        let grandchild = match iter_adjacent(
685            self.txn,
686            self.channel,
687            *dest,
688            EdgeFlags::FOLDER,
689            EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
690        ) {
691            Ok(mut adj) => match adj.next() {
692                Some(Ok(n)) => n,
693                None => unreachable!(),
694                Some(Err(e)) => return Some(Err(e.0)),
695            },
696            Err(e) => return Some(Err(e.0)),
697        };
698        Some(Ok((
699            grandchild.dest(),
700            grandchild.introduced_by(),
701            perms,
702            basename,
703        )))
704    }
705}
706
707/// Returns a list of files under the given key.  The root key is
708/// [`pristine::Vertex::ROOT`](../pristine/constant.Vertex::ROOT.html).
709pub fn iter_graph_children<'txn, 'changes, T, P>(
710    txn: &'txn T,
711    changes: &'changes P,
712    channel: &'txn T::Graph,
713    key: Position<ChangeId>,
714) -> Result<GraphChildren<'txn, 'changes, T, P>, T::GraphError>
715where
716    T: GraphTxnT,
717    P: ChangeStore,
718{
719    Ok(GraphChildren {
720        buf: Vec::new(),
721        adj: iter_adjacent(
722            txn,
723            channel,
724            key.inode_vertex(),
725            EdgeFlags::FOLDER,
726            EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
727        )
728        .map_err(|e| e.0)?,
729        adj2: None,
730        txn,
731        channel,
732        changes,
733    })
734}
735
736/// An iterator over the basenames of an "inode key" in the graph.
737///
738/// See [`iter_basenames`](fn.iter_basenames.html).
739pub struct GraphBasenames<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
740    txn: &'txn T,
741    channel: &'txn T::Graph,
742    adj: AdjacentIterator<'txn, T>,
743    changes: &'changes P,
744    buf: Vec<u8>,
745}
746
747impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
748    for GraphBasenames<'txn, 'changes, T, P>
749{
750    type Item = Result<(Position<ChangeId>, InodeMetadata, String), T::GraphError>;
751    fn next(&mut self) -> Option<Self::Item> {
752        loop {
753            let parent = match self.adj.next() {
754                Some(Ok(n)) => n,
755                Some(Err(e)) => return Some(Err(e.0)),
756                None => return None,
757            };
758            let dest = self
759                .txn
760                .find_block_end(self.channel, parent.dest())
761                .unwrap();
762            if dest.start == dest.end {
763                // non-null root.
764                return None;
765            }
766            let mut buf = std::mem::take(&mut self.buf);
767            buf.resize(dest.end - dest.start, 0);
768            let FileMetadata {
769                basename,
770                metadata: perms,
771                ..
772            } = self
773                .changes
774                .get_file_meta(
775                    |p| self.txn.get_external(&p).ok().map(|x| x.into()),
776                    *dest,
777                    &mut buf,
778                )
779                .unwrap();
780            let basename = basename.to_owned();
781            self.buf = buf;
782            match iter_adjacent(
783                self.txn,
784                self.channel,
785                *dest,
786                EdgeFlags::FOLDER | EdgeFlags::PARENT,
787                EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
788            ) {
789                Ok(mut adj) => match adj.next() {
790                    Some(Ok(grandparent)) => {
791                        return Some(Ok((grandparent.dest(), perms, basename)));
792                    }
793                    Some(Err(e)) => return Some(Err(e.0)),
794                    None => {}
795                },
796                Err(e) => return Some(Err(e.0)),
797            }
798        }
799    }
800}
801
802/// List all the basenames of an "inode key" in the graph (more than
803/// one name means a conflict).
804///
805/// See also [`iter_paths`](fn.iter_paths.html).
806pub fn iter_basenames<'txn, 'changes, T, P>(
807    txn: &'txn T,
808    changes: &'changes P,
809    channel: &'txn T::Graph,
810    pos: Position<ChangeId>,
811) -> Result<GraphBasenames<'txn, 'changes, T, P>, T::GraphError>
812where
813    T: GraphTxnT,
814    P: ChangeStore,
815{
816    Ok(GraphBasenames {
817        buf: Vec::new(),
818        adj: iter_adjacent(
819            txn,
820            channel,
821            pos.inode_vertex(),
822            EdgeFlags::FOLDER | EdgeFlags::PARENT,
823            EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
824        )
825        .map_err(|e| e.0)?,
826        txn,
827        channel,
828        changes,
829    })
830}
831
832/// Traverse the paths in the graph to a key. **Warning:** there might
833/// be a number of paths exponential in the number of conflicts.
834///
835/// This function takes a closure `f`, which gets called on each path
836/// with an iterator over the keys from the root to `key`. This
837/// function stops when `f` returns `false` (or when all the paths
838/// have been traversed).
839///
840/// See also [`iter_basenames`](fn.iter_basenames.html).
841pub fn iter_paths<T: GraphTxnT, F: FnMut(&mut dyn Iterator<Item = Position<ChangeId>>) -> bool>(
842    txn: &T,
843    graph: &T::Graph,
844    key: Position<ChangeId>,
845    mut f: F,
846) -> Result<(), TxnErr<T::GraphError>> {
847    let mut stack: Vec<(Position<ChangeId>, bool)> = vec![(key, true)];
848    while let Some((cur_key, on_stack)) = stack.pop() {
849        if cur_key.is_root() {
850            if !f(&mut stack
851                .iter()
852                .filter_map(|(key, on_path)| if *on_path { Some(*key) } else { None }))
853            {
854                break;
855            }
856        } else if !on_stack {
857            stack.push((cur_key, true));
858            let len = stack.len();
859            let f = EdgeFlags::parent_folder();
860            for parent in
861                iter_adjacent(txn, graph, cur_key.inode_vertex(), f, f | EdgeFlags::PSEUDO)?
862            {
863                let parent_dest = txn.find_block_end(graph, parent?.dest()).unwrap();
864                for grandparent in
865                    iter_adjacent(txn, graph, *parent_dest, f, f | EdgeFlags::PSEUDO)?
866                {
867                    stack.push((grandparent?.dest(), false))
868                }
869            }
870            if stack.len() == len {
871                stack.pop();
872            }
873        }
874    }
875    Ok(())
876}
877
878pub(crate) fn follow_oldest_path<T: ChannelTxnT, C: ChangeStore>(
879    changes: &C,
880    txn: &T,
881    channel: &T::Channel,
882    path: &str,
883) -> Result<(Position<ChangeId>, bool), FsErrorC<C::Error, T>> {
884    use crate::pristine::*;
885    debug!("follow_oldest_path = {:?}", path);
886    let mut current = Position::ROOT;
887    let flag0 = EdgeFlags::FOLDER;
888    let flag1 = flag0 | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
889    let mut name_buf = Vec::new();
890    let mut ambiguous = false;
891    let mut seen = HashSet::new();
892    for c in crate::path::components(path) {
893        'outer: loop {
894            let mut next = None;
895            debug!("follow_oldest_path, loop {:?}", current);
896            for name in iter_adjacent(
897                txn,
898                txn.graph(channel),
899                current.inode_vertex(),
900                flag0,
901                flag1,
902            )? {
903                let name = name?;
904                let name_dest = txn.find_block(txn.graph(channel), name.dest()).unwrap();
905                debug!("name_dest = {:?}", name_dest);
906                if name_dest.start == name_dest.end {
907                    // non-null root, just continue.
908                    current = iter_adjacent(txn, txn.graph(channel), *name_dest, flag0, flag1)?
909                        .next()
910                        .unwrap()?
911                        .dest();
912                    continue 'outer;
913                }
914                name_buf.resize(name_dest.end - name_dest.start, 0);
915                debug!("getting contents {:?}", name);
916                changes
917                    .get_contents(
918                        |h| txn.get_external(&h).ok().map(|x| x.into()),
919                        *name_dest,
920                        &mut name_buf,
921                    )
922                    .map_err(FsErrorC::Changestore)?;
923                debug!("{:?}", std::str::from_utf8(&name_buf));
924                let FileMetadata { basename, .. } = FileMetadata::read(&name_buf);
925                if basename == c {
926                    let age = txn
927                        .get_changeset(txn.changes(channel), &name.dest().change)
928                        .unwrap();
929                    if let Some((ref mut next, ref mut next_age)) = next {
930                        ambiguous = true;
931                        if age < *next_age {
932                            *next = name_dest;
933                            *next_age = age;
934                        }
935                    } else {
936                        next = Some((name_dest, age));
937                    }
938                }
939            }
940            if let Some((next, _)) = next {
941                let current_ = iter_adjacent(txn, txn.graph(channel), *next, flag0, flag1)?
942                    .next()
943                    .unwrap()?
944                    .dest();
945                if seen.insert(current_) {
946                    current = current_;
947                    break;
948                }
949            } else {
950                return Err(FsErrorC::NotFound(FsNotFound(path.to_string())));
951            }
952        }
953    }
954    Ok((current, ambiguous))
955}
956
957pub struct FindPath {
958    pub path: Vec<String>,
959    pub all_alive: bool,
960}
961
962/// Compute either the oldest or the youngest filesystem path to a
963/// graph vertex.
964pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
965    changes: &C,
966    txn: &T,
967    channel: &T::Channel,
968    youngest: bool,
969    mut v: Position<ChangeId>,
970) -> Result<Option<FindPath>, crate::output::FileError<C::Error, T>> {
971    debug!("oldest_path = {:?}", v);
972    let mut path = Vec::new();
973    let mut name_buf = Vec::new();
974    let mut seen = HashSet::new();
975    let flag0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
976    let flag1 = EdgeFlags::all();
977    let mut all_alive = true;
978    'outer: while !v.change.is_root() {
979        debug!("path = {:?}", path);
980        let mut next_v = None;
981        let mut alive = false;
982        let inode_vertex = match txn.find_block_end(txn.graph(channel), v) {
983            Ok(block) => block,
984            Err(BlockError::Block { block, .. }) => {
985                debug!("block not found {:?}", block);
986                assert!(path.is_empty());
987                return Ok(None);
988            }
989            Err(BlockError::Txn(t)) => return Err(crate::output::FileError::Txn(TxnErr(t))),
990        };
991        debug!("inode_vertex = {:?}", inode_vertex);
992        if *inode_vertex != v.inode_vertex() {
993            info!(
994                "find_path: {:?} != {:?}, this may be due to a corrupt change",
995                inode_vertex,
996                v.inode_vertex()
997            );
998            return Ok(None);
999        }
1000        for name in iter_adjacent(txn, txn.graph(channel), v.inode_vertex(), flag0, flag1)? {
1001            let name = name?;
1002            if name.dest().is_root() {
1003                break 'outer;
1004            }
1005            if !name.flag().contains(EdgeFlags::PARENT) {
1006                continue;
1007            }
1008
1009            debug!("oldest_path, name = {:?}", name);
1010            let age = txn
1011                .get_changeset(txn.changes(channel), &name.dest().change)?
1012                .unwrap();
1013
1014            let name_dest = txn.find_block_end(txn.graph(channel), name.dest()).unwrap();
1015            debug!("name_dest = {:?}", name_dest);
1016            let mut next = None;
1017            for e in iter_adjacent(txn, txn.graph(channel), *name_dest, flag0, flag1)? {
1018                let e = e?;
1019                if e.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
1020                    next = Some(e);
1021                    break;
1022                }
1023            }
1024            debug!("next = {:?}", next);
1025            if let Some(next) = next {
1026                debug!("oldest_path, next = {:?}", next);
1027                if !next.flag().contains(EdgeFlags::DELETED) {
1028                    alive = true;
1029                } else if alive {
1030                    break;
1031                } else {
1032                    all_alive = false
1033                }
1034                if let Some((_, p_age, _)) = next_v
1035                    && (age > p_age) ^ youngest
1036                {
1037                    debug!("youngest");
1038                    continue;
1039                }
1040                if seen.contains(&next.dest()) {
1041                    debug!("seen");
1042                    continue;
1043                }
1044                next_v = Some((name_dest, age, next.dest()));
1045            }
1046        }
1047        let (name, _, next) = next_v.unwrap();
1048        seen.insert(next);
1049        if name.start == name.end {
1050            // Non-zero root vertex
1051            assert!(next.change.is_root());
1052            break;
1053        }
1054        if alive {
1055            name_buf.resize(name.end - name.start, 0);
1056            debug!("getting contents {:?}", name);
1057
1058            let FileMetadata { basename, .. } = changes
1059                .get_file_meta(
1060                    |p| txn.get_external(&p).ok().map(From::from),
1061                    *name,
1062                    &mut name_buf,
1063                )
1064                .map_err(crate::output::FileError::Changestore)?;
1065
1066            path.push(basename.to_string());
1067        }
1068        debug!("next = {:?}", next);
1069        v = next;
1070    }
1071    path.reverse();
1072    Ok(Some(FindPath { path, all_alive }))
1073}
1074
1075pub fn get_latest_touch<T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>>(
1076    txn: &T,
1077    channel: &T::Channel,
1078    pos: &Position<ChangeId>,
1079) -> Result<(u64, ChangeId), TxnErr<T::GraphError>> {
1080    let mut latest_change = L64(0);
1081    let mut id = ChangeId::ROOT;
1082    let mut touch_iter = Some(txn.iter_touched(pos)?);
1083    let mut log_iter = changeid_rev_log(txn, channel, None)?;
1084    let mut continue_ = true;
1085    while continue_ {
1086        continue_ = false;
1087        if let Some(ref mut it) = touch_iter
1088            && let Some(c) = it.next()
1089        {
1090            debug!("inode, change = {:?}", c);
1091            let (inode, change) = c?;
1092            if inode > pos {
1093                touch_iter = None;
1094            } else if inode == pos
1095                && let Some(&t) = txn.get_changeset(txn.changes(channel), change)?
1096            {
1097                if t >= latest_change {
1098                    latest_change = t;
1099                    id = *change;
1100                }
1101                continue_ = true;
1102            }
1103        }
1104        if let Some(l) = log_iter.next() {
1105            debug!("int = {:?}", l);
1106            let (n, p) = l?;
1107            let touched = txn.get_touched_files(pos, Some(&p.a))?;
1108            if touched == Some(&p.a) {
1109                id = p.a;
1110                latest_change = *n;
1111                break;
1112            }
1113            continue_ = true;
1114        }
1115    }
1116    Ok((latest_change.into(), id))
1117}