]> git.proxmox.com Git - proxmox-backup.git/blob - src/pxar/sequential_decoder.rs
Cargo.toml: pathpatterns, pxar, proxmox-fuse
[proxmox-backup.git] / src / pxar / sequential_decoder.rs
1 //! *pxar* format decoder.
2 //!
3 //! This module contain the code to decode *pxar* archive files.
4 use std::ffi::{CStr, CString};
5 use std::ffi::{OsStr, OsString};
6 use std::io::{Read, Write};
7 use std::os::unix::ffi::{OsStrExt, OsStringExt};
8 use std::os::unix::io::AsRawFd;
9 use std::os::unix::io::FromRawFd;
10 use std::os::unix::io::RawFd;
11 use std::path::{Path, PathBuf};
12
13 use endian_trait::Endian;
14 use anyhow::{bail, format_err, Error};
15 use nix::errno::Errno;
16 use nix::fcntl::OFlag;
17 use nix::sys::stat::Mode;
18 use nix::NixPath;
19
20 use proxmox::tools::io::ReadExt;
21 use proxmox::tools::vec;
22
23 use super::dir_stack::{PxarDir, PxarDirStack};
24 use super::flags;
25 use super::format_definition::*;
26 use super::match_pattern::{MatchPattern, MatchPatternSlice, MatchType};
27
28 use crate::tools::acl;
29 use crate::tools::fs;
30 use crate::tools::xattr;
31
32 // This one need Read, but works without Seek
33 pub struct SequentialDecoder<R: Read> {
34 reader: R,
35 feature_flags: u64,
36 allow_existing_dirs: bool,
37 skip_buffer: Vec<u8>,
38 callback: Option<Box<dyn Fn(&Path) -> Result<(), Error> + Send>>,
39 }
40
41 const HEADER_SIZE: u64 = std::mem::size_of::<PxarHeader>() as u64;
42
43 impl<R: Read> SequentialDecoder<R> {
44
45 pub fn new(
46 reader: R,
47 feature_flags: u64,
48 ) -> Self {
49 let skip_buffer = vec::undefined(64 * 1024);
50
51 Self {
52 reader,
53 feature_flags,
54 allow_existing_dirs: false,
55 skip_buffer,
56 callback: None,
57 }
58 }
59
60 pub fn set_callback<F: Fn(&Path) -> Result<(), Error> + Send + 'static>(&mut self, callback: F ) {
61 self.callback = Some(Box::new(callback));
62 }
63
64 pub fn set_allow_existing_dirs(&mut self, allow: bool) {
65 self.allow_existing_dirs = allow;
66 }
67
68 pub(crate) fn get_reader_mut(&mut self) -> &mut R {
69 &mut self.reader
70 }
71
72 pub(crate) fn read_item<T: Endian>(&mut self) -> Result<T, Error> {
73 let mut result = std::mem::MaybeUninit::<T>::uninit();
74
75 let buffer = unsafe {
76 std::slice::from_raw_parts_mut(result.as_mut_ptr() as *mut u8, std::mem::size_of::<T>())
77 };
78
79 self.reader.read_exact(buffer)?;
80 let result = unsafe { result.assume_init() };
81
82 Ok(result.from_le())
83 }
84
85 pub(crate) fn read_link(&mut self, size: u64) -> Result<PathBuf, Error> {
86 if size < (HEADER_SIZE + 2) {
87 bail!("detected short link target.");
88 }
89 let target_len = size - HEADER_SIZE;
90
91 if target_len > (libc::PATH_MAX as u64) {
92 bail!("link target too long ({}).", target_len);
93 }
94
95 let mut buffer = self.reader.read_exact_allocated(target_len as usize)?;
96
97 let last_byte = buffer.pop().unwrap();
98 if last_byte != 0u8 {
99 bail!("link target not nul terminated.");
100 }
101
102 Ok(PathBuf::from(std::ffi::OsString::from_vec(buffer)))
103 }
104
105 pub(crate) fn read_hardlink(&mut self, size: u64) -> Result<(PathBuf, u64), Error> {
106 if size < (HEADER_SIZE + 8 + 2) {
107 bail!("detected short hardlink header.");
108 }
109 let offset: u64 = self.read_item()?;
110 let target = self.read_link(size - 8)?;
111
112 for c in target.components() {
113 match c {
114 std::path::Component::Normal(_) => { /* OK */ }
115 _ => bail!("hardlink target contains invalid component {:?}", c),
116 }
117 }
118
119 Ok((target, offset))
120 }
121
122 pub(crate) fn read_filename(&mut self, size: u64) -> Result<OsString, Error> {
123 if size < (HEADER_SIZE + 2) {
124 bail!("detected short filename");
125 }
126 let name_len = size - HEADER_SIZE;
127
128 if name_len > ((libc::FILENAME_MAX as u64) + 1) {
129 bail!("filename too long ({}).", name_len);
130 }
131
132 let mut buffer = self.reader.read_exact_allocated(name_len as usize)?;
133
134 let last_byte = buffer.pop().unwrap();
135 if last_byte != 0u8 {
136 bail!("filename entry not nul terminated.");
137 }
138
139 if buffer == b"." || buffer == b".." {
140 bail!("found invalid filename '.' or '..'.");
141 }
142
143 if buffer.iter().any(|b| (*b == b'/' || *b == b'\0')) {
144 bail!("found invalid filename with slashes or nul bytes.");
145 }
146
147 let name = std::ffi::OsString::from_vec(buffer);
148 if name.is_empty() {
149 bail!("found empty filename.");
150 }
151
152 Ok(name)
153 }
154
155 fn has_features(&self, feature_flags: u64) -> bool {
156 (self.feature_flags & feature_flags) == feature_flags
157 }
158
159 fn read_xattr(&mut self, size: usize) -> Result<PxarXAttr, Error> {
160 let buffer = self.reader.read_exact_allocated(size)?;
161
162 let separator = buffer
163 .iter()
164 .position(|c| *c == b'\0')
165 .ok_or_else(|| format_err!("no value found in xattr"))?;
166
167 let (name, value) = buffer.split_at(separator + 1);
168 let c_name = unsafe { CStr::from_bytes_with_nul_unchecked(name) };
169 if !xattr::is_valid_xattr_name(c_name) || xattr::is_security_capability(c_name) {
170 bail!("incorrect xattr name - {:?}.", c_name);
171 }
172
173 Ok(PxarXAttr {
174 name: name.to_vec(),
175 value: value[1..].to_vec(),
176 })
177 }
178
179 fn read_fcaps(&mut self, size: usize) -> Result<PxarFCaps, Error> {
180 let buffer = self.reader.read_exact_allocated(size)?;
181
182 Ok(PxarFCaps { data: buffer })
183 }
184
185 pub(crate) fn read_attributes(&mut self) -> Result<(PxarHeader, PxarAttributes), Error> {
186 let mut attr = PxarAttributes::default();
187 let mut head: PxarHeader = self.read_item()?;
188 let mut size = (head.size - HEADER_SIZE) as usize;
189 loop {
190 match head.htype {
191 PXAR_XATTR => {
192 if self.has_features(flags::WITH_XATTRS) {
193 attr.xattrs.push(self.read_xattr(size)?);
194 } else {
195 self.skip_bytes(size)?;
196 }
197 }
198 PXAR_FCAPS => {
199 if self.has_features(flags::WITH_FCAPS) {
200 attr.fcaps = Some(self.read_fcaps(size)?);
201 } else {
202 self.skip_bytes(size)?;
203 }
204 }
205 PXAR_ACL_USER => {
206 if self.has_features(flags::WITH_ACL) {
207 attr.acl_user.push(self.read_item::<PxarACLUser>()?);
208 } else {
209 self.skip_bytes(size)?;
210 }
211 }
212 PXAR_ACL_GROUP => {
213 if self.has_features(flags::WITH_ACL) {
214 attr.acl_group.push(self.read_item::<PxarACLGroup>()?);
215 } else {
216 self.skip_bytes(size)?;
217 }
218 }
219 PXAR_ACL_GROUP_OBJ => {
220 if self.has_features(flags::WITH_ACL) {
221 attr.acl_group_obj = Some(self.read_item::<PxarACLGroupObj>()?);
222 } else {
223 self.skip_bytes(size)?;
224 }
225 }
226 PXAR_ACL_DEFAULT => {
227 if self.has_features(flags::WITH_ACL) {
228 attr.acl_default = Some(self.read_item::<PxarACLDefault>()?);
229 } else {
230 self.skip_bytes(size)?;
231 }
232 }
233 PXAR_ACL_DEFAULT_USER => {
234 if self.has_features(flags::WITH_ACL) {
235 attr.acl_default_user.push(self.read_item::<PxarACLUser>()?);
236 } else {
237 self.skip_bytes(size)?;
238 }
239 }
240 PXAR_ACL_DEFAULT_GROUP => {
241 if self.has_features(flags::WITH_ACL) {
242 attr.acl_default_group
243 .push(self.read_item::<PxarACLGroup>()?);
244 } else {
245 self.skip_bytes(size)?;
246 }
247 }
248 PXAR_QUOTA_PROJID => {
249 if self.has_features(flags::WITH_QUOTA_PROJID) {
250 attr.quota_projid = Some(self.read_item::<PxarQuotaProjID>()?);
251 } else {
252 self.skip_bytes(size)?;
253 }
254 }
255 _ => break,
256 }
257 head = self.read_item()?;
258 size = (head.size - HEADER_SIZE) as usize;
259 }
260
261 Ok((head, attr))
262 }
263
264 fn restore_attributes(
265 &mut self,
266 fd: RawFd,
267 attr: &PxarAttributes,
268 entry: &PxarEntry,
269 ) -> Result<(), Error> {
270 self.restore_xattrs_fcaps_fd(fd, &attr.xattrs, &attr.fcaps)?;
271
272 let mut acl = acl::ACL::init(5)?;
273 acl.add_entry_full(
274 acl::ACL_USER_OBJ,
275 None,
276 acl::mode_user_to_acl_permissions(entry.mode),
277 )?;
278 acl.add_entry_full(
279 acl::ACL_OTHER,
280 None,
281 acl::mode_other_to_acl_permissions(entry.mode),
282 )?;
283 match &attr.acl_group_obj {
284 Some(group_obj) => {
285 acl.add_entry_full(
286 acl::ACL_MASK,
287 None,
288 acl::mode_group_to_acl_permissions(entry.mode),
289 )?;
290 acl.add_entry_full(acl::ACL_GROUP_OBJ, None, group_obj.permissions)?;
291 }
292 None => {
293 acl.add_entry_full(
294 acl::ACL_GROUP_OBJ,
295 None,
296 acl::mode_group_to_acl_permissions(entry.mode),
297 )?;
298 }
299 }
300 for user in &attr.acl_user {
301 acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
302 }
303 for group in &attr.acl_group {
304 acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
305 }
306 let proc_path = Path::new("/proc/self/fd/").join(fd.to_string());
307 if !acl.is_valid() {
308 bail!("Error while restoring ACL - ACL invalid");
309 }
310 acl.set_file(&proc_path, acl::ACL_TYPE_ACCESS)?;
311
312 if let Some(default) = &attr.acl_default {
313 let mut acl = acl::ACL::init(5)?;
314 acl.add_entry_full(acl::ACL_USER_OBJ, None, default.user_obj_permissions)?;
315 acl.add_entry_full(acl::ACL_GROUP_OBJ, None, default.group_obj_permissions)?;
316 acl.add_entry_full(acl::ACL_OTHER, None, default.other_permissions)?;
317 if default.mask_permissions != std::u64::MAX {
318 acl.add_entry_full(acl::ACL_MASK, None, default.mask_permissions)?;
319 }
320 for user in &attr.acl_default_user {
321 acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
322 }
323 for group in &attr.acl_default_group {
324 acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
325 }
326 if !acl.is_valid() {
327 bail!("Error while restoring ACL - ACL invalid");
328 }
329 acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?;
330 }
331 self.restore_quota_projid(fd, &attr.quota_projid)?;
332
333 Ok(())
334 }
335
336 // Restore xattrs and fcaps to the given RawFd.
337 fn restore_xattrs_fcaps_fd(
338 &mut self,
339 fd: RawFd,
340 xattrs: &[PxarXAttr],
341 fcaps: &Option<PxarFCaps>,
342 ) -> Result<(), Error> {
343 for xattr in xattrs {
344 let name = CString::new(&xattr.name[..])
345 .map_err(|_| format_err!("invalid xattr name with zeroes"))?;
346 if let Err(err) = xattr::fsetxattr(fd, &name, &xattr.value) {
347 bail!("fsetxattr failed with error: {}", err);
348 }
349 }
350 if let Some(fcaps) = fcaps {
351 if let Err(err) = xattr::fsetxattr_fcaps(fd, &fcaps.data) {
352 bail!("fsetxattr_fcaps failed with error: {}", err);
353 }
354 }
355
356 Ok(())
357 }
358
359 fn restore_quota_projid(
360 &mut self,
361 fd: RawFd,
362 projid: &Option<PxarQuotaProjID>,
363 ) -> Result<(), Error> {
364 if let Some(projid) = projid {
365 let mut fsxattr = fs::FSXAttr::default();
366 unsafe {
367 fs::fs_ioc_fsgetxattr(fd, &mut fsxattr).map_err(|err| {
368 format_err!(
369 "error while getting fsxattr to restore quota project id - {}",
370 err
371 )
372 })?;
373 }
374 fsxattr.fsx_projid = projid.projid as u32;
375 unsafe {
376 fs::fs_ioc_fssetxattr(fd, &fsxattr).map_err(|err| {
377 format_err!(
378 "error while setting fsxattr to restore quota project id - {}",
379 err
380 )
381 })?;
382 }
383 }
384
385 Ok(())
386 }
387
388 fn restore_mode(&mut self, entry: &PxarEntry, fd: RawFd) -> Result<(), Error> {
389 let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
390
391 nix::sys::stat::fchmod(fd, mode)?;
392
393 Ok(())
394 }
395
396 fn restore_mode_at(
397 &mut self,
398 entry: &PxarEntry,
399 dirfd: RawFd,
400 filename: &OsStr,
401 ) -> Result<(), Error> {
402 let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
403
404 // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
405 // on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
406 nix::sys::stat::fchmodat(
407 Some(dirfd),
408 filename,
409 mode,
410 nix::sys::stat::FchmodatFlags::FollowSymlink,
411 )?;
412
413 Ok(())
414 }
415
416 fn restore_ugid(&mut self, entry: &PxarEntry, fd: RawFd) -> Result<(), Error> {
417 let uid = entry.uid;
418 let gid = entry.gid;
419
420 let res = unsafe { libc::fchown(fd, uid, gid) };
421 Errno::result(res)?;
422
423 Ok(())
424 }
425
426 fn restore_ugid_at(
427 &mut self,
428 entry: &PxarEntry,
429 dirfd: RawFd,
430 filename: &OsStr,
431 ) -> Result<(), Error> {
432 let uid = entry.uid;
433 let gid = entry.gid;
434
435 let res = filename.with_nix_path(|cstr| unsafe {
436 libc::fchownat(dirfd, cstr.as_ptr(), uid, gid, libc::AT_SYMLINK_NOFOLLOW)
437 })?;
438 Errno::result(res)?;
439
440 Ok(())
441 }
442
443 fn restore_mtime(&mut self, entry: &PxarEntry, fd: RawFd) -> Result<(), Error> {
444 let times = nsec_to_update_timespec(entry.mtime);
445
446 let res = unsafe { libc::futimens(fd, &times[0]) };
447 Errno::result(res)?;
448
449 Ok(())
450 }
451
452 fn restore_mtime_at(
453 &mut self,
454 entry: &PxarEntry,
455 dirfd: RawFd,
456 filename: &OsStr,
457 ) -> Result<(), Error> {
458 let times = nsec_to_update_timespec(entry.mtime);
459
460 let res = filename.with_nix_path(|cstr| unsafe {
461 libc::utimensat(dirfd, cstr.as_ptr(), &times[0], libc::AT_SYMLINK_NOFOLLOW)
462 })?;
463 Errno::result(res)?;
464
465 Ok(())
466 }
467
468 fn restore_device_at(
469 &mut self,
470 entry: &PxarEntry,
471 dirfd: RawFd,
472 filename: &OsStr,
473 device: &PxarDevice,
474 ) -> Result<(), Error> {
475 let rdev = nix::sys::stat::makedev(device.major, device.minor);
476 let mode = ((entry.mode as u32) & libc::S_IFMT) | 0o0600;
477 let res = filename
478 .with_nix_path(|cstr| unsafe { libc::mknodat(dirfd, cstr.as_ptr(), mode, rdev) })?;
479 Errno::result(res)?;
480
481 Ok(())
482 }
483
484 fn restore_socket_at(&mut self, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
485 let mode = libc::S_IFSOCK | 0o0600;
486 let res = filename
487 .with_nix_path(|cstr| unsafe { libc::mknodat(dirfd, cstr.as_ptr(), mode, 0) })?;
488 Errno::result(res)?;
489
490 Ok(())
491 }
492
493 fn restore_fifo_at(&mut self, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
494 let mode = libc::S_IFIFO | 0o0600;
495 let res =
496 filename.with_nix_path(|cstr| unsafe { libc::mkfifoat(dirfd, cstr.as_ptr(), mode) })?;
497 Errno::result(res)?;
498
499 Ok(())
500 }
501
502 pub(crate) fn skip_bytes(&mut self, count: usize) -> Result<(), Error> {
503 let mut done = 0;
504 while done < count {
505 let todo = count - done;
506 let n = if todo > self.skip_buffer.len() {
507 self.skip_buffer.len()
508 } else {
509 todo
510 };
511 let data = &mut self.skip_buffer[..n];
512 self.reader.read_exact(data)?;
513 done += n;
514 }
515 Ok(())
516 }
517
518 fn restore_symlink(
519 &mut self,
520 parent_fd: Option<RawFd>,
521 full_path: &PathBuf,
522 entry: &PxarEntry,
523 filename: &OsStr,
524 ) -> Result<(), Error> {
525 //fixme: create symlink
526 //fixme: restore permission, acls, xattr, ...
527
528 let head: PxarHeader = self.read_item()?;
529 match head.htype {
530 PXAR_SYMLINK => {
531 let target = self.read_link(head.size)?;
532 //println!("TARGET: {:?}", target);
533 if let Some(fd) = parent_fd {
534 if let Err(err) = symlinkat(&target, fd, filename) {
535 bail!("create symlink {:?} failed - {}", full_path, err);
536 }
537 }
538 }
539 _ => bail!(
540 "got unknown header type inside symlink entry {:016x}",
541 head.htype
542 ),
543 }
544
545 if let Some(fd) = parent_fd {
546 // self.restore_mode_at(&entry, fd, filename)?; //not supported on symlinks
547 self.restore_ugid_at(&entry, fd, filename)?;
548 self.restore_mtime_at(&entry, fd, filename)?;
549 }
550
551 Ok(())
552 }
553
554 fn restore_socket(
555 &mut self,
556 parent_fd: Option<RawFd>,
557 entry: &PxarEntry,
558 filename: &OsStr,
559 ) -> Result<(), Error> {
560 if !self.has_features(flags::WITH_SOCKETS) {
561 return Ok(());
562 }
563 if let Some(fd) = parent_fd {
564 self.restore_socket_at(fd, filename)?;
565 self.restore_mode_at(&entry, fd, filename)?;
566 self.restore_ugid_at(&entry, fd, filename)?;
567 self.restore_mtime_at(&entry, fd, filename)?;
568 }
569
570 Ok(())
571 }
572
573 fn restore_fifo(
574 &mut self,
575 parent_fd: Option<RawFd>,
576 entry: &PxarEntry,
577 filename: &OsStr,
578 ) -> Result<(), Error> {
579 if !self.has_features(flags::WITH_FIFOS) {
580 return Ok(());
581 }
582 if let Some(fd) = parent_fd {
583 self.restore_fifo_at(fd, filename)?;
584 self.restore_mode_at(&entry, fd, filename)?;
585 self.restore_ugid_at(&entry, fd, filename)?;
586 self.restore_mtime_at(&entry, fd, filename)?;
587 }
588
589 Ok(())
590 }
591
592 fn restore_device(
593 &mut self,
594 parent_fd: Option<RawFd>,
595 entry: &PxarEntry,
596 filename: &OsStr,
597 ) -> Result<(), Error> {
598 let head: PxarHeader = self.read_item()?;
599 if head.htype != PXAR_DEVICE {
600 bail!(
601 "got unknown header type inside device entry {:016x}",
602 head.htype
603 );
604 }
605 let device: PxarDevice = self.read_item()?;
606 if !self.has_features(flags::WITH_DEVICE_NODES) {
607 return Ok(());
608 }
609 if let Some(fd) = parent_fd {
610 self.restore_device_at(&entry, fd, filename, &device)?;
611 self.restore_mode_at(&entry, fd, filename)?;
612 self.restore_ugid_at(&entry, fd, filename)?;
613 self.restore_mtime_at(&entry, fd, filename)?;
614 }
615
616 Ok(())
617 }
618
619 /// Restores a regular file with its content and associated attributes to the
620 /// folder provided by the raw filedescriptor.
621 /// If None is passed instead of a filedescriptor, the file is not restored but
622 /// the archive reader is skipping over it instead.
623 fn restore_regular_file(
624 &mut self,
625 parent_fd: Option<RawFd>,
626 full_path: &PathBuf,
627 entry: &PxarEntry,
628 filename: &OsStr,
629 ) -> Result<(), Error> {
630 let (head, attr) = self
631 .read_attributes()
632 .map_err(|err| format_err!("Reading of file attributes failed - {}", err))?;
633
634 if let Some(fd) = parent_fd {
635 let flags = OFlag::O_CREAT | OFlag::O_WRONLY | OFlag::O_EXCL;
636 let open_mode = Mode::from_bits_truncate(0o0600 | entry.mode as u32); //fixme: upper 32bits of entry.mode?
637 let mut file = file_openat(fd, filename, flags, open_mode)
638 .map_err(|err| format_err!("open file {:?} failed - {}", full_path, err))?;
639
640 if head.htype != PXAR_PAYLOAD {
641 bail!("got unknown header type for file entry {:016x}", head.htype);
642 }
643
644 if head.size < HEADER_SIZE {
645 bail!("detected short payload");
646 }
647 let need = (head.size - HEADER_SIZE) as usize;
648
649 let mut read_buffer = unsafe { vec::uninitialized(64 * 1024) };
650 let mut done = 0;
651 while done < need {
652 let todo = need - done;
653 let n = if todo > read_buffer.len() {
654 read_buffer.len()
655 } else {
656 todo
657 };
658 let data = &mut read_buffer[..n];
659 self.reader.read_exact(data)?;
660 file.write_all(data)?;
661 done += n;
662 }
663
664 self.restore_ugid(&entry, file.as_raw_fd())?;
665 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
666 self.restore_attributes(file.as_raw_fd(), &attr, &entry)?;
667 self.restore_mode(&entry, file.as_raw_fd())?;
668 self.restore_mtime(&entry, file.as_raw_fd())?;
669 } else {
670 if head.htype != PXAR_PAYLOAD {
671 bail!("got unknown header type for file entry {:016x}", head.htype);
672 }
673 if head.size < HEADER_SIZE {
674 bail!("detected short payload");
675 }
676 self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
677 }
678
679 Ok(())
680 }
681
682 fn restore_dir(
683 &mut self,
684 base_path: &Path,
685 dirs: &mut PxarDirStack,
686 entry: PxarEntry,
687 filename: &OsStr,
688 matched: MatchType,
689 match_pattern: &[MatchPatternSlice],
690 ) -> Result<(), Error> {
691 let (mut head, attr) = self
692 .read_attributes()
693 .map_err(|err| format_err!("Reading of directory attributes failed - {}", err))?;
694
695 let dir = PxarDir::new(filename, entry, attr);
696 dirs.push(dir);
697 if matched == MatchType::Positive {
698 dirs.create_all_dirs(!self.allow_existing_dirs)?;
699 }
700
701 while head.htype == PXAR_FILENAME {
702 let name = self.read_filename(head.size)?;
703 self.restore_dir_entry(base_path, dirs, &name, matched, match_pattern)?;
704 head = self.read_item()?;
705 }
706
707 if head.htype != PXAR_GOODBYE {
708 bail!(
709 "got unknown header type inside directory entry {:016x}",
710 head.htype
711 );
712 }
713
714 if head.size < HEADER_SIZE {
715 bail!("detected short goodbye table");
716 }
717 self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
718
719 let last = dirs
720 .pop()
721 .ok_or_else(|| format_err!("Tried to pop beyond dir root - this should not happen!"))?;
722 if let Some(d) = last.dir {
723 let fd = d.as_raw_fd();
724 self.restore_ugid(&last.entry, fd)?;
725 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
726 self.restore_attributes(fd, &last.attr, &last.entry)?;
727 self.restore_mode(&last.entry, fd)?;
728 self.restore_mtime(&last.entry, fd)?;
729 }
730
731 Ok(())
732 }
733
734 /// Restore an archive into the specified directory.
735 ///
736 /// The directory is created if it does not exist.
737 pub fn restore(&mut self, path: &Path, match_pattern: &[MatchPattern]) -> Result<(), Error> {
738 let mut slices = Vec::new();
739 for pattern in match_pattern {
740 slices.push(pattern.as_slice());
741 }
742 std::fs::create_dir_all(path)
743 .map_err(|err| format_err!("error while creating directory {:?} - {}", path, err))?;
744
745 let dir = nix::dir::Dir::open(
746 path,
747 nix::fcntl::OFlag::O_DIRECTORY,
748 nix::sys::stat::Mode::empty(),
749 )
750 .map_err(|err| format_err!("unable to open target directory {:?} - {}", path, err))?;
751 let fd = dir.as_raw_fd();
752 let mut dirs = PxarDirStack::new(fd);
753 // An empty match pattern list indicates to restore the full archive.
754 let matched = if slices.is_empty() {
755 MatchType::Positive
756 } else {
757 MatchType::None
758 };
759
760 let header: PxarHeader = self.read_item()?;
761 check_ca_header::<PxarEntry>(&header, PXAR_ENTRY)?;
762 let entry: PxarEntry = self.read_item()?;
763
764 let (mut head, attr) = self
765 .read_attributes()
766 .map_err(|err| format_err!("Reading of directory attributes failed - {}", err))?;
767
768 while head.htype == PXAR_FILENAME {
769 let name = self.read_filename(head.size)?;
770 self.restore_dir_entry(path, &mut dirs, &name, matched, &slices)?;
771 head = self.read_item()?;
772 }
773
774 if head.htype != PXAR_GOODBYE {
775 bail!(
776 "got unknown header type inside directory entry {:016x}",
777 head.htype
778 );
779 }
780
781 if head.size < HEADER_SIZE {
782 bail!("detected short goodbye table");
783 }
784 self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
785
786 self.restore_ugid(&entry, fd)?;
787 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
788 self.restore_attributes(fd, &attr, &entry)?;
789 self.restore_mode(&entry, fd)?;
790 self.restore_mtime(&entry, fd)?;
791
792 Ok(())
793 }
794
795 fn restore_dir_entry(
796 &mut self,
797 base_path: &Path,
798 dirs: &mut PxarDirStack,
799 filename: &OsStr,
800 parent_matched: MatchType,
801 match_pattern: &[MatchPatternSlice],
802 ) -> Result<(), Error> {
803 let relative_path = dirs.as_path_buf();
804 let full_path = base_path.join(&relative_path).join(filename);
805
806 let head: PxarHeader = self.read_item()?;
807 if head.htype == PXAR_FORMAT_HARDLINK {
808 let (target, _offset) = self.read_hardlink(head.size)?;
809 let target_path = base_path.join(&target);
810 if dirs.last_dir_fd().is_some() {
811 if let Some(ref callback) = self.callback {
812 (callback)(&full_path)?;
813 }
814 hardlink(&target_path, &full_path)?;
815 }
816 return Ok(());
817 }
818
819 check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?;
820 let entry: PxarEntry = self.read_item()?;
821 let ifmt = entry.mode as u32 & libc::S_IFMT;
822
823 let mut child_pattern = Vec::new();
824 // If parent was a match, then children should be assumed to match too
825 // This is especially the case when the full archive is restored and
826 // there are no match pattern.
827 let mut matched = parent_matched;
828 if !match_pattern.is_empty() {
829 match MatchPatternSlice::match_filename_include(
830 &CString::new(filename.as_bytes())?,
831 ifmt == libc::S_IFDIR,
832 match_pattern,
833 )? {
834 (MatchType::None, _) => matched = MatchType::None,
835 (MatchType::Negative, _) => matched = MatchType::Negative,
836 (MatchType::Positive, _) => matched = MatchType::Positive,
837 (match_type, pattern) => {
838 matched = match_type;
839 child_pattern = pattern;
840 }
841 }
842 }
843
844 let fd = if matched == MatchType::Positive {
845 Some(dirs.create_all_dirs(!self.allow_existing_dirs)?)
846 } else {
847 None
848 };
849
850 if fd.is_some() {
851 if let Some(ref callback) = self.callback {
852 (callback)(&full_path)?;
853 }
854 }
855
856 match ifmt {
857 libc::S_IFDIR => {
858 self.restore_dir(base_path, dirs, entry, &filename, matched, &child_pattern)
859 }
860 libc::S_IFLNK => self.restore_symlink(fd, &full_path, &entry, &filename),
861 libc::S_IFSOCK => self.restore_socket(fd, &entry, &filename),
862 libc::S_IFIFO => self.restore_fifo(fd, &entry, &filename),
863 libc::S_IFBLK | libc::S_IFCHR => self.restore_device(fd, &entry, &filename),
864 libc::S_IFREG => self.restore_regular_file(fd, &full_path, &entry, &filename),
865 _ => Ok(()),
866 }
867 }
868
869 /// List/Dump archive content.
870 ///
871 /// Simply print the list of contained files. This dumps archive
872 /// format details when the verbose flag is set (useful for debug).
873 pub fn dump_entry<W: std::io::Write>(
874 &mut self,
875 path: &mut PathBuf,
876 verbose: bool,
877 output: &mut W,
878 ) -> Result<(), Error> {
879 let print_head = |head: &PxarHeader| {
880 println!("Type: {:016x}", head.htype);
881 println!("Size: {}", head.size);
882 };
883
884 let head: PxarHeader = self.read_item()?;
885 if verbose {
886 println!("Path: {:?}", path);
887 print_head(&head);
888 } else {
889 println!("{:?}", path);
890 }
891
892 if head.htype == PXAR_FORMAT_HARDLINK {
893 let (target, offset) = self.read_hardlink(head.size)?;
894 if verbose {
895 println!("Hardlink: {} {:?}", offset, target);
896 }
897 return Ok(());
898 }
899
900 check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?;
901 let entry: PxarEntry = self.read_item()?;
902
903 if verbose {
904 println!(
905 "Mode: {:08x} {:08x}",
906 entry.mode,
907 (entry.mode as u32) & libc::S_IFDIR
908 );
909 }
910
911 let ifmt = (entry.mode as u32) & libc::S_IFMT;
912
913 if ifmt == libc::S_IFDIR {
914 let mut entry_count = 0;
915
916 loop {
917 let head: PxarHeader = self.read_item()?;
918 if verbose {
919 print_head(&head);
920 }
921
922 // This call covers all the cases of the match statement
923 // regarding extended attributes. These calls will never
924 // break on the loop and can therefore be handled separately.
925 // If the header was matched, true is returned and we can continue
926 if self.dump_if_attribute(&head, verbose)? {
927 continue;
928 }
929
930 match head.htype {
931 PXAR_FILENAME => {
932 let name = self.read_filename(head.size)?;
933 if verbose {
934 println!("Name: {:?}", name);
935 }
936 entry_count += 1;
937 path.push(&name);
938 self.dump_entry(path, verbose, output)?;
939 path.pop();
940 }
941 PXAR_GOODBYE => {
942 let table_size = (head.size - HEADER_SIZE) as usize;
943 if verbose {
944 println!("Goodbye: {:?}", path);
945 self.dump_goodby_entries(entry_count, table_size)?;
946 } else {
947 self.skip_bytes(table_size)?;
948 }
949 break;
950 }
951 _ => panic!("got unexpected header type inside directory"),
952 }
953 }
954 } else if (ifmt == libc::S_IFBLK)
955 || (ifmt == libc::S_IFCHR)
956 || (ifmt == libc::S_IFLNK)
957 || (ifmt == libc::S_IFREG)
958 {
959 loop {
960 let head: PxarHeader = self.read_item()?;
961 if verbose {
962 print_head(&head);
963 }
964
965 // This call covers all the cases of the match statement
966 // regarding extended attributes. These calls will never
967 // break on the loop and can therefore be handled separately.
968 // If the header was matched, true is returned and we can continue
969 if self.dump_if_attribute(&head, verbose)? {
970 continue;
971 }
972
973 match head.htype {
974 PXAR_SYMLINK => {
975 let target = self.read_link(head.size)?;
976 if verbose {
977 println!("Symlink: {:?}", target);
978 }
979 break;
980 }
981 PXAR_DEVICE => {
982 let device: PxarDevice = self.read_item()?;
983 if verbose {
984 println!("Device: {}, {}", device.major, device.minor);
985 }
986 break;
987 }
988 PXAR_PAYLOAD => {
989 let payload_size = (head.size - HEADER_SIZE) as usize;
990 if verbose {
991 println!("Payload: {}", payload_size);
992 }
993 self.skip_bytes(payload_size)?;
994 break;
995 }
996 _ => {
997 panic!("got unexpected header type inside non-directory");
998 }
999 }
1000 }
1001 } else if ifmt == libc::S_IFIFO {
1002 if verbose {
1003 println!("Fifo:");
1004 }
1005 } else if ifmt == libc::S_IFSOCK {
1006 if verbose {
1007 println!("Socket:");
1008 }
1009 } else {
1010 panic!("unknown st_mode");
1011 }
1012 Ok(())
1013 }
1014
1015 fn dump_if_attribute(&mut self, header: &PxarHeader, verbose: bool) -> Result<bool, Error> {
1016 match header.htype {
1017 PXAR_XATTR => {
1018 let xattr = self.read_xattr((header.size - HEADER_SIZE) as usize)?;
1019 if verbose && self.has_features(flags::WITH_XATTRS) {
1020 println!("XAttr: {:?}", xattr);
1021 }
1022 }
1023 PXAR_FCAPS => {
1024 let fcaps = self.read_fcaps((header.size - HEADER_SIZE) as usize)?;
1025 if verbose && self.has_features(flags::WITH_FCAPS) {
1026 println!("FCaps: {:?}", fcaps);
1027 }
1028 }
1029 PXAR_ACL_USER => {
1030 let user = self.read_item::<PxarACLUser>()?;
1031 if verbose && self.has_features(flags::WITH_ACL) {
1032 println!("ACLUser: {:?}", user);
1033 }
1034 }
1035 PXAR_ACL_GROUP => {
1036 let group = self.read_item::<PxarACLGroup>()?;
1037 if verbose && self.has_features(flags::WITH_ACL) {
1038 println!("ACLGroup: {:?}", group);
1039 }
1040 }
1041 PXAR_ACL_GROUP_OBJ => {
1042 let group_obj = self.read_item::<PxarACLGroupObj>()?;
1043 if verbose && self.has_features(flags::WITH_ACL) {
1044 println!("ACLGroupObj: {:?}", group_obj);
1045 }
1046 }
1047 PXAR_ACL_DEFAULT => {
1048 let default = self.read_item::<PxarACLDefault>()?;
1049 if verbose && self.has_features(flags::WITH_ACL) {
1050 println!("ACLDefault: {:?}", default);
1051 }
1052 }
1053 PXAR_ACL_DEFAULT_USER => {
1054 let default_user = self.read_item::<PxarACLUser>()?;
1055 if verbose && self.has_features(flags::WITH_ACL) {
1056 println!("ACLDefaultUser: {:?}", default_user);
1057 }
1058 }
1059 PXAR_ACL_DEFAULT_GROUP => {
1060 let default_group = self.read_item::<PxarACLGroup>()?;
1061 if verbose && self.has_features(flags::WITH_ACL) {
1062 println!("ACLDefaultGroup: {:?}", default_group);
1063 }
1064 }
1065 PXAR_QUOTA_PROJID => {
1066 let quota_projid = self.read_item::<PxarQuotaProjID>()?;
1067 if verbose && self.has_features(flags::WITH_QUOTA_PROJID) {
1068 println!("Quota project id: {:?}", quota_projid);
1069 }
1070 }
1071 _ => return Ok(false),
1072 }
1073
1074 Ok(true)
1075 }
1076
1077 fn dump_goodby_entries(&mut self, entry_count: usize, table_size: usize) -> Result<(), Error> {
1078 const GOODBYE_ITEM_SIZE: usize = std::mem::size_of::<PxarGoodbyeItem>();
1079
1080 if table_size < GOODBYE_ITEM_SIZE {
1081 bail!(
1082 "Goodbye table to small ({} < {})",
1083 table_size,
1084 GOODBYE_ITEM_SIZE
1085 );
1086 }
1087 if (table_size % GOODBYE_ITEM_SIZE) != 0 {
1088 bail!("Goodbye table with strange size ({})", table_size);
1089 }
1090
1091 let entries = table_size / GOODBYE_ITEM_SIZE;
1092
1093 if entry_count != (entries - 1) {
1094 bail!(
1095 "Goodbye table with wrong entry count ({} != {})",
1096 entry_count,
1097 entries - 1
1098 );
1099 }
1100
1101 let mut count = 0;
1102
1103 loop {
1104 let item: PxarGoodbyeItem = self.read_item()?;
1105 count += 1;
1106 if item.hash == PXAR_GOODBYE_TAIL_MARKER {
1107 if count != entries {
1108 bail!("unexpected goodbye tail marker");
1109 }
1110 println!("Goodby tail mark.");
1111 break;
1112 }
1113 println!(
1114 "Goodby item: offset {}, size {}, hash {:016x}",
1115 item.offset, item.size, item.hash
1116 );
1117 if count >= entries {
1118 bail!("too many goodbye items (no tail marker)");
1119 }
1120 }
1121
1122 Ok(())
1123 }
1124 }
1125
1126 fn file_openat(
1127 parent: RawFd,
1128 filename: &OsStr,
1129 flags: OFlag,
1130 mode: Mode,
1131 ) -> Result<std::fs::File, Error> {
1132 let fd =
1133 filename.with_nix_path(|cstr| nix::fcntl::openat(parent, cstr, flags, mode))??;
1134
1135 let file = unsafe { std::fs::File::from_raw_fd(fd) };
1136
1137 Ok(file)
1138 }
1139
1140 fn hardlink(oldpath: &Path, newpath: &Path) -> Result<(), Error> {
1141 oldpath.with_nix_path(|oldpath| {
1142 newpath.with_nix_path(|newpath| {
1143 let res = unsafe { libc::link(oldpath.as_ptr(), newpath.as_ptr()) };
1144 Errno::result(res)?;
1145 Ok(())
1146 })?
1147 })?
1148 }
1149
1150 fn symlinkat(target: &Path, parent: RawFd, linkname: &OsStr) -> Result<(), Error> {
1151 target.with_nix_path(|target| {
1152 linkname.with_nix_path(|linkname| {
1153 let res = unsafe { libc::symlinkat(target.as_ptr(), parent, linkname.as_ptr()) };
1154 Errno::result(res)?;
1155 Ok(())
1156 })?
1157 })?
1158 }
1159
1160 fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] {
1161 // restore mtime
1162 const UTIME_OMIT: i64 = (1 << 30) - 2;
1163 const NANOS_PER_SEC: i64 = 1_000_000_000;
1164
1165 let sec = (mtime_nsec as i64) / NANOS_PER_SEC;
1166 let nsec = (mtime_nsec as i64) % NANOS_PER_SEC;
1167
1168 let times: [libc::timespec; 2] = [
1169 libc::timespec {
1170 tv_sec: 0,
1171 tv_nsec: UTIME_OMIT,
1172 },
1173 libc::timespec {
1174 tv_sec: sec,
1175 tv_nsec: nsec,
1176 },
1177 ];
1178
1179 times
1180 }