diff --git a/usr.bin/truncate/truncate.1 b/usr.bin/truncate/truncate.1 --- a/usr.bin/truncate/truncate.1 +++ b/usr.bin/truncate/truncate.1 @@ -1,6 +1,10 @@ .\" .\" Copyright (c) 2000 Sheldon Hearn . .\" All rights reserved. +.\" Copyright (c) 2021 The FreeBSD Foundation +.\" +.\" Portions of this manual page were written by Ka Ho Ng +.\" 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 @@ .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 @@ .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 @@ (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 @@ .Sh SEE ALSO .Xr dd 1 , .Xr touch 1 , +.Xr fspacectl 2 , .Xr truncate 2 .Sh STANDARDS The @@ -198,3 +242,6 @@ .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 . diff --git a/usr.bin/truncate/truncate.c b/usr.bin/truncate/truncate.c --- a/usr.bin/truncate/truncate.c +++ b/usr.bin/truncate/truncate.c @@ -4,6 +4,11 @@ * Copyright (c) 2000 Sheldon Hearn . * All rights reserved. * + * Copyright (c) 2021 The FreeBSD Foundation + * + * Portions of this software were developed by Ka Ho Ng + * 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 @@ { 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 @@ -(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 @@ 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 @@ } continue; } - if (do_relative) { + if (do_relative == 1) { if (fstat(fd, &sb) == -1) { warn("%s", fname); error++; @@ -144,7 +178,7 @@ } tsize = oflow; } - if (do_round) { + if (do_round == 1) { if (fstat(fd, &sb) == -1) { warn("%s", fname); error++; @@ -166,10 +200,16 @@ 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 @@ 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); }