Skip to content

Commit ac84c84

Browse files
author
Christian Kauhaus
committed
Add docs, release tooling, reorganize pub exports
1 parent dc51390 commit ac84c84

File tree

9 files changed

+159
-49
lines changed

9 files changed

+159
-49
lines changed

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
/target
1+
/dist/
22
**/*.rs.bk
33
.*.sw?
4-
tags
4+
/tags
5+
/target/
6+
/tmp/

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ authors = ["Christian Kauhaus <kc@flyingcircus.io>"]
55
edition = "2018"
66
description = "Rapid restore tool for backy"
77
license = "BSD-3-Clause"
8+
repository = "https://github.com/ckauhaus/backy-extract"
89

910
[dependencies]
1011
atty = "0.2"

Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Set if your compiler cannot find liblzo2
2+
# LIBRARY_PATH = /path/to/liblzo2
3+
4+
DESTDIR := dist
5+
6+
all: release
7+
8+
release: target/release/backy-extract
9+
10+
target/release/backy-extract: src/*.rs src/*/*.rs
11+
cargo build --release
12+
strip $@
13+
14+
VERSION = `cargo read-manifest | jq .version -r`
15+
PV = backy-extract-$(VERSION)
16+
17+
dist: release
18+
install -D target/release/backy-extract -t tmp/$(PV)/bin
19+
install -D -m 0644 README.md -t tmp/$(PV)/share/doc
20+
mkdir -p dist
21+
tar czf dist/$(PV).tar.gz -C tmp $(PV)
22+
rm -r tmp
23+
24+
clean:
25+
cargo clean
26+
rm -rf tmp dist
27+
28+
.PHONY: release dist clean

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
backy-extract
2+
=============
3+
4+
High-performance, standalone backup restore tool for
5+
[backy](https://bitbucket.org/flyingcircus/backy). backy-extract restores a
6+
specified backup revision to stdout or a file/block device target.
7+
8+
9+
Usage
10+
-----
11+
12+
1. Pick a backup revision, for example using `backy status`.
13+
2. Create a restore target, for example with `lvm` or `rbd image`.
14+
3. Extract backup: `backy-extract /srv/backy/vm/Nym6uacWoXGb8VnbksM3yH /dev/rbd0`
15+
16+
17+
Interaction with backy
18+
----------------------
19+
20+
`backy-extract` tries to acquire the *purge lock* before proceeding. So if it
21+
does not seem to start, check if there are running `backy` processes operating
22+
on the same backup.
23+
24+
25+
Sparse mode
26+
-----------
27+
28+
Block devices are assumed to be zeroed (discarded) before restoring. If this is
29+
not the case, invoke `backy-extract` with `--sparse=never`.
30+
31+
32+
Compiling
33+
---------
34+
35+
In general, `cargo build --release` should do the right thing. This application
36+
depends on [liblzo2](http://www.oberhumer.com/opensource/lzo/) being availabe.
37+
If you get compiler/linker errors, try compiling again with `export
38+
LIBRABY_PATH=/path/to/liblzo2`.
39+
40+
A Makefile is supplied to create a statically linked release which should run on
41+
virtually every Linux x86_64 system.
42+
43+
44+
Hacking
45+
-------
46+
47+
Please create issues and submit pull requests at
48+
https://github.com/ckauhaus/backy-extract/.
49+
50+
51+
Author
52+
------
53+
54+
Contact [Christian Kauhaus](kc@flyingcircus.io) for questions, suggestions, and
55+
bug fixes.

src/chunkvec.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::backend::Backend;
2-
use crate::{pos2chunk, Chunk, ExtractError, CHUNKSZ_LOG};
2+
use crate::{pos2chunk, Chunk, Data, ExtractError, CHUNKSZ_LOG};
33

44
use crossbeam::channel::Sender;
55
use failure::{Fail, Fallible, ResultExt};
@@ -31,12 +31,6 @@ impl<'d> Revision<'d> {
3131
}
3232
}
3333

34-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
35-
pub enum Data {
36-
Some(Vec<u8>),
37-
Zero,
38-
}
39-
4034
type ChunkMap<'d> = BTreeMap<&'d str, SmallVec<[usize; 4]>>;
4135

4236
/// All chunks of a revision, grouped by chunk ID.

src/lib.rs

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1+
//! High-performance, multi-threaded backy image restore library.
2+
//!
3+
//! `backy_extract` reads an backup revision from a
4+
//! [backy](https://bitbucket.org/flyingcircus/backy) *chunked v2* data store, decompresses it on
5+
//! the fly and writes it to a restore target using pluggable writeout modules.
6+
17
mod backend;
28
mod chunkvec;
39
#[cfg(test)]
410
mod test_helper;
511
mod writeout;
612

713
use self::backend::Backend;
8-
use self::chunkvec::{ChunkVec, Data};
14+
use self::chunkvec::{ChunkVec};
915
pub use self::writeout::{RandomAccess, Stream};
10-
use self::writeout::{WriteOut, WriteOutBuilder};
1116

1217
use console::{style, StyledObject};
13-
use crossbeam::channel::{bounded, unbounded, Receiver};
18+
use crossbeam::channel::{bounded, unbounded, Sender, Receiver};
1419
use crossbeam::thread;
1520
use failure::{Fail, Fallible, ResultExt};
1621
use fs2::FileExt;
@@ -19,18 +24,60 @@ use lazy_static::lazy_static;
1924
use memmap::MmapMut;
2025
use num_cpus;
2126
use smallvec::SmallVec;
27+
use std::fmt::Debug;
2228
use std::fs::{self, File};
2329
use std::path::{Path, PathBuf};
2430
use std::time::Instant;
2531

26-
// Size of an uncompressed Chunk in the backy store.
32+
/// Size of an uncompressed Chunk in the backy store as 2's exponent.
2733
// This value must be a u32 because it is encoded as 32 bit uint the chunk file header.
2834
pub const CHUNKSZ_LOG: usize = 22; // 4 MiB
2935

3036
lazy_static! {
3137
static ref ZERO_CHUNK: MmapMut = MmapMut::map_anon(1 << CHUNKSZ_LOG).expect("mmap");
3238
}
3339

40+
/// WriteOut factory.
41+
///
42+
/// We use the factory approach to have only the minimum of parameters to the
43+
/// user-facing constructor. Further parameters from the Exctractor are supplied internally by
44+
/// invoking `build` to get the final WriteOut object.
45+
pub trait WriteOutBuilder {
46+
type Impl: WriteOut + Sync + Send;
47+
fn build(self, total_size: u64, threads: u8) -> Self::Impl;
48+
}
49+
50+
/// Abstract writeout (restore) plugin.
51+
///
52+
/// A concrete writer is instantiated via `WriteOutBuilder.build()`.
53+
pub trait WriteOut: Debug {
54+
/// Gets an unordered stream of `Chunk`s which must be written to the restore target according
55+
/// to the chunks' sequence numbers. Writer must send the number of bytes written to the
56+
/// `progress` channel to indicate restore progress in real time.
57+
fn receive(self, chunks: Receiver<Chunk>, progress: Sender<usize>) -> Fallible<()>;
58+
59+
/// Short idenfication for user display. Should contain plugin type and file name.
60+
fn name(&self) -> String;
61+
}
62+
63+
/// Transport of a single image data chunk.
64+
///
65+
/// A chunk needs to be placed into all logical positions that are listed in the `seqs` attribute.
66+
/// Each seq starts at offset (seq << CHUNKSZ_LOG) bytes in the restored image.
67+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
68+
pub struct Chunk {
69+
pub data: Data,
70+
pub seqs: SmallVec<[usize; 4]>,
71+
}
72+
73+
/// Block of uncompressed image contents of length (1 << CHUNKSZ_LOG).
74+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
75+
pub enum Data {
76+
Some(Vec<u8>),
77+
/// Shortcut if the whole block consists only of zeros.
78+
Zero,
79+
}
80+
3481
// Converts file position/size into chunk sequence number
3582
fn pos2chunk(pos: u64) -> usize {
3683
(pos >> CHUNKSZ_LOG) as usize
@@ -41,12 +88,6 @@ fn chunk2pos(seq: usize) -> u64 {
4188
(seq as u64) << CHUNKSZ_LOG
4289
}
4390

44-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
45-
pub struct Chunk {
46-
data: Data,
47-
seqs: SmallVec<[usize; 4]>,
48-
}
49-
5091
fn purgelock(basedir: &Path) -> Fallible<File> {
5192
let f = File::create(basedir.join(".purge"))?;
5293
f.try_lock_exclusive()?;
@@ -94,10 +135,6 @@ impl Extractor {
94135
})
95136
}
96137

97-
fn default_threads() -> u8 {
98-
(num_cpus::get() / 2).max(1).min(60) as u8
99-
}
100-
101138
/// Sets number of decompression threads. Heuristics apply in this method is never called.
102139
pub fn threads(&mut self, n: u8) -> &mut Self {
103140
if n > 0 {
@@ -106,6 +143,10 @@ impl Extractor {
106143
self
107144
}
108145

146+
fn default_threads() -> u8 {
147+
(num_cpus::get() / 2).max(1).min(60) as u8
148+
}
149+
109150
/// Enables/disables a nice progress bar on stderr while restoring.
110151
pub fn progress(&mut self, show: bool) -> &mut Self {
111152
self.progress = if show {

src/writeout/mod.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,3 @@ mod stream;
44

55
pub use self::randomaccess::RandomAccess;
66
pub use self::stream::Stream;
7-
8-
use super::Chunk;
9-
use crossbeam::channel::{Receiver, Sender};
10-
use failure::Fallible;
11-
use std::fmt::Debug;
12-
13-
/// WriteOut factory.
14-
///
15-
/// We use the factory approach to have only the minimum of parameters to the
16-
/// user-facing constructor. Further parameters from the Exctractor are supplied internally by
17-
/// invoking `build` to get the final WriteOut object.
18-
pub trait WriteOutBuilder {
19-
type Impl: WriteOut + Sync + Send;
20-
fn build(self, total_size: u64, threads: u8) -> Self::Impl;
21-
}
22-
23-
/// Abstracts over restore backends.
24-
pub trait WriteOut: Debug {
25-
fn receive(self, chunks: Receiver<Chunk>, progress: Sender<usize>) -> Fallible<()>;
26-
fn name(&self) -> String;
27-
}

src/writeout/randomaccess.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use super::compat::write_all_at;
2-
use crate::{
3-
chunk2pos, pos2chunk, Chunk, Data, PathExt, WriteOut, WriteOutBuilder, CHUNKSZ_LOG, ZERO_CHUNK,
4-
};
2+
use crate::{chunk2pos, pos2chunk, Chunk, Data, PathExt, WriteOut, WriteOutBuilder, CHUNKSZ_LOG, ZERO_CHUNK};
53

64
use crossbeam::channel::{Receiver, Sender};
75
use crossbeam::thread;
@@ -15,13 +13,22 @@ use std::io;
1513
use std::io::prelude::*;
1614
use std::path::{Path, PathBuf};
1715

16+
/// File/block device restore target.
17+
///
18+
/// Chunks are written out-of-order as they are sent to the writer. Zeros are not written (i.e.,
19+
/// skipped over) if sparse mode is enabled.
1820
#[derive(Debug, Clone)]
1921
pub struct RandomAccess {
2022
path: PathBuf,
2123
sparse: Option<bool>,
2224
}
2325

2426
impl RandomAccess {
27+
/// Creates a builder which is finalized later by the [Extractor](struct.Extractor.html). If
28+
/// the user explicitely requests sparse to be active or not, set `sparse` to some bool. If
29+
/// sparse mode is not explicitely set, a heuristic is applied: files which can be truncated
30+
/// are written sparsely. Block devices are written sparsely if a patrol read suggests that
31+
/// they do not contain previously written data, e.g. after a discard.
2532
pub fn new<P: AsRef<Path>>(path: P, sparse: Option<bool>) -> Self {
2633
Self {
2734
path: path.as_ref().to_owned(),

src/writeout/stream.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use super::{WriteOut, WriteOutBuilder};
2-
use crate::{Chunk, Data, CHUNKSZ_LOG, ZERO_CHUNK};
1+
use crate::{Chunk, Data, WriteOutBuilder, WriteOut, CHUNKSZ_LOG, ZERO_CHUNK};
32

43
use crossbeam::channel::{Receiver, Sender};
54
use failure::Fallible;
@@ -8,6 +7,10 @@ use std::fmt;
87
use std::io::Write;
98
use std::rc::Rc;
109

10+
/// Streaming restore target, i.e. write to stdout.
11+
///
12+
/// The incoming chunk stream is assembled into sequence order in memory. Chunks are
13+
/// written out eagerly to keep memory usage to a minimum.
1114
pub struct Stream<W: ?Sized + Write> {
1215
out: Box<W>,
1316
}

0 commit comments

Comments
 (0)