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