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)?;
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    let n = apply_change_to_channel(
447        txn,
448        &mut channel,
449        &mut |_| true,
450        internal,
451        hash,
452        change,
453        workspace,
454    )?;
455    for (_, update) in inode_updates.iter() {
456        info!("updating {:?}", update);
457        update_inode(txn, &channel, internal, update)?;
458    }
459    Ok(n)
460}
461
462/// Same as [apply_local_change_ws], but allocates its own workspace.
463pub fn apply_local_change<
464    T: ChannelMutTxnT + DepsMutTxnT<DepsError = <T as GraphTxnT>::GraphError> + TreeMutTxnT,
465>(
466    txn: &mut T,
467    channel: &ChannelRef<T>,
468    change: &Change,
469    hash: &Hash,
470    inode_updates: &HashMap<usize, InodeUpdate>,
471) -> Result<(u64, Merkle), LocalApplyError<T>> {
472    apply_local_change_ws(
473        txn,
474        channel,
475        change,
476        hash,
477        inode_updates,
478        &mut Workspace::new(),
479    )
480}
481
482fn update_inode<T: ChannelTxnT + TreeMutTxnT>(
483    txn: &mut T,
484    channel: &T::Channel,
485    internal: ChangeId,
486    update: &InodeUpdate,
487) -> Result<(), LocalApplyError<T>> {
488    debug!("update_inode {:?}", update);
489    match *update {
490        InodeUpdate::Add { inode, pos, .. } => {
491            let vertex = Position {
492                change: internal,
493                pos,
494            };
495            if txn
496                .get_graph(txn.graph(channel), &vertex.inode_vertex(), None)?
497                .is_some()
498            {
499                debug!("Adding inodes: {:?} {:?}", inode, vertex);
500                put_inodes_with_rev(txn, &inode, &vertex)?;
501            } else {
502                debug!("Not adding inodes: {:?} {:?}", inode, vertex);
503            }
504        }
505        InodeUpdate::Deleted { inode } => {
506            if let Some(parent) = txn.get_revtree(&inode, None)?.map(|x| x.to_owned()) {
507                del_tree_with_rev(txn, &parent, &inode)?;
508            }
509            // Delete the directory, if it's there.
510            txn.del_tree(&OwnedPathId::inode(inode), Some(&inode))?;
511            if let Some(&vertex) = txn.get_inodes(&inode, None)? {
512                del_inodes_with_rev(txn, &inode, &vertex)?;
513            }
514        }
515    }
516    Ok(())
517}
518
519#[derive(Default)]
520pub struct Workspace {
521    pub parents: HashSet<Vertex<ChangeId>>,
522    pub children: HashSet<Vertex<ChangeId>>,
523    pub pseudo: Vec<(Vertex<ChangeId>, SerializedEdge, Position<Option<Hash>>)>,
524    pub deleted_by: HashSet<ChangeId>,
525    pub up_context: Vec<Vertex<ChangeId>>,
526    pub down_context: Vec<Vertex<ChangeId>>,
527    pub missing_context: crate::missing_context::Workspace,
528    pub rooted: HashMap<Vertex<ChangeId>, bool>,
529    pub adjbuf: Vec<SerializedEdge>,
530    pub alive_folder: HashMap<Vertex<ChangeId>, bool>,
531    pub folder_stack: Vec<(Vertex<ChangeId>, bool)>,
532}
533
534impl Workspace {
535    pub fn new() -> Self {
536        Self::default()
537    }
538    fn clear(&mut self) {
539        self.children.clear();
540        self.parents.clear();
541        self.pseudo.clear();
542        self.deleted_by.clear();
543        self.up_context.clear();
544        self.down_context.clear();
545        self.missing_context.clear();
546        self.rooted.clear();
547        self.adjbuf.clear();
548        self.alive_folder.clear();
549        self.folder_stack.clear();
550    }
551    fn assert_empty(&self) {
552        assert!(self.children.is_empty());
553        assert!(self.parents.is_empty());
554        assert!(self.pseudo.is_empty());
555        assert!(self.deleted_by.is_empty());
556        assert!(self.up_context.is_empty());
557        assert!(self.down_context.is_empty());
558        self.missing_context.assert_empty();
559        assert!(self.rooted.is_empty());
560        assert!(self.adjbuf.is_empty());
561        assert!(self.alive_folder.is_empty());
562        assert!(self.folder_stack.is_empty());
563    }
564}
565
566#[derive(Debug)]
567struct StackElt {
568    vertex: Vertex<ChangeId>,
569    last_alive: Vertex<ChangeId>,
570    is_on_path: bool,
571}
572
573impl StackElt {
574    fn is_alive(&self) -> bool {
575        self.vertex == self.last_alive
576    }
577}
578
579pub(crate) fn repair_zombies<T: GraphMutTxnT + TreeTxnT>(
580    txn: &mut T,
581    channel: &mut T::Graph,
582    root: Position<ChangeId>,
583) -> Result<(), LocalApplyError<T>> {
584    info!("repair_zombies {:?}", root);
585    let mut stack = vec![StackElt {
586        vertex: root.inode_vertex(),
587        last_alive: root.inode_vertex(),
588        is_on_path: false,
589    }];
590
591    let mut visited = BTreeSet::new();
592    let mut descendants = BTreeSet::new();
593
594    while let Some(elt) = stack.pop() {
595        debug!("elt {:?}", elt);
596        if elt.is_on_path {
597            continue;
598        }
599
600        // Has this vertex been visited already?
601        if !visited.insert(elt.vertex) {
602            debug!("already visited!");
603            for (_, r) in descendants.range((elt.vertex, Vertex::ROOT)..=(elt.vertex, Vertex::MAX))
604            {
605                debug!("put_pseudo, descendant {:?} {:?}", elt.last_alive, r);
606                // If we aren't in a direct cycle, reconnect.
607                if elt.last_alive != *r {
608                    put_graph_with_rev(
609                        txn,
610                        channel,
611                        EdgeFlags::PSEUDO,
612                        elt.last_alive,
613                        *r,
614                        ChangeId::ROOT,
615                    )?;
616                }
617            }
618            if !elt.is_alive() {
619                continue;
620            }
621
622            // Reconnect with ancestor.
623            for v in stack.iter().rev() {
624                if v.is_on_path {
625                    debug!("on path: {:?}", v);
626                    // If the last vertex on the path to `current` is not
627                    // alive, a reconnect is needed.
628                    if v.is_alive() {
629                        if v.vertex != elt.vertex {
630                            // We need to reconnect, and we can do it now
631                            // since we won't have a chance to visit that
632                            // edge (because non-PARENT edge we are
633                            // inserting now starts from a vertex that is
634                            // on the path, which means we've already
635                            // pushed all its children onto the stack.).
636                            debug!("alive, put_pseudo {:?} {:?}", v.vertex, elt.vertex);
637                            put_graph_with_rev(
638                                txn,
639                                channel,
640                                EdgeFlags::PSEUDO,
641                                v.vertex,
642                                elt.vertex,
643                                ChangeId::ROOT,
644                            )?;
645                        }
646                        break;
647                    } else {
648                        // Remember that those dead vertices have
649                        // `elt.vertex` as a descendant.
650                        descendants.insert((v.vertex, elt.vertex));
651                    }
652                }
653            }
654
655            continue;
656        }
657
658        // Else, visit its children.
659        stack.push(StackElt {
660            is_on_path: true,
661            ..elt
662        });
663
664        let len = stack.len();
665        // If this is the first visit, find the children, in flag
666        // order (alive first), since we don't want to reconnect
667        // vertices multiple times.
668        for e in iter_adjacent(
669            txn,
670            channel,
671            elt.vertex,
672            EdgeFlags::empty(),
673            EdgeFlags::all(),
674        )? {
675            let e = e?;
676
677            if e.flag().contains(EdgeFlags::PARENT) {
678                if e.flag() & (EdgeFlags::BLOCK | EdgeFlags::DELETED) == EdgeFlags::BLOCK {
679                    // This vertex is alive!
680                    stack[len - 1].last_alive = elt.vertex;
681                }
682                continue;
683            } else if e.flag().contains(EdgeFlags::FOLDER) {
684                // If we are here, at least one child of `root` is
685                // FOLDER, hence all are.
686                return Ok(());
687            }
688
689            let child = txn.find_block(channel, e.dest())?;
690            stack.push(StackElt {
691                vertex: *child,
692                last_alive: elt.last_alive,
693                is_on_path: false,
694            });
695        }
696
697        if len >= 2 && stack[len - 1].is_alive() {
698            // The visited vertex is alive. Change the last_alive of its children
699            for x in &mut stack[len..] {
700                x.last_alive = elt.vertex
701            }
702
703            for v in (stack[..len - 1]).iter().rev() {
704                if v.is_on_path {
705                    debug!("on path: {:?}", v);
706                    // If the last vertex on the path to `current` is not
707                    // alive, a reconnect is needed.
708                    if v.is_alive() {
709                        // We need to reconnect, and we can do it now
710                        // since we won't have a chance to visit that
711                        // edge (because non-PARENT edge we are
712                        // inserting now starts from a vertex that is
713                        // on the path, which means we've already
714                        // pushed all its children onto the stack.).
715                        debug!(
716                            "put_pseudo, alive 2, {:?} {:?}",
717                            v.last_alive,
718                            stack[len - 1].vertex
719                        );
720                        debug!("{:?}", stack);
721                        let edge = Edge {
722                            dest: stack[len - 1].vertex.start_pos(),
723                            flag: EdgeFlags::empty(),
724                            introduced_by: ChangeId::ROOT,
725                        };
726                        match txn.get_graph(channel, &v.last_alive, Some(&edge.into()))? {
727                            Some(e) if e.dest() == edge.dest && e.flag() == EdgeFlags::BLOCK => {}
728                            _ => {
729                                put_graph_with_rev(
730                                    txn,
731                                    channel,
732                                    EdgeFlags::PSEUDO,
733                                    v.last_alive,
734                                    stack[len - 1].vertex,
735                                    ChangeId::ROOT,
736                                )?;
737                            }
738                        }
739                        break;
740                    } else {
741                        // Remember that those dead vertices have
742                        // `stack[len-1].vertex` as a descendant.
743                        descendants.insert((v.vertex, elt.vertex));
744                    }
745                }
746            }
747        }
748
749        // If no children, pop.
750        if stack.len() == len {
751            stack.pop();
752        }
753    }
754
755    Ok(())
756}
757
758pub(crate) fn repair_up<T: GraphMutTxnT + TreeTxnT>(
759    txn: &mut T,
760    channel: &mut T::Graph,
761    root: Position<ChangeId>,
762) -> Result<(), LocalApplyError<T>> {
763    info!("repair_zombies {:?}", root);
764    let mut stack = vec![root.inode_vertex()];
765    let mut visited = BTreeSet::new();
766    let mut add = Vec::new();
767    while let Some(elt) = stack.pop() {
768        if !visited.insert(elt) {
769            continue;
770        }
771        let mut is_alive = false;
772        let mut is_dead = false;
773        for e in iter_adjacent(
774            txn,
775            channel,
776            elt,
777            EdgeFlags::PARENT | EdgeFlags::FOLDER,
778            EdgeFlags::all(),
779        )? {
780            let e = e?;
781            let deleted = e.flag().contains(EdgeFlags::DELETED);
782            if e.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
783                is_alive |= !deleted;
784                is_dead |= deleted;
785            }
786        }
787        if is_dead && !is_alive {
788            for e in iter_adjacent(
789                txn,
790                channel,
791                elt,
792                EdgeFlags::PARENT | EdgeFlags::FOLDER,
793                EdgeFlags::all(),
794            )? {
795                let e = e?;
796                if e.flag()
797                    .contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::FOLDER)
798                {
799                    let parent = txn.find_block_end(channel, e.dest())?;
800                    add.push((*parent, elt));
801                    stack.push(*parent)
802                }
803            }
804
805            for (a, b) in add.drain(..) {
806                put_graph_with_rev(
807                    txn,
808                    channel,
809                    EdgeFlags::PSEUDO | EdgeFlags::FOLDER,
810                    a,
811                    b,
812                    ChangeId::ROOT,
813                )?;
814            }
815        }
816    }
817
818    Ok(())
819}
820
821pub fn clean_obsolete_pseudo_edges<T: GraphMutTxnT + TreeTxnT>(
822    txn: &mut T,
823    channel: &mut T::Graph,
824    ws: &mut Workspace,
825    change_id: ChangeId,
826) -> Result<HashSet<Position<ChangeId>>, LocalApplyError<T>> {
827    info!(
828        "clean_obsolete_pseudo_edges, ws.pseudo.len() = {}",
829        ws.pseudo.len()
830    );
831    let mut alive_folder = std::mem::take(&mut ws.alive_folder);
832    let mut folder_stack = std::mem::take(&mut ws.folder_stack);
833
834    let mut inodes = HashSet::new();
835
836    for (next_vertex, p, inode) in ws.pseudo.drain(..) {
837        debug!(
838            "clean_obsolete_pseudo_edges {:?} {:?} {:?}",
839            next_vertex, p, inode
840        );
841
842        if log_enabled!(log::Level::Debug) {
843            let still_here: Vec<_> = iter_adjacent(
844                txn,
845                channel,
846                next_vertex,
847                EdgeFlags::empty(),
848                EdgeFlags::all(),
849            )?
850            .collect();
851            debug!(
852                "pseudo edge still here ? {:?} {:?}",
853                next_vertex.change.0.0, still_here
854            )
855        }
856
857        let (a, b) = if p.flag().is_parent() {
858            match txn.find_block_end(channel, p.dest()) {
859                Ok(&dest) => (dest, next_vertex),
860                _ => {
861                    continue;
862                }
863            }
864        } else {
865            match txn.find_block(channel, p.dest()) {
866                Ok(&dest) => (next_vertex, dest),
867                _ => {
868                    continue;
869                }
870            }
871        };
872        let a_is_alive = is_alive(txn, channel, &a)?;
873        let b_is_alive = is_alive(txn, channel, &b)?;
874        if a_is_alive && b_is_alive {
875            continue;
876        }
877
878        // If we're deleting a FOLDER edge, repair_context_deleted
879        // will not repair its potential descendants. Hence, we must
880        // also count as "alive" a FOLDER node with alive descendants.
881        if p.flag().is_folder()
882            && folder_has_alive_descendants(txn, channel, &mut alive_folder, &mut folder_stack, b)?
883        {
884            continue;
885        }
886
887        if a.is_empty() && b_is_alive {
888            // In this case, `a` can be an inode, in which case we
889            // can't simply delete the edge, since b would become
890            // unreachable.
891            //
892            // We test this here:
893            let mut is_inode = false;
894            for e in iter_adjacent(
895                txn,
896                channel,
897                a,
898                EdgeFlags::FOLDER | EdgeFlags::PARENT,
899                EdgeFlags::all(),
900            )? {
901                let e = e?;
902                if e.flag().contains(EdgeFlags::FOLDER | EdgeFlags::PARENT) {
903                    is_inode = true;
904                    break;
905                }
906            }
907            if is_inode {
908                continue;
909            }
910        }
911
912        debug!(
913            "deleting {:?} {:?} {:?} {:?} {:?} {:?}",
914            a,
915            b,
916            p.introduced_by(),
917            p.flag(),
918            a_is_alive,
919            b_is_alive,
920        );
921        del_graph_with_rev(
922            txn,
923            channel,
924            p.flag() - EdgeFlags::PARENT,
925            a,
926            b,
927            p.introduced_by(),
928        )?;
929
930        if a_is_alive || (b_is_alive && !p.flag().is_folder()) {
931            // A context repair is needed.
932            inodes.insert(internal_pos(txn, &inode, change_id)?);
933        }
934    }
935
936    ws.alive_folder = alive_folder;
937    ws.folder_stack = folder_stack;
938    Ok(inodes)
939}
940
941fn folder_has_alive_descendants<T: GraphMutTxnT + TreeTxnT>(
942    txn: &mut T,
943    channel: &mut T::Graph,
944    alive: &mut HashMap<Vertex<ChangeId>, bool>,
945    stack: &mut Vec<(Vertex<ChangeId>, bool)>,
946    b: Vertex<ChangeId>,
947) -> Result<bool, LocalApplyError<T>> {
948    if let Some(r) = alive.get(&b) {
949        return Ok(*r);
950    }
951    debug!("alive descendants");
952    stack.clear();
953    stack.push((b, false));
954    while let Some((b, visited)) = stack.pop() {
955        debug!("visiting {:?} {:?}", b, visited);
956        if visited {
957            alive.entry(b).or_insert(false);
958            continue;
959        }
960        stack.push((b, true));
961        for e in iter_adjacent(
962            txn,
963            channel,
964            b,
965            EdgeFlags::empty(),
966            EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
967        )? {
968            let e = e?;
969            debug!("e = {:?}", e);
970            if e.flag().contains(EdgeFlags::FOLDER) {
971                let c = txn.find_block(channel, e.dest())?;
972                stack.push((*c, false));
973            } else {
974                // This is a non-deleted non-folder edge.
975                let c = txn.find_block(channel, e.dest())?;
976                if is_alive(txn, channel, c)? {
977                    // The entire path is alive.
978                    for (x, on_path) in stack.iter() {
979                        if *on_path {
980                            alive.insert(*x, true);
981                        }
982                    }
983                }
984            }
985        }
986    }
987    Ok(*alive.get(&b).unwrap_or(&false))
988}
989
990pub fn collect_missing_contexts<T: GraphMutTxnT + TreeTxnT>(
991    txn: &T,
992    channel: &T::Graph,
993    ws: &mut Workspace,
994    change: &Change,
995    change_id: ChangeId,
996    inodes: &mut HashSet<Position<ChangeId>>,
997) -> Result<(), LocalApplyError<T>> {
998    inodes.extend(
999        ws.missing_context
1000            .unknown_parents
1001            .drain(..)
1002            .map(|x| internal_pos(txn, &x.2, change_id).unwrap()),
1003    );
1004    for atom in change.changes.iter().flat_map(|r| r.iter()) {
1005        match atom {
1006            Atom::NewVertex(n) if !n.flag.is_folder() => {
1007                let inode = internal_pos(txn, &n.inode, change_id)?;
1008                if !inodes.contains(&inode) {
1009                    for up in n.up_context.iter() {
1010                        let up = *txn.find_block_end(channel, internal_pos(txn, up, change_id)?)?;
1011                        if !is_alive(txn, channel, &up)? {
1012                            inodes.insert(inode);
1013                            break;
1014                        }
1015                    }
1016                    for down in n.down_context.iter() {
1017                        let down = *txn.find_block(channel, internal_pos(txn, down, change_id)?)?;
1018                        let mut down_has_other_parents = false;
1019                        for e in iter_adjacent(
1020                            txn,
1021                            channel,
1022                            down,
1023                            EdgeFlags::PARENT,
1024                            EdgeFlags::all() - EdgeFlags::DELETED,
1025                        )? {
1026                            let e = e?;
1027                            if e.introduced_by() != change_id {
1028                                down_has_other_parents = true;
1029                                break;
1030                            }
1031                        }
1032                        if !down_has_other_parents {
1033                            inodes.insert(inode);
1034                            break;
1035                        }
1036                    }
1037                }
1038            }
1039            Atom::NewVertex(_) => {}
1040            Atom::EdgeMap(n) => {
1041                has_missing_edge_context(txn, channel, change_id, change, n, inodes, false)?;
1042            }
1043        }
1044    }
1045    Ok(())
1046}
1047
1048/// Collect inodes with missing contexts into `inodes`.
1049pub(crate) fn has_missing_edge_context<T: GraphMutTxnT + TreeTxnT>(
1050    txn: &T,
1051    channel: &T::Graph,
1052    change_id: ChangeId,
1053    change: &Change,
1054    n: &EdgeMap<Option<Hash>>,
1055    inodes: &mut HashSet<Position<ChangeId>>,
1056    reverse: bool,
1057) -> Result<(), LocalApplyError<T>> {
1058    let inode = internal_pos(txn, &n.inode, change_id)?;
1059    // If the inode is already in there, no need to do anything.
1060    if inodes.contains(&inode) {
1061        return Ok(());
1062    }
1063
1064    let ext: Hash = if reverse {
1065        (*txn.get_external(&change_id).unwrap()).into()
1066    } else {
1067        // Unused, hence we avoid one Sanakirja lookup.
1068        Hash::None
1069    };
1070
1071    for e in n.edges.iter() {
1072        let e = if reverse {
1073            e.reverse(Some(ext))
1074        } else {
1075            e.clone()
1076        };
1077
1078        assert!(!e.flag.contains(EdgeFlags::PARENT));
1079        if e.flag.contains(EdgeFlags::DELETED) {
1080            trace!("repairing context deleted {:?}", e);
1081            if has_missing_context_deleted(txn, channel, change_id, |h| change.knows(&h), &e)
1082                .map_err(LocalApplyError::from_missing)?
1083            {
1084                inodes.insert(inode);
1085                break;
1086            }
1087        } else {
1088            trace!("repairing context nondeleted {:?}", e);
1089            if has_missing_context_nondeleted(txn, channel, change_id, &e)
1090                .map_err(LocalApplyError::from_missing)?
1091            {
1092                inodes.insert(inode);
1093                break;
1094            }
1095        }
1096    }
1097    Ok(())
1098}
1099
1100pub(crate) fn repair_cyclic_paths<T: GraphMutTxnT + TreeTxnT>(
1101    txn: &mut T,
1102    channel: &mut T::Graph,
1103    ws: &mut Workspace,
1104) -> Result<(), LocalApplyError<T>> {
1105    let now = std::time::Instant::now();
1106    let mut files = std::mem::take(&mut ws.missing_context.files);
1107    for file in files.drain() {
1108        if file.is_empty() {
1109            if !is_rooted(txn, channel, file, ws)? {
1110                repair_edge(txn, channel, file, ws)?
1111            }
1112        } else {
1113            let f0 = EdgeFlags::FOLDER;
1114            let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
1115            let mut iter = iter_adjacent(txn, channel, file, f0, f1)?;
1116            if let Some(ee) = iter.next() {
1117                let ee = ee?;
1118                let dest = ee.dest().inode_vertex();
1119                if !is_rooted(txn, channel, dest, ws)? {
1120                    repair_edge(txn, channel, dest, ws)?
1121                }
1122            }
1123        }
1124    }
1125    ws.missing_context.files = files;
1126    crate::TIMERS.lock().unwrap().check_cyclic_paths += now.elapsed();
1127    Ok(())
1128}
1129
1130fn repair_edge<T: GraphMutTxnT + TreeTxnT>(
1131    txn: &mut T,
1132    channel: &mut T::Graph,
1133    to0: Vertex<ChangeId>,
1134    ws: &mut Workspace,
1135) -> Result<(), LocalApplyError<T>> {
1136    debug!("repair_edge {:?}", to0);
1137    let mut stack = vec![(to0, true, true, true)];
1138    ws.parents.clear();
1139    while let Some((current, _, al, anc_al)) = stack.pop() {
1140        if !ws.parents.insert(current) {
1141            continue;
1142        }
1143        debug!("repair_cyclic {:?}", current);
1144        if current != to0 {
1145            stack.push((current, true, al, anc_al));
1146        }
1147        if current.is_root() {
1148            debug!("root");
1149            break;
1150        }
1151        if let Some(&true) = ws.rooted.get(&current) {
1152            debug!("rooted");
1153            break;
1154        }
1155        let f = EdgeFlags::PARENT | EdgeFlags::FOLDER;
1156        let len = stack.len();
1157        for parent in iter_adjacent(txn, channel, current, f, EdgeFlags::all())? {
1158            let parent = parent?;
1159            if parent.flag().is_parent() {
1160                let anc = txn.find_block_end(channel, parent.dest())?;
1161                debug!("is_rooted, parent = {:?}", parent);
1162                let al = match iter_adjacent(
1163                    txn,
1164                    channel,
1165                    *anc,
1166                    f,
1167                    f | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
1168                )?
1169                .next()
1170                {
1171                    Some(e) => {
1172                        e?;
1173                        true
1174                    }
1175                    _ => false,
1176                };
1177                debug!("al = {:?}, flag = {:?}", al, parent.flag());
1178                stack.push((*anc, false, parent.flag().is_deleted(), al));
1179            }
1180        }
1181        if stack.len() == len {
1182            stack.pop();
1183        } else {
1184            (stack[len..]).sort_unstable_by_key(|a| a.3)
1185        }
1186    }
1187    let mut current = to0;
1188    for (next, on_path, del, _) in stack {
1189        if on_path {
1190            if del {
1191                debug!("put_pseudo {:?} {:?}", next, current);
1192                put_graph_with_rev(
1193                    txn,
1194                    channel,
1195                    EdgeFlags::FOLDER | EdgeFlags::PSEUDO,
1196                    next,
1197                    current,
1198                    ChangeId::ROOT,
1199                )?;
1200            }
1201            current = next
1202        }
1203    }
1204    ws.parents.clear();
1205    Ok(())
1206}
1207
1208fn is_rooted<T: GraphTxnT + TreeTxnT>(
1209    txn: &T,
1210    channel: &T::Graph,
1211    v: Vertex<ChangeId>,
1212    ws: &mut Workspace,
1213) -> Result<bool, LocalApplyError<T>> {
1214    let mut alive = false;
1215    assert!(v.is_empty());
1216    for e in iter_adjacent(txn, channel, v, EdgeFlags::empty(), EdgeFlags::all())? {
1217        let e = e?;
1218        if e.flag().contains(EdgeFlags::PARENT) {
1219            if e.flag() & (EdgeFlags::FOLDER | EdgeFlags::DELETED) == EdgeFlags::FOLDER {
1220                alive = true;
1221                break;
1222            }
1223        } else if !e.flag().is_deleted() {
1224            alive = true;
1225            break;
1226        }
1227    }
1228    if !alive {
1229        debug!("is_rooted, not alive");
1230        return Ok(true);
1231    }
1232    // Recycling ws.up_context and ws.parents as a stack and a
1233    // "visited" hashset, respectively.
1234    let stack = &mut ws.up_context;
1235    stack.clear();
1236    stack.push(v);
1237    let visited = &mut ws.parents;
1238    visited.clear();
1239
1240    while let Some(to) = stack.pop() {
1241        debug!("is_rooted, pop = {:?}", to);
1242        if to.is_root() {
1243            stack.clear();
1244            for v in visited.drain() {
1245                ws.rooted.insert(v, true);
1246            }
1247            return Ok(true);
1248        }
1249        if !visited.insert(to) {
1250            continue;
1251        }
1252        if let Some(&rooted) = ws.rooted.get(&to) {
1253            if rooted {
1254                for v in visited.drain() {
1255                    ws.rooted.insert(v, true);
1256                }
1257                return Ok(true);
1258            } else {
1259                continue;
1260            }
1261        }
1262        let f = EdgeFlags::PARENT | EdgeFlags::FOLDER;
1263        for parent in iter_adjacent(
1264            txn,
1265            channel,
1266            to,
1267            f,
1268            f | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
1269        )? {
1270            let parent = parent?;
1271            debug!("is_rooted, parent = {:?}", parent);
1272            stack.push(*txn.find_block_end(channel, parent.dest())?)
1273        }
1274    }
1275    for v in visited.drain() {
1276        ws.rooted.insert(v, false);
1277    }
1278    Ok(false)
1279}
1280
1281pub type AppliedRoot = (Hash, u64, Merkle);
1282
1283pub fn apply_root_change<R: rand::Rng, T: MutTxnT, P: ChangeStore>(
1284    txn: &mut T,
1285    channel: &ChannelRef<T>,
1286    store: &P,
1287    rng: R,
1288) -> Result<Option<AppliedRoot>, ApplyError<P::Error, T>> {
1289    let mut change = {
1290        // If the graph already has a root.
1291        {
1292            let channel = channel.read();
1293            let gr = txn.graph(&*channel);
1294            if let Some(v) = iter_adjacent(
1295                &*txn,
1296                gr,
1297                Vertex::ROOT,
1298                EdgeFlags::FOLDER,
1299                EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1300            )?
1301            .next()
1302            {
1303                let v = txn.find_block(gr, v?.dest())?;
1304                if v.start == v.end {
1305                    // Already has a root
1306                    return Ok(None);
1307                }
1308            } else {
1309                // Non-empty channel without a root
1310            }
1311            // If we are here, either the channel is empty, or it
1312            // isn't and doesn't have a root.
1313        }
1314        let root = Position {
1315            change: Some(Hash::None),
1316            pos: ChangePosition(0u64.into()),
1317        };
1318        use rand::RngExt;
1319        let contents = rng
1320            .sample_iter(rand::distr::StandardUniform)
1321            .take(32)
1322            .collect();
1323        debug!(
1324            "change position {:?} {:?}",
1325            ChangePosition(1u64.into()),
1326            ChangePosition(1u64.into()).0.as_u64()
1327        );
1328        crate::change::LocalChange::make_change(
1329            txn,
1330            channel,
1331            vec![crate::change::Hunk::AddRoot {
1332                name: Atom::NewVertex(NewVertex {
1333                    up_context: vec![root],
1334                    down_context: Vec::new(),
1335                    start: ChangePosition(0u64.into()),
1336                    end: ChangePosition(0u64.into()),
1337                    flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1338                    inode: root,
1339                }),
1340                inode: Atom::NewVertex(NewVertex {
1341                    up_context: vec![Position {
1342                        change: None,
1343                        pos: ChangePosition(0u64.into()),
1344                    }],
1345                    down_context: Vec::new(),
1346                    start: ChangePosition(1u64.into()),
1347                    end: ChangePosition(1u64.into()),
1348                    flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1349                    inode: root,
1350                }),
1351            }],
1352            contents,
1353            crate::change::ChangeHeader::default(),
1354            Vec::new(),
1355        )?
1356    };
1357    let h = store
1358        .save_change(&mut change, |_, _| Ok(()))
1359        .map_err(ApplyError::Changestore)?;
1360    let (n, merkle) = apply_change(store, txn, &mut channel.write(), &h)?;
1361    Ok(Some((h, n, merkle)))
1362}