all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore
@ 2023-12-14 14:48 Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 1/3] compression: Add a FileType enum to ZipEntry Filip Schauer
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-14 14:48 UTC (permalink / raw)
  To: pbs-devel

Include symlinks when restoring files from a backup as a zip file.

Resulting ZIP files were successfully tested on Linux with: zipinfo,
unzip, unar

On Windows the extracted symlinks show up as regular files containing
the path to the destination. As far as I am aware Windows Explorer does
not support symlinks in ZIP files.

Changes since v2:
* Add a FileType enum and embed the symlink target into the enum.
* Add unit tests for ZipEncoder to the proxmox-compression crate

Changes since v1:
* Use P instead of &Path
* Fix compile error due to misplaced comma
* Check content before symlink_target, since regular files are more
  common than symlinks

proxmox:

Filip Schauer (3):
  compression: Add a FileType enum to ZipEntry
  compression: Add support for symlinks in zip files
  compression: Add unit tests for the ZipEncoder

 proxmox-compression/Cargo.toml   |   2 +-
 proxmox-compression/src/zip.rs   |  62 ++++++++++----
 proxmox-compression/tests/zip.rs | 134 +++++++++++++++++++++++++++++++
 3 files changed, 181 insertions(+), 17 deletions(-)
 create mode 100644 proxmox-compression/tests/zip.rs

proxmox-backup:

Filip Schauer (2):
  pxar: Adopt FileType enum when creating a ZipEntry
  fix #4995: pxar: Include symlinks in zip file creation

 pbs-client/src/pxar/extract.rs | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

