Fixing a bug where zombie files could be deleted by unrecord, but not their contents

pmeunier
Apr 14, 2022, 11:17 AM
ZCPGCKKYDBXUTIYGKGRA7C43CJ2WNWH3L6Q7ETSVLYB6AE4HAGTQC

Dependencies

  • [2] ZDK3GNDB Tag transactions (including a massive refactoring of errors)
  • [3] YUJV2OHL changelog
  • [4] 2BKYJ2JM Fixing a bug introduced by the recent refactoring
  • [5] 6YMDOZIB Refactoring apply
  • [6] YN63NUZO Sanakirja 1.0
  • [7] IIV3EL2X Cleanup, formatting, and fixing the Git feature
  • [*] SXEYMYF7 Fixing the bad changes in history (unfortunately, by rebooting).
  • [*] CCLLB7OI Upgrading to Sanakirja 0.15 + version bump
  • [*] HMMMKONL Fixing alive vertices

Change contents

  • edit in libpijul/src/unrecord/mod.rs at line 566
    [4.3349]
    [4.2292]
    // Do not follow edges that are introduced by this patch
    // and are trivial parents (i.e. non-blocks), since they
    // aren't part of the zombie conflict.
  • edit in libpijul/src/unrecord/mod.rs at line 600
    [9.247205]
    [9.247205]
    debug!("repair_edges_context: {:?}", e);
  • edit in libpijul/src/apply.rs at line 479
    [4.145968]
    [9.961591]
    alive_folder: HashMap<Vertex<ChangeId>, bool>,
    folder_stack: Vec<(Vertex<ChangeId>, bool)>,
  • edit in libpijul/src/apply.rs at line 497
    [10.129726]
    [9.962286]
    self.alive_folder.clear();
    self.folder_stack.clear();
  • edit in libpijul/src/apply.rs at line 510
    [10.129809]
    [9.962669]
    assert!(self.alive_folder.is_empty());
    assert!(self.folder_stack.is_empty());
  • edit in libpijul/src/apply.rs at line 521
    [2.42353]
    [9.971451]
    let mut alive_folder = std::mem::replace(&mut ws.alive_folder, HashMap::new());
    let mut folder_stack = std::mem::replace(&mut ws.folder_stack, Vec::new());
  • edit in libpijul/src/apply.rs at line 540
    [4.21011]
    [4.170]
    // If we're deleting a FOLDER edge, repair_context_deleted
    // will not repair its potential descendants. Hence, we must
    // also count as "alive" a FOLDER node with alive descendants.
    if p.flag().is_folder() {
    if folder_has_alive_descendants(txn, channel, &mut alive_folder, &mut folder_stack, b)?
    {
    continue;
    }
    }
    if a.is_empty() && b_is_alive {
    // In this case, `a` can be an inode, in which case we
    // can't simply delete the edge, since b would become
    // unreachable.
    //
    // We test this here:
    let mut is_inode = false;
    for e in iter_adjacent(
    txn,
    channel,
    a,
    EdgeFlags::FOLDER | EdgeFlags::PARENT,
    EdgeFlags::all(),
    )? {
    let e = e?;
    if e.flag().contains(EdgeFlags::FOLDER | EdgeFlags::PARENT) {
    is_inode = true;
    break;
    }
    }
    if is_inode {
    continue;
    }
    }
  • replacement in libpijul/src/apply.rs at line 577
    [4.186][4.186:230]()
    "Deleting {:?} {:?} {:?} {:?}",
    [4.186]
    [4.21996]
    "Deleting {:?} {:?} {:?} {:?} {:?} {:?}",
  • replacement in libpijul/src/apply.rs at line 581
    [4.22057][4.22057:22078]()
    p.flag()
    [4.22057]
    [4.272]
    p.flag(),
    a_is_alive,
    b_is_alive,
  • edit in libpijul/src/apply.rs at line 607
    [4.407]
    [4.21515]
    // Note: if this is a folder edge,
    // repair_missing_up_context will stop immediately, so we
    // don't even need to call it.
  • edit in libpijul/src/apply.rs at line 623
    [9.974238]
    [9.974238]
    ws.alive_folder = alive_folder;
    ws.folder_stack = folder_stack;
  • edit in libpijul/src/apply.rs at line 628
    [11.1060]
    [2.42354]
    fn folder_has_alive_descendants<T: GraphMutTxnT + TreeTxnT>(
    txn: &mut T,
    channel: &mut T::Graph,
    alive: &mut HashMap<Vertex<ChangeId>, bool>,
    stack: &mut Vec<(Vertex<ChangeId>, bool)>,
    b: Vertex<ChangeId>,
    ) -> Result<bool, LocalApplyError<T>> {
    if let Some(r) = alive.get(&b) {
    return Ok(*r);
    }
    debug!("alive descendants");
    stack.clear();
    stack.push((b, false));
    while let Some((b, visited)) = stack.pop() {
    debug!("visiting {:?} {:?}", b, visited);
    if visited {
    if !alive.contains_key(&b) {
    alive.insert(b, false);
    }
    continue;
    }
    stack.push((b, true));
    for e in iter_adjacent(
    txn,
    channel,
    b,
    EdgeFlags::empty(),
    EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
    )? {
    let e = e?;
    if e.flag().contains(EdgeFlags::FOLDER) {
    let c = txn.find_block(channel, e.dest())?;
    stack.push((*c, false));
    } else {
    // This is a non-deleted non-folder edge.
    let c = txn.find_block(channel, e.dest())?;
    if is_alive(txn, channel, &c)? {
    // The entire path is alive.
    for (x, on_path) in stack.iter() {
    if *on_path {
    alive.insert(*x, true);
    }
    }
    }
    }
    }
    }
    Ok(*alive.get(&b).unwrap_or(&false))
    }
  • edit in CHANGELOG.md at line 2
    [3.50]
    [3.50]
    ## Unreleased
  • edit in CHANGELOG.md at line 5
    [3.51]
    [3.51]
    ### Fixed
    - Fixing a bug with name conflicts, where files could end up with 0 alive name.
    - Fixing a few panics/unwraps
    - Fixing a bug where a zombie file could be deleted by `pijul unrecord`, but its contents would stay zombie.