Skip to content

Commit 8b41cec

Browse files
committed
⚗️ Add --fflags option to cli and add private chunk for store fflags
1 parent d0484d1 commit 8b41cec

13 files changed

Lines changed: 470 additions & 64 deletions

File tree

cli/src/chunk.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
mod acl;
2+
mod fflag;
23

34
pub use acl::*;
5+
pub use fflag::*;

cli/src/chunk/fflag.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use pna::{ChunkType, RawChunk};
2+
3+
/// Private chunk type for file flags (fflags).
4+
/// Name follows PNA chunk naming convention where case has semantic meaning:
5+
/// - lowercase first letter: ancillary (not critical)
6+
/// - lowercase second letter: private (not public)
7+
/// - uppercase third letter: reserved
8+
/// - lowercase fourth letter: safe to copy
9+
#[allow(non_upper_case_globals)]
10+
pub const ffLg: ChunkType = unsafe { ChunkType::from_unchecked(*b"ffLg") };
11+
12+
pub fn fflag_chunk(flag: &str) -> RawChunk {
13+
RawChunk::from_data(ffLg, flag.as_bytes())
14+
}

cli/src/command/append.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use crate::{
66
command::{
77
Command, ask_password, check_password,
88
core::{
9-
AclStrategy, CreateOptions, KeepOptions, OwnerOptions, PathFilter, PathTransformers,
10-
PathnameEditor, PermissionStrategy, StoreAs, TimeFilterResolver, TimeOptions,
11-
TimestampStrategy, XattrStrategy, collect_items, create_entry, entry_option,
9+
AclStrategy, CreateOptions, FflagsStrategy, KeepOptions, OwnerOptions, PathFilter,
10+
PathTransformers, PathnameEditor, PermissionStrategy, StoreAs, TimeFilterResolver,
11+
TimeOptions, TimestampStrategy, XattrStrategy, collect_items, create_entry,
12+
entry_option,
1213
re::{bsd::SubstitutionRule, gnu::TransformRule},
1314
read_paths, read_paths_stdin,
1415
},
@@ -339,6 +340,7 @@ fn append_to_archive(args: AppendCommand) -> anyhow::Result<()> {
339340
),
340341
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
341342
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
343+
fflags_strategy: FflagsStrategy::Never,
342344
};
343345
let owner_options = OwnerOptions::new(
344346
args.uname,

cli/src/command/core.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,31 @@ impl AclStrategy {
159159
}
160160
}
161161

162+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
163+
pub(crate) enum FflagsStrategy {
164+
Never,
165+
Always,
166+
}
167+
168+
impl FflagsStrategy {
169+
pub(crate) const fn from_flags(keep_fflags: bool, no_keep_fflags: bool) -> Self {
170+
if no_keep_fflags {
171+
Self::Never
172+
} else if keep_fflags {
173+
Self::Always
174+
} else {
175+
Self::Never
176+
}
177+
}
178+
}
179+
162180
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
163181
pub(crate) struct KeepOptions {
164182
pub(crate) timestamp_strategy: TimestampStrategy,
165183
pub(crate) permission_strategy: PermissionStrategy,
166184
pub(crate) xattr_strategy: XattrStrategy,
167185
pub(crate) acl_strategy: AclStrategy,
186+
pub(crate) fflags_strategy: FflagsStrategy,
168187
}
169188

170189
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
@@ -730,6 +749,23 @@ pub(crate) fn apply_metadata<'p>(
730749
if let XattrStrategy::Always = keep_options.xattr_strategy {
731750
log::warn!("Currently extended attribute is not supported on this platform.");
732751
}
752+
if let FflagsStrategy::Always = keep_options.fflags_strategy {
753+
match utils::fs::get_flags(path) {
754+
Ok(flags) => {
755+
for flag in flags {
756+
entry.add_extra_chunk(crate::chunk::fflag_chunk(&flag));
757+
}
758+
}
759+
Err(e) if e.kind() == std::io::ErrorKind::Unsupported => {
760+
log::warn!(
761+
"File flags are not supported on filesystem for '{}': {}",
762+
path.display(),
763+
e
764+
);
765+
}
766+
Err(e) => return Err(e),
767+
}
768+
}
733769
Ok(entry)
734770
}
735771

