]>
Commit | Line | Data |
---|---|---|
c60d34bd DM |
1 | extern crate proxmox_backup; |
2 | ||
3 | use failure::*; | |
4 | ||
ce7ba139 | 5 | use proxmox_backup::tools; |
4de0e142 | 6 | use proxmox_backup::cli::*; |
ef2f2efb | 7 | use proxmox_backup::api_schema::*; |
dc9a007b | 8 | use proxmox_backup::api_schema::router::*; |
c60d34bd DM |
9 | |
10 | use serde_json::{Value}; | |
11 | ||
02c7d8e5 | 12 | use std::io::{Read, Write}; |
e86c4924 | 13 | use std::path::PathBuf; |
c60d34bd DM |
14 | |
15 | use proxmox_backup::catar::format_definition::*; | |
02c7d8e5 | 16 | use proxmox_backup::catar::encoder::*; |
e86c4924 DM |
17 | use proxmox_backup::catar::decoder::*; |
18 | ||
c60d34bd DM |
19 | use proxmox_backup::tools::*; |
20 | ||
c60d34bd DM |
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 | ||
bf205f94 | 31 | while pos < buffer.len() { |
c60d34bd DM |
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 | ||
6049b71f DM |
52 | fn print_filenames( |
53 | param: Value, | |
54 | _info: &ApiMethod, | |
55 | _rpcenv: &mut RpcEnvironment, | |
56 | ) -> Result<Value, Error> { | |
e86c4924 | 57 | |
6049b71f | 58 | let archive = tools::required_string_param(¶m, "archive")?; |
e86c4924 DM |
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 | ||
6049b71f DM |
75 | fn dump_archive( |
76 | param: Value, | |
77 | _info: &ApiMethod, | |
78 | _rpcenv: &mut RpcEnvironment, | |
79 | ) -> Result<Value, Error> { | |
c60d34bd | 80 | |
6049b71f | 81 | let archive = tools::required_string_param(¶m, "archive")?; |
c60d34bd DM |
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 | ||
6049b71f DM |
124 | fn create_archive( |
125 | param: Value, | |
126 | _info: &ApiMethod, | |
127 | _rpcenv: &mut RpcEnvironment, | |
128 | ) -> Result<Value, Error> { | |
c60d34bd | 129 | |
6049b71f DM |
130 | let archive = tools::required_string_param(¶m, "archive")?; |
131 | let source = tools::required_string_param(¶m, "source")?; | |
02c7d8e5 DM |
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 | ||
219ef0e6 | 145 | CaTarEncoder::encode(source, &mut dir, None, &mut writer, false)?; |
c60d34bd | 146 | |
02c7d8e5 | 147 | writer.flush()?; |
c60d34bd DM |
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( | |
02c7d8e5 | 157 | create_archive, |
c60d34bd DM |
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"]) | |
ce7ba139 DM |
163 | .completion_cb("archive", tools::complete_file_name) |
164 | .completion_cb("source", tools::complete_file_name) | |
165 | .into() | |
c60d34bd | 166 | ) |
e86c4924 DM |
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"]) | |
ce7ba139 | 174 | .completion_cb("archive", tools::complete_file_name) |
e86c4924 DM |
175 | .into() |
176 | ) | |
c60d34bd DM |
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"]) | |
ce7ba139 | 184 | .completion_cb("archive", tools::complete_file_name) |
c60d34bd DM |
185 | .into() |
186 | ); | |
187 | ||
698d9d44 | 188 | run_cli_command(cmd_def.into()); |
c60d34bd | 189 | } |