-- 
2.39.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH v3 proxmox 1/3] compression: Add a FileType enum to ZipEntry
  2023-12-14 14:48 [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore Filip Schauer
@ 2023-12-14 14:48 ` Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files Filip Schauer
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-14 14:48 UTC (permalink / raw)
  To: pbs-devel

Specify the type of a ZipEntry with a new FileType enum
instead of a boolean is_file.

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 proxmox-compression/src/zip.rs | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/proxmox-compression/src/zip.rs b/proxmox-compression/src/zip.rs
index d2d3fd8..069e8bc 100644
--- a/proxmox-compression/src/zip.rs
+++ b/proxmox-compression/src/zip.rs
@@ -71,6 +71,11 @@ fn epoch_to_dos(epoch: i64) -> (u16, u16) {
     (date, time)
 }
 
+pub enum FileType {
+    Directory,
+    Regular,
+}
+
 #[derive(Endian)]
 #[repr(C, packed)]
 struct Zip64Field {
@@ -202,16 +207,16 @@ pub struct ZipEntry {
     uncompressed_size: u64,
     compressed_size: u64,
     offset: u64,
-    is_file: bool,
+    file_type: FileType,
     is_utf8_filename: bool,
 }
 
 impl ZipEntry {
     /// Creates a new ZipEntry
     ///
-    /// if is_file is false the path will contain an trailing separator,
+    /// if file is a directory the path will contain a trailing separator,
     /// so that the zip file understands that it is a directory
-    pub fn new<P: AsRef<Path>>(path: P, mtime: i64, mode: u16, is_file: bool) -> Self {
+    pub fn new<P: AsRef<Path>>(path: P, mtime: i64, mode: u16, file_type: FileType) -> Self {
         let mut relpath = PathBuf::new();
 
         for comp in path.as_ref().components() {
@@ -220,7 +225,7 @@ impl ZipEntry {
             }
         }
 
-        if !is_file {
+        if matches!(file_type, FileType::Directory) {
             relpath.push(""); // adds trailing slash
         }
 
@@ -235,7 +240,7 @@ impl ZipEntry {
             uncompressed_size: 0,
             compressed_size: 0,
             offset: 0,
-            is_file,
+            file_type,
             is_utf8_filename,
         }
     }
@@ -342,6 +347,11 @@ impl ZipEntry {
             )
         };
 
+        let file_type_attr = match self.file_type {
+            FileType::Regular => 0o100000,
+            FileType::Directory => 0o040000,
+        };
+
         write_struct(
             &mut buf,
             CentralDirectoryFileHeader {
@@ -360,7 +370,8 @@ impl ZipEntry {
                 comment_len: 0,
                 start_disk: 0,
                 internal_flags: 0,
-                external_flags: (self.mode as u32) << 16 | (!self.is_file as u32) << 4,
+                external_flags: (self.mode as u32 | file_type_attr) << 16
+                    | (matches!(self.file_type, FileType::Directory) as u32) << 4,
                 offset,
             },
         )
@@ -449,7 +460,7 @@ where
 ///         "foo.txt",
 ///         0,
 ///         0o100755,
-///         true,
+///         FileType::Regular,
 ///     ), Some(source)).await?;
 ///
 ///     zip.finish().await?;
@@ -503,6 +514,7 @@ impl<W: AsyncWrite + Unpin> ZipEncoder<W> {
 
             entry.crc32 = crc32;
         }
+
         self.byte_count += entry.write_data_descriptor(&mut target).await?;
         self.target = Some(target);
 
@@ -658,10 +670,10 @@ where
 
             if entry.file_type().is_file() {
                 let file = tokio::fs::File::open(entry.path()).await?;
-                let ze = ZipEntry::new(entry_path_no_base, mtime, mode, true);
+                let ze = ZipEntry::new(entry_path_no_base, mtime, mode, FileType::Regular);
                 Ok(Some((ze, Some(file))))
             } else if entry.file_type().is_dir() {
-                let ze = ZipEntry::new(entry_path_no_base, mtime, mode, false);
+                let ze = ZipEntry::new(entry_path_no_base, mtime, mode, FileType::Directory);
                 let content: Option<tokio::fs::File> = None;
                 Ok(Some((ze, content)))
             } else {
-- 
2.39.2





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-14 14:48 [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 1/3] compression: Add a FileType enum to ZipEntry Filip Schauer
@ 2023-12-14 14:48 ` Filip Schauer
  2023-12-20 13:20   ` Wolfgang Bumiller
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 3/3] compression: Add unit tests for the ZipEncoder Filip Schauer
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Filip Schauer @ 2023-12-14 14:48 UTC (permalink / raw)
  To: pbs-devel

Add support for symlinks to ZipEntry. A symlink is encoded by or-ing its
attributes with S_IFLNK as seen in the kernel in
include/uapi/linux/stat.h

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 proxmox-compression/src/zip.rs | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/proxmox-compression/src/zip.rs b/proxmox-compression/src/zip.rs
index 069e8bc..a3b2346 100644
--- a/proxmox-compression/src/zip.rs
+++ b/proxmox-compression/src/zip.rs
@@ -74,6 +74,7 @@ fn epoch_to_dos(epoch: i64) -> (u16, u16) {
 pub enum FileType {
     Directory,
     Regular,
+    Symlink(OsString),
 }
 
 #[derive(Endian)]
@@ -350,6 +351,7 @@ impl ZipEntry {
         let file_type_attr = match self.file_type {
             FileType::Regular => 0o100000,
             FileType::Directory => 0o040000,
+            FileType::Symlink(_) => 0o120000,
         };
 
         write_struct(
@@ -497,22 +499,28 @@ impl<W: AsyncWrite + Unpin> ZipEncoder<W> {
             .ok_or_else(|| format_err!("had no target during add entry"))?;
         entry.offset = self.byte_count.try_into()?;
         self.byte_count += entry.write_local_header(&mut target).await?;
-        if let Some(content) = content {
-            let mut reader = HashWrapper::new(content);
+
+        if content.is_some() || matches!(entry.file_type, FileType::Symlink(_)) {
             let mut enc = DeflateEncoder::with_quality(target, Level::Fastest);
 
-            enc.compress(&mut reader).await?;
+            if let Some(content) = content {
+                let mut reader = HashWrapper::new(content);
+                enc.compress(&mut reader).await?;
+                entry.crc32 = reader.finish().0;
+            } else if let FileType::Symlink(symlink_target) = &entry.file_type {
+                let cursor = std::io::Cursor::new(symlink_target.as_bytes());
+                let mut reader = HashWrapper::new(cursor);
+                enc.compress(&mut reader).await?;
+                entry.crc32 = reader.finish().0;
+            }
+
             let total_in = enc.total_in();
             let total_out = enc.total_out();
             target = enc.into_inner();
 
-            let (crc32, _reader) = reader.finish();
-
             self.byte_count += total_out as usize;
             entry.compressed_size = total_out;
             entry.uncompressed_size = total_in;
-
-            entry.crc32 = crc32;
         }
 
         self.byte_count += entry.write_data_descriptor(&mut target).await?;
@@ -676,6 +684,16 @@ where
                 let ze = ZipEntry::new(entry_path_no_base, mtime, mode, FileType::Directory);
                 let content: Option<tokio::fs::File> = None;
                 Ok(Some((ze, content)))
+            } else if entry.file_type().is_symlink() {
+                let target = std::fs::read_link(entry.path())?;
+                let ze = ZipEntry::new(
+                    entry_path_no_base,
+                    mtime,
+                    mode,
+                    FileType::Symlink(target.into()),
+                );
+                let content: Option<tokio::fs::File> = None;
+                Ok(Some((ze, content)))
             } else {
                 // ignore other file types
                 Ok::<_, Error>(None)
-- 
2.39.2





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH v3 proxmox 3/3] compression: Add unit tests for the ZipEncoder
  2023-12-14 14:48 [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 1/3] compression: Add a FileType enum to ZipEntry Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files Filip Schauer
@ 2023-12-14 14:48 ` Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 backup 1/2] pxar: Adopt FileType enum when creating a ZipEntry Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 backup 2/2] fix #4995: pxar: Include symlinks in zip file creation Filip Schauer
  4 siblings, 0 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-14 14:48 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 proxmox-compression/Cargo.toml   |   2 +-
 proxmox-compression/tests/zip.rs | 134 +++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 proxmox-compression/tests/zip.rs

