truncate(1): Add hole-punching support

This commit adds hole-punching support to the truncate(1) utility. If
the option -d is specified, truncate(1) performs zeroing, and if
possible hole-punching in case the operation is supported by the
underlying file system of the specified files.

Sponsored by:	The FreeBSD Foundation
Reviewed by:	kib
Differential Revision:	https://reviews.freebsd.org/D31556
This commit is contained in:
Ka Ho Ng 2021-08-19 18:30:41 +08:00
parent 78267c2e70
commit 5ee2c35751
2 changed files with 115 additions and 27 deletions

View File

@ -1,6 +1,10 @@
.\"
.\" Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
.\" All rights reserved.
.\" Copyright (c) 2021 The FreeBSD Foundation
.\"
.\" Portions of this manual page were written by Ka Ho Ng <khng@FreeBSD.org>
.\" under sponsorship from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@ -25,12 +29,12 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 27, 2020
.Dd August 18, 2021
.Dt TRUNCATE 1
.Os
.Sh NAME
.Nm truncate
.Nd truncate or extend the length of files
.Nd truncate, extend the length of files, or perform space management in files
.Sh SYNOPSIS
.Nm
.Op Fl c
@ -39,7 +43,7 @@
.Sm off
.Op Cm + | - | % | /
.Ar size
.Op Cm K | k | M | m | G | g | T | t
.Op Cm SUFFIX
.Sm on
.Xc
.Ek
@ -50,10 +54,32 @@
.Fl r Ar rfile
.Ek
.Ar
.Nm
.Op Fl c
.Bk -words
.Fl d
.Oo
.Fl o Xo
.Sm off
.Ar offset
.Op Cm SUFFIX
.Sm on
.Xc
.Oc
.Fl l Xo
.Sm off
.Ar length
.Op Cm SUFFIX
.Sm on
.Xc
.Ek
.Ar
.Sh DESCRIPTION
The
.Nm
utility adjusts the length of each regular file given on the command-line.
utility adjusts the length of each regular file given on the command-line, or
performs space management with the given offset and the length over a regular
file given on the command-line.
.Pp
The following options are available:
.Bl -tag -width indent
@ -71,7 +97,7 @@ Truncate or extend files to the length of the file
.Sm off
.Op Cm + | - | % | /
.Ar size
.Op Cm K | k | M | m | G | g | T | t
.Op Cm SUFFIX
.Sm on
.Xc
If the
@ -100,10 +126,28 @@ Otherwise, the
.Ar size
argument specifies an absolute length to which all files
should be extended or reduced as appropriate.
.It Fl d
Zero a region in the specified file.
If the underlying file system of the given file supports hole-punching,
file system space deallocation may be performed in the operation region.
.It Fl o Ar offset
The space management operation is performed at the given
.Ar offset
bytes in the file.
If this option is not specified, the operation is performed at the beginning of the file.
.It Fl l Ar length
The length of the operation range in bytes.
This option must always be specified if option
.Fl d
is specified, and must be greater than 0.
.El
.Pp
The
.Ar size
argument may be suffixed with one of
.Ar size ,
.Ar offset
and
.Ar length
arguments may be suffixed with one of
.Cm K ,
.Cm M ,
.Cm G
@ -112,7 +156,6 @@ or
(either upper or lower case) to indicate a multiple of
Kilobytes, Megabytes, Gigabytes or Terabytes
respectively.
.El
.Pp
Exactly one of the
.Fl r
@ -183,6 +226,7 @@ ls -l test_file*
.Sh SEE ALSO
.Xr dd 1 ,
.Xr touch 1 ,
.Xr fspacectl 2 ,
.Xr truncate 2
.Sh STANDARDS
The
@ -198,3 +242,6 @@ The
.Nm
utility was written by
.An Sheldon Hearn Aq Mt sheldonh@starjuice.net .
Hole-punching support of this
utility was developed by
.An Ka Ho Ng Aq Mt khng@FreeBSD.org .

View File

