]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/pxar.rs
tree-wide: use 'dyn' for all trait objects
[proxmox-backup.git] / src / bin / pxar.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::Write;
13 use std::path::{Path, PathBuf};
14 use std::fs::OpenOptions;
15 use std::os::unix::fs::OpenOptionsExt;
16
17 use proxmox_backup::pxar;
18
19 fn print_filenames(
20 param: Value,
21 _info: &ApiMethod,
22 _rpcenv: &mut dyn RpcEnvironment,
23 ) -> Result<Value, Error> {
24
25 let archive = tools::required_string_param(&param, "archive")?;
26 let file = std::fs::File::open(archive)?;
27
28 let mut reader = std::io::BufReader::new(file);
29
30 let mut feature_flags = pxar::CA_FORMAT_DEFAULT;
31 feature_flags ^= pxar::CA_FORMAT_WITH_XATTRS;
32 feature_flags ^= pxar::CA_FORMAT_WITH_FCAPS;
33 let mut decoder = pxar::SequentialDecoder::new(&mut reader, feature_flags);
34
35 let stdout = std::io::stdout();
36 let mut out = stdout.lock();
37
38 let mut path = PathBuf::from(".");
39 decoder.dump_entry(&mut path, false, &mut out)?;
40
41 Ok(Value::Null)
42 }
43
44 fn dump_archive(
45 param: Value,
46 _info: &ApiMethod,
47 _rpcenv: &mut dyn RpcEnvironment,
48 ) -> Result<Value, Error> {
49
50 let archive = tools::required_string_param(&param, "archive")?;
51 let with_xattrs = param["with-xattrs"].as_bool().unwrap_or(false);
52 let with_fcaps = param["with-fcaps"].as_bool().unwrap_or(false);
53 let with_acls = param["with-acls"].as_bool().unwrap_or(false);
54 let file = std::fs::File::open(archive)?;
55
56 let mut reader = std::io::BufReader::new(file);
57
58 let mut feature_flags = pxar::CA_FORMAT_DEFAULT;
59 if !with_xattrs {
60 feature_flags ^= pxar::CA_FORMAT_WITH_XATTRS;
61 }
62 if !with_fcaps {
63 feature_flags ^= pxar::CA_FORMAT_WITH_FCAPS;
64 }
65 if !with_acls {
66 feature_flags ^= pxar::CA_FORMAT_WITH_ACL;
67 }
68 let mut decoder = pxar::SequentialDecoder::new(&mut reader, feature_flags);
69
70 let stdout = std::io::stdout();
71 let mut out = stdout.lock();
72
73 println!("PXAR dump: {}", archive);
74
75 let mut path = PathBuf::new();
76 decoder.dump_entry(&mut path, true, &mut out)?;
77
78 Ok(Value::Null)
79 }
80
81 fn extract_archive(
82 param: Value,
83 _info: &ApiMethod,
84 _rpcenv: &mut dyn RpcEnvironment,
85 ) -> Result<Value, Error> {
86
87 let archive = tools::required_string_param(&param, "archive")?;
88 let target = tools::required_string_param(&param, "target")?;
89 let verbose = param["verbose"].as_bool().unwrap_or(false);
90 let no_xattrs = param["no-xattrs"].as_bool().unwrap_or(false);
91 let no_fcaps = param["no-fcaps"].as_bool().unwrap_or(false);
92 let no_acls = param["no-acls"].as_bool().unwrap_or(false);
93
94 let file = std::fs::File::open(archive)?;
95
96 let mut reader = std::io::BufReader::new(file);
97 let mut feature_flags = pxar::CA_FORMAT_DEFAULT;
98 if no_xattrs {
99 feature_flags ^= pxar::CA_FORMAT_WITH_XATTRS;
100 }
101 if no_fcaps {
102 feature_flags ^= pxar::CA_FORMAT_WITH_FCAPS;
103 }
104 if no_acls {
105 feature_flags ^= pxar::CA_FORMAT_WITH_ACL;
106 }
107
108 let mut decoder = pxar::SequentialDecoder::new(&mut reader, feature_flags);
109
110 decoder.restore(Path::new(target), & |path| {
111 if verbose {
112 println!("{:?}", path);
113 }
114 Ok(())
115 })?;
116
117 Ok(Value::Null)
118 }
119
120 fn create_archive(
121 param: Value,
122 _info: &ApiMethod,
123 _rpcenv: &mut dyn RpcEnvironment,
124 ) -> Result<Value, Error> {
125
126 let archive = tools::required_string_param(&param, "archive")?;
127 let source = tools::required_string_param(&param, "source")?;
128 let verbose = param["verbose"].as_bool().unwrap_or(false);
129 let all_file_systems = param["all-file-systems"].as_bool().unwrap_or(false);
130 let no_xattrs = param["no-xattrs"].as_bool().unwrap_or(false);
131 let no_fcaps = param["no-fcaps"].as_bool().unwrap_or(false);
132 let no_acls = param["no-acls"].as_bool().unwrap_or(false);
133
134 let source = PathBuf::from(source);
135
136 let mut dir = nix::dir::Dir::open(
137 &source, nix::fcntl::OFlag::O_NOFOLLOW, nix::sys::stat::Mode::empty())?;
138
139 let file = OpenOptions::new()
140 .create_new(true)
141 .write(true)
142 .mode(0o640)
143 .open(archive)?;
144
145 let mut writer = std::io::BufWriter::with_capacity(1024*1024, file);
146 let mut feature_flags = pxar::CA_FORMAT_DEFAULT;
147 if no_xattrs {
148 feature_flags ^= pxar::CA_FORMAT_WITH_XATTRS;
149 }
150 if no_fcaps {
151 feature_flags ^= pxar::CA_FORMAT_WITH_FCAPS;
152 }
153 if no_acls {
154 feature_flags ^= pxar::CA_FORMAT_WITH_ACL;
155 }
156
157 pxar::Encoder::encode(source, &mut dir, &mut writer, all_file_systems, verbose, feature_flags)?;
158
159 writer.flush()?;
160
161 Ok(Value::Null)
162 }
163
164 fn main() {
165
166 let cmd_def = CliCommandMap::new()
167 .insert("create", CliCommand::new(
168 ApiMethod::new(
169 create_archive,
170 ObjectSchema::new("Create new .pxar archive.")
171 .required("archive", StringSchema::new("Archive name"))
172 .required("source", StringSchema::new("Source directory."))
173 .optional("verbose", BooleanSchema::new("Verbose output.").default(false))
174 .optional("no-xattrs", BooleanSchema::new("Ignore extended file attributes.").default(false))
175 .optional("no-fcaps", BooleanSchema::new("Ignore file capabilities.").default(false))
176 .optional("no-acls", BooleanSchema::new("Ignore access control list entries.").default(false))
177 .optional("all-file-systems", BooleanSchema::new("Include mounted sudirs.").default(false))
178 ))
179 .arg_param(vec!["archive", "source"])
180 .completion_cb("archive", tools::complete_file_name)
181 .completion_cb("source", tools::complete_file_name)
182 .into()
183 )
184 .insert("extract", CliCommand::new(
185 ApiMethod::new(
186 extract_archive,
187 ObjectSchema::new("Extract an archive.")
188 .required("archive", StringSchema::new("Archive name."))
189 .required("target", StringSchema::new("Target directory."))
190 .optional("verbose", BooleanSchema::new("Verbose output.").default(false))
191 .optional("no-xattrs", BooleanSchema::new("Ignore extended file attributes.").default(false))
192 .optional("no-fcaps", BooleanSchema::new("Ignore file capabilities.").default(false))
193 .optional("no-acls", BooleanSchema::new("Ignore access control list entries.").default(false))
194 ))
195 .arg_param(vec!["archive", "target"])
196 .completion_cb("archive", tools::complete_file_name)
197 .completion_cb("target", tools::complete_file_name)
198 .into()
199 )
200 .insert("list", CliCommand::new(
201 ApiMethod::new(
202 print_filenames,
203 ObjectSchema::new("List the contents of an archive.")
204 .required("archive", StringSchema::new("Archive name."))
205 ))
206 .arg_param(vec!["archive"])
207 .completion_cb("archive", tools::complete_file_name)
208 .into()
209 )
210 .insert("dump", CliCommand::new(
211 ApiMethod::new(
212 dump_archive,
213 ObjectSchema::new("Textual dump of archive contents (debug toolkit).")
214 .required("archive", StringSchema::new("Archive name."))
215 .optional("with-xattrs", BooleanSchema::new("Dump extended file attributes.").default(false))
216 .optional("with-fcaps", BooleanSchema::new("Dump file capabilities.").default(false))
217 .optional("with-acls", BooleanSchema::new("Dump access control list entries.").default(false))
218 ))
219 .arg_param(vec!["archive"])
220 .completion_cb("archive", tools::complete_file_name)
221 .into()
222 );
223
224 run_cli_command(cmd_def.into());
225 }