diff --git a/proxmox-compression/Cargo.toml b/proxmox-compression/Cargo.toml
index 49735cb..8c9c47e 100644
--- a/proxmox-compression/Cargo.toml
+++ b/proxmox-compression/Cargo.toml
@@ -27,5 +27,5 @@ proxmox-io = { workspace = true, features = [ "tokio" ] }
 proxmox-lang.workspace = true
 
 [dev-dependencies]
-tokio = { workspace = true, features = [ "macros" ] }
+tokio = { workspace = true, features = [ "macros", "rt" ] }
 
diff --git a/proxmox-compression/tests/zip.rs b/proxmox-compression/tests/zip.rs
new file mode 100644
index 0000000..f684ccc
--- /dev/null
+++ b/proxmox-compression/tests/zip.rs
@@ -0,0 +1,134 @@
+use proxmox_compression::zip::{FileType, ZipEncoder, ZipEntry};
+
+use anyhow::{ensure, Error};
+use flate2::{Decompress, FlushDecompress};
+use std::ffi::OsString;
+use tokio::test;
+
+fn check_zip_with_one_file(
+    bytes: Vec<u8>,
+    expected_file_name: &str,
+    expected_content: &[u8],
+    expected_file_attributes: u16,
+    is_file: bool,
+) -> Result<(), Error> {
+    ensure!(
+        &bytes[..4] == b"PK\x03\x04",
+        "ZIP magic number does not match"
+    );
+
+    let general_purpose_flags = &bytes[6..8];
+    let size_compressed = &bytes[18..22];
+    let size_uncompressed = &bytes[22..26];
+    let file_name_len = (bytes[26] as usize) | ((bytes[27] as usize) << 8);
+    let extra_len = (bytes[28] as usize) | ((bytes[29] as usize) << 8);
+    let file_name = &bytes[30..30 + file_name_len];
+    let mut offset = 30 + file_name_len;
+
+    assert_eq!(expected_file_name.as_bytes(), file_name);
+
+    offset += extra_len;
+
+    if is_file {
+        let mut decompress = Decompress::new(false);
+        let mut decompressed_data = vec![0u8; expected_content.len()];
+        decompress.decompress(
+            &bytes[offset..],
+            &mut decompressed_data,
+            FlushDecompress::Finish,
+        )?;
+
+        assert_eq!(decompressed_data, expected_content);
+
+        offset += decompress.total_in() as usize;
+    }
+
+    // Optional data descriptor
+    if &bytes[offset..offset + 4] == b"PK\x07\x08" {
+        offset += 4;
+
+        if (general_purpose_flags[0] & 0x08) == 0x08 {
+            offset += 12;
+
+            if size_compressed == b"\xff\xff\xff\xff" && size_uncompressed == b"\xff\xff\xff\xff" {
+                offset += 8;
+            }
+        }
+    }
+
+    ensure!(
+        &bytes[offset..offset + 4] == b"PK\x01\x02",
+        "Missing central directory file header"
+    );
+
+    let external_file_attributes = &bytes[offset + 38..offset + 42];
+    let file_attributes = u16::from_le_bytes(external_file_attributes[2..4].try_into()?);
+
+    assert_eq!(expected_file_attributes, file_attributes);
+
+    Ok(())
+}
+
+#[test]
+async fn test_zip_text_file() -> Result<(), Error> {
+    let mut target = Vec::new();
+
+    let source_contents = "bar";
+    let source = source_contents.as_bytes();
+
+    let mut zip_encoder = ZipEncoder::new(&mut target);
+    zip_encoder
+        .add_entry(
+            ZipEntry::new("foo.txt", 0, 0o755, FileType::Regular),
+            Some(source),
+        )
+        .await?;
+
+    zip_encoder.finish().await?;
+
+    check_zip_with_one_file(target, "foo.txt", b"bar", 0o100755, true)?;
+
+    Ok(())
+}
+
+#[test]
+async fn test_zip_symlink() -> Result<(), Error> {
+    let mut target = Vec::new();
+
+    let link_path = OsString::from("/dev/null");
+
+    let mut zip_encoder = ZipEncoder::new(&mut target);
+    let content: Option<tokio::fs::File> = None;
+    zip_encoder
+        .add_entry(
+            ZipEntry::new("link", 0, 0o755, FileType::Symlink(link_path)),
+            content,
+        )
+        .await?;
+
+    zip_encoder.finish().await?;
+
+    check_zip_with_one_file(target, "link", b"/dev/null", 0o120755, true)?;
+
+    Ok(())
+}
+
+#[test]
+async fn test_zip_directory() -> Result<(), Error> {
+    let mut target = Vec::new();
+
+    let mut zip_encoder = ZipEncoder::new(&mut target);
+    let content: Option<tokio::fs::File> = None;
+    zip_encoder
+        .add_entry(
+            ZipEntry::new("directory", 0, 0o755, FileType::Directory),
+            content,
+        )
+        .await?;
+
+    zip_encoder.finish().await?;
+
+    check_zip_with_one_file(target, "directory/", b"", 0o40755, false)?;
+
+    Ok(())
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH v3 backup 1/2] pxar: Adopt FileType enum when creating a ZipEntry
  2023-12-14 14:48 [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore Filip Schauer
                   ` (2 preceding siblings ...)
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 3/3] compression: Add unit tests for the ZipEncoder Filip Schauer
@ 2023-12-14 14:48 ` Filip Schauer
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 backup 2/2] fix #4995: pxar: Include symlinks in zip file creation Filip Schauer
  4 siblings, 0 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-14 14:48 UTC (permalink / raw)
  To: pbs-devel