cli/src/command/create.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use crate::{
66
command::{
77
Command, ask_password, check_password,
88
core::{
9-
AclStrategy, CreateOptions, KeepOptions, MIN_SPLIT_PART_BYTES, OwnerOptions,
10-
PathFilter, PathTransformers, PathnameEditor, PermissionStrategy, StoreAs,
11-
TimeFilterResolver, TimeOptions, TimestampStrategy, XattrStrategy, collect_items,
12-
create_entry, entry_option,
9+
AclStrategy, CreateOptions, FflagsStrategy, KeepOptions, MIN_SPLIT_PART_BYTES,
10+
OwnerOptions, PathFilter, PathTransformers, PathnameEditor, PermissionStrategy,
11+
StoreAs, TimeFilterResolver, TimeOptions, TimestampStrategy, XattrStrategy,
12+
collect_items, create_entry, entry_option,
1313
re::{bsd::SubstitutionRule, gnu::TransformRule},
1414
read_paths, read_paths_stdin, write_split_archive,
1515
},
@@ -423,6 +423,7 @@ fn create_archive(args: CreateCommand) -> anyhow::Result<()> {
423423
),
424424
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
425425
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
426+
fflags_strategy: FflagsStrategy::Never,
426427
};
427428
let owner_options = OwnerOptions::new(
428429
args.uname,

cli/src/command/extract.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#[cfg(feature = "memmap")]
22
use crate::command::core::run_entries;
3-
#[cfg(feature = "acl")]
43
use crate::ext::*;
54
#[cfg(any(unix, windows))]
65
use crate::utils::fs::lchown;
@@ -9,8 +8,9 @@ use crate::{
98
command::{
109
Command, ask_password,
1110
core::{
12-
AclStrategy, KeepOptions, OwnerOptions, PathFilter, PathTransformers, PathnameEditor,
13-
PermissionStrategy, TimestampStrategy, XattrStrategy, collect_split_archives,
11+
AclStrategy, FflagsStrategy, KeepOptions, OwnerOptions, PathFilter, PathTransformers,
12+
PathnameEditor, PermissionStrategy, TimestampStrategy, XattrStrategy,
13+
collect_split_archives,
1414
path_lock::PathLocks,
1515
re::{bsd::SubstitutionRule, gnu::TransformRule},
1616
read_paths, run_process_archive,
@@ -280,6 +280,7 @@ fn extract_archive(args: ExtractCommand) -> anyhow::Result<()> {
280280
),
281281
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
282282
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
283+
fflags_strategy: FflagsStrategy::Never,
283284
};
284285
let owner_options = OwnerOptions::new(
285286
args.uname,
@@ -754,6 +755,22 @@ where
754755
if let AclStrategy::Always = keep_options.acl_strategy {
755756
log::warn!("Please enable `acl` feature and rebuild and install pna.");
756757
}
758+
if let FflagsStrategy::Always = keep_options.fflags_strategy {
759+
let flags = item.fflags();
760+
if !flags.is_empty() {
761+
match utils::fs::set_flags(path, &flags) {
762+
Ok(()) => {}
763+
Err(e) if e.kind() == std::io::ErrorKind::Unsupported => {
764+
log::warn!(
765+
"File flags are not supported on filesystem for '{}': {}",
766+
path.display(),
767+
e
768+
);
769+
}
770+
Err(e) => return Err(e),
771+
}
772+
}
773+
}
757774
Ok(())
758775
}
759776

cli/src/command/stdio.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use crate::{
88
append::{open_archive_then_seek_to_end, run_append_archive},
99
ask_password, check_password,
1010
core::{
11-
AclStrategy, CreateOptions, KeepOptions, OwnerOptions, PathFilter, PathTransformers,
12-
PathnameEditor, PermissionStrategy, TimeFilterResolver, TimeOptions, TimestampStrategy,
13-
XattrStrategy, collect_items, collect_split_archives, entry_option,
11+
AclStrategy, CreateOptions, FflagsStrategy, KeepOptions, OwnerOptions, PathFilter,
12+
PathTransformers, PathnameEditor, PermissionStrategy, TimeFilterResolver, TimeOptions,
13+
TimestampStrategy, XattrStrategy, collect_items, collect_split_archives, entry_option,
1414
path_lock::PathLocks,
1515
re::{bsd::SubstitutionRule, gnu::TransformRule},
1616
read_paths,
@@ -73,6 +73,7 @@ use std::{env, io, path::PathBuf, sync::Arc, time::SystemTime};
7373
group(ArgGroup::new("ctime-newer-than-source").args(["newer_ctime", "newer_ctime_than"])),
7474
group(ArgGroup::new("mtime-older-than-source").args(["older_mtime", "older_mtime_than"])),
7575
group(ArgGroup::new("mtime-newer-than-source").args(["newer_mtime", "newer_mtime_than"])),
76+
group(ArgGroup::new("keep-fflags-flag").args(["keep_fflags", "no_keep_fflags"])),
7677
)]
7778
#[cfg_attr(windows, command(
7879
group(ArgGroup::new("windows-unstable-keep-permission").args(["keep_permission", "no_keep_permission"]).requires("unstable")),
@@ -190,6 +191,18 @@ pub(crate) struct StdioCommand {
190191
help = "Do not archive acl of files. This is the inverse option of --keep-acl (unstable)"
191192
)]
192193
no_keep_acl: bool,
194+
#[arg(
195+
long,
196+
visible_aliases = ["preserve-fflags", "fflags"],
197+
help = "Archiving the file flags of the files (unstable)"
198+
)]
199+
keep_fflags: bool,
200+
#[arg(
201+
long,
202+
visible_aliases = ["no-preserve-fflags", "no-fflags"],
203+
help = "Do not archive file flags of files. This is the inverse option of --keep-fflags (unstable)"
204+
)]
205+
no_keep_fflags: bool,
193206
#[arg(long, help = "Solid mode archive")]
194207
pub(crate) solid: bool,
195208
#[command(flatten)]
@@ -526,6 +539,7 @@ fn run_create_archive(args: StdioCommand) -> anyhow::Result<()> {
526539
),
527540
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
528541
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
542+
fflags_strategy: FflagsStrategy::from_flags(args.keep_fflags, args.no_keep_fflags),
529543
};
530544
let owner_options = OwnerOptions::new(
531545
args.uname,
@@ -606,6 +620,7 @@ fn run_extract_archive(args: StdioCommand) -> anyhow::Result<()> {
606620
),
607621
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
608622
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
623+
fflags_strategy: FflagsStrategy::from_flags(args.keep_fflags, args.no_keep_fflags),
609624
},
610625
owner_options: OwnerOptions::new(
611626
args.uname,
@@ -736,6 +751,7 @@ fn run_append(args: StdioCommand) -> anyhow::Result<()> {
736751
),
737752
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
738753
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
754+
fflags_strategy: FflagsStrategy::from_flags(args.keep_fflags, args.no_keep_fflags),
739755
};
740756
let owner_options = OwnerOptions::new(
741757
args.uname,

cli/src/command/update.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ use crate::{
1010
command::{
1111
Command, ask_password, check_password,
1212
core::{
13-
AclStrategy, CreateOptions, KeepOptions, OwnerOptions, PathFilter, PathTransformers,
14-
PathnameEditor, PermissionStrategy, TimeFilterResolver, TimeOptions, TimestampStrategy,
15-
TransformStrategy, TransformStrategyKeepSolid, TransformStrategyUnSolid, XattrStrategy,
16-
collect_items, collect_split_archives, create_entry, entry_option,
13+
AclStrategy, CreateOptions, FflagsStrategy, KeepOptions, OwnerOptions, PathFilter,
14+
PathTransformers, PathnameEditor, PermissionStrategy, TimeFilterResolver, TimeOptions,
15+
TimestampStrategy, TransformStrategy, TransformStrategyKeepSolid,
16+
TransformStrategyUnSolid, XattrStrategy, collect_items, collect_split_archives,
17+
create_entry, entry_option,
1718
re::{bsd::SubstitutionRule, gnu::TransformRule},
1819
read_paths, read_paths_stdin,
1920
},
@@ -348,6 +349,7 @@ fn update_archive<Strategy: TransformStrategy>(args: UpdateCommand) -> anyhow::R
348349
),
349350
xattr_strategy: XattrStrategy::from_flags(args.keep_xattr, args.no_keep_xattr),
350351
acl_strategy: AclStrategy::from_flags(args.keep_acl, args.no_keep_acl),
352+
fflags_strategy: FflagsStrategy::Never,
351353
};
352354
let owner_options = OwnerOptions::new(
353355
args.uname,

cli/src/ext.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub(crate) type Acls = HashMap<AcePlatform, Vec<Ace>>;
1313

1414
pub(crate) trait NormalEntryExt {
1515
fn acl(&self) -> io::Result<Acls>;
16+
fn fflags(&self) -> Vec<String>;
1617
}
1718

1819
impl<T> NormalEntryExt for NormalEntry<T>
@@ -43,6 +44,20 @@ where
4344
}
4445
Ok(acls)
4546
}
47+
48+
#[inline]
49+
fn fflags(&self) -> Vec<String> {
50+
self.extra_chunks()
51+
.iter()
52+
.filter_map(|c| {
53+
if c.ty() == chunk::ffLg {
54+
std::str::from_utf8(c.data()).ok().map(str::to_string)
55+
} else {
56+
None
57+
}
58+
})
59+
.collect()
60+
}
4661
}
4762

