restore.v

1.5 KB · raw

1//! Writing a tree back onto disk.2//!3//! A restore writes exactly the target's files and deletes the tracked files4//! the target does not have. Ignored paths are never touched, and every file5//! lands through a temp file and a rename, so an interrupted restore leaves a6//! readable tree rather than half-written files.78module snapshot910import os11import src.storage1213pub struct RestoreReport {14pub:15	written int16	removed int17}1819pub fn restore_files(store &storage.Store, root string, target []FileEntry, remove []string) !RestoreReport {20	mut removed := 021	for path in remove {22		full := os.join_path(root, path)23		if !os.is_file(full) {24			continue25		}26		os.rm(full)!27		removed++28		prune_empty_dirs(root, os.dir(full))29	}30	for entry in target {31		data := store.get_object(entry.hash)!32		write_file(os.join_path(root, entry.path), data, entry.executable)!33	}34	return RestoreReport{35		written: target.len36		removed: removed37	}38}3940fn write_file(path string, data []u8, executable bool) ! {41	os.mkdir_all(os.dir(path), os.MkdirParams{})!42	tmp := '${path}.koh-tmp-${os.getpid()}'43	os.write_file_array(tmp, data)!44	mode := if executable { 0o755 } else { 0o644 }45	os.chmod(tmp, mode)!46	os.mv(tmp, path, os.MvParams{})!47}4849// prune_empty_dirs clears directories a restore emptied, never the repo root.50fn prune_empty_dirs(root string, dir string) {51	mut current := dir52	for current.len > root.len && current.starts_with(root) {53		if (os.ls(current) or { return }).len > 0 {54			return55		}56		os.rmdir(current) or { return }57		current = os.dir(current)58	}59}