From: Emil Karlson <jkarl...@tuxera.com> Add a new option to truncate to the smallest size that contains all its extents ie. truncate to the offset of the end of the last extent in the file. This may be convenient when passing very large images with long sparse tails to systems that do not handle sparse images reasonably and do not need the sparse tails. --- src/truncate.c | 51 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-)
diff --git a/src/truncate.c b/src/truncate.c index f43637e9b..175b4adac 100644 --- a/src/truncate.c +++ b/src/truncate.c @@ -43,6 +43,9 @@ static bool no_create; /* (-o) If true, --size refers to blocks not bytes */ static bool block_mode; +/* (-d) If true, truncate to the offset of the end of the last extent */ +static bool data_size; + /* (-r) Reference file to use size from */ static char const *ref_file; @@ -50,6 +53,7 @@ static struct option const longopts[] = { {"no-create", no_argument, NULL, 'c'}, {"io-blocks", no_argument, NULL, 'o'}, + {"data-size", no_argument, NULL, 'd'}, {"reference", required_argument, NULL, 'r'}, {"size", required_argument, NULL, 's'}, {GETOPT_HELP_OPTION_DECL}, @@ -88,7 +92,9 @@ reads as zero bytes.\n\ "), stdout); fputs (_("\ -r, --reference=RFILE base size on RFILE\n\ - -s, --size=SIZE set or adjust the file size by SIZE bytes\n"), stdout); + -s, --size=SIZE set or adjust the file size by SIZE bytes\n\ + -d, --data-size set or adjust the file size to last data extent\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); emit_size_note (); @@ -193,6 +199,37 @@ do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize, nsize = fsize + ssize; } } + else if (data_size) + { + off_t last_hole = 0, offset = 0; + while (0 <= (offset = lseek (fd, offset, SEEK_DATA))) + { + if (0 <= (last_hole = lseek (fd, offset, SEEK_HOLE))) + { + offset = last_hole; + } + else + { + if (ENXIO != errno) + { + error (0, errno, _("failed to seek holes in %s"), + quoteaf (fname), (intmax_t) nsize); + return false; + } + return EXIT_FAILURE; + } + } + if (ENXIO == errno) + { + nsize = last_hole; + } + else + { + error (0, errno, _("unexpected error when seeking data in %s"), + quoteaf (fname), (intmax_t) nsize); + return false; + } + } else nsize = ssize; if (nsize < 0) @@ -228,7 +265,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1) + while ((c = getopt_long (argc, argv, "codr:s:", longopts, NULL)) != -1) { switch (c) { @@ -240,6 +277,10 @@ main (int argc, char **argv) block_mode = true; break; + case 'd': + data_size = true; + break; + case 'r': ref_file = optarg; break; @@ -303,10 +344,10 @@ main (int argc, char **argv) argc -= optind; /* must specify either size or reference file */ - if (!ref_file && !got_size) + if (!ref_file && !got_size && !data_size) { - error (0, 0, _("you must specify either %s or %s"), - quote_n (0, "--size"), quote_n (1, "--reference")); + error (0, 0, _("you must specify either %s, %s or %s"), + quote_n (0, "--size"), quote_n (1, "--reference"), quote_n (1, "--data-size")); usage (EXIT_FAILURE); } /* must specify a relative size with a reference file */ -- 2.19.1