@ -4,6 +4,11 @@
* Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
* All rights reserved.
*
* Copyright (c) 2021 The FreeBSD Foundation
*
* Portions of this software were developed by Ka Ho Ng <khng@FreeBSD.org>
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -49,26 +54,36 @@ main(int argc, char **argv)
{
struct stat sb;
mode_t omode;
off_t oflow, rsize, sz, tsize, round;
off_t oflow, rsize, sz, tsize, round, off, len;
uint64_t usz;
int ch, error, fd, oflags;
int ch, error, fd, oflags, r;
int do_dealloc;
int do_truncate;
int no_create;
int do_relative;
int do_round;
int do_refer;
int got_size;
char *fname, *rname;
struct spacectl_range sr;
fd = -1;
rsize = tsize = sz = 0;
no_create = do_relative = do_round = do_refer = got_size = 0;
error = 0;
rsize = tsize = sz = off = 0;
len = -1;
do_dealloc = no_create = do_relative = do_round = do_refer =
got_size = 0;
do_truncate = 1;
error = r = 0;
rname = NULL;
while ((ch = getopt(argc, argv, "cr:s:")) != -1)
while ((ch = getopt(argc, argv, "cdr:s:o:l:")) != -1)
switch (ch) {
case 'c':
no_create = 1;
break;
case 'd':
do_dealloc = 1;
do_truncate = 0;
break;
case 'r':
do_refer = 1;
rname = optarg;
@ -89,6 +104,22 @@ main(int argc, char **argv)
-(off_t)usz : (off_t)usz;
got_size = 1;
break;
case 'o':
if (expand_number(optarg, &usz) == -1 ||
(off_t)usz < 0)
errx(EXIT_FAILURE,
"invalid offset argument `%s'", optarg);
off = usz;
break;
case 'l':
if (expand_number(optarg, &usz) == -1 ||
(off_t)usz <= 0)
errx(EXIT_FAILURE,
"invalid length argument `%s'", optarg);
len = usz;
break;
default:
usage();
/* NOTREACHED */
@ -98,19 +129,22 @@ main(int argc, char **argv)
argc -= optind;
/*
* Exactly one of do_refer or got_size must be specified. Since
* do_relative implies got_size, do_relative and do_refer are
* also mutually exclusive. See usage() for allowed invocations.
* Exactly one of do_refer, got_size or do_dealloc must be specified.
* Since do_relative implies got_size, do_relative, do_refer and
* do_dealloc are also mutually exclusive. If do_dealloc is specified,
* the length argument must be set. See usage() for allowed
* invocations.
*/
if (do_refer + got_size != 1 || argc < 1)
if (argc < 1 || do_refer + got_size + do_dealloc != 1 ||
(do_dealloc == 1 && len == -1))
usage();
if (do_refer) {
if (do_refer == 1) {
if (stat(rname, &sb) == -1)
err(EXIT_FAILURE, "%s", rname);
tsize = sb.st_size;
} else if (do_relative || do_round)
} else if (do_relative == 1 || do_round == 1)
rsize = sz;
else
else if (do_dealloc == 0)
tsize = sz;
if (no_create)
@ -129,7 +163,7 @@ main(int argc, char **argv)
}
continue;
}
if (do_relative) {
if (do_relative == 1) {
if (fstat(fd, &sb) == -1) {
warn("%s", fname);
error++;
@ -144,7 +178,7 @@ main(int argc, char **argv)
}
tsize = oflow;
}
if (do_round) {
if (do_round == 1) {
if (fstat(fd, &sb) == -1) {
warn("%s", fname);
error++;
@ -166,10 +200,16 @@ main(int argc, char **argv)
if (tsize < 0)
tsize = 0;
if (ftruncate(fd, tsize) == -1) {
if (do_dealloc == 1) {
sr.r_offset = off;
sr.r_len = len;
r = fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, &sr);
}
if (do_truncate == 1)
r = ftruncate(fd, tsize);
if (r == -1) {
warn("%s", fname);
error++;
continue;
}
}
if (fd != -1)
@ -181,8 +221,9 @@ main(int argc, char **argv)
static void
usage(void)
{
fprintf(stderr, "%s\n%s\n",
fprintf(stderr, "%s\n%s\n%s\n",
"usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...",
" truncate [-c] -r rfile file ...");
" truncate [-c] -r rfile file ...",
" truncate [-c] -d [-o offset[K|k|M|m|G|g|T|t]] -l length[K|k|M|m|G|g|T|t] file ...");
exit(EXIT_FAILURE);
}