Use a FileType enum instead of a boolean to specify whether a ZipEntry
refers to a directory or a regular file.

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 pbs-client/src/pxar/extract.rs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/pbs-client/src/pxar/extract.rs b/pbs-client/src/pxar/extract.rs
index f78e06c2..d8bbd3e1 100644
--- a/pbs-client/src/pxar/extract.rs
+++ b/pbs-client/src/pxar/extract.rs
@@ -24,7 +24,7 @@ use proxmox_io::{sparse_copy, sparse_copy_async};
 use proxmox_sys::c_result;
 use proxmox_sys::fs::{create_path, CreateOptions};
 
-use proxmox_compression::zip::{ZipEncoder, ZipEntry};
+use proxmox_compression::zip::{FileType, ZipEncoder, ZipEntry};
 
 use crate::pxar::dir_stack::PxarDirStack;
 use crate::pxar::metadata;
@@ -997,7 +997,7 @@ where
                 path,
                 metadata.stat.mtime.secs,
                 metadata.stat.mode as u16,
-                false,
+                FileType::Directory,
             );
             zip.add_entry::<FileContents<T>>(entry, None).await?;
         }
@@ -1016,7 +1016,7 @@ where
                         path,
                         metadata.stat.mtime.secs,
                         metadata.stat.mode as u16,
-                        true,
+                        FileType::Regular,
                     );
                     zip.add_entry(entry, decoder.contents())
                         .await
@@ -1034,7 +1034,7 @@ where
                         path,
                         metadata.stat.mtime.secs,
                         metadata.stat.mode as u16,
-                        true,
+                        FileType::Regular,
                     );
                     zip.add_entry(entry, decoder.contents())
                         .await
