Initial commit.

This commit is contained in:
raute 2020-06-27 15:27:52 +02:00
commit 2179a13aa6
5 changed files with 355 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

209
Cargo.lock generated Normal file
View File

@ -0,0 +1,209 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "bekape"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"regex",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "chrono"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
dependencies = [
"num-integer",
"num-traits",
"time",
]
[[package]]
name = "clap"
version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "hermit-abi"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg",
]
[[package]]
name = "regex"
version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "bekape"
version = "0.1.0"
authors = ["raute"]
edition = "2018"
[dependencies]
chrono = "0.4"
clap = "2.33"
regex = "1.3"

8
README.md Normal file
View File

@ -0,0 +1,8 @@
# Bekape
I want to produce a very simple backup program that can be used by people who know nothing about their computer.
It will need an easy GUI that lets you choose the folder to backup and an external drive and start the backup.
For now there is no GUI, just a first try to to incremental backups using hardlinks.
Pre-alpha. You really don't want to use this yet.

127
src/main.rs Normal file
View File

@ -0,0 +1,127 @@
use clap::{App, Arg};
use regex::Regex;
use std::fs;
use std::time::SystemTime;
use std::vec::Vec;
fn browse_recursively(dir: &str) -> (Vec<String>, Vec<String>) {
let mut content = Vec::new();
let mut errors = Vec::new();
let ls = fs::read_dir(dir);
if let Ok(ls) = ls {
for entry in ls {
if let Ok(entry) = entry {
let full_path = entry.path().to_string_lossy().to_string();
if let Ok(file_type) = entry.file_type() {
if file_type.is_dir() {
let mut result = browse_recursively(&full_path);
content.append(&mut result.0);
errors.append(&mut result.1);
} else {
content.push(full_path);
}
} else {
errors.push(full_path);
}
}
}
} else {
errors.push(dir.to_string());
}
(content, errors)
}
fn get_file_metadata(file: &str) -> (SystemTime, u64) {
if let Ok(metadata) = fs::metadata(file) {
let time = metadata.modified().unwrap_or(std::time::UNIX_EPOCH);
let size = metadata.len();
return (time, size);
};
(std::time::UNIX_EPOCH, 0)
}
fn backup_element(path: &str, src_root: &str, bkp_root: &str, prev_bkp_root: &Option<String>) {
let src_file = format!("{}{}", src_root, path);
let bkp_file = format!("{}{}", bkp_root, path);
let p = bkp_file.rfind('/').unwrap(); // TODO: Is unwrap okay here?
let bkp_folder = bkp_file[0..p].to_string();
let _ = fs::create_dir_all(&bkp_folder);
let mut backup_done = false;
if let Some(prev_bkp_root) = prev_bkp_root {
let prev_bkp_file = format!("{}{}", prev_bkp_root, path);
if std::path::Path::new(&prev_bkp_file).exists() {
let prev_bkp_metadata = get_file_metadata(&prev_bkp_file);
let src_metadata = get_file_metadata(&src_file);
backup_done = prev_bkp_metadata.0 > src_metadata.0
&& prev_bkp_metadata.1 == src_metadata.1
&& fs::hard_link(&prev_bkp_file, &bkp_file).is_ok();
}
}
if !backup_done && fs::copy(&src_file, &bkp_file).is_err() {
eprintln!("Could not copy: {} -> {}", src_file, bkp_file);
}
}
fn main() {
let matches = App::new("bekape")
.about("Simple backup program.")
.arg(
Arg::with_name("source")
.help("The folder to backup")
.required(true),
)
.arg(
Arg::with_name("destination")
.help("The folder to store the backup")
.required(true),
)
.get_matches();
let bkp_src = matches.value_of("source").unwrap();
let bkp_dst = matches.value_of("destination").unwrap();
let dt = chrono::offset::Utc::now().format("%Y-%m-%d_%H-%M-%S");
let backup_folder = format!("{}/backup_{}_wip", bkp_dst, dt);
if fs::create_dir(&backup_folder).is_err() {
eprintln!("Could not create backup directory: {}", &backup_folder);
std::process::exit(1);
}
// find previous backup
let backup_regex = Regex::new(r"backup_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$").unwrap();
let ls = fs::read_dir(bkp_dst);
let prev_backup = if let Ok(ls) = ls {
let mut ls = ls
.filter_map(|s| s.ok())
.map(|s| s.path().to_string_lossy().trim().to_owned())
.filter(|s| backup_regex.is_match(&s))
.collect::<Vec<_>>();
if ls.is_empty() {
None
} else {
ls.sort();
ls.last().cloned()
}
} else {
None
};
let result = browse_recursively(&bkp_src);
let content = result.0.iter().map(|s| {
let l = bkp_src.len();
s[l..].to_string()
});
for err in result.1 {
eprintln!("Could not read {}.", err);
}
for elem in content {
backup_element(&elem, &bkp_src, &backup_folder, &prev_backup);
}
let new_name = backup_folder.replace("_wip", "");
if fs::rename(&backup_folder, &new_name).is_err() {
eprintln!("Could not rename folder.");
}
}