1 use std
::io
::{Read, Write}
;
3 use std
::task
::{Context, Poll}
;
14 PROXMOX_TAPE_BLOCK_SIZE
,
15 PROXMOX_BACKUP_SNAPSHOT_ARCHIVE_MAGIC_1_0
,
20 /// Write a set of files as `pxar` archive to the tape
22 /// This ignores file attributes like ACLs and xattrs.
24 /// Returns `Ok(Some(content_uuid))` on succees, and `Ok(None)` if
25 /// `LEOM` was detected before all data was written. The stream is
26 /// marked inclomplete in that case and does not contain all data (The
27 /// backup task must rewrite the whole file on the next media).
28 pub fn tape_write_snapshot_archive
<'a
>(
29 writer
: &mut (dyn TapeWrite
+ 'a
),
30 snapshot_reader
: &SnapshotReader
,
31 ) -> Result
<Option
<Uuid
>, std
::io
::Error
> {
33 let snapshot
= snapshot_reader
.snapshot().to_string();
34 let file_list
= snapshot_reader
.file_list();
36 let header_data
= snapshot
.as_bytes().to_vec();
38 let header
= MediaContentHeader
::new(
39 PROXMOX_BACKUP_SNAPSHOT_ARCHIVE_MAGIC_1_0
, header_data
.len() as u32);
40 let content_uuid
= header
.uuid
.into();
42 let root_metadata
= pxar
::Metadata
::dir_builder(0o0664).build();
44 let mut file_copy_buffer
= proxmox
::tools
::vec
::undefined(PROXMOX_TAPE_BLOCK_SIZE
);
46 let result
: Result
<(), std
::io
::Error
> = proxmox
::try_block
!({
48 let leom
= writer
.write_header(&header
, &header_data
)?
;
50 return Err(std
::io
::Error
::from_raw_os_error(nix
::errno
::Errno
::ENOSPC
as i32));
53 let mut encoder
= pxar
::encoder
::sync
::Encoder
::new(PxarTapeWriter
::new(writer
), &root_metadata
)?
;
55 for filename
in file_list
.iter() {
57 let mut file
= snapshot_reader
.open_file(filename
)
58 .map_err(|err
| proxmox
::io_format_err
!("open file '{}' failed - {}", filename
, err
))?
;
59 let metadata
= file
.metadata()?
;
60 let file_size
= metadata
.len();
62 let metadata
: pxar
::Metadata
= metadata
.into();
64 if !metadata
.is_regular_file() {
65 proxmox
::io_bail
!("file '{}' is not a regular file", filename
);
68 let mut remaining
= file_size
;
69 let mut out
= encoder
.create_file(&metadata
, filename
, file_size
)?
;
70 while remaining
!= 0 {
71 let got
= file
.read(&mut file_copy_buffer
[..])?
;
72 if got
as u64 > remaining
{
73 proxmox
::io_bail
!("file '{}' changed while reading", filename
);
75 out
.write_all(&file_copy_buffer
[..got
])?
;
76 remaining
-= got
as u64;
80 proxmox
::io_bail
!("file '{}' shrunk while reading", filename
);
89 writer
.finish(false)?
;
90 Ok(Some(content_uuid
))
93 if err
.is_errno(nix
::errno
::Errno
::ENOSPC
) && writer
.logical_end_of_media() {
94 writer
.finish(true)?
; // mark as incomplete
103 // Helper to create pxar archives on tape
105 // We generate and error at LEOM,
106 struct PxarTapeWriter
<'a
, T
: TapeWrite
+ ?Sized
> {
110 impl<'a
, T
: TapeWrite
+ ?Sized
> PxarTapeWriter
<'a
, T
> {
111 pub fn new(inner
: &'a
mut T
) -> Self {
116 impl<'a
, T
: TapeWrite
+ ?Sized
> pxar
::encoder
::SeqWrite
for PxarTapeWriter
<'a
, T
> {
119 self: Pin
<&mut Self>,
122 ) -> Poll
<std
::io
::Result
<usize>> {
123 let this
= unsafe { self.get_unchecked_mut() }
;
124 Poll
::Ready(match this
.inner
.write_all(buf
) {
127 Err(std
::io
::Error
::from_raw_os_error(nix
::errno
::Errno
::ENOSPC
as i32))
132 Err(err
) => Err(err
),
136 fn poll_flush(self: Pin
<&mut Self>, _cx
: &mut Context
) -> Poll
<std
::io
::Result
<()>> {