Skip to content

Commit

Permalink
Add owning user and group, together with unix permissions (#194)
Browse files Browse the repository at this point in the history
* added basic dac mode

* compressed index size in basic test increased to 90

* index tests pass

* added permissions to restore module

* print backed up mode for each file

* added proper octal formatting

* added mode display to restore

* added dac to test so it can pass

* made tests pass

* restructured file

* renamed Permissions to UnixMode and reformatted mode display

* added SUID, SGID, and sticky

* spelling correction

* added unix mode to ls command

* changed default mode from 0o777 to 0o775

* edited comments

* fixed tests to work on DevContainer

* added long_listing option "-l" to backup, restore, and ls commands

* attempt to fix windows build

* reordered code

* filetime::set_symlink_file_times not used on windows

* added from<UnixMode> for Permissions on not(unix)

* added From<&str> for UnixMode

* fixed fmt

* fixed From<&str> for UnixMode and typo in From<Permissions> in not(unix)

* refactored to (hopefully) build on windows

* replaced another instance of umode.into in not(unix) cfg

* fixed UnixMode::readonly function

* made tests easier so they pass on WIndows

* disabled permission restore on Windows

* fixed syntax error

* cargo fmt

* removed problem messages as they were causing tests to fail

* added users crate to target.'cfg(unix)'.dependencies

* added owning user and group to necessary files

* added ownership to restore function

* removed debug print

* created long_listing test for unix

* removed unused code

* added test to verify user, group, and mode are backed up and restored properly

* fmt and clippy

* added comment

* fixed not(unix) build error

* split regex onto multiple lines for readability

* added owner to is_unchanged_from

* fixed serialization test to match new mode default

* added mask to only compare permissions, sticky, and set bits

* added unix_mode crate

* added comment

* use unix_mode crate and don't compare filetype

* removed partial from<str> impl

* renamed Entry::umode to unix_mode

* Changed UnixMode to a tuple type struct

* added format changes to doc

* errors in restoring permissions or owner are now recoverable

* format

* compacted Owner serialization

* format

* added nix as a dev-dependency
for testing user and group

* removed nix dependency

* added test backup_unix_permissions
to properly test unix_mode field

* added todo to comments

* added long_listing_old_archive test

* removed & to appease clippy

* changed unix_mode to strip the inode type bits

* added fixed column alignment of 10 to owner

* changed UnixMode to store mode bits as Option

* removed ampersands to appease clippy

* format

* removed more ampersands to appease clippy

* removed more needless borrows in tests

* removed more needless borrows in tests

* removed more ampersands

* removed more needless borrows

* fixed windows build error

* removed more borrows to appease clippy

* Set root directory permissions in test

* Update docs for owner/group

* Split out Unix permission tests to their own file

* Add a new old_archive with names and permissions

* Remove an unnecessary borrow #clippy

* Refactor owner/group restore code a bit

* Turn off CI clippy and rustfmt

Co-authored-by: Martin Pool <[email protected]>

Fixes #46
  • Loading branch information
believeinlain authored Jan 1, 2023
1 parent d703dd1 commit d2f7629
Show file tree
Hide file tree
Showing 27 changed files with 805 additions and 29 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ jobs:
run: cargo build --all-targets
- name: Test
run: cargo test -- --include-ignored
- name: Clippy
run: cargo clippy --all-targets -- -D clippy::all
- name: rustfmt
run: cargo fmt --all -- --check
# Clippy and rustfmt are excellent tools but are turned off here because it's too
# easy for PRs to fail due to irrelevant changes including Clippy flagging problems
# that it did not notice before.
# - name: clippy
# run: cargo clippy --all-targets -- -d clippy::all
# - name: rustfmt
# run: cargo fmt --all -- --check
50 changes: 49 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ thousands = "0.2.0"
tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "fmt"] }
unix_mode = "0.1"
url = "2.2.2"

[target.'cfg(unix)'.dependencies]
users = "0.11"
nix = "0.25"

[dependencies.clap]
version = "4.0"
features = ["derive", "deprecated", "wrap_help"]
Expand Down
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Conserve release history

## v0.6.17

Not released yet!

- New: Support for storing, restoring, and listing Unix owner, group, and permissions.
Thanks to @believeinlain.

## v0.6.16

Released 2022-08-12
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ The current data format (called "0.6") will be readable by future releases for a

Be aware Conserve is developed as a part-time non-commercial project and there's no guarantee of support or reliability. Bug reports are welcome but I cannot promise they will receive a resolution within any particular time frame.