@@ -1046,7 +1046,7 @@ where
                         path,
                         metadata.stat.mtime.secs,
                         metadata.stat.mode as u16,
-                        false,
+                        FileType::Directory,
                     );
                     zip.add_entry::<FileContents<T>>(entry, None).await?;
                 }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH v3 backup 2/2] fix #4995: pxar: Include symlinks in zip file creation
  2023-12-14 14:48 [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore Filip Schauer
                   ` (3 preceding siblings ...)
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 backup 1/2] pxar: Adopt FileType enum when creating a ZipEntry Filip Schauer
@ 2023-12-14 14:48 ` Filip Schauer
  4 siblings, 0 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-14 14:48 UTC (permalink / raw)
  To: pbs-devel

Include symlinks when restoring a backup as a zip file.

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 pbs-client/src/pxar/extract.rs | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/pbs-client/src/pxar/extract.rs b/pbs-client/src/pxar/extract.rs
index d8bbd3e1..b10609e0 100644
--- a/pbs-client/src/pxar/extract.rs
+++ b/pbs-client/src/pxar/extract.rs
@@ -1050,6 +1050,19 @@ where
                     );
                     zip.add_entry::<FileContents<T>>(entry, None).await?;
                 }
+                EntryKind::Symlink(link) => {
+                    log::debug!("adding '{}' to zip", path.display());
+                    let realpath = Path::new(link);
+
+                    let entry = ZipEntry::new(
+                        path,
+                        metadata.stat.mtime.secs,
+                        metadata.stat.mode as u16,
+                        FileType::Symlink(realpath.into()),
+                    );
+
+                    zip.add_entry::<FileContents<T>>(entry, None).await?;
+                }
                 _ => {} // ignore all else
             };
         }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files Filip Schauer
@ 2023-12-20 13:20   ` Wolfgang Bumiller
  2023-12-21 11:37     ` Filip Schauer
  0 siblings, 1 reply; 12+ messages in thread
From: Wolfgang Bumiller @ 2023-12-20 13:20 UTC (permalink / raw)
  To: Filip Schauer; +Cc: pbs-devel, Dominik Csapak

With the link of a symlink being encoded in the contents, I wonder if we
should just do the same in the code. Not normally what I'd go for in
rust, but...

