1use crate::HashSet;
18use crate::changestore::*;
19use crate::pristine::*;
20use crate::small_string::*;
21use std::iter::Iterator;
22
23#[derive(Error)]
24pub enum FsError<T: TreeTxnT> {
25 #[error("Inode not found")]
26 InodeNotFound(Inode),
27 #[error(transparent)]
28 NotFound(#[from] FsNotFound),
29 #[error("File already in repository: {0}")]
30 AlreadyInRepo(String),
31 #[error(transparent)]
32 Tree(#[from] TreeErr<T::TreeError>),
33 #[error("Invalid path: {0}")]
34 InvalidPath(String),
35 #[error(transparent)]
36 SmallString(#[from] crate::small_string::Error),
37}
38
39impl<T: TreeTxnT> std::fmt::Debug for FsError<T> {
40 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
41 match self {
42 FsError::InodeNotFound(inode) => write!(fmt, "Inode not found: {}", inode.to_base32()),
43 FsError::NotFound(e) => std::fmt::Debug::fmt(e, fmt),
44 FsError::AlreadyInRepo(e) => write!(fmt, "File already in repository: {}", e),
45 FsError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
46 FsError::InvalidPath(e) => write!(fmt, "Invalid path: {}", e),
47 FsError::SmallString(e) => write!(fmt, "{}", e),
48 }
49 }
50}
51
52#[derive(Error)]
53pub enum FsErrorC<C: std::error::Error + 'static, T: GraphTxnT> {
54 #[error(transparent)]
55 Txn(#[from] TxnErr<T::GraphError>),
56 #[error(transparent)]
57 Changestore(C),
58 #[error(transparent)]
59 NotFound(#[from] FsNotFound),
60}
61
62impl<C: std::error::Error + 'static, T: GraphTxnT> std::fmt::Debug for FsErrorC<C, T> {
63 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
64 match self {
65 FsErrorC::Txn(e) => std::fmt::Debug::fmt(e, fmt),
66 FsErrorC::Changestore(e) => std::fmt::Debug::fmt(e, fmt),
67 FsErrorC::NotFound(e) => std::fmt::Debug::fmt(e, fmt),
68 }
69 }
70}
71
72#[derive(Debug, Error)]
73#[error("Path not found: {0}")]
74pub struct FsNotFound(String);
75
76pub fn create_new_inode<T: TreeMutTxnT>(
77 txn: &mut T,
78 parent_id: &PathId,
79 salt: u64,
80) -> Result<Inode, TreeErr<T::TreeError>> {
81 use std::hash::BuildHasher;
82 let mut i: u64 = crate::Hasher::default().hash_one((parent_id, salt));
83 let mut inode = Inode(L64(i.to_le()));
84 while txn.get_revtree(&inode, None)?.is_some() {
85 i += 1;
86 inode = Inode(L64(i.to_le()));
87 }
88 Ok(inode)
89}
90
91pub fn is_directory<T: TreeTxnT>(txn: &T, inode: Inode) -> Result<bool, TreeErr<T::TreeError>> {
94 if inode == Inode::ROOT {
95 return Ok(true);
96 }
97 for x in txn.iter_tree(&OwnedPathId::inode(inode), None)? {
98 let (pid, _) = x?;
99 if pid.parent_inode < inode {
100 continue;
101 } else if pid.parent_inode > inode {
102 break;
103 }
104 return Ok(true);
105 }
106 Ok(false)
107}
108
109fn closest_in_repo_ancestor<'a, T: TreeTxnT>(
110 txn: &T,
111 path: &'a str,
112) -> Result<(Inode, std::iter::Peekable<crate::path::Components<'a>>), TreeErr<T::TreeError>> {
113 let mut components = crate::path::components(path).peekable();
114 let mut fileid = OwnedPathId::inode(Inode::ROOT);
115 while let Some(c) = components.peek() {
116 trace!("component {:?}", c);
117 fileid.basename.clone_from_str(c);
118 trace!("{:?}", fileid);
119 let mut found = false;
120 for x in txn.iter_tree(&fileid, None)? {
121 let (id, inode) = x?;
122 trace!(
123 "id = {:?}, inode = {:?}, cmp = {:?}",
124 id,
125 inode,
126 id > &fileid
127 );
128 if id > &fileid {
129 break;
130 } else if id < &fileid {
131 continue;
132 }
133 found = true;
134 fileid.parent_inode = *inode;
135 break;
136 }
137 if found {
138 components.next();
139 } else {
140 break;
141 }
142 }
143 Ok((fileid.parent_inode, components))
144}
145
146pub fn find_inode<T: TreeTxnT>(txn: &T, path: &str) -> Result<Inode, FsError<T>> {
148 debug!("find_inode");
149 let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
150 debug!("/find_inode");
151 if let Some(c) = remaining_path_components.next() {
152 debug!("c = {:?}", c);
153 Err(FsNotFound(path.to_string()).into())
154 } else {
155 Ok(inode)
156 }
157}
158
159pub fn is_tracked<T: TreeTxnT>(txn: &T, path: &str) -> Result<bool, TreeErr<T::TreeError>> {
161 debug!("is_tracked {:?}", path);
162 let (_, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
163 debug!("/is_tracked {:?}", path);
164 Ok(remaining_path_components.next().is_none())
165}
166
167pub struct GetVertex {
168 pub remaining: bool,
169 pub result: Option<Position<ChangeId>>,
170}
171
172pub fn get_vertex<T: TreeTxnT>(txn: &T, path: &str) -> Result<GetVertex, TreeErr<T::TreeError>> {
174 debug!("is_tracked {:?}", path);
175 let (inode, mut remaining_path_components) = closest_in_repo_ancestor(txn, path)?;
176 debug!("/is_tracked {:?}", path);
177 if remaining_path_components.next().is_none() {
178 Ok(GetVertex {
179 remaining: true,
180 result: txn.get_inodes(&inode, None)?.cloned(),
181 })
182 } else {
183 Ok(GetVertex {
184 remaining: false,
185 result: None,
186 })
187 }
188}
189
190pub fn inode_filename<T: TreeTxnT>(
192 txn: &T,
193 inode: Inode,
194) -> Result<Option<String>, TreeErr<T::TreeError>> {
195 debug!("inode_filename {:?}", inode);
196 let mut components = Vec::new();
197 let mut current = inode;
198 loop {
199 match txn.get_revtree(¤t, None)? {
200 Some(v) => {
201 components.push(v.basename.to_owned());
202 current = v.parent_inode;
203 if current == Inode::ROOT {
204 break;
205 }
206 }
207 None => {
208 debug!("filename_of_inode: not in tree");
209 return Ok(None);
210 }
211 }
212 }
213
214 let mut path = String::new();
215 for c in components.iter().rev() {
216 if !path.is_empty() {
217 path.push('/')
218 }
219 path.push_str(c.as_str());
220 }
221 debug!("inode_filename = {:?}", path);
222 Ok(Some(path))
223}
224
225pub fn make_new_child<T: TreeMutTxnT>(
228 txn: &mut T,
229 parent_inode: Inode,
230 filename: &str,
231 is_dir: bool,
232 child_inode: Option<Inode>,
233 salt: u64,
234) -> Result<Inode, FsError<T>> {
235 let parent_id = OwnedPathId {
236 parent_inode,
237 basename: filename.parse()?,
238 };
239
240 if let Some(&inode) = txn.get_tree(&parent_id, None)? {
241 debug!("inode = {:?}", inode);
242 if let Some(child) = child_inode {
243 if child == inode {
244 Ok(inode)
246 } else {
247 del_tree_with_rev(txn, &parent_id, &inode)?;
248 if let Some(&vertex) = txn.get_inodes(&inode, None)? {
249 del_inodes_with_rev(txn, &inode, &vertex)?;
250 }
251 put_tree_with_rev(txn, &parent_id, &child)?;
252 Ok(child)
253 }
254 } else {
255 Err(FsError::AlreadyInRepo(filename.to_string()))
256 }
257 } else {
258 let child_inode = match child_inode {
259 None => create_new_inode(txn, &parent_id, salt)?,
260 Some(i) => i,
261 };
262 debug!("make_new_child: {:?} {:?}", parent_id, child_inode);
263 put_tree_with_rev(txn, &parent_id, &child_inode)?;
264
265 if is_dir {
266 let dir_id = OwnedPathId {
267 parent_inode: child_inode,
268 basename: SmallString::new(),
269 };
270 txn.put_tree(&dir_id, &child_inode)?;
271 };
272 Ok(child_inode)
273 }
274}
275
276pub fn add_inode<T: TreeMutTxnT>(
277 txn: &mut T,
278 inode: Option<Inode>,
279 path: &str,
280 is_dir: bool,
281 salt: u64,
282) -> Result<Inode, FsError<T>> {
283 debug!("add_inode");
284 if let Some(parent) = crate::path::parent(path) {
285 let (current_inode, unrecorded_path) = closest_in_repo_ancestor(txn, parent)?;
286 let mut current_inode = current_inode;
287 debug!("add_inode: closest = {:?}", current_inode);
288 for c in unrecorded_path {
289 debug!("unrecorded: {:?}", c);
290 current_inode = make_new_child(txn, current_inode, c, true, None, salt)?;
291 }
292 let file_name = crate::path::file_name(path).unwrap();
293 debug!("add_inode: file_name = {:?}", file_name);
294 current_inode = make_new_child(txn, current_inode, file_name, is_dir, inode, salt)?;
295 Ok(current_inode)
296 } else {
297 Err(FsError::InvalidPath(path.to_string()))
298 }
299}
300
301pub fn move_file<T: TreeMutTxnT>(
307 txn: &mut T,
308 origin: &str,
309 destination: &str,
310 salt: u64,
311) -> Result<(), FsError<T>> {
312 debug!("move_file: {},{}", origin, destination);
313 move_file_by_inode(txn, find_inode(txn, origin)?, destination, salt)?;
314 Ok(())
315}
316
317pub fn move_file_by_inode<T: TreeMutTxnT>(
318 txn: &mut T,
319 inode: Inode,
320 destination: &str,
321 salt: u64,
322) -> Result<(), FsError<T>> {
323 debug!("inode = {:?}", inode);
324 let fileref = if let Some(inode) = txn.get_revtree(&inode, None)? {
325 inode.to_owned()
326 } else {
327 return Err(FsError::InodeNotFound(inode));
328 };
329 debug!("fileref = {:?}", fileref);
330 del_tree_with_rev(txn, &fileref, &inode)?;
331 debug!("inode={:?} destination={}", inode, destination);
332 let is_dir = txn
333 .get_tree(
334 &OwnedPathId {
335 parent_inode: inode,
336 basename: SmallString::new(),
337 },
338 None,
339 )?
340 .is_some();
341 add_inode(txn, Some(inode), destination, is_dir, salt)?;
342 Ok(())
343}
344
345pub(crate) fn rec_delete<T: TreeMutTxnT>(
346 txn: &mut T,
347 parent: &PathId,
348 inode: Inode,
349 delete_inodes: bool,
350) -> Result<(), FsError<T>> {
351 let file_id = OwnedPathId {
352 parent_inode: inode,
353 basename: SmallString::new(),
354 };
355 let mut children = Vec::new();
356 let mut is_dir = false;
357 for x in txn.iter_tree(&file_id, None)? {
358 let (k, inode) = x?;
359 assert!(k.parent_inode >= file_id.parent_inode);
360 if k.parent_inode > file_id.parent_inode {
361 break;
362 }
363 debug!("iter_tree: {:?} {:?}", k, inode);
364 is_dir = true;
365 if !k.basename.is_empty() {
366 children.push((k.to_owned(), *inode))
367 }
368 }
369 for (k, inode_) in children {
370 assert_ne!(inode, inode_);
371 rec_delete(txn, &k, inode_, delete_inodes)?;
372 }
373 debug!(
374 "rec_delete: {:?}, {:?}, {:?}, {:?}",
375 parent, file_id, inode, is_dir
376 );
377 if is_dir {
378 assert!(inode.is_root() || txn.del_tree(&file_id, Some(&inode))?);
379 }
380 if !inode.is_root() && del_tree_with_rev(txn, parent, &inode)? {
381 if delete_inodes && let Some(&vertex) = txn.get_inodes(&inode, None)? {
382 del_inodes_with_rev(txn, &inode, &vertex)?;
383 }
384 } else {
385 debug!("rec_delete: {:?} {:?} not present", parent, inode);
386 }
387 Ok(())
388}
389
390pub fn remove_file<T: TreeMutTxnT>(txn: &mut T, path: &str) -> Result<(), FsError<T>> {
392 debug!("remove file {:?}", path);
393 let inode = find_inode(txn, path)?;
394 let parent = if inode.is_root() {
395 OwnedPathId {
396 parent_inode: Inode::ROOT,
397 basename: SmallString::new(),
398 }
399 } else {
400 txn.get_revtree(&inode, None)?.unwrap().to_owned()
401 };
402 debug!("remove file {:?} {:?}", parent, inode);
403 rec_delete(txn, &parent, inode, false)?;
404 Ok(())
405}
406
407pub struct WorkingCopyChildren<'txn, T: TreeTxnT> {
413 iter: crate::pristine::Cursor<T, &'txn T, T::TreeCursor, PathId, Inode>,
414 inode: Inode,
415}
416
417impl<'txn, T: TreeTxnT> Iterator for WorkingCopyChildren<'txn, T> {
418 type Item = Result<(SmallString, Inode), T::TreeError>;
419 fn next(&mut self) -> Option<Self::Item> {
420 loop {
421 match self.iter.next() {
422 Some(Ok((k, v))) => {
423 if k.parent_inode == self.inode {
424 if !k.basename.is_empty() {
425 return Some(Ok((k.basename.to_owned(), *v)));
426 }
427 } else if k.parent_inode > self.inode {
428 return None;
429 }
430 }
431 None => return None,
432 Some(Err(e)) => return Some(Err(e.0)),
433 }
434 }
435 }
436}
437
438pub fn working_copy_children<'a, T: TreeTxnT>(
440 txn: &'a T,
441 inode: Inode,
442) -> Result<WorkingCopyChildren<'a, T>, T::TreeError> {
443 Ok(WorkingCopyChildren {
444 iter: txn
445 .iter_tree(
446 &OwnedPathId {
447 parent_inode: inode,
448 basename: SmallString::new(),
449 },
450 None,
451 )
452 .map_err(|e| e.0)?,
453 inode,
454 })
455}
456
457pub struct WorkingCopyIterator<'txn, T: TreeTxnT> {
461 stack: Vec<(Inode, String)>,
462 txn: &'txn T,
463}
464
465impl<'txn, T: TreeTxnT> Iterator for WorkingCopyIterator<'txn, T> {
466 type Item = Result<(Inode, String, bool), T::TreeError>;
467 fn next(&mut self) -> Option<Self::Item> {
468 loop {
469 if let Some((inode, name)) = self.stack.pop() {
470 let fileid = OwnedPathId {
471 parent_inode: inode,
472 basename: SmallString::default(),
473 };
474 let len = self.stack.len();
475 let iter = match self.txn.iter_tree(&fileid, None) {
476 Ok(iter) => iter,
477 Err(e) => return Some(Err(e.0)),
478 };
479 let mut is_folder = false;
480 for x in iter {
481 let (k, v) = match x {
482 Ok(x) => x,
483 Err(e) => return Some(Err(e.0)),
484 };
485 if k.parent_inode < inode {
486 continue;
487 } else if k.parent_inode > inode {
488 break;
489 }
490 is_folder = true;
491 if !k.basename.is_empty() {
492 let mut name = name.clone();
493 crate::path::push(&mut name, k.basename.as_str());
494 self.stack.push((*v, name))
495 }
496 }
497 (self.stack[len..]).reverse();
498 if !name.is_empty() {
499 return Some(Ok((inode, name, is_folder)));
500 }
501 } else {
502 return None;
503 }
504 }
505 }
506}
507
508pub fn iter_working_copy<'a, T: TreeTxnT>(txn: &'a T, root: Inode) -> WorkingCopyIterator<'a, T> {
510 WorkingCopyIterator {
511 stack: vec![(root, String::new())],
512 txn,
513 }
514}
515
516pub struct GraphDescendants<'txn, T: GraphTxnT> {
522 txn: &'txn T,
523 channel: &'txn T::Graph,
524 stack: Vec<AdjacentIterator<'txn, T>>,
525 visited: HashSet<Position<ChangeId>>,
526}
527
528impl<'txn, T: GraphTxnT> Iterator for GraphDescendants<'txn, T> {
529 type Item = Result<Position<ChangeId>, T::GraphError>;
530 fn next(&mut self) -> Option<Self::Item> {
531 while let Some(mut adj) = self.stack.pop() {
532 match adj.next() {
533 Some(Ok(child)) => {
534 self.stack.push(adj);
535 let dest = match self.txn.find_block(self.channel, child.dest()) {
536 Ok(dest) => dest,
537 Err(BlockError::Txn(t)) => return Some(Err(t)),
538 Err(e) => panic!("{}", e),
539 };
540 let grandchild = match iter_adjacent(
541 self.txn,
542 self.channel,
543 *dest,
544 EdgeFlags::FOLDER,
545 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
546 ) {
547 Ok(mut x) => match x.next().unwrap() {
548 Ok(x) => x,
549 Err(e) => return Some(Err(e.0)),
550 },
551 Err(e) => return Some(Err(e.0)),
552 };
553
554 if self.visited.insert(grandchild.dest()) {
555 match iter_adjacent(
556 self.txn,
557 self.channel,
558 grandchild.dest().inode_vertex(),
559 EdgeFlags::FOLDER,
560 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
561 ) {
562 Ok(adj) => self.stack.push(adj),
563 Err(e) => return Some(Err(e.0)),
564 }
565 }
566 return Some(Ok(grandchild.dest()));
567 }
568 Some(Err(e)) => return Some(Err(e.0)),
569 None => {
570 }
572 }
573 }
574 None
575 }
576}
577
578pub fn iter_graph_descendants<'txn, T: GraphTxnT>(
581 txn: &'txn T,
582 channel: &'txn T::Graph,
583 key: Position<ChangeId>,
584) -> Result<GraphDescendants<'txn, T>, T::GraphError> {
585 Ok(GraphDescendants {
586 stack: vec![
587 iter_adjacent(
588 txn,
589 channel,
590 key.inode_vertex(),
591 EdgeFlags::FOLDER,
592 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
593 )
594 .map_err(|e| e.0)?,
595 ],
596 visited: HashSet::default(),
597 txn,
598 channel,
599 })
600}
601
602pub struct GraphChildren<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
608 txn: &'txn T,
609 channel: &'txn T::Graph,
610 adj: AdjacentIterator<'txn, T>,
611 adj2: Option<AdjacentIterator<'txn, T>>,
612 changes: &'changes P,
613 buf: Vec<u8>,
614}
615
616impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
617 for GraphChildren<'txn, 'changes, T, P>
618{
619 type Item = Result<(Position<ChangeId>, ChangeId, InodeMetadata, String), T::GraphError>;
620 fn next(&mut self) -> Option<Self::Item> {
621 let dest = loop {
622 debug!("adj2 = {:?}", self.adj2.is_some());
623 if let Some(mut adj2) = self.adj2.take() {
624 match adj2.next() {
625 None => {}
626 Some(Ok(ch)) => {
627 self.adj2 = Some(adj2);
628 break self.txn.find_block(self.channel, ch.dest()).unwrap();
629 }
630 Some(Err(e)) => return Some(Err(e.0)),
631 }
632 }
633 match self.adj.next() {
634 Some(Ok(child)) => {
635 let d = self.txn.find_block(self.channel, child.dest()).unwrap();
636 if d.start == d.end {
637 match iter_adjacent(
638 self.txn,
639 self.channel,
640 *d,
641 EdgeFlags::FOLDER,
642 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
643 )
644 .and_then(|mut it| it.next().unwrap())
645 .and_then(|x| {
646 iter_adjacent(
647 self.txn,
648 self.channel,
649 x.dest().inode_vertex(),
650 EdgeFlags::FOLDER,
651 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
652 )
653 }) {
654 Ok(adj) => self.adj2 = Some(adj),
655 Err(e) => return Some(Err(e.0)),
656 }
657 } else {
658 break d;
659 }
660 }
661 Some(Err(e)) => return Some(Err(e.0)),
662 None => return None,
663 }
664 };
665 debug!("dest = {:?}", dest);
666
667 let mut buf = std::mem::take(&mut self.buf);
668 buf.resize(dest.end - dest.start, 0);
669 let FileMetadata {
670 basename,
671 metadata: perms,
672 ..
673 } = self
674 .changes
675 .get_file_meta(
676 |p| self.txn.get_external(&p).ok().map(|x| x.into()),
677 *dest,
678 &mut buf,
679 )
680 .unwrap();
681 let basename = basename.to_string();
682 self.buf = buf;
683
684 let grandchild = match iter_adjacent(
685 self.txn,
686 self.channel,
687 *dest,
688 EdgeFlags::FOLDER,
689 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
690 ) {
691 Ok(mut adj) => match adj.next() {
692 Some(Ok(n)) => n,
693 None => unreachable!(),
694 Some(Err(e)) => return Some(Err(e.0)),
695 },
696 Err(e) => return Some(Err(e.0)),
697 };
698 Some(Ok((
699 grandchild.dest(),
700 grandchild.introduced_by(),
701 perms,
702 basename,
703 )))
704 }
705}
706
707pub fn iter_graph_children<'txn, 'changes, T, P>(
710 txn: &'txn T,
711 changes: &'changes P,
712 channel: &'txn T::Graph,
713 key: Position<ChangeId>,
714) -> Result<GraphChildren<'txn, 'changes, T, P>, T::GraphError>
715where
716 T: GraphTxnT,
717 P: ChangeStore,
718{
719 Ok(GraphChildren {
720 buf: Vec::new(),
721 adj: iter_adjacent(
722 txn,
723 channel,
724 key.inode_vertex(),
725 EdgeFlags::FOLDER,
726 EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
727 )
728 .map_err(|e| e.0)?,
729 adj2: None,
730 txn,
731 channel,
732 changes,
733 })
734}
735
736pub struct GraphBasenames<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> {
740 txn: &'txn T,
741 channel: &'txn T::Graph,
742 adj: AdjacentIterator<'txn, T>,
743 changes: &'changes P,
744 buf: Vec<u8>,
745}
746
747impl<'txn, 'changes, T: GraphTxnT, P: ChangeStore + 'changes> Iterator
748 for GraphBasenames<'txn, 'changes, T, P>
749{
750 type Item = Result<(Position<ChangeId>, InodeMetadata, String), T::GraphError>;
751 fn next(&mut self) -> Option<Self::Item> {
752 loop {
753 let parent = match self.adj.next() {
754 Some(Ok(n)) => n,
755 Some(Err(e)) => return Some(Err(e.0)),
756 None => return None,
757 };
758 let dest = self
759 .txn
760 .find_block_end(self.channel, parent.dest())
761 .unwrap();
762 if dest.start == dest.end {
763 return None;
765 }
766 let mut buf = std::mem::take(&mut self.buf);
767 buf.resize(dest.end - dest.start, 0);
768 let FileMetadata {
769 basename,
770 metadata: perms,
771 ..
772 } = self
773 .changes
774 .get_file_meta(
775 |p| self.txn.get_external(&p).ok().map(|x| x.into()),
776 *dest,
777 &mut buf,
778 )
779 .unwrap();
780 let basename = basename.to_owned();
781 self.buf = buf;
782 match iter_adjacent(
783 self.txn,
784 self.channel,
785 *dest,
786 EdgeFlags::FOLDER | EdgeFlags::PARENT,
787 EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
788 ) {
789 Ok(mut adj) => match adj.next() {
790 Some(Ok(grandparent)) => {
791 return Some(Ok((grandparent.dest(), perms, basename)));
792 }
793 Some(Err(e)) => return Some(Err(e.0)),
794 None => {}
795 },
796 Err(e) => return Some(Err(e.0)),
797 }
798 }
799 }
800}
801
802pub fn iter_basenames<'txn, 'changes, T, P>(
807 txn: &'txn T,
808 changes: &'changes P,
809 channel: &'txn T::Graph,
810 pos: Position<ChangeId>,
811) -> Result<GraphBasenames<'txn, 'changes, T, P>, T::GraphError>
812where
813 T: GraphTxnT,
814 P: ChangeStore,
815{
816 Ok(GraphBasenames {
817 buf: Vec::new(),
818 adj: iter_adjacent(
819 txn,
820 channel,
821 pos.inode_vertex(),
822 EdgeFlags::FOLDER | EdgeFlags::PARENT,
823 EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
824 )
825 .map_err(|e| e.0)?,
826 txn,
827 channel,
828 changes,
829 })
830}
831
832pub fn iter_paths<T: GraphTxnT, F: FnMut(&mut dyn Iterator<Item = Position<ChangeId>>) -> bool>(
842 txn: &T,
843 graph: &T::Graph,
844 key: Position<ChangeId>,
845 mut f: F,
846) -> Result<(), TxnErr<T::GraphError>> {
847 let mut stack: Vec<(Position<ChangeId>, bool)> = vec![(key, true)];
848 while let Some((cur_key, on_stack)) = stack.pop() {
849 if cur_key.is_root() {
850 if !f(&mut stack
851 .iter()
852 .filter_map(|(key, on_path)| if *on_path { Some(*key) } else { None }))
853 {
854 break;
855 }
856 } else if !on_stack {
857 stack.push((cur_key, true));
858 let len = stack.len();
859 let f = EdgeFlags::parent_folder();
860 for parent in
861 iter_adjacent(txn, graph, cur_key.inode_vertex(), f, f | EdgeFlags::PSEUDO)?
862 {
863 let parent_dest = txn.find_block_end(graph, parent?.dest()).unwrap();
864 for grandparent in
865 iter_adjacent(txn, graph, *parent_dest, f, f | EdgeFlags::PSEUDO)?
866 {
867 stack.push((grandparent?.dest(), false))
868 }
869 }
870 if stack.len() == len {
871 stack.pop();
872 }
873 }
874 }
875 Ok(())
876}
877
878pub(crate) fn follow_oldest_path<T: ChannelTxnT, C: ChangeStore>(
879 changes: &C,
880 txn: &T,
881 channel: &T::Channel,
882 path: &str,
883) -> Result<(Position<ChangeId>, bool), FsErrorC<C::Error, T>> {
884 use crate::pristine::*;
885 debug!("follow_oldest_path = {:?}", path);
886 let mut current = Position::ROOT;
887 let flag0 = EdgeFlags::FOLDER;
888 let flag1 = flag0 | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
889 let mut name_buf = Vec::new();
890 let mut ambiguous = false;
891 let mut seen = HashSet::new();
892 for c in crate::path::components(path) {
893 'outer: loop {
894 let mut next = None;
895 debug!("follow_oldest_path, loop {:?}", current);
896 for name in iter_adjacent(
897 txn,
898 txn.graph(channel),
899 current.inode_vertex(),
900 flag0,
901 flag1,
902 )? {
903 let name = name?;
904 let name_dest = txn.find_block(txn.graph(channel), name.dest()).unwrap();
905 debug!("name_dest = {:?}", name_dest);
906 if name_dest.start == name_dest.end {
907 current = iter_adjacent(txn, txn.graph(channel), *name_dest, flag0, flag1)?
909 .next()
910 .unwrap()?
911 .dest();
912 continue 'outer;
913 }
914 name_buf.resize(name_dest.end - name_dest.start, 0);
915 debug!("getting contents {:?}", name);
916 changes
917 .get_contents(
918 |h| txn.get_external(&h).ok().map(|x| x.into()),
919 *name_dest,
920 &mut name_buf,
921 )
922 .map_err(FsErrorC::Changestore)?;
923 debug!("{:?}", std::str::from_utf8(&name_buf));
924 let FileMetadata { basename, .. } = FileMetadata::read(&name_buf);
925 if basename == c {
926 let age = txn
927 .get_changeset(txn.changes(channel), &name.dest().change)
928 .unwrap();
929 if let Some((ref mut next, ref mut next_age)) = next {
930 ambiguous = true;
931 if age < *next_age {
932 *next = name_dest;
933 *next_age = age;
934 }
935 } else {
936 next = Some((name_dest, age));
937 }
938 }
939 }
940 if let Some((next, _)) = next {
941 let current_ = iter_adjacent(txn, txn.graph(channel), *next, flag0, flag1)?
942 .next()
943 .unwrap()?
944 .dest();
945 if seen.insert(current_) {
946 current = current_;
947 break;
948 }
949 } else {
950 return Err(FsErrorC::NotFound(FsNotFound(path.to_string())));
951 }
952 }
953 }
954 Ok((current, ambiguous))
955}
956
957pub struct FindPath {
958 pub path: Vec<String>,
959 pub all_alive: bool,
960}
961
962pub fn find_path<T: ChannelTxnT, C: ChangeStore>(
965 changes: &C,
966 txn: &T,
967 channel: &T::Channel,
968 youngest: bool,
969 mut v: Position<ChangeId>,
970) -> Result<Option<FindPath>, crate::output::FileError<C::Error, T>> {
971 debug!("oldest_path = {:?}", v);
972 let mut path = Vec::new();
973 let mut name_buf = Vec::new();
974 let mut seen = HashSet::new();
975 let flag0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
976 let flag1 = EdgeFlags::all();
977 let mut all_alive = true;
978 'outer: while !v.change.is_root() {
979 debug!("path = {:?}", path);
980 let mut next_v = None;
981 let mut alive = false;
982 let inode_vertex = match txn.find_block_end(txn.graph(channel), v) {
983 Ok(block) => block,
984 Err(BlockError::Block { block, .. }) => {
985 debug!("block not found {:?}", block);
986 assert!(path.is_empty());
987 return Ok(None);
988 }
989 Err(BlockError::Txn(t)) => return Err(crate::output::FileError::Txn(TxnErr(t))),
990 };
991 debug!("inode_vertex = {:?}", inode_vertex);
992 if *inode_vertex != v.inode_vertex() {
993 info!(
994 "find_path: {:?} != {:?}, this may be due to a corrupt change",
995 inode_vertex,
996 v.inode_vertex()
997 );
998 return Ok(None);
999 }
1000 for name in iter_adjacent(txn, txn.graph(channel), v.inode_vertex(), flag0, flag1)? {
1001 let name = name?;
1002 if name.dest().is_root() {
1003 break 'outer;
1004 }
1005 if !name.flag().contains(EdgeFlags::PARENT) {
1006 continue;
1007 }
1008
1009 debug!("oldest_path, name = {:?}", name);
1010 let age = txn
1011 .get_changeset(txn.changes(channel), &name.dest().change)?
1012 .unwrap();
1013
1014 let name_dest = txn.find_block_end(txn.graph(channel), name.dest()).unwrap();
1015 debug!("name_dest = {:?}", name_dest);
1016 let mut next = None;
1017 for e in iter_adjacent(txn, txn.graph(channel), *name_dest, flag0, flag1)? {
1018 let e = e?;
1019 if e.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER) {
1020 next = Some(e);
1021 break;
1022 }
1023 }
1024 debug!("next = {:?}", next);
1025 if let Some(next) = next {
1026 debug!("oldest_path, next = {:?}", next);
1027 if !next.flag().contains(EdgeFlags::DELETED) {
1028 alive = true;
1029 } else if alive {
1030 break;
1031 } else {
1032 all_alive = false
1033 }
1034 if let Some((_, p_age, _)) = next_v
1035 && (age > p_age) ^ youngest
1036 {
1037 debug!("youngest");
1038 continue;
1039 }
1040 if seen.contains(&next.dest()) {
1041 debug!("seen");
1042 continue;
1043 }
1044 next_v = Some((name_dest, age, next.dest()));
1045 }
1046 }
1047 let (name, _, next) = next_v.unwrap();
1048 seen.insert(next);
1049 if name.start == name.end {
1050 assert!(next.change.is_root());
1052 break;
1053 }
1054 if alive {
1055 name_buf.resize(name.end - name.start, 0);
1056 debug!("getting contents {:?}", name);
1057
1058 let FileMetadata { basename, .. } = changes
1059 .get_file_meta(
1060 |p| txn.get_external(&p).ok().map(From::from),
1061 *name,
1062 &mut name_buf,
1063 )
1064 .map_err(crate::output::FileError::Changestore)?;
1065
1066 path.push(basename.to_string());
1067 }
1068 debug!("next = {:?}", next);
1069 v = next;
1070 }
1071 path.reverse();
1072 Ok(Some(FindPath { path, all_alive }))
1073}
1074
1075pub fn get_latest_touch<T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>>(
1076 txn: &T,
1077 channel: &T::Channel,
1078 pos: &Position<ChangeId>,
1079) -> Result<(u64, ChangeId), TxnErr<T::GraphError>> {
1080 let mut latest_change = L64(0);
1081 let mut id = ChangeId::ROOT;
1082 let mut touch_iter = Some(txn.iter_touched(pos)?);
1083 let mut log_iter = changeid_rev_log(txn, channel, None)?;
1084 let mut continue_ = true;
1085 while continue_ {
1086 continue_ = false;
1087 if let Some(ref mut it) = touch_iter
1088 && let Some(c) = it.next()
1089 {
1090 debug!("inode, change = {:?}", c);
1091 let (inode, change) = c?;
1092 if inode > pos {
1093 touch_iter = None;
1094 } else if inode == pos
1095 && let Some(&t) = txn.get_changeset(txn.changes(channel), change)?
1096 {
1097 if t >= latest_change {
1098 latest_change = t;
1099 id = *change;
1100 }
1101 continue_ = true;
1102 }
1103 }
1104 if let Some(l) = log_iter.next() {
1105 debug!("int = {:?}", l);
1106 let (n, p) = l?;
1107 let touched = txn.get_touched_files(pos, Some(&p.a))?;
1108 if touched == Some(&p.a) {
1109 id = p.a;
1110 latest_change = *n;
1111 break;
1112 }
1113 continue_ = true;
1114 }
1115 }
1116 Ok((latest_change.into(), id))
1117}