Initial commit.
This commit is contained in:
commit
2179a13aa6
|
@ -0,0 +1 @@
|
|||
target
|
|
@ -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"
|
|
@ -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"
|
|
@ -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.
|
|
@ -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.");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue