Skip to main content

pijul_core/
apply.rs

1//! Apply a change.
2use crate::change::{Atom, Change, EdgeMap, NewVertex};
3use crate::changestore::ChangeStore;
4use crate::missing_context::*;
5use crate::pristine::*;
6use crate::record::InodeUpdate;
7use crate::{HashMap, HashSet};
8use std::collections::BTreeSet;
9use thiserror::Error;
10pub mod edge;
11pub(crate) use edge::*;
12pub mod vertex;
13pub(crate) use vertex::*;
14
15pub enum ApplyError<ChangestoreError: std::error::Error, T: GraphTxnT + TreeTxnT> {
16    Changestore(ChangestoreError),
17    LocalChange(LocalApplyError<T>),
18    MakeChange(crate::change::MakeChangeError<T>),
19}
20
21impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Debug for ApplyError<C, T> {
22    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
23        match self {
24            ApplyError::Changestore(e) => std::fmt::Debug::fmt(e, fmt),
25            ApplyError::LocalChange(e) => std::fmt::Debug::fmt(e, fmt),
26            ApplyError::MakeChange(e) => std::fmt::Debug::fmt(e, fmt),
27        }
28    }
29}
30
31impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Display for ApplyError<C, T> {
32    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
33        match self {
34            ApplyError::Changestore(e) => std::fmt::Display::fmt(e, fmt),
35            ApplyError::LocalChange(e) => std::fmt::Display::fmt(e, fmt),
36            ApplyError::MakeChange(e) => std::fmt::Display::fmt(e, fmt),
37        }
38    }
39}
40
41impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> std::error::Error for ApplyError<C, T> {}
42
43#[derive(Error)]
44pub enum LocalApplyError<T: GraphTxnT + TreeTxnT> {
45    DependencyMissing { hash: crate::pristine::Hash },
46    ChangeAlreadyOnChannel { hash: crate::pristine::Hash },
47    Txn(#[from] TxnErr<T::GraphError>),
48    Tree(#[from] TreeErr<T::TreeError>),
49    Block { block: Position<ChangeId> },
50    InvalidChange,
51    Corruption,
52    MakeChange(#[from] crate::change::MakeChangeError<T>),
53}
54
55impl<T: GraphTxnT + TreeTxnT> std::fmt::Debug for LocalApplyError<T> {
56    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
57        match self {
58            LocalApplyError::DependencyMissing { hash } => {
59                write!(fmt, "Dependency missing: {:?}", hash)
60            }
61            LocalApplyError::ChangeAlreadyOnChannel { hash } => {
62                write!(fmt, "Change already on channel: {:?}", hash)
63            }
64            LocalApplyError::Txn(e) => std::fmt::Debug::fmt(e, fmt),
65            LocalApplyError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
66            LocalApplyError::Block { block } => write!(fmt, "Block error: {:?}", block),
67            LocalApplyError::InvalidChange => write!(fmt, "Invalid change"),
68            LocalApplyError::Corruption => write!(fmt, "Corruption"),
69            LocalApplyError::MakeChange(e) => std::fmt::Debug::fmt(e, fmt),
70        }
71    }
72}
73
74impl<T: GraphTxnT + TreeTxnT> std::fmt::Display for LocalApplyError<T> {
75    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
76        match self {
77            LocalApplyError::DependencyMissing { hash } => {
78                write!(fmt, "Dependency missing: {:?}", hash)
79            }
80            LocalApplyError::ChangeAlreadyOnChannel { hash } => {
81                write!(fmt, "Change already on channel: {:?}", hash)
82            }
83            LocalApplyError::Txn(e) => std::fmt::Display::fmt(e, fmt),
84            LocalApplyError::Tree(e) => std::fmt::Display::fmt(e, fmt),
85            LocalApplyError::Block { block } => write!(fmt, "Block error: {:?}", block),
86            LocalApplyError::InvalidChange => write!(fmt, "Invalid change"),
87            LocalApplyError::Corruption => write!(fmt, "Corruption"),
88            LocalApplyError::MakeChange(e) => std::fmt::Display::fmt(e, fmt),
89        }
90    }
91}
92
93impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> From<crate::pristine::TxnErr<T::GraphError>>
94    for ApplyError<C, T>
95{
96    fn from(err: crate::pristine::TxnErr<T::GraphError>) -> Self {
97        ApplyError::LocalChange(LocalApplyError::Txn(err))
98    }
99}
100
101impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> From<crate::change::MakeChangeError<T>>
102    for ApplyError<C, T>
103{
104    fn from(err: crate::change::MakeChangeError<T>) -> Self {
105        ApplyError::MakeChange(err)
106    }
107}
108
109impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> From<crate::pristine::TreeErr<T::TreeError>>
110    for ApplyError<C, T>
111{
112    fn from(err: crate::pristine::TreeErr<T::TreeError>) -> Self {
113        ApplyError::LocalChange(LocalApplyError::Tree(err))
114    }
115}
116
117impl<T: GraphTxnT + TreeTxnT> LocalApplyError<T> {
118    fn from_missing(err: MissingError<T::GraphError>) -> Self {
119        match err {
120            MissingError::Txn(e) => LocalApplyError::Txn(TxnErr(e)),
121            MissingError::Block(e) => e.into(),
122            MissingError::Inconsistent(_) => LocalApplyError::InvalidChange,
123        }
124    }
125}
126
127impl<T: GraphTxnT + TreeTxnT> From<crate::pristine::InconsistentChange<T::GraphError>>
128    for LocalApplyError<T>
129{
130    fn from(err: crate::pristine::InconsistentChange<T::GraphError>) -> Self {
131        match err {
132            InconsistentChange::Txn(e) => LocalApplyError::Txn(TxnErr(e)),
133            _ => LocalApplyError::InvalidChange,
134        }
135    }
136}
137
138impl<T: GraphTxnT + TreeTxnT> From<crate::pristine::BlockError<T::GraphError>>
139    for LocalApplyError<T>
140{
141    fn from(err: crate::pristine::BlockError<T::GraphError>) -> Self {
142        match err {
143            BlockError::Txn(e) => LocalApplyError::Txn(TxnErr(e)),
144            BlockError::Block { block } => LocalApplyError::Block { block },
145        }
146    }
147}
148
149impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> From<crate::pristine::BlockError<T::GraphError>>
150    for ApplyError<C, T>
151{
152    fn from(err: crate::pristine::BlockError<T::GraphError>) -> Self {
153        ApplyError::LocalChange(LocalApplyError::from(err))
154    }
155}
156
157/// Apply a change to a channel. This function does not update the
158/// inodes/tree tables, i.e. the correspondence between the pristine
159/// and the working copy. Therefore, this function must be used only
160/// on remote changes, or on "bare" repositories.
161pub fn apply_change_ws<T: MutTxnT, P: ChangeStore>(
162    changes: &P,
163    txn: &mut T,
164    channel: &mut T::Channel,
165    hash: &Hash,
166    workspace: &mut Workspace,
167) -> Result<(u64, Merkle), ApplyError<P::Error, T>> {
168    debug!("apply_change {:?}", hash.to_base32());
169    workspace.clear();
170    let change = changes.get_change(hash).map_err(ApplyError::Changestore)?;
171
172    for hash in change.dependencies.iter() {
173        if let Hash::None = hash {
174            continue;
175        }
176        if let Some(int) = txn.get_internal(&hash.into())?
177            && txn.get_changeset(txn.changes(channel), int)?.is_some()
178        {
179            continue;
180        }
181        return Err(ApplyError::LocalChange(
182            LocalApplyError::DependencyMissing { hash: *hash },
183        ));
184    }
185
186    let internal = if let Some(&p) = txn.get_internal(&hash.into())? {
187        p
188    } else {
189        let internal: ChangeId = make_changeid(txn, hash)?;
190        register_change(txn, &internal, hash, &change)?;
191        internal
192    };
193    debug!("internal = {:?}", internal);
194    apply_change_to_channel(
195        txn,
196        channel,
197        &mut |h| changes.knows(h, hash).unwrap(),
198        internal,
199        hash,
200        &change,
201        workspace,
202    )
203    .map_err(ApplyError::LocalChange)
204}
205
206pub fn apply_change_rec_ws<T: TxnT + MutTxnT, P: ChangeStore>(
207    changes: &P,
208    txn: &mut T,
209    channel: &mut T::Channel,
210    hash: &Hash,
211    workspace: &mut Workspace,
212    deps_only: bool,
213) -> Result<(), ApplyError<P::Error, T>> {
214    debug!("apply_change {:?}", hash.to_base32());
215    workspace.clear();
216    let mut dep_stack = vec![(*hash, true, !deps_only)];
217    let mut visited = HashSet::default();
218    while let Some((hash, first, actually_apply)) = dep_stack.pop() {
219        let change = changes.get_change(&hash).map_err(ApplyError::Changestore)?;
220        let shash: SerializedHash = (&hash).into();
221        if first {
222            if !visited.insert(hash) {
223                continue;
224            }
225            if let Some(change_id) = txn.get_internal(&shash)?
226                && txn
227                    .get_changeset(txn.changes(channel), change_id)?
228                    .is_some()
229            {
230                continue;
231            }
232
233            dep_stack.push((hash, false, actually_apply));
234            for &hash in change.dependencies.iter() {
235                if let Hash::None = hash {
236                    continue;
237                }
238                dep_stack.push((hash, true, true))
239            }
240        } else if actually_apply {
241            let applied = if let Some(int) = txn.get_internal(&shash)? {
242                txn.get_changeset(txn.changes(channel), int)?.is_some()
243            } else {
244                false
245            };
246            if !applied {
247                let internal = if let Some(&p) = txn.get_internal(&shash)? {
248                    p
249                } else {
250                    let internal: ChangeId = make_changeid(txn, &hash)?;
251                    register_change(txn, &internal, &hash, &change)?;
252                    internal
253                };
254                debug!("internal = {:?}", internal);
255                workspace.clear();
256                apply_change_to_channel(
257                    txn,
258                    channel,
259                    &mut |h| changes.knows(h, &hash).unwrap(),
260                    internal,
261                    &hash,
262                    &change,
263                    workspace,
264                )
265                .map_err(ApplyError::LocalChange)?;
266            }
267        }
268    }
269    Ok(())
270}
271
272/// Same as [apply_change_ws], but allocates its own workspace.
273pub fn apply_change<T: MutTxnT, P: ChangeStore>(
274    changes: &P,
275    txn: &mut T,
276    channel: &mut T::Channel,
277    hash: &Hash,
278) -> Result<(u64, Merkle), ApplyError<P::Error, T>> {
279    apply_change_ws(changes, txn, channel, hash, &mut Workspace::new())
280}
281
282/// Same as [apply_change], but with a wrapped `txn` and `channel`.
283pub fn apply_change_arc<T: MutTxnT, P: ChangeStore>(
284    changes: &P,
285    txn: &ArcTxn<T>,
286    channel: &ChannelRef<T>,
287    hash: &Hash,
288) -> Result<(u64, Merkle), ApplyError<P::Error, T>> {
289    apply_change_ws(
290        changes,
291        &mut *txn.write(),
292        &mut *channel.write(),
293        hash,
294        &mut Workspace::new(),
295    )
296}
297
298/// Same as [apply_change_ws], but allocates its own workspace.
299pub fn apply_change_rec<T: MutTxnT, P: ChangeStore>(
300    changes: &P,
301    txn: &mut T,
302    channel: &mut T::Channel,
303    hash: &Hash,
304    deps_only: bool,
305) -> Result<(), ApplyError<P::Error, T>> {
306    apply_change_rec_ws(
307        changes,
308        txn,
309        channel,
310        hash,
311        &mut Workspace::new(),
312        deps_only,
313    )
314}
315
316fn apply_change_to_channel<T: ChannelMutTxnT + TreeTxnT, F: FnMut(&Hash) -> bool>(
317    txn: &mut T,
318    channel: &mut T::Channel,
319    changes: &mut F,
320    change_id: ChangeId,
321    hash: &Hash,
322    change: &Change,
323    ws: &mut Workspace,
324) -> Result<(u64, Merkle), LocalApplyError<T>> {
325    ws.assert_empty();
326    let n = txn.apply_counter(channel);
327    debug!("apply_change_to_channel {:?} {:?}", change_id, hash);
328    let merkle =
329        if let Some(m) = txn.put_changes(channel, change_id, txn.apply_counter(channel), hash)? {
330            m
331        } else {
332            return Err(LocalApplyError::ChangeAlreadyOnChannel { hash: *hash });
333        };
334    debug!("apply change to channel");
335    let now = std::time::Instant::now();
336    for (n, change_) in change.changes.iter().enumerate() {
337        debug!("Applying {} {:?} (1)", n, change_);
338        for change_ in change_.iter() {
339            match *change_ {
340                Atom::NewVertex(ref n) => put_newvertex(
341                    txn,
342                    T::graph_mut(channel),
343                    changes,
344                    change,
345                    ws,
346                    change_id,
347                    n,
348                )?,
349                Atom::EdgeMap(ref n) => {
350                    for edge in n.edges.iter() {
351                        if !edge.flag.contains(EdgeFlags::DELETED) {
352                            put_newedge(
353                                txn,
354                                T::graph_mut(channel),
355                                ws,
356                                change_id,
357                                n.inode,
358                                edge,
359                                |_, _| true,
360                                |h| change.knows(h),
361                            )?;
362                        }
363                    }
364                }
365            }
366        }
367    }
368    for change_ in change.changes.iter() {
369        debug!("Applying {:?} (2)", change_);
370        for change_ in change_.iter() {
371            if let Atom::EdgeMap(ref n) = *change_ {
372                for edge in n.edges.iter() {
373                    if edge.flag.contains(EdgeFlags::DELETED) {
374                        put_newedge(
375                            txn,
376                            T::graph_mut(channel),
377                            ws,
378                            change_id,
379                            n.inode,
380                            edge,
381                            |_, _| true,
382                            |h| change.knows(h),
383                        )?;
384                    }
385                }
386            }
387        }
388    }
389    crate::TIMERS.lock().unwrap().apply += now.elapsed();
390
391    let mut inodes = clean_obsolete_pseudo_edges(txn, T::graph_mut(channel), ws, change_id)?;
392    collect_missing_contexts(txn, txn.graph(channel), ws, change, change_id, &mut inodes)?;
393    for &i in inodes.iter() {
394        repair_zombies(txn, T::graph_mut(channel), i, None)?;
395    }
396    for &i in inodes.iter() {
397        repair_up(txn, T::graph_mut(channel), i)?;
398    }
399
400    detect_folder_conflict_resolutions(
401        txn,
402        T::graph_mut(channel),
403        &mut ws.missing_context,
404        change_id,
405        change,
406    )
407    .map_err(LocalApplyError::from_missing)?;
408
409    repair_cyclic_paths(txn, T::graph_mut(channel), ws)?;
410    info!("done applying change");
411    Ok((n, merkle))
412}
413
414/// Apply a change created locally: serialize it, compute its hash, and
415/// apply it. This function also registers changes in the filesystem
416/// introduced by the change (file additions, deletions and moves), to
417/// synchronise the pristine and the working copy after the
418/// application.
419pub fn apply_local_change_ws<
420    T: ChannelMutTxnT + DepsMutTxnT<DepsError = <T as GraphTxnT>::GraphError> + TreeMutTxnT,
421>(
422    txn: &mut T,
423    channel: &ChannelRef<T>,
424    change: &Change,
425    hash: &Hash,
426    inode_updates: &HashMap<usize, InodeUpdate>,
427    workspace: &mut Workspace,
428) -> Result<(u64, Merkle), LocalApplyError<T>> {
429    let mut channel = channel.write();
430    let internal: ChangeId = make_changeid(txn, hash)?;
431    debug!("make_changeid {:?} {:?}", hash, internal);
432
433    for hash in change.dependencies.iter() {
434        if let Hash::None = hash {
435            continue;
436        }
437        if let Some(int) = txn.get_internal(&hash.into())?
438            && txn.get_changeset(txn.changes(&channel), int)?.is_some()
439        {
440            continue;
441        }
442        return Err(LocalApplyError::DependencyMissing { hash: *hash });
443    }
444
445    register_change(txn, &internal, hash, change)?;
446    // A locally-recorded change is brand new, so no change already in the
447    // channel can "know" it: this must match `changes.knows(h, hash)` from the
448    // non-local path (apply_change_ws), which is always false here. Using
449    // `|_| true` instead suppressed the zombie markings a fresh apply creates
450    // (inserts into a deleted/zombie context), making record produce a
451    // non-canonical graph that diverged from replay.
452    let n = apply_change_to_channel(
453        txn,
454        &mut channel,
455        &mut |_| false,
456        internal,
457        hash,
458        change,
459        workspace,
460    )?;
461    for (_, update) in inode_updates.iter() {
462        info!("updating {:?}", update);
463        update_inode(txn, &channel, internal, update)?;
464    }
465    Ok(n)
466}
467
468/// Same as [apply_local_change_ws], but allocates its own workspace.
469pub fn apply_local_change<
470    T: ChannelMutTxnT + DepsMutTxnT<DepsError = <T as GraphTxnT>::GraphError> + TreeMutTxnT,
471>(
472    txn: &mut T,
473    channel: &ChannelRef<T>,
474    change: &Change,
475    hash: &Hash,
476    inode_updates: &HashMap<usize, InodeUpdate>,
477) -> Result<(u64, Merkle), LocalApplyError<T>> {
478    apply_local_change_ws(
479        txn,
480        channel,
481        change,
482        hash,
483        inode_updates,
484        &mut Workspace::new(),
485    )
486}
487
488fn update_inode<T: ChannelTxnT + TreeMutTxnT>(
489    txn: &mut T,
490    channel: &T::Channel,
491    internal: ChangeId,
492    update: &InodeUpdate,
493) -> Result<(), LocalApplyError<T>> {
494    debug!("update_inode {:?}", update);
495    match *update {
496        InodeUpdate::Add { inode, pos, .. } => {
497            let vertex = Position {
498                change: internal,
499                pos,
500            };
501            if txn
502                .get_graph(txn.graph(channel), &vertex.inode_vertex(), None)?
503                .is_some()
504            {
505                debug!("Adding inodes: {:?} {:?}", inode, vertex);
506                put_inodes_with_rev(txn, &inode, &vertex)?;
507            } else {
508                debug!("Not adding inodes: {:?} {:?}", inode, vertex);
509            }
510        }
511        InodeUpdate::Deleted { inode } => {
512            if let Some(parent) = txn.get_revtree(&inode, None)?.map(|x| x.to_owned()) {
513                del_tree_with_rev(txn, &parent, &inode)?;
514            }
515            // Delete the directory, if it's there.
516            txn.del_tree(&OwnedPathId::inode(inode), Some(&inode))?;
517            if let Some(&vertex) = txn.get_inodes(&inode, None)? {
518                del_inodes_with_rev(txn, &inode, &vertex)?;
519            }
520        }
521    }
522    Ok(())
523}
524
525#[derive(Default)]
526pub struct Workspace {
527    pub parents: HashSet<Vertex<ChangeId>>,
528    pub children: HashSet<Vertex<ChangeId>>,
529    pub pseudo: Vec<(Vertex<ChangeId>, SerializedEdge, Position<Option<Hash>>)>,
530    pub deleted_by: HashSet<ChangeId>,
531    pub up_context: Vec<Vertex<ChangeId>>,
532    pub down_context: Vec<Vertex<ChangeId>>,
533    pub missing_context: crate::missing_context::Workspace,
534    pub rooted: HashMap<Vertex<ChangeId>, bool>,
535    pub adjbuf: Vec<SerializedEdge>,
536    pub alive_folder: HashMap<Vertex<ChangeId>, bool>,
537    pub folder_stack: Vec<(Vertex<ChangeId>, bool)>,
538}
539
540impl Workspace {
541    pub fn new() -> Self {
542        Self::default()
543    }
544    fn clear(&mut self) {
545        self.children.clear();
546        self.parents.clear();
547        self.pseudo.clear();
548        self.deleted_by.clear();
549        self.up_context.clear();
550        self.down_context.clear();
551        self.missing_context.clear();
552        self.rooted.clear();
553        self.adjbuf.clear();
554        self.alive_folder.clear();
555        self.folder_stack.clear();
556    }
557    fn assert_empty(&self) {
558        assert!(self.children.is_empty());
559        assert!(self.parents.is_empty());
560        assert!(self.pseudo.is_empty());
561        assert!(self.deleted_by.is_empty());
562        assert!(self.up_context.is_empty());
563        assert!(self.down_context.is_empty());
564        self.missing_context.assert_empty();
565        assert!(self.rooted.is_empty());
566        assert!(self.adjbuf.is_empty());
567        assert!(self.alive_folder.is_empty());
568        assert!(self.folder_stack.is_empty());
569    }
570}
571
572#[derive(Debug)]
573struct StackElt {
574    vertex: Vertex<ChangeId>,
575    last_alive: Vertex<ChangeId>,
576    is_on_path: bool,
577}
578
579impl StackElt {
580    fn is_alive(&self) -> bool {
581        self.vertex == self.last_alive
582    }
583}
584
585/// Optional per-inode log populated by `repair_zombies` when unrecording, so the
586/// caller can drop leftover zombie markings without a *second* DFS of the same
587/// inode. Apply passes `None` and pays nothing.
588#[derive(Default)]
589pub(crate) struct ZombieRepairLog {
590    /// `(from, to)` of each DELETED|BLOCK edge introduced by the change being
591    /// unrecorded that still remains in the inode: a leftover zombie marking.
592    pub leftover: Vec<(Vertex<ChangeId>, Vertex<ChangeId>)>,
593}
594
595pub(crate) fn repair_zombies<T: GraphMutTxnT + TreeTxnT>(
596    txn: &mut T,
597    channel: &mut T::Graph,
598    root: Position<ChangeId>,
599    mut log: Option<(&mut ZombieRepairLog, ChangeId)>,
600) -> Result<(), LocalApplyError<T>> {
601    info!("repair_zombies {:?}", root);
602    let mut stack = vec![StackElt {
603        vertex: root.inode_vertex(),
604        last_alive: root.inode_vertex(),
605        is_on_path: false,
606    }];
607
608    let mut visited = BTreeSet::new();
609    let mut descendants = BTreeSet::new();
610
611    while let Some(elt) = stack.pop() {
612        debug!("elt {:?}", elt);
613        if elt.is_on_path {
614            continue;
615        }
616
617        // Has this vertex been visited already?
618        if !visited.insert(elt.vertex) {
619            debug!("already visited!");
620            for (_, r) in descendants.range((elt.vertex, Vertex::ROOT)..=(elt.vertex, Vertex::MAX))
621            {
622                debug!("put_pseudo, descendant {:?} {:?}", elt.last_alive, r);
623                // If we aren't in a direct cycle, reconnect.
624                if elt.last_alive != *r {
625                    put_graph_with_rev(
626                        txn,
627                        channel,
628                        EdgeFlags::PSEUDO,
629                        elt.last_alive,
630                        *r,
631                        ChangeId::ROOT,
632                    )?;
633                }
634            }
635            // `elt.is_alive()` (vertex == last_alive) only reflects the vertex's
636            // own aliveness on its *first* visit. On a revisit reached through a
637            // different path, `last_alive` is an ancestor, so we must test the
638            // vertex's real aliveness — otherwise an alive zombie reachable from
639            // several alive ancestors only gets reconnected from the first one.
640            // Reconnecting it from every such ancestor is fine: the redundant
641            // pseudo-edges are pruned at output time.
642            if !is_alive(txn, channel, &elt.vertex)? {
643                continue;
644            }
645
646            // Reconnect with ancestor.
647            for v in stack.iter().rev() {
648                if v.is_on_path {
649                    debug!("on path: {:?}", v);
650                    // If the last vertex on the path to `current` is not
651                    // alive, a reconnect is needed.
652                    if v.is_alive() {
653                        if v.vertex != elt.vertex {
654                            // We need to reconnect, and we can do it now
655                            // since we won't have a chance to visit that
656                            // edge (because non-PARENT edge we are
657                            // inserting now starts from a vertex that is
658                            // on the path, which means we've already
659                            // pushed all its children onto the stack.).
660                            debug!("alive, put_pseudo {:?} {:?}", v.vertex, elt.vertex);
661                            put_graph_with_rev(
662                                txn,
663                                channel,
664                                EdgeFlags::PSEUDO,
665                                v.vertex,
666                                elt.vertex,
667                                ChangeId::ROOT,
668                            )?;
669                        }
670                        break;
671                    } else {
672                        // Remember that those dead vertices have
673                        // `elt.vertex` as a descendant.
674                        descendants.insert((v.vertex, elt.vertex));
675                    }
676                }
677            }
678
679            continue;
680        }
681
682        // Else, visit its children.
683        stack.push(StackElt {
684            is_on_path: true,
685            ..elt
686        });
687
688        let len = stack.len();
689        // If this is the first visit, find the children, in flag
690        // order (alive first), since we don't want to reconnect
691        // vertices multiple times.
692        for e in iter_adjacent(
693            txn,
694            channel,
695            elt.vertex,
696            EdgeFlags::empty(),
697            EdgeFlags::all(),
698        )? {
699            let e = e?;
700
701            if e.flag().contains(EdgeFlags::PARENT) {
702                if e.flag() & (EdgeFlags::BLOCK | EdgeFlags::DELETED) == EdgeFlags::BLOCK {
703                    // This vertex is alive!
704                    stack[len - 1].last_alive = elt.vertex;
705                }
706                continue;
707            } else if e.flag().contains(EdgeFlags::FOLDER) {
708                // If we are here, at least one child of `root` is
709                // FOLDER, hence all are.
710                return Ok(());
711            }
712
713            let child = txn.find_block(channel, e.dest())?;
714            // Record leftover zombie markings owned by the unrecorded change,
715            // seen for free during this descent (edge is already non-PARENT,
716            // non-FOLDER here).
717            if let Some((l, cid)) = log.as_mut() {
718                if e.introduced_by() == *cid && e.flag().is_deleted() && e.flag().is_block() {
719                    l.leftover.push((elt.vertex, *child));
720                }
721            }
722            stack.push(StackElt {
723                vertex: *child,
724                last_alive: elt.last_alive,
725                is_on_path: false,
726            });
727        }
728
729        if len >= 2 && stack[len - 1].is_alive() {
730            // The visited vertex is alive. Change the last_alive of its children
731            for x in &mut stack[len..] {
732                x.last_alive = elt.vertex
733            }
734
735            for v in (stack[..len - 1]).iter().rev() {
736                if v.is_on_path {
737                    debug!("on path: {:?}", v);
738                    // If the last vertex on the path to `current` is not
739                    // alive, a reconnect is needed.
740                    if v.is_alive() {
741                        // We need to reconnect, and we can do it now
742                        // since we won't have a chance to visit that
743                        // edge (because non-PARENT edge we are
744                        // inserting now starts from a vertex that is
745                        // on the path, which means we've already
746                        // pushed all its children onto the stack.).
747                        debug!(
748                            "put_pseudo, alive 2, {:?} {:?}",
749                            v.last_alive,
750                            stack[len - 1].vertex
751                        );
752                        debug!("{:?}", stack);
753                        let edge = Edge {
754                            dest: stack[len - 1].vertex.start_pos(),
755                            flag: EdgeFlags::empty(),
756                            introduced_by: ChangeId::ROOT,
757                        };
758                        match txn.get_graph(channel, &v.last_alive, Some(&edge.into()))? {
759                            Some(e) if e.dest() == edge.dest && e.flag() == EdgeFlags::BLOCK => {}
760                            _ => {
761                                put_graph_with_rev(
762                                    txn,
763                                    channel,
764                                    EdgeFlags::PSEUDO,
765                                    v.last_alive,
766                                    stack[len - 1].vertex,
767                                    ChangeId::ROOT,
768                                )?;
769                            }
770                        }
771                        break;
772                    } else {
773                        // Remember that those dead vertices have
774                        // `stack[len-1].vertex` as a descendant.
775                        descendants.insert((v.vertex, elt.vertex));
776                    }
777                }
778            }
779        }
780
781        // If no children, pop.
782        if stack.len() == len {
783            stack.pop();
784        }
785    }
786
787    Ok(())
788}
789
790pub(crate) fn repair_up<T: GraphMutTxnT + TreeTxnT>(
791    txn: &mut T,
792    channel: &mut T::Graph,
793    root: Position<ChangeId>,
794) -> Result<(), LocalApplyError<T>> {
795    info!("repair_zombies {:?}", root);
796    let mut stack = vec![root.inode_vertex()];
797    let mut visited = BTreeSet::new();
798    let mut add = Vec::new();
799    while let Some(elt) = stack.pop() {
800        if !visited.insert(elt) {
801            continue;
802        }
803        let mut is_alive = false;
804        let mut is_dead = false;
805        for e in iter_adjacent(
806            txn,
807            channel,
808            elt,
809            EdgeFlags::PARENT | EdgeFlags::FOLDER,
810            EdgeFlags::all(),
811        )? {
812            let e = e?;
813            let deleted = e.flag().contains(EdgeFlags::DELETED);
814            if e.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
815                is_alive |= !deleted;
816                is_dead |= deleted;
817            }
818        }
819        if is_dead && !is_alive {
820            for e in iter_adjacent(
821                txn,
822                channel,
823                elt,
824                EdgeFlags::PARENT | EdgeFlags::FOLDER,
825                EdgeFlags::all(),
826            )? {
827                let e = e?;
828                if e.flag()
829                    .contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::FOLDER)
830                {
831                    let parent = txn.find_block_end(channel, e.dest())?;
832                    add.push((*parent, elt));
833                    stack.push(*parent)
834                }
835            }
836
837            for (a, b) in add.drain(..) {
838                put_graph_with_rev(
839                    txn,
840                    channel,
841                    EdgeFlags::PSEUDO | EdgeFlags::FOLDER,
842                    a,
843                    b,
844                    ChangeId::ROOT,
845                )?;
846            }
847        }
848    }
849
850    Ok(())
851}
852
853pub fn clean_obsolete_pseudo_edges<T: GraphMutTxnT + TreeTxnT>(
854    txn: &mut T,
855    channel: &mut T::Graph,
856    ws: &mut Workspace,
857    change_id: ChangeId,
858) -> Result<HashSet<Position<ChangeId>>, LocalApplyError<T>> {
859    info!(
860        "clean_obsolete_pseudo_edges, ws.pseudo.len() = {}",
861        ws.pseudo.len()
862    );
863    let mut alive_folder = std::mem::take(&mut ws.alive_folder);
864    let mut folder_stack = std::mem::take(&mut ws.folder_stack);
865
866    let mut inodes = HashSet::new();
867
868    for (next_vertex, p, inode) in ws.pseudo.drain(..) {
869        debug!(
870            "clean_obsolete_pseudo_edges {:?} {:?} {:?}",
871            next_vertex, p, inode
872        );
873
874        if log_enabled!(log::Level::Debug) {
875            let still_here: Vec<_> = iter_adjacent(
876                txn,
877                channel,
878                next_vertex,
879                EdgeFlags::empty(),
880                EdgeFlags::all(),
881            )?
882            .collect();
883            debug!(
884                "pseudo edge still here ? {:?} {:?}",
885                next_vertex.change.0.0, still_here
886            )
887        }
888
889        let (a, b) = if p.flag().is_parent() {
890            match txn.find_block_end(channel, p.dest()) {
891                Ok(&dest) => (dest, next_vertex),
892                _ => {
893                    continue;
894                }
895            }
896        } else {
897            match txn.find_block(channel, p.dest()) {
898                Ok(&dest) => (next_vertex, dest),
899                _ => {
900                    continue;
901                }
902            }
903        };
904        let a_is_alive = is_alive(txn, channel, &a)?;
905        let b_is_alive = is_alive(txn, channel, &b)?;
906        if a_is_alive && b_is_alive {
907            continue;
908        }
909
910        // If we're deleting a FOLDER edge, repair_context_deleted
911        // will not repair its potential descendants. Hence, we must
912        // also count as "alive" a FOLDER node with alive descendants.
913        if p.flag().is_folder()
914            && folder_has_alive_descendants(txn, channel, &mut alive_folder, &mut folder_stack, b)?
915        {
916            continue;
917        }
918
919        if a.is_empty() && b_is_alive {
920            // In this case, `a` can be an inode, in which case we
921            // can't simply delete the edge, since b would become
922            // unreachable.
923            //
924            // We test this here:
925            let mut is_inode = false;
926            for e in iter_adjacent(
927                txn,
928                channel,
929                a,
930                EdgeFlags::FOLDER | EdgeFlags::PARENT,
931                EdgeFlags::all(),
932            )? {
933                let e = e?;
934                if e.flag().contains(EdgeFlags::FOLDER | EdgeFlags::PARENT) {
935                    is_inode = true;
936                    break;
937                }
938            }
939            if is_inode {
940                continue;
941            }
942        }
943
944        debug!(
945            "deleting {:?} {:?} {:?} {:?} {:?} {:?}",
946            a,
947            b,
948            p.introduced_by(),
949            p.flag(),
950            a_is_alive,
951            b_is_alive,
952        );
953        del_graph_with_rev(
954            txn,
955            channel,
956            p.flag() - EdgeFlags::PARENT,
957            a,
958            b,
959            p.introduced_by(),
960        )?;
961
962        if a_is_alive || (b_is_alive && !p.flag().is_folder()) {
963            // A context repair is needed.
964            inodes.insert(internal_pos(txn, &inode, change_id)?);
965        }
966    }
967
968    ws.alive_folder = alive_folder;
969    ws.folder_stack = folder_stack;
970    Ok(inodes)
971}
972
973fn folder_has_alive_descendants<T: GraphMutTxnT + TreeTxnT>(
974    txn: &mut T,
975    channel: &mut T::Graph,
976    alive: &mut HashMap<Vertex<ChangeId>, bool>,
977    stack: &mut Vec<(Vertex<ChangeId>, bool)>,
978    b: Vertex<ChangeId>,
979) -> Result<bool, LocalApplyError<T>> {
980    if let Some(r) = alive.get(&b) {
981        return Ok(*r);
982    }
983    debug!("alive descendants");
984    stack.clear();
985    stack.push((b, false));
986    while let Some((b, visited)) = stack.pop() {
987        debug!("visiting {:?} {:?}", b, visited);
988        if visited {
989            alive.entry(b).or_insert(false);
990            continue;
991        }
992        stack.push((b, true));
993        for e in iter_adjacent(
994            txn,
995            channel,
996            b,
997            EdgeFlags::empty(),
998            EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
999        )? {
1000            let e = e?;
1001            debug!("e = {:?}", e);
1002            if e.flag().contains(EdgeFlags::FOLDER) {
1003                let c = txn.find_block(channel, e.dest())?;
1004                stack.push((*c, false));
1005            } else {
1006                // This is a non-deleted non-folder edge.
1007                let c = txn.find_block(channel, e.dest())?;
1008                if is_alive(txn, channel, c)? {
1009                    // The entire path is alive.
1010                    for (x, on_path) in stack.iter() {
1011                        if *on_path {
1012                            alive.insert(*x, true);
1013                        }
1014                    }
1015                }
1016            }
1017        }
1018    }
1019    Ok(*alive.get(&b).unwrap_or(&false))
1020}
1021
1022pub fn collect_missing_contexts<T: GraphMutTxnT + TreeTxnT>(
1023    txn: &T,
1024    channel: &T::Graph,
1025    ws: &mut Workspace,
1026    change: &Change,
1027    change_id: ChangeId,
1028    inodes: &mut HashSet<Position<ChangeId>>,
1029) -> Result<(), LocalApplyError<T>> {
1030    inodes.extend(
1031        ws.missing_context
1032            .unknown_parents
1033            .drain(..)
1034            .map(|x| internal_pos(txn, &x.2, change_id).unwrap()),
1035    );
1036    for atom in change.changes.iter().flat_map(|r| r.iter()) {
1037        match atom {
1038            Atom::NewVertex(n) if !n.flag.is_folder() => {
1039                let inode = internal_pos(txn, &n.inode, change_id)?;
1040                if !inodes.contains(&inode) {
1041                    for up in n.up_context.iter() {
1042                        let up = *txn.find_block_end(channel, internal_pos(txn, up, change_id)?)?;
1043                        if !is_alive(txn, channel, &up)? {
1044                            inodes.insert(inode);
1045                            break;
1046                        }
1047                    }
1048                    for down in n.down_context.iter() {
1049                        let down = *txn.find_block(channel, internal_pos(txn, down, change_id)?)?;
1050                        let mut down_has_other_parents = false;
1051                        for e in iter_adjacent(
1052                            txn,
1053                            channel,
1054                            down,
1055                            EdgeFlags::PARENT,
1056                            EdgeFlags::all() - EdgeFlags::DELETED,
1057                        )? {
1058                            let e = e?;
1059                            if e.introduced_by() != change_id {
1060                                down_has_other_parents = true;
1061                                break;
1062                            }
1063                        }
1064                        if !down_has_other_parents {
1065                            inodes.insert(inode);
1066                            break;
1067                        }
1068                    }
1069                }
1070            }
1071            Atom::NewVertex(_) => {}
1072            Atom::EdgeMap(n) => {
1073                has_missing_edge_context(txn, channel, change_id, change, n, inodes, false)?;
1074            }
1075        }
1076    }
1077    Ok(())
1078}
1079
1080/// Collect inodes with missing contexts into `inodes`.
1081pub(crate) fn has_missing_edge_context<T: GraphMutTxnT + TreeTxnT>(
1082    txn: &T,
1083    channel: &T::Graph,
1084    change_id: ChangeId,
1085    change: &Change,
1086    n: &EdgeMap<Option<Hash>>,
1087    inodes: &mut HashSet<Position<ChangeId>>,
1088    reverse: bool,
1089) -> Result<(), LocalApplyError<T>> {
1090    let inode = internal_pos(txn, &n.inode, change_id)?;
1091    // If the inode is already in there, no need to do anything.
1092    if inodes.contains(&inode) {
1093        return Ok(());
1094    }
1095
1096    let ext: Hash = if reverse {
1097        (*txn.get_external(&change_id).unwrap()).into()
1098    } else {
1099        // Unused, hence we avoid one Sanakirja lookup.
1100        Hash::None
1101    };
1102
1103    for e in n.edges.iter() {
1104        let e = if reverse {
1105            e.reverse(Some(ext))
1106        } else {
1107            e.clone()
1108        };
1109
1110        assert!(!e.flag.contains(EdgeFlags::PARENT));
1111        if e.flag.contains(EdgeFlags::DELETED) {
1112            trace!("repairing context deleted {:?}", e);
1113            if has_missing_context_deleted(txn, channel, change_id, |h| change.knows(&h), &e)
1114                .map_err(LocalApplyError::from_missing)?
1115            {
1116                inodes.insert(inode);
1117                break;
1118            }
1119        } else {
1120            trace!("repairing context nondeleted {:?}", e);
1121            if has_missing_context_nondeleted(txn, channel, change_id, &e)
1122                .map_err(LocalApplyError::from_missing)?
1123            {
1124                inodes.insert(inode);
1125                break;
1126            }
1127        }
1128    }
1129    Ok(())
1130}
1131
1132pub(crate) fn repair_cyclic_paths<T: GraphMutTxnT + TreeTxnT>(
1133    txn: &mut T,
1134    channel: &mut T::Graph,
1135    ws: &mut Workspace,
1136) -> Result<(), LocalApplyError<T>> {
1137    let now = std::time::Instant::now();
1138    let mut files = std::mem::take(&mut ws.missing_context.files);
1139    for file in files.drain() {
1140        if file.is_empty() {
1141            if !is_rooted(txn, channel, file, ws)? {
1142                repair_edge(txn, channel, file, ws)?
1143            }
1144        } else {
1145            let f0 = EdgeFlags::FOLDER;
1146            let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
1147            let mut iter = iter_adjacent(txn, channel, file, f0, f1)?;
1148            if let Some(ee) = iter.next() {
1149                let ee = ee?;
1150                let dest = ee.dest().inode_vertex();
1151                if !is_rooted(txn, channel, dest, ws)? {
1152                    repair_edge(txn, channel, dest, ws)?
1153                }
1154            }
1155        }
1156    }
1157    ws.missing_context.files = files;
1158    crate::TIMERS.lock().unwrap().check_cyclic_paths += now.elapsed();
1159    Ok(())
1160}
1161
1162fn repair_edge<T: GraphMutTxnT + TreeTxnT>(
1163    txn: &mut T,
1164    channel: &mut T::Graph,
1165    to0: Vertex<ChangeId>,
1166    ws: &mut Workspace,
1167) -> Result<(), LocalApplyError<T>> {
1168    debug!("repair_edge {:?}", to0);
1169    let mut stack = vec![(to0, true, true, true)];
1170    ws.parents.clear();
1171    while let Some((current, _, al, anc_al)) = stack.pop() {
1172        if !ws.parents.insert(current) {
1173            continue;
1174        }
1175        debug!("repair_cyclic {:?}", current);
1176        if current != to0 {
1177            stack.push((current, true, al, anc_al));
1178        }
1179        if current.is_root() {
1180            debug!("root");
1181            break;
1182        }
1183        if let Some(&true) = ws.rooted.get(&current) {
1184            debug!("rooted");
1185            break;
1186        }
1187        let f = EdgeFlags::PARENT | EdgeFlags::FOLDER;
1188        let len = stack.len();
1189        for parent in iter_adjacent(txn, channel, current, f, EdgeFlags::all())? {
1190            let parent = parent?;
1191            if parent.flag().is_parent() {
1192                let anc = txn.find_block_end(channel, parent.dest())?;
1193                debug!("is_rooted, parent = {:?}", parent);
1194                let al = match iter_adjacent(
1195                    txn,
1196                    channel,
1197                    *anc,
1198                    f,
1199                    f | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
1200                )?
1201                .next()
1202                {
1203                    Some(e) => {
1204                        e?;
1205                        true
1206                    }
1207                    _ => false,
1208                };
1209                debug!("al = {:?}, flag = {:?}", al, parent.flag());
1210                stack.push((*anc, false, parent.flag().is_deleted(), al));
1211            }
1212        }
1213        if stack.len() == len {
1214            stack.pop();
1215        } else {
1216            (stack[len..]).sort_unstable_by_key(|a| a.3)
1217        }
1218    }
1219    let mut current = to0;
1220    for (next, on_path, del, _) in stack {
1221        if on_path {
1222            if del {
1223                debug!("put_pseudo {:?} {:?}", next, current);
1224                put_graph_with_rev(
1225                    txn,
1226                    channel,
1227                    EdgeFlags::FOLDER | EdgeFlags::PSEUDO,
1228                    next,
1229                    current,
1230                    ChangeId::ROOT,
1231                )?;
1232            }
1233            current = next
1234        }
1235    }
1236    ws.parents.clear();
1237    Ok(())
1238}
1239
1240fn is_rooted<T: GraphTxnT + TreeTxnT>(
1241    txn: &T,
1242    channel: &T::Graph,
1243    v: Vertex<ChangeId>,
1244    ws: &mut Workspace,
1245) -> Result<bool, LocalApplyError<T>> {
1246    let mut alive = false;
1247    assert!(v.is_empty());
1248    for e in iter_adjacent(txn, channel, v, EdgeFlags::empty(), EdgeFlags::all())? {
1249        let e = e?;
1250        if e.flag().contains(EdgeFlags::PARENT) {
1251            if e.flag() & (EdgeFlags::FOLDER | EdgeFlags::DELETED) == EdgeFlags::FOLDER {
1252                alive = true;
1253                break;
1254            }
1255        } else if !e.flag().is_deleted() {
1256            alive = true;
1257            break;
1258        }
1259    }
1260    if !alive {
1261        debug!("is_rooted, not alive");
1262        return Ok(true);
1263    }
1264    // Recycling ws.up_context and ws.parents as a stack and a
1265    // "visited" hashset, respectively.
1266    let stack = &mut ws.up_context;
1267    stack.clear();
1268    stack.push(v);
1269    let visited = &mut ws.parents;
1270    visited.clear();
1271
1272    while let Some(to) = stack.pop() {
1273        debug!("is_rooted, pop = {:?}", to);
1274        if to.is_root() {
1275            stack.clear();
1276            for v in visited.drain() {
1277                ws.rooted.insert(v, true);
1278            }
1279            return Ok(true);
1280        }
1281        if !visited.insert(to) {
1282            continue;
1283        }
1284        if let Some(&rooted) = ws.rooted.get(&to) {
1285            if rooted {
1286                for v in visited.drain() {
1287                    ws.rooted.insert(v, true);
1288                }
1289                return Ok(true);
1290            } else {
1291                continue;
1292            }
1293        }
1294        let f = EdgeFlags::PARENT | EdgeFlags::FOLDER;
1295        for parent in iter_adjacent(
1296            txn,
1297            channel,
1298            to,
1299            f,
1300            f | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
1301        )? {
1302            let parent = parent?;
1303            debug!("is_rooted, parent = {:?}", parent);
1304            stack.push(*txn.find_block_end(channel, parent.dest())?)
1305        }
1306    }
1307    for v in visited.drain() {
1308        ws.rooted.insert(v, false);
1309    }
1310    Ok(false)
1311}
1312
1313pub type AppliedRoot = (Hash, u64, Merkle);
1314
1315pub fn apply_root_change<R: rand::Rng, T: MutTxnT, P: ChangeStore>(
1316    txn: &mut T,
1317    channel: &ChannelRef<T>,
1318    store: &P,
1319    rng: R,
1320) -> Result<Option<AppliedRoot>, ApplyError<P::Error, T>> {
1321    let mut change = {
1322        // If the graph already has a root.
1323        {
1324            let channel = channel.read();
1325            let gr = txn.graph(&*channel);
1326            if let Some(v) = iter_adjacent(
1327                &*txn,
1328                gr,
1329                Vertex::ROOT,
1330                EdgeFlags::FOLDER,
1331                EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1332            )?
1333            .next()
1334            {
1335                let v = txn.find_block(gr, v?.dest())?;
1336                if v.start == v.end {
1337                    // Already has a root
1338                    return Ok(None);
1339                }
1340            } else {
1341                // Non-empty channel without a root
1342            }
1343            // If we are here, either the channel is empty, or it
1344            // isn't and doesn't have a root.
1345        }
1346        let root = Position {
1347            change: Some(Hash::None),
1348            pos: ChangePosition(0u64.into()),
1349        };
1350        use rand::RngExt;
1351        let contents = rng
1352            .sample_iter(rand::distr::StandardUniform)
1353            .take(32)
1354            .collect();
1355        debug!(
1356            "change position {:?} {:?}",
1357            ChangePosition(1u64.into()),
1358            ChangePosition(1u64.into()).0.as_u64()
1359        );
1360        crate::change::LocalChange::make_change(
1361            txn,
1362            channel,
1363            vec![crate::change::Hunk::AddRoot {
1364                name: Atom::NewVertex(NewVertex {
1365                    up_context: vec![root],
1366                    down_context: Vec::new(),
1367                    start: ChangePosition(0u64.into()),
1368                    end: ChangePosition(0u64.into()),
1369                    flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1370                    inode: root,
1371                }),
1372                inode: Atom::NewVertex(NewVertex {
1373                    up_context: vec![Position {
1374                        change: None,
1375                        pos: ChangePosition(0u64.into()),
1376                    }],
1377                    down_context: Vec::new(),
1378                    start: ChangePosition(1u64.into()),
1379                    end: ChangePosition(1u64.into()),
1380                    flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1381                    inode: root,
1382                }),
1383            }],
1384            contents,
1385            crate::change::ChangeHeader::default(),
1386            Vec::new(),
1387        )?
1388    };
1389    let h = store
1390        .save_change(&mut change, |_, _| Ok(()))
1391        .map_err(ApplyError::Changestore)?;
1392    let (n, merkle) = apply_change(store, txn, &mut channel.write(), &h)?;
1393    Ok(Some((h, n, merkle)))
1394}