use crate::change::*;
use crate::changestore::*;
use crate::pristine::*;
use crate::small_string::*;
pub fn undo_file_addition<T: TreeMutTxnT>(
txn: &mut T,
change_id: ChangeId,
new_vertex: &NewVertex<Option<Hash>>,
) -> Result<(), TreeErr<T::TreeError>> {
if new_vertex.start == new_vertex.end {
let pos = Position {
change: change_id,
pos: new_vertex.start,
};
if let Some(&inode) = txn.get_revinodes(&pos, None)? {
del_inodes_with_rev(txn, &inode, &pos)?;
}
}
Ok(())
}
pub fn undo_file_deletion<T: ChannelTxnT + TreeMutTxnT + TreeTxnT, P: ChangeStore>(
txn: &mut T,
changes: &P,
channel: &T::Channel,
change_id: ChangeId,
newedges: &EdgeMap<Option<Hash>>,
salt: u64,
) -> Result<(), super::UnrecordError<P::Error, T>> {
for e in newedges.edges.iter().rev() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
let source =
*txn.find_block_end(txn.graph(channel), internal_pos(txn, &e.from, change_id)?)?;
if e.flag.contains(EdgeFlags::FOLDER) && e.to.start_pos() == e.to.end_pos() {
let dest = internal_pos(txn, &e.to.start_pos(), change_id)?;
restore(txn, changes, channel, source, dest, salt)?
}
}
Ok(())
}
fn restore<T: ChannelTxnT + TreeMutTxnT + TreeTxnT, P: ChangeStore>(
txn: &mut T,
changes: &P,
channel: &T::Channel,
source: Vertex<ChangeId>,
dest: Position<ChangeId>,
salt: u64,
) -> Result<(), super::UnrecordError<P::Error, T>> {
let mut stack = vec![(source, dest)];
let mut return_value = Some(Inode::ROOT);
while let Some((source, dest)) = stack.pop() {
debug!("source = {:?}, dest = {:?}", source, dest);
if let Some(parent_inode) = return_value {
if parent_inode != Inode::ROOT {
return_value = restore_inode(txn, changes, source, dest, parent_inode, salt)?;
continue;
}
} else {
continue;
}
let mut source_parent = None;
for e in iter_adjacent(
txn,
txn.graph(channel),
source,
EdgeFlags::PARENT | EdgeFlags::FOLDER,
EdgeFlags::all(),
)? {
let e = e?;
if e.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
source_parent = Some(e.dest());
break;
}
}
let source_parent = source_parent.unwrap();
if source_parent.change.is_root() {
return_value = restore_inode(txn, changes, source, dest, Inode::ROOT, salt)?;
} else if let Some(&inode) = txn.get_revinodes(&source_parent, None)? {
return_value = restore_inode(txn, changes, source, dest, inode, salt)?;
} else {
let grandparent = find_youngest_parent(txn, channel, source_parent.inode_vertex())?;
if grandparent.is_root() || grandparent.start == grandparent.end {
return_value = restore_inode(txn, changes, source, dest, Inode::ROOT, salt)?;
} else {
stack.push((source, dest));
stack.push((grandparent, source_parent));
}
}
}
Ok(())
}
fn restore_inode<T: TreeMutTxnT + GraphTxnT + TreeTxnT, P: ChangeStore>(
txn: &mut T,
changes: &P,
source: Vertex<ChangeId>,
dest: Position<ChangeId>,
parent_inode: Inode,
salt: u64,
) -> Result<Option<Inode>, super::UnrecordError<P::Error, T>> {
let mut name = vec![0; source.end - source.start];
let FileMetadata {
basename, metadata, ..
} = changes
.get_file_meta(
|h| txn.get_external(&h).unwrap().map(From::from),
source,
&mut name,
)
.map_err(super::UnrecordError::Changestore)?;
let basename = SmallString::from_str(basename);
if let Some(inode) = txn.get_revinodes(&dest, None)? {
Ok(Some(*inode))
} else {
let file_id = OwnedPathId {
parent_inode,
basename,
};
if txn.get_tree(&file_id, None)?.is_none() {
let inode = crate::fs::create_new_inode(txn, &file_id, salt)?;
put_tree_with_rev(txn, &file_id, &inode)?;
put_inodes_with_rev(txn, &inode, &dest)?;
if metadata.is_dir() {
let id = OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
};
txn.put_tree(&id, &inode)?;
}
Ok(Some(inode))
} else {
Ok(None)
}
}
}
fn find_youngest_parent<T: ChannelTxnT>(
txn: &T,
channel: &T::Channel,
current: Vertex<ChangeId>,
) -> Result<Vertex<ChangeId>, BlockError<T::GraphError>> {
let mut next = None;
for e in iter_adjacent(
txn,
txn.graph(channel),
current,
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK,
)? {
let e = e?;
if !e.flag().contains(EdgeFlags::FOLDER | EdgeFlags::PARENT) {
continue;
}
if e.flag().contains(EdgeFlags::DELETED) {
debug!("deleted: {:?}", e.introduced_by());
let age = txn
.get_changeset(txn.changes(channel), &e.introduced_by())?
.unwrap();
if let Some((ref mut age0, ref mut v)) = next {
if age > *age0 {
*age0 = age;
*v = e.dest()
}
} else {
next = Some((age, e.dest()))
}
} else {
next = Some((&L64(0), e.dest()));
break;
}
}
Ok(*txn.find_block_end(txn.graph(channel), next.unwrap().1)?)
}
pub fn undo_file_reinsertion<
P: ChangeStore,
T: GraphTxnT + TreeMutTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,
>(
txn: &mut T,
change_id: ChangeId,
newedges: &EdgeMap<Option<Hash>>,
) -> Result<(), super::UnrecordError<P::Error, T>> {
for e in newedges.edges.iter() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
if e.to.start_pos() == e.to.end_pos() {
let position = internal_pos(txn, &e.to.start_pos(), change_id)?;
if let Some(&inode) = txn.get_revinodes(&position, None)? {
del_inodes_with_rev(txn, &inode, &position)?;
}
}
}
Ok(())
}