On Thu, Dec 14, 2023 at 03:48:21PM +0100, Filip Schauer wrote:
> Add support for symlinks to ZipEntry. A symlink is encoded by or-ing its
> attributes with S_IFLNK as seen in the kernel in
> include/uapi/linux/stat.h
> 
> Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
> ---
>  proxmox-compression/src/zip.rs | 32 +++++++++++++++++++++++++-------
>  1 file changed, 25 insertions(+), 7 deletions(-)
> 
> diff --git a/proxmox-compression/src/zip.rs b/proxmox-compression/src/zip.rs
> index 069e8bc..a3b2346 100644
> --- a/proxmox-compression/src/zip.rs
> +++ b/proxmox-compression/src/zip.rs
> @@ -74,6 +74,7 @@ fn epoch_to_dos(epoch: i64) -> (u16, u16) {
>  pub enum FileType {
>      Directory,
>      Regular,
> +    Symlink(OsString),

... then this enum could be #[repr(u32)] using the values from the hunk
below as discriminants here directly.
And without the OsString in there this could be all of `Clone + Copy +
Eq + PartialEq`, turning all the `matches!()` in this series into
comparisons with `==`.

>  }
>  
>  #[derive(Endian)]
> @@ -350,6 +351,7 @@ impl ZipEntry {


(maybe include a comment that these are the same as the S_IF* constants,
since Dominik had asked about more documentation in the previous
version ;-) )

>          let file_type_attr = match self.file_type {
>              FileType::Regular => 0o100000,
>              FileType::Directory => 0o040000,
> +            FileType::Symlink(_) => 0o120000,
>          };
>  
>          write_struct(
> @@ -497,22 +499,28 @@ impl<W: AsyncWrite + Unpin> ZipEncoder<W> {
>              .ok_or_else(|| format_err!("had no target during add entry"))?;
>          entry.offset = self.byte_count.try_into()?;
>          self.byte_count += entry.write_local_header(&mut target).await?;
> -        if let Some(content) = content {
> -            let mut reader = HashWrapper::new(content);
> +
> +        if content.is_some() || matches!(entry.file_type, FileType::Symlink(_)) {
>              let mut enc = DeflateEncoder::with_quality(target, Level::Fastest);
>  
> -            enc.compress(&mut reader).await?;
> +            if let Some(content) = content {
> +                let mut reader = HashWrapper::new(content);
> +                enc.compress(&mut reader).await?;
> +                entry.crc32 = reader.finish().0;
> +            } else if let FileType::Symlink(symlink_target) = &entry.file_type {
> +                let cursor = std::io::Cursor::new(symlink_target.as_bytes());
> +                let mut reader = HashWrapper::new(cursor);
> +                enc.compress(&mut reader).await?;
> +                entry.crc32 = reader.finish().0;

^ and AFAICT this entire hunk would simply disappear?

> +            }
> +
>              let total_in = enc.total_in();
>              let total_out = enc.total_out();
>              target = enc.into_inner();
>  
> -            let (crc32, _reader) = reader.finish();
> -
>              self.byte_count += total_out as usize;
>              entry.compressed_size = total_out;
>              entry.uncompressed_size = total_in;
> -
> -            entry.crc32 = crc32;
>          }
>  
>          self.byte_count += entry.write_data_descriptor(&mut target).await?;
> @@ -676,6 +684,16 @@ where
>                  let ze = ZipEntry::new(entry_path_no_base, mtime, mode, FileType::Directory);
>                  let content: Option<tokio::fs::File> = None;
>                  Ok(Some((ze, content)))
> +            } else if entry.file_type().is_symlink() {
> +                let target = std::fs::read_link(entry.path())?;
> +                let ze = ZipEntry::new(
> +                    entry_path_no_base,
> +                    mtime,
> +                    mode,
> +                    FileType::Symlink(target.into()),
> +                );
> +                let content: Option<tokio::fs::File> = None;
> +                Ok(Some((ze, content)))
>              } else {
>                  // ignore other file types
>                  Ok::<_, Error>(None)
> -- 
> 2.39.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-20 13:20   ` Wolfgang Bumiller
@ 2023-12-21 11:37     ` Filip Schauer
  2023-12-21 12:03       ` Wolfgang Bumiller
  2023-12-21 12:11       ` Wolfgang Bumiller
  0 siblings, 2 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-21 11:37 UTC (permalink / raw)
  To: Wolfgang Bumiller; +Cc: pbs-devel, Dominik Csapak

On 20/12/2023 14:20, Wolfgang Bumiller wrote:
> With the link of a symlink being encoded in the contents, I wonder if we
> should just do the same in the code. Not normally what I'd go for in
> rust, but...
>
> On Thu, Dec 14, 2023 at 03:48:21PM +0100, Filip Schauer wrote:
>> Add support for symlinks to ZipEntry. A symlink is encoded by or-ing its
>> attributes with S_IFLNK as seen in the kernel in
>> include/uapi/linux/stat.h
>>
>> Signed-off-by: Filip Schauer<f.schauer@proxmox.com>
>> ---
>>   proxmox-compression/src/zip.rs | 32 +++++++++++++++++++++++++-------
>>   1 file changed, 25 insertions(+), 7 deletions(-)
>>
>> diff --git a/proxmox-compression/src/zip.rs b/proxmox-compression/src/zip.rs
>> index 069e8bc..a3b2346 100644
>> --- a/proxmox-compression/src/zip.rs
>> +++ b/proxmox-compression/src/zip.rs
>> @@ -74,6 +74,7 @@ fn epoch_to_dos(epoch: i64) -> (u16, u16) {
>>   pub enum FileType {
>>       Directory,
>>       Regular,
>> +    Symlink(OsString),
> ... then this enum could be #[repr(u32)] using the values from the hunk
> below as discriminants here directly.
> And without the OsString in there this could be all of `Clone + Copy +
> Eq + PartialEq`, turning all the `matches!()` in this series into
> comparisons with `==`.


Do you want to pass the symlink target as an additional argument to
add_entry? If so, add_entry would take an enum FileType, an Option
content and an Option symlink_target. Depending on the FileType, at
least one of the Options would be None. This approach does not seem very
clean to me.

I just thought of another way:

How about inserting the content into FileType::Regular. This would
eliminate the need to pass None to add_entry. Therefore, it would be
impossible to pass conflicting parameters to the function, such as
FileType::Regular with content = None. This change would not get rid of
the hunk you mentioned, but the code seems cleaner to me this way.




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-21 11:37     ` Filip Schauer
@ 2023-12-21 12:03       ` Wolfgang Bumiller
  2023-12-21 12:15         ` Filip Schauer
  2023-12-21 12:11       ` Wolfgang Bumiller
  1 sibling, 1 reply; 12+ messages in thread
From: Wolfgang Bumiller @ 2023-12-21 12:03 UTC (permalink / raw)
  To: Filip Schauer; +Cc: pbs-devel

On Thu, Dec 21, 2023 at 12:37:42PM +0100, Filip Schauer wrote:
> On 20/12/2023 14:20, Wolfgang Bumiller wrote:
> > With the link of a symlink being encoded in the contents, I wonder if we
> > should just do the same in the code. Not normally what I'd go for in
> > rust, but...
> > 
> > On Thu, Dec 14, 2023 at 03:48:21PM +0100, Filip Schauer wrote:
> > > Add support for symlinks to ZipEntry. A symlink is encoded by or-ing its
> > > attributes with S_IFLNK as seen in the kernel in
> > > include/uapi/linux/stat.h
> > > 
> > > Signed-off-by: Filip Schauer<f.schauer@proxmox.com>
> > > ---
> > >   proxmox-compression/src/zip.rs | 32 +++++++++++++++++++++++++-------
> > >   1 file changed, 25 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/proxmox-compression/src/zip.rs b/proxmox-compression/src/zip.rs
> > > index 069e8bc..a3b2346 100644
> > > --- a/proxmox-compression/src/zip.rs
> > > +++ b/proxmox-compression/src/zip.rs
> > > @@ -74,6 +74,7 @@ fn epoch_to_dos(epoch: i64) -> (u16, u16) {
> > >   pub enum FileType {
> > >       Directory,
> > >       Regular,
> > > +    Symlink(OsString),
> > ... then this enum could be #[repr(u32)] using the values from the hunk
> > below as discriminants here directly.
> > And without the OsString in there this could be all of `Clone + Copy +
> > Eq + PartialEq`, turning all the `matches!()` in this series into
> > comparisons with `==`.
> 
> 
> Do you want to pass the symlink target as an additional argument to
> add_entry? If so, add_entry would take an enum FileType, an Option
> content and an Option symlink_target. Depending on the FileType, at
> least one of the Options would be None. This approach does not seem very
> clean to me.

Why would it be a separate parameter? Target == Content.




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-21 11:37     ` Filip Schauer
  2023-12-21 12:03       ` Wolfgang Bumiller
@ 2023-12-21 12:11       ` Wolfgang Bumiller
  2024-01-24 10:19         ` Filip Schauer
  1 sibling, 1 reply; 12+ messages in thread
From: Wolfgang Bumiller @ 2023-12-21 12:11 UTC (permalink / raw)
  To: Filip Schauer; +Cc: pbs-devel

forgot to include this in my other reply:

On Thu, Dec 21, 2023 at 12:37:42PM +0100, Filip Schauer wrote:
> On 20/12/2023 14:20, Wolfgang Bumiller wrote:
(...)
> I just thought of another way:
> 
> How about inserting the content into FileType::Regular. This would
> eliminate the need to pass None to add_entry. Therefore, it would be
> impossible to pass conflicting parameters to the function, such as
> FileType::Regular with content = None. This change would not get rid of
> the hunk you mentioned, but the code seems cleaner to me this way.

Works, too.
If FileType has a getter to just get an Option<Reader> (possibly even as
an internal `.*take*_reader()` since `add_entry` gets the full entry
by-move anyway) that part of the code can still just gets an option as
before without caring about the file mode so can otherwise stay the
same.




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-21 12:03       ` Wolfgang Bumiller
@ 2023-12-21 12:15         ` Filip Schauer
  0 siblings, 0 replies; 12+ messages in thread
From: Filip Schauer @ 2023-12-21 12:15 UTC (permalink / raw)
  To: Wolfgang Bumiller; +Cc: pbs-devel


On 21/12/2023 13:03, Wolfgang Bumiller wrote:
> On Thu, Dec 21, 2023 at 12:37:42PM +0100, Filip Schauer wrote:
>> On 20/12/2023 14:20, Wolfgang Bumiller wrote:
>>> With the link of a symlink being encoded in the contents, I wonder if we
>>> should just do the same in the code. Not normally what I'd go for in
>>> rust, but...
>>>
>>> On Thu, Dec 14, 2023 at 03:48:21PM +0100, Filip Schauer wrote:
>>>> Add support for symlinks to ZipEntry. A symlink is encoded by or-ing its
>>>> attributes with S_IFLNK as seen in the kernel in
>>>> include/uapi/linux/stat.h
>>>>
>>>> Signed-off-by: Filip Schauer<f.schauer@proxmox.com>
>>>> ---
>>>>    proxmox-compression/src/zip.rs | 32 +++++++++++++++++++++++++-------
>>>>    1 file changed, 25 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/proxmox-compression/src/zip.rs b/proxmox-compression/src/zip.rs
>>>> index 069e8bc..a3b2346 100644
>>>> --- a/proxmox-compression/src/zip.rs
>>>> +++ b/proxmox-compression/src/zip.rs
>>>> @@ -74,6 +74,7 @@ fn epoch_to_dos(epoch: i64) -> (u16, u16) {
>>>>    pub enum FileType {
>>>>        Directory,
>>>>        Regular,
>>>> +    Symlink(OsString),
>>> ... then this enum could be #[repr(u32)] using the values from the hunk
>>> below as discriminants here directly.
>>> And without the OsString in there this could be all of `Clone + Copy +
>>> Eq + PartialEq`, turning all the `matches!()` in this series into
>>> comparisons with `==`.
>>
>> Do you want to pass the symlink target as an additional argument to
>> add_entry? If so, add_entry would take an enum FileType, an Option
>> content and an Option symlink_target. Depending on the FileType, at
>> least one of the Options would be None. This approach does not seem very
>> clean to me.
> Why would it be a separate parameter? Target == Content.

Never mind then. I misunderstood.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files
  2023-12-21 12:11       ` Wolfgang Bumiller
@ 2024-01-24 10:19         ` Filip Schauer
  0 siblings, 0 replies; 12+ messages in thread
From: Filip Schauer @ 2024-01-24 10:19 UTC (permalink / raw)
  To: Wolfgang Bumiller; +Cc: pbs-devel

New patch available:

https://lists.proxmox.com/pipermail/pbs-devel/2024-January/007679.html

On 21/12/2023 13:11, Wolfgang Bumiller wrote:
> forgot to include this in my other reply:
>
> On Thu, Dec 21, 2023 at 12:37:42PM +0100, Filip Schauer wrote:
>> On 20/12/2023 14:20, Wolfgang Bumiller wrote:
> (...)
>> I just thought of another way:
>>
>> How about inserting the content into FileType::Regular. This would
>> eliminate the need to pass None to add_entry. Therefore, it would be
>> impossible to pass conflicting parameters to the function, such as
>> FileType::Regular with content = None. This change would not get rid of
>> the hunk you mentioned, but the code seems cleaner to me this way.
> Works, too.
> If FileType has a getter to just get an Option<Reader> (possibly even as
> an internal `.*take*_reader()` since `add_entry` gets the full entry
> by-move anyway) that part of the code can still just gets an option as
> before without caring about the file mode so can otherwise stay the
> same.




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2024-01-24 10:19 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-14 14:48 [pbs-devel] [PATCH v3 many] fix #4995: Include symlinks in zip file restore Filip Schauer
2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 1/3] compression: Add a FileType enum to ZipEntry Filip Schauer
2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 2/3] compression: Add support for symlinks in zip files Filip Schauer
2023-12-20 13:20   ` Wolfgang Bumiller
2023-12-21 11:37     ` Filip Schauer
2023-12-21 12:03       ` Wolfgang Bumiller
2023-12-21 12:15         ` Filip Schauer
2023-12-21 12:11       ` Wolfgang Bumiller
2024-01-24 10:19         ` Filip Schauer
2023-12-14 14:48 ` [pbs-devel] [PATCH v3 proxmox 3/3] compression: Add unit tests for the ZipEncoder Filip Schauer
2023-12-14 14:48 ` [pbs-devel] [PATCH v3 backup 1/2] pxar: Adopt FileType enum when creating a ZipEntry Filip Schauer
2023-12-14 14:48 ` [pbs-devel] [PATCH v3 backup 2/2] fix #4995: pxar: Include symlinks in zip file creation Filip Schauer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal