bug-tar
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 1/4] reflink: align data on block size on archive create


From: Matteo Croce
Subject: [PATCH 1/4] reflink: align data on block size on archive create
Date: Sat, 26 Oct 2024 03:27:14 +0200

From: Matteo Croce <teknoraver@meta.com>

Add a --reflink option which, on archive creation, aligns file data
on a 4k block boundary. This is done by padding the header with a
standard "comment" pax record, which will be ignored on extraction.

This will be used when extracting files using reflinks, as one of the
requirements is that the data is aligned on a 4k boundary.
---
 src/buffer.c  | 13 +++++++++++++
 src/common.h  | 18 ++++++++++++++++++
 src/create.c  | 21 +++++++++++++++++++++
 src/tar.c     |  9 +++++++++
 src/xheader.c | 12 +++++++++++-
 5 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/src/buffer.c b/src/buffer.c
index 570c8666..988392fa 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1127,6 +1127,19 @@ close_archive (void)
   if (verify_option)
     verify_volume ();
 
+  if (reflink_option && access_mode == ACCESS_WRITE)
+    {
+      off_t pos = rmtlseek (archive, 0, SEEK_END);
+      /* Pad the archive to the next reflink block boundary,
+       * otherwise reflink will fail extracting the last file. */
+      if (pos % REFLINK_BLOCK_SIZE != 0)
+        {
+          pos = round_up (pos, REFLINK_BLOCK_SIZE);
+          if (ftruncate (archive, pos) != 0)
+            close_error (_("Cannot pad archive to reflink block boundary"));
+        }
+    }
+
   if (rmtclose (archive) != 0)
     close_error (*archive_name_cursor);
 
diff --git a/src/common.h b/src/common.h
index 07ac5fc7..c1d7431a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -277,6 +277,9 @@ extern int acls_option;
 /* If positive, save the user and root xattrs.  */
 extern int xattrs_option;
 
+/* If true, use reflinks when possible.  */
+extern bool reflink_option;
+
 /* When set, strip the given number of file name components from the file name
    before extracting */
 extern size_t strip_name_components;
@@ -396,6 +399,14 @@ extern bool show_transformed_names_option;
    timestamps from archives with an unusual member order. It is automatically
    set for incremental archives. */
 extern bool delay_directory_restore_option;
+
+/* Rounds up n to the next multiple of m. Only works if m is a power of two. */
+COMMON_INLINE unsigned long round_up (unsigned long n, unsigned long m)
+{
+  m--;
+  return (n + m) & ~m;
+}
+
 
 /* Declarations for each module.  */
 
@@ -702,6 +713,8 @@ char const *code_timespec (struct timespec ts,
                           char tsbuf[TIMESPEC_STRSIZE_BOUND]);
 struct timespec decode_timespec (char const *, char **, bool);
 
+enum { REFLINK_BLOCK_SIZE = 4096 };
+
 /* Return true if T does not represent an out-of-range or invalid value.  */
 COMMON_INLINE bool
 valid_timespec (struct timespec t)
@@ -886,6 +899,11 @@ void update_archive (void);
 
 /* Module xheader.c.  */
 
+struct comment {
+  char *comment;
+  size_t length;
+};
+
 void xheader_decode (struct tar_stat_info *stat);
 void xheader_decode_global (struct xheader *xhdr);
 void xheader_store (char const *keyword, struct tar_stat_info *st,
diff --git a/src/create.c b/src/create.c
index 5f71ca97..0e6bed1a 100644
--- a/src/create.c
+++ b/src/create.c
@@ -954,6 +954,27 @@ start_header (struct tar_stat_info *st)
          for (i = 0; i < st->xattr_map.xm_size; i++)
            xheader_store (st->xattr_map.xm_map[i].xkey, st, &i);
         }
+      if (reflink_option && S_ISREG (st->stat.st_mode) && st->stat.st_size)
+       {
+         size_t pos = (unsigned long)records_written * record_size + 
(current_block->buffer - record_start->buffer);
+         size_t rem = pos % REFLINK_BLOCK_SIZE;
+         ssize_t n = REFLINK_BLOCK_SIZE - 1024 - rem - st->xhdr.size - 14;
+         static char comment_buf[REFLINK_BLOCK_SIZE];
+
+         if (n < 0)
+           n += REFLINK_BLOCK_SIZE;
+
+         if (comment_buf[0] == 0)
+           memset (comment_buf, ' ', sizeof (comment_buf));
+
+         struct comment comment =
+           {
+             .comment = comment_buf,
+             .length = n,
+           };
+
+         xheader_store ("comment", st, &comment);
+       }
     }
 
   return header;
diff --git a/src/tar.c b/src/tar.c
index 3d965dfd..869a77bc 100644
--- a/src/tar.c
+++ b/src/tar.c
@@ -90,6 +90,7 @@ int xattrs_option;
 size_t strip_name_components;
 bool show_omitted_dirs_option;
 bool sparse_option;
+bool reflink_option;
 intmax_t tar_sparse_major;
 intmax_t tar_sparse_minor;
 enum hole_detection_method hole_detection;
@@ -408,6 +409,7 @@ enum
   QUOTING_STYLE_OPTION,
   RECORD_SIZE_OPTION,
   RECURSIVE_UNLINK_OPTION,
+  REFLINK_OPTION,
   REMOVE_FILES_OPTION,
   RESTRICT_OPTION,
   RMT_COMMAND_OPTION,
@@ -589,6 +591,8 @@ static struct argp_option options[] = {
   {"check-device", CHECK_DEVICE_OPTION, NULL, 0,
    N_("check device numbers when creating incremental archives (default)"),
    GRID_MODIFIER },
+  {"reflink", REFLINK_OPTION, 0, 0,
+   N_("Use reflinks when available"), GRID_MODIFIER },
 
   {NULL, 0, NULL, 0,
    N_("Overwrite control:"), GRH_OVERWRITE },
@@ -1776,6 +1780,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
       sparse_option = true;
       break;
 
+    case REFLINK_OPTION:
+      set_archive_format ("posix");
+      reflink_option = true;
+      break;
+
     case SKIP_OLD_FILES_OPTION:
       set_old_files_option (SKIP_OLD_FILES, args->loc);
       break;
diff --git a/src/xheader.c b/src/xheader.c
index 79d30939..e4398434 100644
--- a/src/xheader.c
+++ b/src/xheader.c
@@ -1697,9 +1697,19 @@ sparse_minor_decoder (struct tar_stat_info *st,
     st->sparse_minor = u;
 }
 
+static void
+comment_coder (MAYBE_UNUSED struct tar_stat_info const *st,
+              char const *keyword,
+               struct xheader *xhdr,
+              void const *data)
+{
+  struct comment *comment = (struct comment *)data;
+  xheader_print_n (xhdr, keyword, comment->comment, comment->length);
+}
+
 struct xhdr_tab const xhdr_tab[] = {
   { "atime",    atime_coder,    atime_decoder,    0, false },
-  { "comment",  dummy_coder,    dummy_decoder,    0, false },
+  { "comment",  comment_coder,  dummy_decoder,    0, false },
   { "charset",  dummy_coder,    dummy_decoder,    0, false },
   { "ctime",    ctime_coder,    ctime_decoder,    0, false },
   { "gid",      gid_coder,      gid_decoder,      0, false },
-- 
2.46.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]