1 use std
::collections
::HashSet
;
3 use std
::fs
::OpenOptions
;
4 use std
::os
::unix
::fs
::OpenOptionsExt
;
5 use std
::path
::{Path, PathBuf}
;
7 use anyhow
::{format_err, Error}
;
8 use futures
::future
::FutureExt
;
10 use tokio
::signal
::unix
::{signal, SignalKind}
;
12 use pathpatterns
::{MatchEntry, MatchType, PatternFlag}
;
14 use proxmox
::api
::cli
::*;
15 use proxmox
::api
::api
;
17 use proxmox_backup
::tools
;
18 use proxmox_backup
::pxar
::{flags, fuse, format_single_line_entry, ENCODER_MAX_ENTRIES}
;
20 fn extract_archive_from_reader
<R
: std
::io
::Read
>(
24 allow_existing_dirs
: bool
,
26 match_list
: &[MatchEntry
],
27 ) -> Result
<(), Error
> {
28 proxmox_backup
::pxar
::extract_archive(
29 pxar
::decoder
::Decoder
::from_std(reader
)?
,
36 println
!("{:?}", path
);
46 description
: "Archive name.",
49 description
: "List of paths or pattern matching files to restore",
53 description
: "Path or pattern matching files to restore.",
58 description
: "Target directory",
62 description
: "Verbose output.",
67 description
: "Ignore extended file attributes.",
72 description
: "Ignore file capabilities.",
77 description
: "Ignore access control list entries.",
81 "allow-existing-dirs": {
82 description
: "Allows directories to already exist on restore.",
87 description
: "File containing match pattern for files to restore.",
91 description
: "Ignore device nodes.",
96 description
: "Ignore fifos.",
101 description
: "Ignore sockets.",
108 /// Extract an archive.
111 pattern
: Option
<Vec
<String
>>,
112 target
: Option
<String
>,
117 allow_existing_dirs
: bool
,
118 files_from
: Option
<String
>,
119 no_device_nodes
: bool
,
122 ) -> Result
<(), Error
> {
123 let mut feature_flags
= flags
::DEFAULT
;
125 feature_flags ^
= flags
::WITH_XATTRS
;
128 feature_flags ^
= flags
::WITH_FCAPS
;
131 feature_flags ^
= flags
::WITH_ACL
;
134 feature_flags ^
= flags
::WITH_DEVICE_NODES
;
137 feature_flags ^
= flags
::WITH_FIFOS
;
140 feature_flags ^
= flags
::WITH_SOCKETS
;
143 let pattern
= pattern
.unwrap_or_else(Vec
::new
);
144 let target
= target
.as_ref().map_or_else(|| ".", String
::as_str
);
146 let mut match_list
= Vec
::new();
147 if let Some(filename
) = &files_from
{
148 for line
in proxmox_backup
::tools
::file_get_non_comment_lines(filename
)?
{
150 .map_err(|err
| format_err
!("error reading {}: {}", filename
, err
))?
;
152 MatchEntry
::parse_pattern(line
, PatternFlag
::PATH_NAME
, MatchType
::Include
)
153 .map_err(|err
| format_err
!("bad pattern in file '{}': {}", filename
, err
))?
,
158 for entry
in pattern
{
160 MatchEntry
::parse_pattern(entry
, PatternFlag
::PATH_NAME
, MatchType
::Include
)
161 .map_err(|err
| format_err
!("error in pattern: {}", err
))?
,
166 let stdin
= std
::io
::stdin();
167 let mut reader
= stdin
.lock();
168 extract_archive_from_reader(
178 println
!("PXAR extract: {}", archive
);
180 let file
= std
::fs
::File
::open(archive
)?
;
181 let mut reader
= std
::io
::BufReader
::new(file
);
182 extract_archive_from_reader(
199 description
: "Archive name.",
202 description
: "Source directory.",
205 description
: "Verbose output.",
210 description
: "Ignore extended file attributes.",
215 description
: "Ignore file capabilities.",
220 description
: "Ignore access control list entries.",
224 "all-file-systems": {
225 description
: "Include mounted sudirs.",
230 description
: "Ignore device nodes.",
235 description
: "Ignore fifos.",
240 description
: "Ignore sockets.",
245 description
: "List of paths or pattern matching files to exclude.",
249 description
: "Path or pattern matching files to restore",
254 description
: "Max number of entries loaded at once into memory",
256 default: ENCODER_MAX_ENTRIES
as isize,
258 maximum
: std
::isize::MAX
,
263 /// Create a new .pxar archive.
271 all_file_systems
: bool
,
272 no_device_nodes
: bool
,
275 exclude
: Option
<Vec
<String
>>,
277 ) -> Result
<(), Error
> {
279 let input
= exclude
.unwrap_or_else(Vec
::new
);
280 let mut pattern_list
= Vec
::with_capacity(input
.len());
283 MatchEntry
::parse_pattern(entry
, PatternFlag
::PATH_NAME
, MatchType
::Exclude
)
284 .map_err(|err
| format_err
!("error in exclude pattern: {}", err
))?
,
290 let device_set
= if all_file_systems
{
296 let source
= PathBuf
::from(source
);
298 let dir
= nix
::dir
::Dir
::open(
300 nix
::fcntl
::OFlag
::O_NOFOLLOW
,
301 nix
::sys
::stat
::Mode
::empty(),
304 let file
= OpenOptions
::new()
310 let writer
= std
::io
::BufWriter
::with_capacity(1024 * 1024, file
);
311 let mut feature_flags
= flags
::DEFAULT
;
313 feature_flags ^
= flags
::WITH_XATTRS
;
316 feature_flags ^
= flags
::WITH_FCAPS
;
319 feature_flags ^
= flags
::WITH_ACL
;
322 feature_flags ^
= flags
::WITH_DEVICE_NODES
;
325 feature_flags ^
= flags
::WITH_FIFOS
;
328 feature_flags ^
= flags
::WITH_SOCKETS
;
331 let writer
= pxar
::encoder
::sync
::StandardWriter
::new(writer
);
332 proxmox_backup
::pxar
::create_archive(
341 println
!("{:?}", path
);
345 entries_max
as usize,
355 archive
: { description: "Archive name." }
,
356 mountpoint
: { description: "Mountpoint for the file system." }
,
358 description
: "Verbose output, running in the foreground (for debugging).",
365 /// Mount the archive to the provided mountpoint via FUSE.
366 async
fn mount_archive(
370 ) -> Result
<(), Error
> {
371 let archive
= Path
::new(&archive
);
372 let mountpoint
= Path
::new(&mountpoint
);
373 let options
= OsStr
::new("ro,default_permissions");
375 let session
= fuse
::Session
::mount_path(&archive
, &options
, verbose
, mountpoint
)
377 .map_err(|err
| format_err
!("pxar mount failed: {}", err
))?
;
379 let mut interrupt
= signal(SignalKind
::interrupt())?
;
382 res
= session
.fuse() => res?
,
383 _
= interrupt
.recv().fuse() => {
385 eprintln
!("interrupted");
397 description
: "Archive name.",
400 description
: "Verbose output.",
407 /// List the contents of an archive.
408 fn dump_archive(archive
: String
, verbose
: bool
) -> Result
<(), Error
> {
409 for entry
in pxar
::decoder
::Decoder
::open(archive
)?
{
413 println
!("{}", format_single_line_entry(&entry
));
415 println
!("{:?}", entry
.path());
422 let cmd_def
= CliCommandMap
::new()
425 CliCommand
::new(&API_METHOD_CREATE_ARCHIVE
)
426 .arg_param(&["archive", "source"])
427 .completion_cb("archive", tools
::complete_file_name
)
428 .completion_cb("source", tools
::complete_file_name
),
432 CliCommand
::new(&API_METHOD_EXTRACT_ARCHIVE
)
433 .arg_param(&["archive", "target"])
434 .completion_cb("archive", tools
::complete_file_name
)
435 .completion_cb("target", tools
::complete_file_name
)
436 .completion_cb("files-from", tools
::complete_file_name
),
440 CliCommand
::new(&API_METHOD_MOUNT_ARCHIVE
)
441 .arg_param(&["archive", "mountpoint"])
442 .completion_cb("archive", tools
::complete_file_name
)
443 .completion_cb("mountpoint", tools
::complete_file_name
),
447 CliCommand
::new(&API_METHOD_DUMP_ARCHIVE
)
448 .arg_param(&["archive"])
449 .completion_cb("archive", tools
::complete_file_name
),
452 let rpcenv
= CliEnvironment
::new();
453 run_cli_command(cmd_def
, rpcenv
, Some(|future
| {
454 proxmox_backup
::tools
::runtime
::main(future
)