1use backend::*;
2use patch::*;
3use record::InodeUpdate;
4use {ErrorKind, Result};
5
6use rand;
7use std;
8use std::borrow::Cow;
9use std::collections::{HashMap, HashSet};
10use std::fs;
11use std::path::{Path, PathBuf};
12use tempdir;
13
14#[cfg(not(windows))]
15use std::os::unix::fs::PermissionsExt;
16
17#[cfg(not(windows))]
18fn set_permissions(name: &Path, permissions: u16) -> Result<()> {
19 let metadata = std::fs::metadata(&name)?;
20 let mut current = metadata.permissions();
21 debug!(
22 "setting mode for {:?} to {:?} (currently {:?})",
23 name, permissions, current
24 );
25 current.set_mode(permissions as u32);
26 std::fs::set_permissions(name, current)?;
27 Ok(())
28}
29
30#[cfg(windows)]
31fn set_permissions(_name: &Path, _permissions: u16) -> Result<()> {
32 Ok(())
33}
34
35#[derive(Debug)]
36struct OutputItem {
37 parent: Inode,
38 meta: FileMetadata,
39 key: Key<PatchId>,
40 inode: Option<Inode>,
41 is_zombie: bool,
42 related: Related,
43}
44
45#[derive(Debug, PartialEq, Eq)]
46pub enum Related {
47 No,
48 Ancestor,
49 Exact,
50}
51
52fn is_related(prefixes: &Prefixes, key: Key<PatchId>) -> Related {
53 if prefixes.0.is_empty() {
54 return Related::Exact;
55 }
56 for pref in prefixes.0.iter() {
57 let mut is_first = true;
58 for &p in pref {
59 if p == key {
60 if is_first {
61 return Related::Exact;
62 } else {
63 return Related::Ancestor;
64 }
65 }
66 is_first = false
67 }
68 }
69 Related::No
70}
71
72impl<'env, T: rand::Rng> MutTxn<'env, T> {
73 fn filename_of_inode(&self, inode: Inode, working_copy: &Path) -> Option<PathBuf> {
75 let mut components = Vec::new();
76 let mut current = inode;
77 loop {
78 match self.get_revtree(current) {
79 Some(v) => {
80 components.push(v.basename.to_owned());
81 current = v.parent_inode.clone();
82 if current == ROOT_INODE {
83 break;
84 }
85 }
86 None => {
87 debug!("filename_of_inode: not in tree");
88 return None;
89 }
90 }
91 }
92 let mut working_copy = working_copy.to_path_buf();
93 for c in components.iter().rev() {
94 working_copy.push(c.as_small_str().as_str());
95 }
96 Some(working_copy)
97 }
98
99 fn collect_children(
101 &mut self,
102 branch: &Branch,
103 path: &Path,
104 key: Key<PatchId>,
105 inode: Inode,
106 base_path: &mut PathBuf,
107 prefixes: &Prefixes,
108 files: &mut HashMap<PathBuf, HashMap<Key<PatchId>, OutputItem>>,
109 ) -> Result<()> {
110 debug!("collect_children {:?}", base_path);
111 let e = Edge::zero(EdgeFlags::FOLDER_EDGE);
112 for (_, b) in self.iter_nodes(&branch, Some((key, Some(&e))))
113 .take_while(|&(k, b)| {
114 k == key
115 && b.flag
116 <= EdgeFlags::FOLDER_EDGE | EdgeFlags::PSEUDO_EDGE | EdgeFlags::EPSILON_EDGE
117 }) {
118 debug!("b={:?}", b);
119 let cont_b = self.get_contents(b.dest).unwrap();
120 let (_, b_key) = self.iter_nodes(&branch, Some((b.dest, Some(&e))))
121 .next()
122 .unwrap();
123 let b_inode = self.get_revinodes(b_key.dest);
124
125 if cont_b.as_slice().len() < 2 {
128 error!("cont_b {:?} b.dest {:?}", cont_b, b.dest);
129 return Err(ErrorKind::WrongFileHeader(b.dest).into());
130 }
131 let (perms, basename) = cont_b.as_slice().split_at(2);
132
133 let perms = FileMetadata::from_contents(perms);
134 let basename = std::str::from_utf8(basename).unwrap();
135 debug!("filename: {:?} {:?}", perms, basename);
136 let name = path.join(basename);
137 let related = is_related(&prefixes, b_key.dest);
138 debug!("related {:?} = {:?}", base_path, related);
139 if related != Related::No {
140 let v = files.entry(name).or_insert(HashMap::new());
141 if v.get(&b.dest).is_none() {
142 let is_zombie = {
143 let e = Edge::zero(
144 EdgeFlags::FOLDER_EDGE | EdgeFlags::PARENT_EDGE
145 | EdgeFlags::DELETED_EDGE,
146 );
147 self.iter_nodes(&branch, Some((b_key.dest, Some(&e))))
148 .take_while(|&(k, b)| k == b_key.dest && b.flag == e.flag)
149 .next()
150 .is_some()
151 };
152 debug!("is_zombie = {:?}", is_zombie);
153 v.insert(
154 b.dest,
155 OutputItem {
156 parent: inode,
157 meta: perms,
158 key: b_key.dest,
159 inode: b_inode,
160 is_zombie,
161 related,
162 },
163 );
164 }
165 }
166 }
167 Ok(())
168 }
169
170 pub fn list_conflict_files(
175 &mut self,
176 branch_name: &str,
177 prefixes: &[&str],
178 ) -> Result<Vec<PathBuf>> {
179 let mut files = HashMap::new();
180 let mut next_files = HashMap::new();
181 let branch = self.open_branch(branch_name)?;
182 let mut base_path = PathBuf::new();
183 let prefixes = prefixes.to_prefixes(self, &branch);
184 self.collect_children(
185 &branch,
186 "".as_ref(),
187 ROOT_KEY,
188 ROOT_INODE,
189 &mut base_path,
190 &prefixes,
191 &mut files,
192 )?;
193
194 let mut ret = vec![];
195 let mut forward = Vec::new();
196 while !files.is_empty() {
197 next_files.clear();
198 for (a, b) in files.drain() {
199 for (_, output_item) in b {
200 if let Some(inode) = output_item.inode {
203 if output_item.is_zombie {
204 ret.push(a.clone())
205 }
206 if output_item.meta.is_dir() {
207 self.collect_children(
208 &branch,
209 &a,
210 output_item.key,
211 inode,
212 &mut base_path,
213 &prefixes,
214 &mut next_files,
215 )?;
216 } else {
217 let mut graph = self.retrieve(&branch, output_item.key);
218 let mut buf = std::io::sink();
219 if self.output_file(&branch, &mut buf, &mut graph, &mut forward)? {
220 ret.push(a.clone())
221 }
222 }
223 }
224 }
225 }
226 std::mem::swap(&mut files, &mut next_files);
227 }
228 Ok(ret)
229 }
230
231 fn make_conflicting_name(&self, name: &mut PathBuf, name_key: Key<PatchId>) {
232 let basename = {
233 let basename = name.file_name().unwrap().to_string_lossy();
234 format!("{}.{}", basename, &name_key.patch.to_base58())
235 };
236 name.set_file_name(&basename);
237 }
238
239 fn output_alive_files(
240 &mut self,
241 branch: &mut Branch,
242 prefixes: &Prefixes,
243 working_copy: &Path,
244 ) -> Result<()> {
245 debug!("working copy {:?}", working_copy);
246 let mut files = HashMap::new();
247 let mut next_files = HashMap::new();
248 let mut base_path = PathBuf::new();
249 self.collect_children(
250 branch,
251 "".as_ref(),
252 ROOT_KEY,
253 ROOT_INODE,
254 &mut base_path,
255 prefixes,
256 &mut files,
257 )?;
258
259 let mut done = HashSet::new();
260
261 while !files.is_empty() {
262 debug!("files {:?}", files);
263 next_files.clear();
264 for (a, b) in files.drain() {
265 let b_len = b.len();
266 for (name_key, output_item) in b {
267 if !done.insert(output_item.key) {
278 debug!("already done {:?}", output_item.key);
279 continue;
280 }
281
282 let mut name = if b_len > 1
283 {
285 let mut name = a.clone();
287 self.make_conflicting_name(&mut name, name_key);
288 Cow::Owned(name)
289 } else {
290 Cow::Borrowed(&a)
291 };
292 let file_name = name.file_name().unwrap().to_string_lossy();
293 base_path.push(Path::new(file_name.as_ref()));
294 let file_id = OwnedFileId {
295 parent_inode: output_item.parent,
296 basename: SmallString::from_str(&file_name),
297 };
298 let working_copy_name = working_copy.join(name.as_ref());
299
300 let status = if output_item.is_zombie {
301 FileStatus::Zombie
302 } else {
303 FileStatus::Ok
304 };
305
306 let inode = if let Some(inode) = output_item.inode {
307 if let Some(ref current_name) = self.filename_of_inode(inode, "".as_ref()) {
311 if current_name != name.as_ref() {
312 let current_name = working_copy.join(current_name);
313 debug!("renaming {:?} to {:?}", current_name, working_copy_name);
314 let parent = self.get_revtree(inode).unwrap().to_owned();
315 self.del_revtree(inode, None)?;
316 self.del_tree(&parent.as_file_id(), None)?;
317
318 debug!("file_id: {:?}", file_id);
319 if let Some(p) = working_copy_name.parent() {
320 std::fs::create_dir_all(p)?
321 }
322 if let Err(e) = std::fs::rename(¤t_name, &working_copy_name) {
323 error!(
324 "while renaming {:?} to {:?}: {:?}",
325 current_name, working_copy_name, e
326 )
327 }
328 }
329 }
330 self.put_tree(&file_id.as_file_id(), inode)?;
331 self.put_revtree(inode, &file_id.as_file_id())?;
332 if let Some(header) = self.get_inodes(inode) {
334 debug!("header {:?}", header);
335 let mut header = header.to_owned();
336 header.status = status;
337 self.replace_inodes(inode, header)?;
338 } else {
339 let header = FileHeader {
340 key: output_item.key,
341 metadata: output_item.meta,
342 status,
343 };
344 debug!("no header {:?}", header);
345 self.replace_inodes(inode, header)?;
346 self.replace_revinodes(output_item.key, inode)?;
347 }
348 inode
349 } else {
350 let inode = self.create_new_inode();
352 let file_header = FileHeader {
353 key: output_item.key,
354 metadata: output_item.meta,
355 status,
356 };
357 self.replace_inodes(inode, file_header)?;
358 self.replace_revinodes(output_item.key, inode)?;
359 debug!("file_id: {:?}", file_id);
360 self.put_tree(&file_id.as_file_id(), inode)?;
361 self.put_revtree(inode, &file_id.as_file_id())?;
362 inode
363 };
364 if output_item.meta.is_dir() {
365 std::fs::create_dir_all(&working_copy_name)?;
367 if let Related::Exact = output_item.related {
368 self.collect_children(
369 branch,
370 &name,
371 output_item.key,
372 inode,
373 &mut base_path,
374 &Prefixes(Vec::new()),
375 &mut next_files,
376 )?
377 } else {
378 self.collect_children(
379 branch,
380 &name,
381 output_item.key,
382 inode,
383 &mut base_path,
384 prefixes,
385 &mut next_files,
386 )?
387 }
388 } else {
389 debug!(
391 "creating file {:?}, key {:?} {:?}",
392 &name, output_item.key, working_copy_name
393 );
394 let mut f = std::fs::File::create(&working_copy_name).unwrap();
395 debug!("done");
396 let mut l = self.retrieve(branch, output_item.key);
397 let mut forward = Vec::new();
398 self.output_file(branch, &mut f, &mut l, &mut forward)?;
399 self.remove_redundant_edges(branch, &forward)?
400 }
401 base_path.pop();
402 set_permissions(&working_copy_name, output_item.meta.permissions())?
403 }
404 }
405 std::mem::swap(&mut files, &mut next_files);
406 }
407 Ok(())
408 }
409
410 fn output_repository_assuming_no_pending_patch(
411 &mut self,
412 prefixes: &Prefixes,
413 branch: &mut Branch,
414 working_copy: &Path,
415 pending_patch_id: PatchId,
416 ) -> Result<()> {
417 debug!(
418 "inodes: {:?}",
419 self.iter_inodes(None)
420 .map(|(u, v)| (u.to_owned(), v.to_owned()))
421 .collect::<Vec<_>>()
422 );
423 let dead: Vec<_> = self.iter_tree(None)
425 .filter_map(|(k, v)| {
426 debug!("{:?} {:?}", k, v);
427 if let Some(key) = self.get_inodes(v) {
428 if key.key.patch == pending_patch_id || self.is_alive_or_zombie(branch, key.key)
429 {
430 None
432 } else {
433 Some((k.to_owned(), v, self.filename_of_inode(v, working_copy)))
434 }
435 } else {
436 debug!("not in inodes");
437 Some((k.to_owned(), v, None))
438 }
439 })
440 .collect();
441 debug!("dead: {:?}", dead);
442
443 for (ref parent, inode, ref name) in dead {
445 self.remove_inode_rec(inode)?;
446 debug!("removed");
447 if let Some(ref name) = *name {
448 debug!("deleting {:?}", name);
449 if let Ok(meta) = fs::metadata(name) {
450 if let Err(e) = if meta.is_dir() {
451 fs::remove_dir_all(name)
452 } else {
453 fs::remove_file(name)
454 } {
455 error!("while deleting {:?}: {:?}", name, e);
456 }
457 }
458 } else {
459 self.del_tree(&parent.as_file_id(), Some(inode))?;
460 self.del_revtree(inode, Some(&parent.as_file_id()))?;
461 }
462 }
463 debug!("done deleting dead files");
464 self.output_alive_files(branch, prefixes, working_copy)?;
469 debug!("done raw_output_repository");
470 Ok(())
471 }
472
473 fn remove_inode_rec(&mut self, inode: Inode) -> Result<()> {
474 let mut to_kill = vec![inode];
476 while let Some(inode) = to_kill.pop() {
477 debug!("kill dead {:?}", inode.to_hex());
478 let header = self.get_inodes(inode).map(|x| x.to_owned());
479 if let Some(header) = header {
480 self.del_inodes(inode, None)?;
481 self.del_revinodes(header.key, None)?;
482 let mut kills = Vec::new();
483 for (k, v) in self.iter_revtree(Some((inode, None)))
485 .take_while(|&(k, _)| k == inode)
486 {
487 kills.push((k.clone(), v.to_owned()))
488 }
489 for &(k, ref v) in kills.iter() {
490 self.del_tree(&v.as_file_id(), Some(k))?;
491 self.del_revtree(k, Some(&v.as_file_id()))?;
492 }
493 let inode_fileid = OwnedFileId {
495 parent_inode: inode.clone(),
496 basename: SmallString::from_str(""),
497 };
498 to_kill.extend(
499 self.iter_tree(Some((&inode_fileid.as_file_id(), None)))
500 .take_while(|&(ref k, _)| k.parent_inode == inode)
501 .map(|(_, v)| v.to_owned()),
502 )
503 }
504 }
505 Ok(())
506 }
507
508 pub fn output_repository(
509 &mut self,
510 branch: &mut Branch,
511 working_copy: &Path,
512 prefixes: &Prefixes,
513 pending: &Patch,
514 local_pending: &HashSet<InodeUpdate>,
515 ) -> Result<()> {
516 debug!("begin output repository");
517
518 debug!("applying pending patch");
519 let tempdir = tempdir::TempDir::new("pijul")?;
520 let hash = pending.save(tempdir.path(), None)?;
521 let internal =
522 self.apply_local_patch(branch, working_copy, &hash, pending, local_pending, true)?;
523
524 debug!("applied");
525
526 debug!("prefixes {:?}", prefixes);
528 self.output_repository_assuming_no_pending_patch(
529 &prefixes,
530 branch,
531 working_copy,
532 internal,
533 )?;
534
535 debug!("unrecording pending patch");
536 self.unrecord(branch, internal, pending)?;
537 Ok(())
538 }
539
540 pub fn output_repository_no_pending(
541 &mut self,
542 branch: &mut Branch,
543 working_copy: &Path,
544 prefixes: &Prefixes,
545 ) -> Result<()> {
546 debug!("begin output repository {:?}", prefixes);
547
548 debug!("prefixes {:?}", prefixes);
550 self.output_repository_assuming_no_pending_patch(
551 &prefixes,
552 branch,
553 working_copy,
554 ROOT_PATCH_ID,
555 )?;
556 Ok(())
557 }
558
559 pub(crate) fn output_partials(&mut self, branch_name: &str, prefixes: &Prefixes) -> Result<()> {
560 for p in prefixes.0.iter() {
561 self.put_partials(branch_name, p[0])?;
562 }
563 Ok(())
564 }
565}
566
567#[derive(Debug)]
568pub struct Prefixes(Vec<Vec<Key<PatchId>>>);
569
570impl Prefixes {
571 pub fn empty() -> Self {
572 Prefixes(Vec::new())
573 }
574}
575
576pub trait ToPrefixes {
577 fn to_prefixes<T>(&self, txn: &MutTxn<T>, branch: &Branch) -> Prefixes;
578}
579
580impl<'a> ToPrefixes for &'a [&'a str] {
581 fn to_prefixes<T>(&self, txn: &MutTxn<T>, branch: &Branch) -> Prefixes {
582 Prefixes(
583 self.iter()
584 .flat_map(|pref| txn.prefix_keys(&branch, pref))
585 .collect(),
586 )
587 }
588}
589
590impl<'a> ToPrefixes for &'a [Inode] {
591 fn to_prefixes<T>(&self, txn: &MutTxn<T>, _: &Branch) -> Prefixes {
592 Prefixes(
593 self.iter()
594 .map(|pref| {
595 let mut result = Vec::new();
596 let mut current = *pref;
597 loop {
598 if current == ROOT_INODE {
599 result.push(ROOT_KEY);
600 break;
601 }
602 result.push(txn.get_inodes(current).unwrap().key);
603 match txn.get_revtree(current) {
604 Some(v) => current = v.parent_inode.clone(),
605 None => break,
606 }
607 }
608 result
609 })
610 .collect(),
611 )
612 }
613}