1 extern crate proxmox_backup
;
5 use proxmox_backup
::tools
;
6 use proxmox_backup
::cli
::command
::*;
7 use proxmox_backup
::api
::schema
::*;
8 use proxmox_backup
::api
::router
::*;
10 use serde_json
::{Value}
;
12 use std
::io
::{Read, Write}
;
13 use std
::path
::PathBuf
;
15 use proxmox_backup
::catar
::format_definition
::*;
16 use proxmox_backup
::catar
::encoder
::*;
17 use proxmox_backup
::catar
::decoder
::*;
19 use proxmox_backup
::tools
::*;
21 fn print_goodby_entries(buffer
: &[u8]) -> Result
<(), Error
> {
22 println
!("GOODBY START: {}", buffer
.len());
24 let entry_size
= std
::mem
::size_of
::<CaFormatGoodbyeItem
>();
25 if (buffer
.len() % entry_size
) != 0 {
26 bail
!("unexpected goodby item size ({})", entry_size
);
31 while pos
< buffer
.len() {
33 let item
= map_struct
::<CaFormatGoodbyeItem
>(&buffer
[pos
..pos
+entry_size
])?
;
35 if item
.hash
== CA_FORMAT_GOODBYE_TAIL_MARKER
{
36 println
!(" Entry Offset: {}", item
.offset
);
37 if item
.size
!= (buffer
.len() + 16) as u64 {
38 bail
!("gut unexpected goodby entry size (tail marker)");
41 println
!(" Offset: {}", item
.offset
);
42 println
!(" Size: {}", item
.size
);
43 println
!(" Hash: {:016x}", item
.hash
);
55 _rpcenv
: &mut RpcEnvironment
,
56 ) -> Result
<Value
, Error
> {
58 let archive
= tools
::required_string_param(¶m
, "archive")?
;
59 let file
= std
::fs
::File
::open(archive
)?
;
61 let mut reader
= std
::io
::BufReader
::new(file
);
63 let mut decoder
= CaTarDecoder
::new(&mut reader
)?
;
65 let root
= decoder
.root();
67 let stdout
= std
::io
::stdout();
68 let mut out
= stdout
.lock();
70 decoder
.print_filenames(&mut out
, &mut PathBuf
::from("."), &root
)?
;
78 _rpcenv
: &mut RpcEnvironment
,
79 ) -> Result
<Value
, Error
> {
81 let archive
= tools
::required_string_param(¶m
, "archive")?
;
82 let mut file
= std
::fs
::File
::open(archive
)?
;
84 println
!("CATAR {}", archive
);
86 let mut buffer
= [0u8; 16];
91 file
.read_exact(&mut buffer
)?
;
93 let header
= map_struct
::<CaFormatHeader
>(&mut buffer
)?
;
95 println
!("Type: {:016x}", header
.htype
);
96 println
!("Size: {}", header
.size
);
98 let mut rest
= vec
![0u8; (header
.size
as usize) - 16];
99 file
.read_exact(&mut rest
)?
;
101 if header
.htype
== CA_FORMAT_FILENAME
{
102 let name
= read_os_string(&rest
);
103 let hash
= compute_goodbye_hash(&rest
[..rest
.len()-1]);
104 println
!("Name: {:?} {:016x}", name
, hash
);
107 if header
.htype
== CA_FORMAT_ENTRY
{
108 let entry
= map_struct
::<CaFormatEntry
>(&mut rest
)?
;
109 println
!("Mode: {:08x} {:08x}", entry
.mode
, (entry
.mode
as u32) & libc
::S_IFDIR
);
110 if ((entry
.mode
as u32) & libc
::S_IFMT
) == libc
::S_IFDIR
{
114 if header
.htype
== CA_FORMAT_GOODBYE
{
116 print_goodby_entries(&rest
)?
;
117 if nesting
== 0 { break; }
127 _rpcenv
: &mut RpcEnvironment
,
128 ) -> Result
<Value
, Error
> {
130 let archive
= tools
::required_string_param(¶m
, "archive")?
;
131 let source
= tools
::required_string_param(¶m
, "source")?
;
133 let source
= std
::path
::PathBuf
::from(source
);
135 let mut dir
= nix
::dir
::Dir
::open(
136 &source
, nix
::fcntl
::OFlag
::O_NOFOLLOW
, nix
::sys
::stat
::Mode
::empty())?
;
138 let file
= std
::fs
::OpenOptions
::new()
143 let mut writer
= std
::io
::BufWriter
::with_capacity(1024*1024, file
);
145 CaTarEncoder
::encode(source
, &mut dir
, None
, &mut writer
)?
;
154 let cmd_def
= CliCommandMap
::new()
155 .insert("create", CliCommand
::new(
158 ObjectSchema
::new("Create new catar archive.")
159 .required("archive", StringSchema
::new("Archive name"))
160 .required("source", StringSchema
::new("Source directory."))
162 .arg_param(vec
!["archive", "source"])
163 .completion_cb("archive", tools
::complete_file_name
)
164 .completion_cb("source", tools
::complete_file_name
)
167 .insert("list", CliCommand
::new(
170 ObjectSchema
::new("List the contents of an archive.")
171 .required("archive", StringSchema
::new("Archive name."))
173 .arg_param(vec
!["archive"])
174 .completion_cb("archive", tools
::complete_file_name
)
177 .insert("dump", CliCommand
::new(
180 ObjectSchema
::new("Textual dump of archive contents (debug toolkit).")
181 .required("archive", StringSchema
::new("Archive name."))
183 .arg_param(vec
!["archive"])
184 .completion_cb("archive", tools
::complete_file_name
)
188 if let Err(err
) = run_cli_command(&cmd_def
.into()) {
189 eprintln
!("Error: {}", err
);
191 std
::process
::exit(-1);