use crate::changestore::*;
use crate::pristine::*;
use crate::small_string::*;
use crate::HashSet;
use std::iter::Iterator;
#[derive(Error)]
pub enum FsError<T: TreeTxnT> {
#[error("Inode not found")]
InodeNotFound(Inode),
#[error(transparent)]
NotFound(#[from] FsNotFound),
#[error("File already in repository: {0}")]
AlreadyInRepo(String),
#[error(transparent)]
Tree(#[from] TreeErr<T::TreeError>),
#[error("Invalid path: {0}")]
InvalidPath(String),
}
impl<T: TreeTxnT> std::fmt::Debug for FsError<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
FsError::InodeNotFound(inode) => write!(fmt, "Inode not found: {}", inode.to_base32()),
FsError::NotFound(e) => std::fmt::Debug::fmt(e, fmt),
FsError::AlreadyInRepo(e) => write!(fmt, "File already in repository: {}", e),
FsError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
FsError::InvalidPath(e) => write!(fmt, "Invalid path: {}", e),
}
}
}
#[derive(Error)]
pub enum FsErrorC<C: std::error::Error + 'static, T: GraphTxnT> {
#[error(transparent)]
Txn(#[from] TxnErr<T::GraphError>),
#[error(transparent)]
Changestore(C),
#[error(transparent)]
NotFound(#[from] FsNotFound),
}
impl<C: std::error::Error + 'static, T: GraphTxnT> std::fmt::Debug for FsErrorC<C, T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
FsErrorC::Txn(e) => std::fmt::Debug::fmt(e, fmt),
FsErrorC::Changestore(e) => std::fmt::Debug::fmt(e, fmt),
FsErrorC::NotFound(e) => std::fmt::Debug::fmt(e, fmt),
}
}
}
#[derive(Debug, Error)]
#[error("Path not found: {0}")]
pub struct FsNotFound(String);
pub fn create_new_inode<T: TreeMutTxnT>(
txn: &mut T,
parent_id: &PathId,
salt: u64,
) -> Result<Inode, TreeErr<T::TreeError>> {
use std::hash::{BuildHasher, Hash, Hasher};
let mut s = crate::Hasher::default().build_hasher();
(parent_id, salt).hash(&mut s);
let mut i: u64 = s.finish();
let mut inode = Inode(L64(i.to_le()));
while txn.get_revtree(&inode, None)?.is_some() {
i += 1;
inode = Inode(L64(i.to_le()));
}
Ok(inode)
}
pub fn is_directory<T: TreeTxnT>(txn: &T, inode: Inode) -> Result<bool, TreeErr<T::TreeError>> {
if inode == Inode::ROOT {
return Ok(true);
}
for x in txn.iter_tree(&OwnedPathId::inode(inode), None)? {
let (pid, _) = x?;
if pid.parent_inode < inode {
continue;
} else if pid.parent_inode > inode {
break;
}
return Ok(true);
}
Ok(false)
}
fn closest_in_repo_ancestor<'a, T: TreeTxnT>(
txn: &T,
path: &'a str,
) -> Result<(Inode, std::iter::Peekable<crate::path::Components<'a>>), TreeErr<T::TreeError>> {
let mut components = crate::path::components(path).peekable();
let mut fileid = OwnedPathId::inode(Inode::ROOT);
while let Some(c) = components.peek() {
trace!("component {:?}", c);
fileid.basename.clone_from_str(c);
trace!("{:?}", fileid);
let mut found = false;
for x in txn.iter_tree(&fileid, None)? {
let (id, inode) = x?;
trace!(
"id = {:?}, inode = {:?}, cmp = {:?}",
id,
inode,
id > &fileid
);
if id > &fileid {
break;
} else if id < &fileid {
continue;
}
found = true;
fileid.parent_inode = *inode;
break;
}
if found {
components.next();
} else {
break;
}
}
Ok((fileid.parent_inode, components))
}
pub fn find_inode<T: TreeTxnT>(txn: &T, path: &str) -> Result<Inode, FsError<T>> {
debug!("find_inode");
let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
debug!("/find_inode");
if let Some(c) = remaining_path_components.next() {
debug!("c = {:?}", c);
Err(FsNotFound(path.to_string()).into())
} else {
Ok(inode)
}
}
pub fn is_tracked<T: TreeTxnT>(txn: &T, path: &str) -> Result<bool, TreeErr<T::TreeError>> {
debug!("is_tracked {:?}", path);
let (_, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
debug!("/is_tracked {:?}", path);
Ok(remaining_path_components.next().is_none())
}
pub fn inode_filename<T: TreeTxnT>(
txn: &T,
inode: Inode,
) -> Result<Option<String>, TreeErr<T::TreeError>> {
debug!("inode_filename {:?}", inode);
let mut components = Vec::new();
let mut current = inode;
loop {
match txn.get_revtree(¤t, None)? {
Some(v) => {
components.push(v.basename.to_owned());
current = v.parent_inode;
if current == Inode::ROOT {
break;
}
}
None => {
debug!("filename_of_inode: not in tree");
return Ok(None);
}
}
}
let mut path = String::new();
for c in components.iter().rev() {
if !path.is_empty() {
path.push('/')
}
path.push_str(c.as_str());
}
debug!("inode_filename = {:?}", path);
Ok(Some(path))
}
pub fn make_new_child<T: TreeMutTxnT>(
txn: &mut T,
parent_inode: Inode,
filename: &str,
is_dir: bool,
child_inode: Option<Inode>,
salt: u64,
) -> Result<Inode, FsError<T>> {
let parent_id = OwnedPathId {
parent_inode,
basename: SmallString::from_str(filename),
};
if let Some(&inode) = txn.get_tree(&parent_id, None)? {
debug!("inode = {:?}", inode);
if let Some(child) = child_inode {
if child == inode {
Ok(inode)
} else {
del_tree_with_rev(txn, &parent_id, &inode)?;
if let Some(&vertex) = txn.get_inodes(&inode, None)? {
del_inodes_with_rev(txn, &inode, &vertex)?;
}
put_tree_with_rev(txn, &parent_id, &child)?;
Ok(child)
}
} else {
Err(FsError::AlreadyInRepo(filename.to_string()))
}
} else {
let child_inode = match child_inode {
None => create_new_inode(txn, &parent_id, salt)?,
Some(i) => i,
};
debug!("make_new_child: {:?} {:?}", parent_id, child_inode);
put_tree_with_rev(txn, &parent_id, &child_inode)?;
if is_dir {
let dir_id = OwnedPathId {
parent_inode: child_inode,
basename: SmallString::new(),
};
txn.put_tree(&dir_id, &child_inode)?;
};
Ok(child_inode)
}
}
pub fn add_inode<T: TreeMutTxnT>(
txn: &mut T,
inode: Option<Inode>,
path: &str,
is_dir: bool,
salt: u64,
) -> Result<Inode, FsError<T>> {
debug!("add_inode");
if let Some(parent) = crate::path::parent(path) {
let (current_inode, unrecorded_path) = closest_in_repo_ancestor(txn, parent)?;
let mut current_inode = current_inode;
debug!("add_inode: closest = {:?}", current_inode);
for c in unrecorded_path {
debug!("unrecorded: {:?}", c);
current_inode = make_new_child(txn, current_inode, c, true, None, salt)?;
}
let file_name = crate::path::file_name(path).unwrap();
debug!("add_inode: file_name = {:?}", file_name);
current_inode = make_new_child(txn, current_inode, file_name, is_dir, inode, salt)?;
Ok(current_inode)
} else {
Err(FsError::InvalidPath(path.to_string()))
}
}
pub fn move_file<T: TreeMutTxnT>(
txn: &mut T,
origin: &str,
destination: &str,
salt: u64,
) -> Result<(), FsError<T>> {
debug!("move_file: {},{}", origin, destination);
move_file_by_inode(txn, find_inode(txn, origin)?, destination, salt)?;
Ok(())
}
pub fn move_file_by_inode<T: TreeMutTxnT>(
txn: &mut T,
inode: Inode,
destination: &str,
salt: u64,
) -> Result<(), FsError<T>> {
debug!("inode = {:?}", inode);
let fileref = if let Some(inode) = txn.get_revtree(&inode, None)? {
inode.to_owned()
} else {
return Err(FsError::InodeNotFound(inode));
};
debug!("fileref = {:?}", fileref);
del_tree_with_rev(txn, &fileref, &inode)?;
debug!("inode={:?} destination={}", inode, destination);
let is_dir = txn
.get_tree(
&OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
},
None,
)?
.is_some();
add_inode(txn, Some(inode), destination, is_dir, salt)?;
Ok(())
}
pub(crate) fn rec_delete<T: TreeMutTxnT>(
txn: &mut T,
parent: &PathId,
inode: Inode,
delete_inodes: bool,
) -> Result<(), FsError<T>> {
let file_id = OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
};
let mut children = Vec::new();
let mut is_dir = false;
for x in txn.iter_tree(&file_id, None)? {
let (k, inode) = x?;
assert!(k.parent_inode >= file_id.parent_inode);
if k.parent_inode > file_id.parent_inode {
break;
}
debug!("iter_tree: {:?} {:?}", k, inode);
is_dir = true;
if !k.basename.is_empty() {
children.push((k.to_owned(), *inode))
}
}
for (k, inode_) in children {
assert_ne!(inode, inode_);
rec_delete(txn, &k, inode_, delete_inodes)?;
}
debug!(
"rec_delete: {:?}, {:?}, {:?}, {:?}",
parent, file_id, inode, is_dir
);
if is_dir {
assert!(inode.is_root() || txn.del_tree(&file_id, Some(&inode))?);
}
if !inode.is_root() && del_tree_with_rev(txn, &parent, &inode)? {
if delete_inodes {
if let Some(&vertex) = txn.get_inodes(&inode, None)? {
del_inodes_with_rev(txn, &inode, &vertex)?;
}
}
} else {
debug!("rec_delete: {:?} {:?} not present", parent, inode);
}
Ok(())
}
pub fn remove_file<T: TreeMutTxnT>(txn: &mut T, path: &str) -> Result<(), FsError<T>> {
debug!("remove file {:?}", path);
let inode = find_inode(txn, path)?;
let parent = if inode.is_root() {
OwnedPathId {
parent_inode: Inode::ROOT,
basename: SmallString::new(),
}
} else {
txn.get_revtree(&inode, None)?.unwrap().to_owned()
};
debug!("remove file {:?} {:?}", parent, inode);
rec_delete(txn, &parent, inode, false)?;
Ok(())
}
pub struct WorkingCopyChildren<'txn, T: TreeTxnT> {
iter: crate::pristine::Cursor<T, &'txn T, T::TreeCursor, PathId, Inode>,
inode: Inode,
}
impl<'txn, T: TreeTxnT> Iterator for WorkingCopyChildren<'txn, T> {
type Item = Result<(SmallString, Inode), T::TreeError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.iter.next() {
Some(Ok((k, v))) => {
if k.parent_inode == self.inode {
if k.basename.len() > 0 {
return Some(Ok((k.basename.to_owned(), *v)));
}
} else if k.parent_inode > self.inode {
return None;
}
}
None => return None,
Some(Err(e)) => return Some(Err(e.0)),
}
}
}
}
pub fn working_copy_children<T: TreeTxnT>(
txn: &T,
inode: Inode,
) -> Result<WorkingCopyChildren<T>, T::TreeError> {
Ok(WorkingCopyChildren {
iter: txn
.iter_tree(
&OwnedPathId {
parent_inode: inode,
basename: SmallString::new(),
},
None,
)
.map_err(|e| e.0)?,
inode,
})
}
pub struct WorkingCopyIterator<'txn, T: TreeTxnT> {
stack: Vec<(Inode, String)>,
txn: &'txn T,
}
impl<'txn, T: TreeTxnT> Iterator for WorkingCopyIterator<'txn, T> {
type Item = Result<(Inode, String, bool), T::TreeError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((inode, name)) = self.stack.pop() {
let fileid = OwnedPathId {
parent_inode: inode,
basename: SmallString::from_str(""),
};
let len = self.stack.len();
let iter = match self.txn.iter_tree(&fileid, None) {
Ok(iter) => iter,
Err(e) => return Some(Err(e.0)),
};
let mut is_folder = false;
for x in iter {
let (k, v) = match x {
Ok(x) => x,
Err(e) => return Some(Err(e.0)),
};
if k.parent_inode < inode {
continue;
} else if k.parent_inode > inode {
break;
}
is_folder = true;
if !k.basename.is_empty() {
let mut name = name.clone();
crate::path::push(&mut name, k.basename.as_str());
self.stack.push((*v, name))
}
}
(&mut self.stack[len..]).reverse();
if !name.is_empty() {
return Some(Ok((inode, name, is_folder)));
}
} else {
return None;
}
}
}
}
pub fn iter_working_copy<T: TreeTxnT>(txn: &T, root: Inode) -> WorkingCopyIterator<T> {
WorkingCopyIterator {
stack: vec![(root, String::new())],
txn,
}
}
pub struct GraphDescendants<'txn, T: GraphTxnT> {
txn: &'txn T,
channel: &'txn T::Graph,
stack: Vec<AdjacentIterator<'txn, T>>,
visited: HashSet<Position<ChangeId>>,
}
impl<'txn, T: GraphTxnT> Iterator for GraphDescendants<'txn, T> {
type Item = Result<Position<ChangeId>, T::GraphError>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut adj) = self.stack.pop() {
match adj.next() {
Some(Ok(child)) => {
self.stack.push(adj);
let dest = match self.txn.find_block(self.channel, child.dest()) {
Ok(dest) => dest,
Err(BlockError::Txn(t)) => return Some(Err(t)),
Err(e) => panic!("{}", e),
};
let grandchild = match iter_adjacent(
self.txn,
self.channel,
*dest,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(mut x) => match x.next().unwrap() {
Ok(x) => x,
Err(e) => return Some(Err(e.0)),
},
Err(e) => return Some(Err(e.0)),
};
if self.visited.insert(grandchild.dest()) {
match iter_adjacent(
self.txn,
self.channel,
grandchild.dest().inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(adj) => self.stack.push(adj),
Err(e) => return Some(Err(e.0)),
}
}
return Some(Ok(grandchild.dest()));
}
Some(Err(e)) => return Some(Err(e.0)),
None => {
}
}
}
None
}
}
pub fn iter_graph_descendants<'txn, T: GraphTxnT>(
txn: &'txn T,
channel: &'txn T::Graph,
key: Position<ChangeId>,
) -> Result<GraphDescendants<'txn, T>, T::GraphError> {
Ok(GraphDescendants {
stack: vec![iter_adjacent(
txn,
&channel,
key.inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
.map_err(|e| e.0)?],
visited: HashSet::default(),
txn,
channel,
})
}
pub struct GraphChildren<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
txn: &'txn T,
channel: &'txn T::Graph,
adj: AdjacentIterator<'txn, T>,
adj2: Option<AdjacentIterator<'txn, T>>,
changes: &'changes P,
buf: Vec<u8>,
}
impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
for GraphChildren<'txn, 'changes, T, P>
{
type Item = Result<(Position<ChangeId>, ChangeId, InodeMetadata, String), T::GraphError>;
fn next(&mut self) -> Option<Self::Item> {
let dest = loop {
debug!("adj2 = {:?}", self.adj2.is_some());
if let Some(mut adj2) = self.adj2.take() {
match adj2.next() {
None => {}
Some(Ok(ch)) => {
self.adj2 = Some(adj2);
break self.txn.find_block(self.channel, ch.dest()).unwrap();
}
Some(Err(e)) => return Some(Err(e.0)),
}
}
match self.adj.next() {
Some(Ok(child)) => {
let d = self.txn.find_block(self.channel, child.dest()).unwrap();
if d.start == d.end {
match iter_adjacent(
self.txn,
self.channel,
*d,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
.and_then(|mut it| it.next().unwrap())
.and_then(|x| {
iter_adjacent(
self.txn,
self.channel,
x.dest().inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
}) {
Ok(adj) => self.adj2 = Some(adj),
Err(e) => return Some(Err(e.0)),
}
} else {
break d;
}
}
Some(Err(e)) => return Some(Err(e.0)),
None => return None,
}
};
debug!("dest = {:?}", dest);
let mut buf = std::mem::replace(&mut self.buf, Vec::new());
buf.resize(dest.end - dest.start, 0);
let FileMetadata {
basename,
metadata: perms,
..
} = self
.changes
.get_file_meta(
|p| self.txn.get_external(&p).unwrap().map(|x| x.into()),
*dest,
&mut buf,
)
.unwrap();
let basename = basename.to_string();
self.buf = buf;
let grandchild = match iter_adjacent(
self.txn,
self.channel,
*dest,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(mut adj) => match adj.next() {
Some(Ok(n)) => n,
None => unreachable!(),
Some(Err(e)) => return Some(Err(e.0)),
},
Err(e) => return Some(Err(e.0)),
};
Some(Ok((
grandchild.dest(),
grandchild.introduced_by(),
perms,
basename,
)))
}
}
pub fn iter_graph_children<'txn, 'changes, T, P>(
txn: &'txn T,
changes: &'changes P,
channel: &'txn T::Graph,
key: Position<ChangeId>,
) -> Result<GraphChildren<'txn, 'changes, T, P>, T::GraphError>
where
T: GraphTxnT,
P: ChangeStore,
{
Ok(GraphChildren {
buf: Vec::new(),
adj: iter_adjacent(
txn,
channel,
key.inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)
.map_err(|e| e.0)?,
adj2: None,
txn,
channel,
changes,
})
}
pub struct GraphBasenames<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
txn: &'txn T,
channel: &'txn T::Graph,
adj: AdjacentIterator<'txn, T>,
changes: &'changes P,
buf: Vec<u8>,
}
impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
for GraphBasenames<'txn, 'changes, T, P>
{
type Item = Result<(Position<ChangeId>, InodeMetadata, String), T::GraphError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let parent = match self.adj.next() {
Some(Ok(n)) => n,
Some(Err(e)) => return Some(Err(e.0)),
None => return None,
};
let dest = self
.txn
.find_block_end(&self.channel, parent.dest())
.unwrap();
if dest.start == dest.end {
return None;
}
let mut buf = std::mem::replace(&mut self.buf, Vec::new());
buf.resize(dest.end - dest.start, 0);
let FileMetadata {
basename,
metadata: perms,
..
} = self
.changes
.get_file_meta(
|p| self.txn.get_external(&p).unwrap().map(|x| x.into()),
*dest,
&mut buf,
)
.unwrap();
let basename = basename.to_owned();
self.buf = buf;
match iter_adjacent(
self.txn,
&self.channel,
*dest,
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
) {
Ok(mut adj) => match adj.next() {
Some(Ok(grandparent)) => {
return Some(Ok((grandparent.dest(), perms, basename)))
}
Some(Err(e)) => return Some(Err(e.0)),
None => {}
},
Err(e) => return Some(Err(e.0)),
}
}
}
}
pub fn iter_basenames<'txn, 'changes, T, P>(
txn: &'txn T,
changes: &'changes P,
channel: &'txn T::Graph,
pos: Position<ChangeId>,
) -> Result<GraphBasenames<'txn, 'changes, T, P>, T::GraphError>
where
T: GraphTxnT,
P: ChangeStore,
{
Ok(GraphBasenames {
buf: Vec::new(),
adj: iter_adjacent(
txn,
channel,
pos.inode_vertex(),
EdgeFlags::FOLDER | EdgeFlags::PARENT,
EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
)
.map_err(|e| e.0)?,
txn,
channel,
changes,
})
}
pub fn iter_paths<T: GraphTxnT, F: FnMut(&mut dyn Iterator<Item = Position<ChangeId>>) -> bool>(
txn: &T,
graph: &T::Graph,
key: Position<ChangeId>,
mut f: F,
) -> Result<(), TxnErr<T::GraphError>> {
let mut stack: Vec<(Position<ChangeId>, bool)> = vec![(key, true)];
while let Some((cur_key, on_stack)) = stack.pop() {
if cur_key.is_root() {
if !f(&mut stack
.iter()
.filter_map(|(key, on_path)| if *on_path { Some(*key) } else { None }))
{
break;
}
} else if !on_stack {
stack.push((cur_key, true));
let len = stack.len();
let f = EdgeFlags::parent_folder();
for parent in iter_adjacent(
txn,
&graph,
cur_key.inode_vertex(),
f,
f | EdgeFlags::PSEUDO,
)? {
let parent_dest = txn.find_block_end(&graph, parent?.dest()).unwrap();
for grandparent in
iter_adjacent(txn, &graph, *parent_dest, f, f | EdgeFlags::PSEUDO)?
{
stack.push((grandparent?.dest(), false))
}
}
if stack.len() == len {
stack.pop();
}
}
}
Ok(())
}
pub(crate) fn follow_oldest_path<T: ChannelTxnT, C: ChangeStore>(
changes: &C,
txn: &T,
channel: &T::Channel,
path: &str,
) -> Result<(Position<ChangeId>, bool), FsErrorC<C::Error, T>> {
use crate::pristine::*;
debug!("follow_oldest_path = {:?}", path);
let mut current = Position::ROOT;
let flag0 = EdgeFlags::FOLDER;
let flag1 = flag0 | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
let mut name_buf = Vec::new();
let mut ambiguous = false;
let mut seen = HashSet::new();
for c in crate::path::components(path) {
'outer: loop {
let mut next = None;
debug!("follow_oldest_path, loop {:?}", current);
for name in iter_adjacent(
txn,
txn.graph(channel),
current.inode_vertex(),
flag0,
flag1,
)? {
let name = name?;
let name_dest = txn.find_block(txn.graph(channel), name.dest()).unwrap();
debug!("name_dest = {:?}", name_dest);
if name_dest.start == name_dest.end {
current = iter_adjacent(txn, txn.graph(channel), *name_dest, flag0, flag1)?
.next()
.unwrap()?
.dest();
continue 'outer;
}
name_buf.resize(name_dest.end - name_dest.start, 0);
debug!("getting contents {:?}", name);
changes
.get_contents(
|h| txn.get_external(&h).unwrap().map(|x| x.into()),
*name_dest,
&mut name_buf,
)
.map_err(FsErrorC::Changestore)?;
debug!("{:?}", std::str::from_utf8(&name_buf));
let FileMetadata { basename, .. } = FileMetadata::read(&name_buf);
if basename == c {
let age = txn
.get_changeset(txn.changes(&channel), &name.dest().change)
.unwrap();
if let Some((ref mut next, ref mut next_age)) = next {
ambiguous = true;
if age < *next_age {
*next = name_dest;
*next_age = age;
}
} else {
next = Some((name_dest, age));
}
}
}
if let Some((next, _)) = next {
let current_ = iter_adjacent(txn, txn.graph(channel), *next, flag0, flag1)?
.next()
.unwrap()?
.dest();
if seen.insert(current_) {
current = current_;
break;
}
} else {
return Err(FsErrorC::NotFound(FsNotFound(path.to_string())));
}
}
}
Ok((current, ambiguous))
}
pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
changes: &C,
txn: &T,
channel: &T::Channel,
youngest: bool,
mut v: Position<ChangeId>,
) -> Result<Option<(String, bool)>, crate::output::FileError<C::Error, T>> {
debug!("oldest_path = {:?}", v);
let mut path = Vec::new();
let mut name_buf = Vec::new();
let mut seen = HashSet::new();
let flag0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
let flag1 = EdgeFlags::all();
let mut all_alive = true;
'outer: while !v.change.is_root() {
debug!("path = {:?}", path);
let mut next_v = None;
let mut alive = false;
let inode_vertex = match txn.find_block_end(txn.graph(channel), v) {
Ok(block) => block,
Err(BlockError::Block { block, .. }) => {
debug!("block not found {:?}", block);
assert!(path.is_empty());
return Ok(None);
}
Err(BlockError::Txn(t)) => return Err(crate::output::FileError::Txn(TxnErr(t))),
};
debug!("inode_vertex = {:?}", inode_vertex);
if *inode_vertex != v.inode_vertex() {
info!(
"find_path: {:?} != {:?}, this may be due to a corrupt change",
inode_vertex,
v.inode_vertex()
);
return Ok(None);
}
for name in iter_adjacent(txn, txn.graph(channel), v.inode_vertex(), flag0, flag1)? {
let name = name?;
if name.dest().is_root() {
break 'outer;
}
if !name.flag().contains(EdgeFlags::PARENT) {
continue;
}
debug!("oldest_path, name = {:?}", name);
let age = txn
.get_changeset(txn.changes(&channel), &name.dest().change)?
.unwrap();
let name_dest = txn.find_block_end(txn.graph(channel), name.dest()).unwrap();
debug!("name_dest = {:?}", name_dest);
let mut next = None;
for e in iter_adjacent(txn, txn.graph(channel), *name_dest, flag0, flag1)? {
let e = e?;
if e.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
next = Some(e);
break;
}
}
debug!("next = {:?}", next);
if let Some(next) = next {
debug!("oldest_path, next = {:?}", next);
if !next.flag().contains(EdgeFlags::DELETED) {
alive = true;
} else if alive {
break;
} else {
all_alive = false
}
if let Some((_, p_age, _)) = next_v {
if (age > p_age) ^ youngest {
debug!("youngest");
continue;
}
}
if seen.get(&next.dest()).is_some() {
debug!("seen");
continue;
}
next_v = Some((name_dest, age, next.dest()));
}
}
let (name, _, next) = next_v.unwrap();
seen.insert(next);
if name.start == name.end {
assert!(next.change.is_root());
break;
}
if alive {
name_buf.resize(name.end - name.start, 0);
debug!("getting contents {:?}", name);
let FileMetadata { basename, .. } = changes
.get_file_meta(
|p| txn.get_external(&p).unwrap().map(From::from),
*name,
&mut name_buf,
)
.map_err(crate::output::FileError::Changestore)?;
path.push(basename.to_string());
}
debug!("next = {:?}", next);
v = next;
}
path.reverse();
Ok(Some((path.join("/"), all_alive)))
}
pub fn get_latest_touch<'a, T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>>(
txn: &T,
channel: &T::Channel,
pos: &Position<ChangeId>,
) -> Result<(u64, ChangeId), TxnErr<T::GraphError>> {
let mut latest_change = L64(0);
let mut id = ChangeId::ROOT;
let mut touch_iter = Some(txn.iter_touched(pos)?);
let mut log_iter = changeid_rev_log(txn, &channel, None)?;
let mut continue_ = true;
while continue_ {
continue_ = false;
if let Some(ref mut it) = touch_iter {
if let Some(c) = it.next() {
debug!("inode, change = {:?}", c);
let (inode, change) = c?;
if inode > pos {
touch_iter = None;
} else if inode == pos {
if let Some(&t) = txn.get_changeset(txn.changes(&channel), &change)? {
if t >= latest_change {
latest_change = t;
id = *change;
}
}
continue_ = true;
}
}
}
if let Some(l) = log_iter.next() {
debug!("int = {:?}", l);
let (n, p) = l?;
let touched = txn.get_touched_files(pos, Some(&p.a))?;
if touched == Some(&p.a) {
id = p.a;
latest_change = *n;
break;
}
continue_ = true;
}
}
Ok((latest_change.into(), id))
}