1#![recursion_limit = "128"]
29#[macro_use]
30extern crate bitflags;
31extern crate chrono;
32#[macro_use]
33extern crate log;
34
35extern crate base64;
36extern crate bincode;
37extern crate bs58;
38extern crate byteorder;
39#[macro_use]
40extern crate error_chain;
41extern crate flate2;
42extern crate hex;
43extern crate ignore;
44extern crate openssl;
45extern crate rand;
46extern crate sanakirja;
47extern crate serde;
48#[macro_use]
49extern crate serde_derive;
50extern crate serde_json;
51extern crate tempdir;
52extern crate thrussh_keys;
53
54use std::path::Path;
55use std::collections::HashSet;
56use std::io::Write;
57use std::path::PathBuf;
58pub use sanakirja::Transaction;
59
60error_chain! {
61 foreign_links {
62 IO(std::io::Error);
63 Sanakirja(sanakirja::Error);
64 Bincode(bincode::Error);
65 Utf8(std::str::Utf8Error);
66 Serde(serde_json::Error);
67 OpenSSL(openssl::error::Error);
68 OpenSSLStack(openssl::error::ErrorStack);
69 Keys(thrussh_keys::Error);
70 Base58Decode(bs58::decode::DecodeError);
71 }
72 errors {
73 AlreadyAdded {}
74 FileNotInRepo(path: PathBuf) {
75 description("File not tracked")
76 display("File {:?} not tracked", path.display())
77 }
78 NoDb(root: backend::Root) {}
79 WrongHash {}
80 EOF {}
81 WrongPatchSignature {
82 description("Wrong patch signature")
83 display("Wrong patch signature")
84 }
85 BranchNameAlreadyExists(name: String) {
86 description("Branch name already exists")
87 display("Branch name {:?} already exists", name)
88 }
89 WrongFileHeader(node: Key<PatchId>) {
90 description("Wrong file header (possible branch corruption)")
91 display("Wrong file header (possible branch corruption): {:?}", node)
92 }
93 FileNameCount(node: Key<PatchId>) {
94 description("A file name doesn't have exactly one child")
95 display("A file name doesn't have exactly one child: {:?}", node)
96 }
97 }
98}
99
100impl Error {
101 pub fn lacks_space(&self) -> bool {
102 match self.0 {
103 ErrorKind::Sanakirja(sanakirja::Error::NotEnoughSpace) => true,
104 _ => false,
105 }
106 }
107}
108
109#[macro_use]
110mod backend;
111pub mod fs_representation;
112mod file_operations;
113
114pub mod patch;
115
116mod conflict;
117pub mod graph;
118mod optimal_diff;
119mod record;
120pub mod apply;
121mod output;
122mod unrecord;
123
124pub use backend::{ApplyTimestamp, Branch, Edge, EdgeFlags, FileId, FileMetadata, FileStatus,
125 GenericTxn, Hash, HashRef, Inode, Key, LineId, MutTxn, OwnedFileId, PatchId,
126 Repository, SmallStr, SmallString, Txn, DEFAULT_BRANCH, ROOT_INODE, ROOT_KEY};
127
128pub use record::{InodeUpdate, RecordState};
129pub use patch::{Patch, PatchHeader};
130pub use sanakirja::value::Value;
131pub use output::{Prefixes, ToPrefixes};
132use fs_representation::ID_LENGTH;
133use std::io::Read;
134use rand::Rng;
135
136impl<'env, T: rand::Rng> backend::MutTxn<'env, T> {
137 pub fn output_changes_file<P: AsRef<Path>>(&mut self, branch: &Branch, path: P) -> Result<()> {
138 let changes_file =
139 fs_representation::branch_changes_file(path.as_ref(), branch.name.as_str());
140 let mut branch_id: Vec<u8> = vec![b'\n'; ID_LENGTH + 1];
141 {
142 if let Ok(mut file) = std::fs::File::open(&changes_file) {
143 file.read_exact(&mut branch_id)?;
144 }
145 }
146 let mut branch_id = if let Ok(s) = String::from_utf8(branch_id) {
147 s
148 } else {
149 "\n".to_string()
150 };
151 if branch_id.as_bytes()[0] == b'\n' {
152 branch_id.truncate(0);
153 let mut rng = rand::thread_rng();
154 branch_id.extend(rng.gen_ascii_chars().take(ID_LENGTH));
155 branch_id.push('\n');
156 }
157
158 let mut file = std::fs::File::create(&changes_file)?;
159 file.write_all(&branch_id.as_bytes())?;
160 for (s, hash) in self.iter_applied(&branch, None) {
161 let hash_ext = self.get_external(hash).unwrap();
162 writeln!(file, "{}:{}", hash_ext.to_base58(), s)?
163 }
164 Ok(())
165 }
166
167 pub fn branch_patches(&mut self, branch: &Branch) -> HashSet<(backend::Hash, ApplyTimestamp)> {
168 self.iter_patches(branch, None)
169 .map(|(patch, time)| (self.external_hash(patch).to_owned(), time))
170 .collect()
171 }
172
173 pub fn fork(&mut self, branch: &Branch, new_name: &str) -> Result<Branch> {
174 if branch.name.as_str() == new_name {
175 Err(ErrorKind::BranchNameAlreadyExists(new_name.to_string()).into())
176 } else {
177 Ok(Branch {
178 db: self.txn.fork(&mut self.rng, &branch.db)?,
179 patches: self.txn.fork(&mut self.rng, &branch.patches)?,
180 revpatches: self.txn.fork(&mut self.rng, &branch.revpatches)?,
181 name: SmallString::from_str(new_name),
182 apply_counter: branch.apply_counter,
183 })
184 }
185 }
186 pub fn add_file<P: AsRef<Path>>(&mut self, path: P, is_dir: bool) -> Result<()> {
187 self.add_inode(None, path.as_ref(), is_dir)
188 }
189
190 fn file_nodes_fold_<A, F: FnMut(A, Key<PatchId>) -> A>(
191 &self,
192 branch: &Branch,
193 root: Key<PatchId>,
194 level: usize,
195 mut init: A,
196 f: &mut F,
197 ) -> Result<A> {
198 for (k, v) in self.iter_nodes(&branch, Some((root, None)))
199 .take_while(|&(k, v)| {
200 k.is_root() && v.flag.contains(EdgeFlags::FOLDER_EDGE)
201 && !v.flag.contains(EdgeFlags::PARENT_EDGE)
202 }) {
203 debug!("file_nodes_fold_: {:?} {:?}", k, v);
204 if level & 1 == 0 && level > 0 {
205 init = f(init, k)
206 }
207 init = self.file_nodes_fold_(branch, v.dest, level + 1, init, f)?
208 }
209 Ok(init)
210 }
211
212 pub fn file_nodes_fold<A, F: FnMut(A, Key<PatchId>) -> A>(
213 &self,
214 branch: &Branch,
215 init: A,
216 mut f: F,
217 ) -> Result<A> {
218 self.file_nodes_fold_(branch, ROOT_KEY, 0, init, &mut f)
219 }
220}
221
222impl<T: Transaction, R> backend::GenericTxn<T, R> {
223 pub fn is_alive(&self, branch: &Branch, key: Key<PatchId>) -> bool {
226 debug!("is_alive {:?}?", key);
227 let mut alive = key == ROOT_KEY;
228 let e = Edge::zero(EdgeFlags::PARENT_EDGE);
229 for (k, v) in self.iter_nodes(&branch, Some((key, Some(&e)))) {
230 if k != key {
231 break;
232 }
233 alive = alive
234 || (!v.flag.contains(EdgeFlags::DELETED_EDGE)
235 && !v.flag.contains(EdgeFlags::PSEUDO_EDGE))
236 }
237 alive
238 }
239
240 pub fn is_alive_or_zombie(&self, branch: &Branch, key: Key<PatchId>) -> bool {
244 debug!("is_alive_or_zombie {:?}?", key);
245 if key == ROOT_KEY {
246 return true;
247 }
248 let e = Edge::zero(EdgeFlags::PARENT_EDGE);
249 for (k, v) in self.iter_nodes(&branch, Some((key, Some(&e)))) {
250 if k != key {
251 break;
252 }
253 debug!("{:?}", v);
254 if v.flag.contains(EdgeFlags::PARENT_EDGE) && !v.flag.contains(EdgeFlags::DELETED_EDGE)
255 {
256 return true;
257 }
258 }
259 false
260 }
261
262 pub fn has_edge(
265 &self,
266 branch: &Branch,
267 key: Key<PatchId>,
268 min: EdgeFlags,
269 max: EdgeFlags,
270 ) -> bool {
271 let e = Edge::zero(min);
272 if let Some((k, v)) = self.iter_nodes(&branch, Some((key, Some(&e)))).next() {
273 debug!("has_edge {:?}", v.flag);
274 k == key && (v.flag <= max)
275 } else {
276 false
277 }
278 }
279
280 pub fn get_file<'a>(&'a self, branch: &Branch, key: Key<PatchId>) -> Vec<Key<PatchId>> {
282 let mut stack = vec![key.to_owned()];
283 let mut seen = HashSet::new();
284 let mut names = Vec::new();
285 loop {
286 match stack.pop() {
287 None => break,
288 Some(key) if !seen.contains(&key) => {
289 debug!("key {:?}, None", key);
290 seen.insert(key.clone());
291 let e = Edge::zero(EdgeFlags::PARENT_EDGE);
292 for (_, v) in self.iter_nodes(branch, Some((key, None)))
293 .take_while(|&(k, _)| k == key)
294 {
295 debug!("all_edges: {:?}", v);
296 }
297 for (_, v) in self.iter_nodes(branch, Some((key, Some(&e))))
298 .take_while(|&(k, _)| k == key)
299 {
300 debug!("get_file {:?}", v);
301 if v.flag | EdgeFlags::PSEUDO_EDGE
302 == EdgeFlags::PARENT_EDGE | EdgeFlags::PSEUDO_EDGE
303 {
304 debug!("push!");
305 stack.push(v.dest.clone())
306 } else if v.flag
307 .contains(EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE)
308 {
309 names.push(key);
310 }
311 }
312 }
313 _ => {}
314 }
315 }
316 debug!("get_file returning {:?}", names);
317 names
318 }
319
320 pub fn get_file_names<'a>(
321 &'a self,
322 branch: &Branch,
323 key: Key<PatchId>,
324 ) -> Vec<(Key<PatchId>, Vec<&'a str>)> {
325 let mut names = vec![(key, Vec::new())];
326 debug!("inode: {:?}", names);
327 let mut next_names = Vec::new();
329 let mut only_roots = false;
330 let mut inodes = HashSet::new();
331 while !only_roots {
332 next_names.clear();
333 only_roots = true;
334 for (inode, names) in names.drain(..) {
335 if !inodes.contains(&inode) {
336 inodes.insert(inode.clone());
337
338 if inode != ROOT_KEY {
339 only_roots = false;
340 }
341 let names_ = self.file_names(branch, inode);
342 if names_.is_empty() {
343 next_names.push((inode, names));
344 break;
345 } else {
346 debug!("names_ = {:?}", names_);
347 for (inode_, _, base) in names_ {
348 let mut names = names.clone();
349 names.push(base);
350 next_names.push((inode_, names))
351 }
352 }
353 }
354 }
355 std::mem::swap(&mut names, &mut next_names)
356 }
357 debug!("end: {:?}", names);
358 for &mut (_, ref mut name) in names.iter_mut() {
359 name.reverse()
360 }
361 names
362 }
363}
364
365fn make_remote<'a, I: Iterator<Item = &'a Hash>>(
366 target: &Path,
367 remote: I,
368) -> Result<(Vec<(Hash, Patch)>, usize)> {
369 use fs_representation::*;
370 use std::io::BufReader;
371 use std::fs::File;
372 let mut patches = Vec::new();
373 let mut patches_dir = patches_dir(target).to_path_buf();
374 let mut size_increase = 0;
375
376 for h in remote {
377 patches_dir.push(&patch_file_name(h.as_ref()));
378
379 debug!("opening {:?}", patches_dir);
380 let file = try!(File::open(&patches_dir));
381 let mut file = BufReader::new(file);
382 let (h, _, patch) = Patch::from_reader_compressed(&mut file)?;
383
384 size_increase += patch.size_upper_bound();
385 patches.push((h.clone(), patch));
386
387 patches_dir.pop();
388 }
389 Ok((patches, size_increase))
390}
391
392pub fn apply_resize<'a, I, F, P: output::ToPrefixes>(
399 target: &Path,
400 branch_name: &str,
401 remote: I,
402 partial_paths: P,
403 apply_cb: F,
404) -> Result<()>
405where
406 I: Iterator<Item = &'a Hash>,
407 F: FnMut(usize, &Hash),
408{
409 let (patches, size_increase) = make_remote(target, remote)?;
410 apply_resize_patches(
411 target,
412 branch_name,
413 &patches,
414 size_increase,
415 partial_paths,
416 apply_cb,
417 )
418}
419
420pub fn apply_resize_patches<'a, F, P: output::ToPrefixes>(
422 target: &Path,
423 branch_name: &str,
424 patches: &[(Hash, Patch)],
425 size_increase: usize,
426 partial_paths: P,
427 apply_cb: F,
428) -> Result<()>
429where
430 F: FnMut(usize, &Hash),
431{
432 use fs_representation::*;
433 info!("applying patches with size_increase {:?}", size_increase);
434 let pristine_dir = pristine_dir(target).to_path_buf();
435 let repo = Repository::open(pristine_dir, Some(size_increase as u64))?;
436 let mut txn = repo.mut_txn_begin(rand::thread_rng())?;
437 let mut branch = txn.open_branch(branch_name)?;
438 txn.apply_patches(&mut branch, target, &patches, partial_paths, apply_cb)?;
439 txn.commit_branch(branch)?;
440 txn.commit().map_err(Into::into)
441}
442
443pub fn apply_resize_no_output<'a, F, I>(
450 target: &Path,
451 branch_name: &str,
452 remote: I,
453 apply_cb: F,
454) -> Result<()>
455where
456 I: Iterator<Item = &'a Hash>,
457 F: FnMut(usize, &Hash),
458{
459 let (patches, size_increase) = make_remote(target, remote)?;
460 apply_resize_patches_no_output(target, branch_name, &patches, size_increase, apply_cb)
461}
462
463pub fn apply_resize_patches_no_output<'a, F>(
464 target: &Path,
465 branch_name: &str,
466 patches: &[(Hash, Patch)],
467 size_increase: usize,
468 mut apply_cb: F,
469) -> Result<()>
470where
471 F: FnMut(usize, &Hash),
472{
473 use fs_representation::*;
474 debug!("apply_resize_no_output: patches = {:?}", patches);
475 let pristine_dir = pristine_dir(target).to_path_buf();
476 let repo = try!(Repository::open(pristine_dir, Some(size_increase as u64)));
477 let mut txn = try!(repo.mut_txn_begin(rand::thread_rng()));
478 let mut branch = txn.open_branch(branch_name)?;
479 let mut new_patches_count = 0;
480 for &(ref p, ref patch) in patches.iter() {
481 debug!("apply_patches: {:?}", p);
482 txn.apply_patches_rec(&mut branch, &patches, p, patch, &mut new_patches_count)?;
483 apply_cb(new_patches_count, p);
484 }
485 info!("branch: {:?}", branch);
486 txn.commit_branch(branch)?;
487 txn.commit()?;
488 Ok(())
489}
490
491pub fn unrecord_no_resize(
495 repo_dir: &Path,
496 repo_root: &Path,
497 branch_name: &str,
498 selected: &mut Vec<(Hash, Patch)>,
499 increase: u64,
500) -> Result<()> {
501 debug!("unrecord_no_resize: {:?}", repo_dir);
502 let repo = try!(Repository::open(repo_dir, Some(increase)));
503
504 let mut txn = try!(repo.mut_txn_begin(rand::thread_rng()));
505 let mut branch = txn.open_branch(branch_name)?;
506 let mut timestamps = Vec::new();
507 while let Some((hash, patch)) = selected.pop() {
508 let internal = txn.get_internal(hash.as_ref()).unwrap().to_owned();
509 debug!("Unrecording {:?}", hash);
510 if let Some(ts) = txn.get_patch(&branch.patches, internal) {
511 timestamps.push(ts);
512 }
513 txn.unrecord(&mut branch, internal, &patch)?;
514 debug!("Done unrecording {:?}", hash);
515 }
516
517 if let Err(e) = txn.output_changes_file(&branch, repo_root) {
518 error!("no changes file: {:?}", e)
519 }
520 try!(txn.commit_branch(branch));
521 try!(txn.commit());
522 Ok(())
523}