]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/catar.rs
src/catar/encoder.rs: add verbose flags
[proxmox-backup.git] / src / bin / catar.rs
1 extern crate proxmox_backup;
2
3 use failure::*;
4
5 use proxmox_backup::tools;
6 use proxmox_backup::cli::*;
7 use proxmox_backup::api_schema::*;
8 use proxmox_backup::api_schema::router::*;
9
10 use serde_json::{Value};
11
12 use std::io::{Read, Write};
13 use std::path::PathBuf;
14
15 use proxmox_backup::catar::format_definition::*;
16 use proxmox_backup::catar::encoder::*;
17 use proxmox_backup::catar::decoder::*;
18
19 use proxmox_backup::tools::*;
20
21 fn print_goodby_entries(buffer: &[u8]) -> Result<(), Error> {
22 println!("GOODBY START: {}", buffer.len());
23
24 let entry_size = std::mem::size_of::<CaFormatGoodbyeItem>();
25 if (buffer.len() % entry_size) != 0 {
26 bail!("unexpected goodby item size ({})", entry_size);
27 }
28
29 let mut pos = 0;
30
31 while pos < buffer.len() {
32
33 let item = map_struct::<CaFormatGoodbyeItem>(&buffer[pos..pos+entry_size])?;
34
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)");
39 }
40 } else {
41 println!(" Offset: {}", item.offset);
42 println!(" Size: {}", item.size);
43 println!(" Hash: {:016x}", item.hash);
44 }
45
46 pos += entry_size;
47 }
48
49 Ok(())
50 }
51
52 fn print_filenames(
53 param: Value,
54 _info: &ApiMethod,
55 _rpcenv: &mut RpcEnvironment,
56 ) -> Result<Value, Error> {
57
58 let archive = tools::required_string_param(&param, "archive")?;
59 let file = std::fs::File::open(archive)?;
60
61 let mut reader = std::io::BufReader::new(file);
62
63 let mut decoder = CaTarDecoder::new(&mut reader)?;
64
65 let root = decoder.root();
66
67 let stdout = std::io::stdout();
68 let mut out = stdout.lock();
69
70 decoder.print_filenames(&mut out, &mut PathBuf::from("."), &root)?;
71
72 Ok(Value::Null)
73 }
74
75 fn dump_archive(
76 param: Value,
77 _info: &ApiMethod,
78 _rpcenv: &mut RpcEnvironment,
79 ) -> Result<Value, Error> {
80
81 let archive = tools::required_string_param(&param, "archive")?;
82 let mut file = std::fs::File::open(archive)?;
83
84 println!("CATAR {}", archive);
85
86 let mut buffer = [0u8; 16];
87
88 let mut nesting = 0;
89
90 loop {
91 file.read_exact(&mut buffer)?;
92
93 let header = map_struct::<CaFormatHeader>(&mut buffer)?;
94
95 println!("Type: {:016x}", header.htype);
96 println!("Size: {}", header.size);
97
98 let mut rest = vec![0u8; (header.size as usize) - 16];
99 file.read_exact(&mut rest)?;
100
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);
105 }
106
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 {
111 nesting += 1;
112 }
113 }
114 if header.htype == CA_FORMAT_GOODBYE {
115 nesting -= 1;
116 print_goodby_entries(&rest)?;
117 if nesting == 0 { break; }
118 }
119 }
120
121 Ok(Value::Null)
122 }
123
124 fn create_archive(
125 param: Value,
126 _info: &ApiMethod,
127 _rpcenv: &mut RpcEnvironment,
128 ) -> Result<Value, Error> {
129
130 let archive = tools::required_string_param(&param, "archive")?;
131 let source = tools::required_string_param(&param, "source")?;
132
133 let source = std::path::PathBuf::from(source);
134
135 let mut dir = nix::dir::Dir::open(
136 &source, nix::fcntl::OFlag::O_NOFOLLOW, nix::sys::stat::Mode::empty())?;
137
138 let file = std::fs::OpenOptions::new()
139 .create_new(true)
140 .write(true)
141 .open(archive)?;
142
143 let mut writer = std::io::BufWriter::with_capacity(1024*1024, file);
144
145 CaTarEncoder::encode(source, &mut dir, None, &mut writer, false)?;
146
147 writer.flush()?;
148
149 Ok(Value::Null)
150 }
151
152 fn main() {
153
154 let cmd_def = CliCommandMap::new()
155 .insert("create", CliCommand::new(
156 ApiMethod::new(
157 create_archive,
158 ObjectSchema::new("Create new catar archive.")
159 .required("archive", StringSchema::new("Archive name"))
160 .required("source", StringSchema::new("Source directory."))
161 ))
162 .arg_param(vec!["archive", "source"])
163 .completion_cb("archive", tools::complete_file_name)
164 .completion_cb("source", tools::complete_file_name)
165 .into()
166 )
167 .insert("list", CliCommand::new(
168 ApiMethod::new(
169 print_filenames,
170 ObjectSchema::new("List the contents of an archive.")
171 .required("archive", StringSchema::new("Archive name."))
172 ))
173 .arg_param(vec!["archive"])
174 .completion_cb("archive", tools::complete_file_name)
175 .into()
176 )
177 .insert("dump", CliCommand::new(
178 ApiMethod::new(
179 dump_archive,
180 ObjectSchema::new("Textual dump of archive contents (debug toolkit).")
181 .required("archive", StringSchema::new("Archive name."))
182 ))
183 .arg_param(vec!["archive"])
184 .completion_cb("archive", tools::complete_file_name)
185 .into()
186 );
187
188 run_cli_command(cmd_def.into());
189 }