As of October 2022 I am primarily spending my open-souce time on [cargo-mutants](https://github.com/sourcefrog/cargo-mutants). When that is feature complete, which is anticipated by early-mid 2023, I will likely come back to working more on Conserve.
As of October 2022 I am primarily spending my open-source time on [cargo-mutants](https://github.com/sourcefrog/cargo-mutants). When that is feature complete, which is anticipated by early-mid 2023, I will likely come back to working more on Conserve.

There is still room for several performance improvements and features. See the [issue tracker][issues] for a list.

Expand Down
4 changes: 4 additions & 0 deletions doc/format.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ An index entry is a json dict with keys
- `mtime`: integer seconds past the Unix epoch
- `mtime_nanos`: (optional) fractional part of the mtime, as nanoseconds.
- `kind`: one of `"File"`, `"Dir"`, `"Symlink"`
- `unix_mode`: the unix mode bits consisting of the sticky bit, set uid bit,
set gid bit, and permission bits
- `user`: optionally, a string specifying the file owner
- `group`: optionally, a string specifying the primary group owner
- `addrs`: a list of tuples of:
- `hash`: data block hash: from the current or any parent directory
- `start`: the offset within the uncompressed content of the block for the
Expand Down
17 changes: 16 additions & 1 deletion src/backup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub struct BackupOptions {
/// Exclude these globs from the backup.
pub exclude: Exclude,

/// If printing filenames, include metadata such as file permissions
pub long_listing: bool,

pub max_entries_per_hunk: usize,
}

Expand All @@ -44,6 +47,7 @@ impl Default for BackupOptions {
print_filenames: false,
exclude: Exclude::nothing(),
max_entries_per_hunk: crate::index::MAX_ENTRIES_PER_HUNK,
long_listing: false,
}
}
}
Expand Down Expand Up @@ -117,7 +121,18 @@ pub fn backup(
}
Ok(Some(diff_kind)) => {
if options.print_filenames && diff_kind != DiffKind::Unchanged {
writeln!(view, "{} {}", diff_kind.as_sigil(), entry.apath())?;
if options.long_listing {
writeln!(
view,
"{} {} {} {}",
diff_kind.as_sigil(),
entry.unix_mode(),
entry.owner(),
entry.apath()
)?;
} else {
writeln!(view, "{} {}", diff_kind.as_sigil(), entry.apath())?;
}
}
view.update(|model| match diff_kind {
DiffKind::Changed => model.entries_changed += 1,
Expand Down
17 changes: 17 additions & 0 deletions src/bin/conserve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ enum Command {
exclude_from: Vec<String>,
#[arg(long)]
no_stats: bool,
/// Show permissions, owner, and group in verbose output.
#[arg(long, short = 'l')]
long_listing: bool,
},

#[command(subcommand)]
Expand Down Expand Up @@ -123,6 +126,10 @@ enum Command {
exclude: Vec<String>,
#[arg(long, short = 'E')]
exclude_from: Vec<String>,

/// Show permissions, owner, and group.
#[arg(short = 'l')]
long_listing: bool,
},

/// Copy a stored tree to a restore directory.
Expand All @@ -143,6 +150,9 @@ enum Command {
only_subtree: Option<Apath>,
#[arg(long)]
no_stats: bool,
/// Show permissions, owner, and group in verbose output.
#[arg(long, short = 'l')]
long_listing: bool,
},

/// Show the total size of files in a stored tree or source directory, with exclusions.
Expand Down Expand Up @@ -248,12 +258,14 @@ impl Command {
exclude,
exclude_from,
no_stats,
long_listing,
} => {
let exclude = ExcludeBuilder::from_args(exclude, exclude_from)?.build()?;
let source = &LiveTree::open(source)?;
let options = BackupOptions {
print_filenames: *verbose,
exclude,
long_listing: *long_listing,
..Default::default()
};
let stats = backup(&Archive::open(open_transport(archive)?)?, source, &options)?;
Expand Down Expand Up @@ -348,6 +360,7 @@ impl Command {
stos,
exclude,
exclude_from,
long_listing,
} => {
let exclude = ExcludeBuilder::from_args(exclude, exclude_from)?.build()?;
if let Some(archive) = &stos.archive {
Expand All @@ -356,12 +369,14 @@ impl Command {
stored_tree_from_opt(archive, &stos.backup)?
.iter_entries(Apath::root(), exclude)?,
&mut stdout,
*long_listing,
)?;
} else {
show::show_entry_names(
LiveTree::open(stos.source.clone().unwrap())?
.iter_entries(Apath::root(), exclude)?,
&mut stdout,
*long_listing,
)?;
}
}
Expand All @@ -375,6 +390,7 @@ impl Command {
exclude_from,
only_subtree,
no_stats,
long_listing,
} => {
let band_selection = band_selection_policy_from_opt(backup);
let archive = Archive::open(open_transport(archive)?)?;
Expand All @@ -385,6 +401,7 @@ impl Command {
only_subtree: only_subtree.clone(),
band_selection,
overwrite: *force_overwrite,
long_listing: *long_listing,
};

let stats = restore(&archive, destination, &options)?;
Expand Down
6 changes: 6 additions & 0 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use std::fmt::Debug;

use crate::kind::Kind;
use crate::owner::Owner;
use crate::unix_mode::UnixMode;
use crate::unix_time::UnixTime;
use crate::*;

Expand All @@ -26,12 +28,16 @@ pub trait Entry: Debug + Eq + PartialEq {
fn mtime(&self) -> UnixTime;
fn size(&self) -> Option<u64>;
fn symlink_target(&self) -> &Option<String>;
fn unix_mode(&self) -> UnixMode;
fn owner(&self) -> Owner;

/// True if the metadata supports an assumption the file contents have
/// not changed.
fn is_unchanged_from<O: Entry>(&self, basis_entry: &O) -> bool {
basis_entry.kind() == self.kind()
&& basis_entry.mtime() == self.mtime()
&& basis_entry.size() == self.size()
&& basis_entry.unix_mode() == self.unix_mode()
&& basis_entry.owner() == self.owner()
}
}
Loading

0 comments on commit d2f7629

Please sign in to comment.