4863
pub(crate) trait PermissionExt {

cli/src/utils/fs.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,50 @@ pub(crate) fn lchown<P: AsRef<Path>>(
6666
inner(path.as_ref(), owner, group)
6767
}
6868

69+
pub(crate) fn get_flags<P: AsRef<Path>>(path: P) -> io::Result<Vec<String>> {
70+
#[cfg(any(
71+
target_os = "macos",
72+
target_os = "linux",
73+
target_os = "android",
74+
target_os = "freebsd"
75+
))]
76+
fn inner(path: &Path) -> io::Result<Vec<String>> {
77+
crate::utils::os::unix::fs::get_flags(path)
78+
}
79+
#[cfg(not(any(
80+
target_os = "macos",
81+
target_os = "linux",
82+
target_os = "android",
83+
target_os = "freebsd"
84+
)))]
85+
fn inner(_path: &Path) -> io::Result<Vec<String>> {
86+
Ok(Vec::new())
87+
}
88+
inner(path.as_ref())
89+
}
90+
91+
pub(crate) fn set_flags<P: AsRef<Path>>(path: P, flags: &[String]) -> io::Result<()> {
92+
#[cfg(any(
93+
target_os = "macos",
94+
target_os = "linux",
95+
target_os = "android",
96+
target_os = "freebsd"
97+
))]
98+
fn inner(path: &Path, flags: &[String]) -> io::Result<()> {
99+
crate::utils::os::unix::fs::set_flags(path, flags)
100+
}
101+
#[cfg(not(any(
102+
target_os = "macos",
103+
target_os = "linux",
104+
target_os = "android",
105+
target_os = "freebsd"
106+
)))]
107+
fn inner(_path: &Path, _flags: &[String]) -> io::Result<()> {
108+
Ok(())
109+
}
110+
inner(path.as_ref(), flags)
111+
}
112+
69113
#[inline]
70114
pub(crate) fn file_create(path: impl AsRef<Path>, overwrite: bool) -> io::Result<fs::File> {
71115
if overwrite {

0 commit comments

Comments
 (0)