Hi, I have attached a patch for importing truncate(1) from FreeBSD. Please let me know if you come across any issues while testing it.
Regards, zerous
# HG changeset patch # User Naveen Narayanan <[email protected]> # Date 1761077270 -7200 # Tue Oct 21 22:07:50 2025 +0200 # Branch trunk # Node ID 18356b65cc89a38396f66914a1f4fcfa3b2681ba # Parent 03d10f218fce5630dbd63fdd135fec096db9f6ca Import truncate(1) from FreeBSD diff -r 03d10f218fce -r 18356b65cc89 distrib/sets/lists/base/mi --- a/distrib/sets/lists/base/mi Sun Oct 19 18:56:19 2025 +0000 +++ b/distrib/sets/lists/base/mi Tue Oct 21 22:07:50 2025 +0200 @@ -886,6 +886,7 @@ ./usr/bin/tpm_version base-tpm-bin tpm ./usr/bin/tput base-util-bin ./usr/bin/tr base-util-bin +./usr/bin/truncate base-util-bin ./usr/bin/tradcpp base-util-bin ./usr/bin/true base-util-bin ./usr/bin/tset base-util-bin diff -r 03d10f218fce -r 18356b65cc89 distrib/sets/lists/man/mi --- a/distrib/sets/lists/man/mi Sun Oct 19 18:56:19 2025 +0000 +++ b/distrib/sets/lists/man/mi Tue Oct 21 22:07:50 2025 +0200 @@ -4263,6 +4263,7 @@ ./usr/share/man/man1/tpm_version.1 man-tpm-man tpm,.man ./usr/share/man/man1/tput.1 man-util-man .man ./usr/share/man/man1/tr.1 man-util-man .man +./usr/share/man/man1/truncate.1 man-util-man .man ./usr/share/man/man1/tradcpp.1 man-util-man .man ./usr/share/man/man1/true.1 man-util-man .man ./usr/share/man/man1/tset.1 man-util-man .man diff -r 03d10f218fce -r 18356b65cc89 distrib/sets/lists/manhtml/mi --- a/distrib/sets/lists/manhtml/mi Sun Oct 19 18:56:19 2025 +0000 +++ b/distrib/sets/lists/manhtml/mi Tue Oct 21 22:07:50 2025 +0200 @@ -617,6 +617,7 @@ ./usr/share/man/html1/tpm_version.html man-tpm-htmlman tpm,html ./usr/share/man/html1/tput.html man-util-htmlman html ./usr/share/man/html1/tr.html man-util-htmlman html +./usr/share/man/html1/truncate.html man-util-htmlman html ./usr/share/man/html1/tradcpp.html man-util-htmlman html ./usr/share/man/html1/true.html man-util-htmlman html ./usr/share/man/html1/tset.html man-util-htmlman html diff -r 03d10f218fce -r 18356b65cc89 distrib/sets/lists/tests/mi --- a/distrib/sets/lists/tests/mi Sun Oct 19 18:56:19 2025 +0000 +++ b/distrib/sets/lists/tests/mi Tue Oct 21 22:07:50 2025 +0200 @@ -6745,6 +6745,11 @@ ./usr/tests/usr.bin/tr/Atffile tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/tr/Kyuafile tests-usr.bin-tests compattestfile,atf,kyua ./usr/tests/usr.bin/tr/t_basic tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/truncate tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/truncate/Atffile tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/truncate/t_grow tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/truncate/t_misc tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/truncate/t_shrink tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/unifdef tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/unifdef/Atffile tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/unifdef/Kyuafile tests-usr.bin-tests compattestfile,atf,kyua diff -r 03d10f218fce -r 18356b65cc89 etc/mtree/NetBSD.dist.tests --- a/etc/mtree/NetBSD.dist.tests Sun Oct 19 18:56:19 2025 +0000 +++ b/etc/mtree/NetBSD.dist.tests Tue Oct 21 22:07:50 2025 +0200 @@ -479,6 +479,7 @@ ./usr/tests/usr.bin/printf ./usr/tests/usr.bin/pwhash ./usr/tests/usr.bin/realpath +./usr/tests/usr.bin/truncate ./usr/tests/usr.bin/rump_server ./usr/tests/usr.bin/sdiff ./usr/tests/usr.bin/sed diff -r 03d10f218fce -r 18356b65cc89 tests/usr.bin/Makefile --- a/tests/usr.bin/Makefile Sun Oct 19 18:56:19 2025 +0000 +++ b/tests/usr.bin/Makefile Tue Oct 21 22:07:50 2025 +0200 @@ -9,7 +9,7 @@ diff dirname error find fstat ftp gcov gdb grep gzip id indent \ infocmp jot ld locale m4 make mixerctl mkdep mtree nbperf \ netpgpverify patch pkill pr printf pwhash realpath rump_server \ - shmif_dumpbus shmif_pcapin sdiff sed sort stat tar tmux tr \ + shmif_dumpbus shmif_pcapin sdiff sed sort stat tar tmux tr truncate \ unifdef uniq vmstat xlint ztest .if ${MKCXX} != "no" diff -r 03d10f218fce -r 18356b65cc89 tests/usr.bin/truncate/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/usr.bin/truncate/Makefile Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,11 @@ +# $NetBSD$ + +.include <bsd.own.mk> + +TESTSDIR= ${TESTSBASE}/usr.bin/truncate + +TESTS_SH+= t_grow +TESTS_SH+= t_shrink +TESTS_SH+= t_misc + +.include <bsd.test.mk> diff -r 03d10f218fce -r 18356b65cc89 tests/usr.bin/truncate/t_grow.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/usr.bin/truncate/t_grow.sh Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,117 @@ +# $NetBSD$ +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +server="rump_server -lrumpvfs -lrumpdev_disk -lrumpfs_ffs -d key=/dev/dk,hostpath=ffs.img,size=host" + +export RUMP_SERVER=unix://ffs + +exmount='rumpfs on / type rumpfs (local) +/dev/dk on /mnt type ffs (local) +' + +t_init() +{ + atf_check -s exit:0 -o ignore -e ignore newfs -b 32k -f 4k -F -s 10M ffs.img + + atf_check -s exit:0 -o ignore -e ignore ${server} ${RUMP_SERVER} + + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 -o ignore -e ignore mkdir /rump/mnt + export RUMPHIJACK=vfs=all,blanket=/dev/dk:/mnt + atf_check -s exit:0 -o ignore -e ignore mount /dev/dk /mnt + atf_check -s exit:0 -o inline:"${exmount}" mount +} + +atf_test_case t_cleanup cleanup +t_cleanup() +{ + rump.halt +} + +atf_test_case abs_grow +abs_grow_head() +{ + atf_set "descr" "Tests that truncate can grow a file to absolute length" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +abs_grow_body() +{ + t_init + + # truncate should allocate the first and the last block only + atf_check -s exit:0 -o ignore -e ignore truncate -s 10M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((10 * 1024 * 1024)) + + # truncate should allocate 3 blocks in this case since it extends the file + atf_check -s exit:0 -o ignore -e ignore truncate -s 20M /mnt/test_file + atf_check_equal $(du /mnt/test_file | awk '{print $1}') 192 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((20 * 1024 * 1024)) +} + +atf_test_case rela_grow +rela_grow_head() +{ + atf_set "descr" "Check truncate can grow file to relative length" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +rela_grow_body() +{ + t_init + + # truncate should allocate the first and the last block only + atf_check -s exit:0 -o ignore -e ignore truncate -s 10M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((10 * 1024 * 1024)) + + # truncate should allocate 3 blocks in this case since it extends the file + atf_check -s exit:0 -o ignore -e ignore truncate -s +10M /mnt/test_file + atf_check_equal $(du /mnt/test_file | awk '{print $1}') 192 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((20 * 1024 * 1024)) +} + +atf_test_case nocreate +nocreate_head() +{ + atf_set "descr" "Check file is not created with -c" + atf_set "use.fs" "true" +} +nocreate_body() +{ + t_init + + # test_file should not exist + atf_check -s exit:0 -o ignore -e ignore truncate -c -s 10M /mnt/test_file + atf_check -s not-exit:0 -e ignore 'ls /mnt/test_file' +} + +atf_init_test_cases() { + atf_add_test_case abs_grow + atf_add_test_case rela_grow + atf_add_test_case nocreate +} diff -r 03d10f218fce -r 18356b65cc89 tests/usr.bin/truncate/t_misc.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/usr.bin/truncate/t_misc.sh Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,146 @@ +# $NetBSD$ +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +server="rump_server -lrumpvfs -lrumpdev_disk -lrumpfs_ffs -d key=/dev/dk,hostpath=ffs.img,size=host" + +export RUMP_SERVER=unix://ffs + +exmount='rumpfs on / type rumpfs (local) +/dev/dk on /mnt type ffs (local) +' + +t_init() +{ + atf_check -s exit:0 -o ignore -e ignore newfs -b 32k -f 4k -F -s 10M ffs.img + atf_check -s exit:0 -o ignore -e ignore ${server} ${RUMP_SERVER} + + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 -o ignore -e ignore mkdir /rump/mnt + export RUMPHIJACK=vfs=all,blanket=/dev/dk:/mnt + atf_check -s exit:0 -o ignore -e ignore mount /dev/dk /mnt + atf_check -s exit:0 -o inline:"${exmount}" mount +} + +atf_test_case t_cleanup cleanup +t_cleanup() +{ + rump.halt +} + +atf_test_case reference +reference_head() +{ + atf_set "descr" "Tests that truncate can use a reference file" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +reference_body() +{ + t_init + + # create a reference file + atf_check -s exit:0 -o ignore -e ignore truncate -s 10M /mnt/test_file1 + atf_check_equal $(du -s /mnt/test_file1 | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file1 | awk '{print $5}') $((10 * 1024 * 1024)) + + # truncate should extend the file to the length of the reference file + atf_check -s exit:0 -o ignore -e ignore truncate -r /mnt/test_file1 /mnt/test_file2 + atf_check_equal $(du -s /mnt/test_file2 | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file2 | awk '{print $5}') $((10 * 1024 * 1024)) +} + +atf_test_case negative +negative_head() +{ + atf_set "descr" "Tests that truncate treats negative size as zero" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +negative_body() +{ + t_init + + # create/extend a file + atf_check -s exit:0 -o ignore -e ignore truncate -s 10M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((10 * 1024 * 1024)) + + # truncate should shrink the file to zero size + atf_check -s exit:0 -o ignore -e ignore truncate -s -100M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 0 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') 0 +} + +atf_test_case roundup +roundup_head() +{ + atf_set "descr" "Tests that truncate rounds up the size of the file" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +roundup_body() +{ + t_init + + # create/extend a file + atf_check -s exit:0 -o ignore -e ignore truncate -s 6M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((6 * 1024 * 1024)) + + # truncate should round up the size of the file + atf_check -s exit:0 -o ignore -e ignore truncate -s %5M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 192 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((10 * 1024 * 1024)) +} + +atf_test_case rounddown +rounddown_head() +{ + atf_set "descr" "Tests that truncate rounds down the size of the file" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +rounddown_body() +{ + t_init + + # create/extend a file + atf_check -s exit:0 -o ignore -e ignore truncate -s 9M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((9 * 1024 * 1024)) + + # truncate should round down the size of the file + atf_check -s exit:0 -o ignore -e ignore truncate -s /2M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 64 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((8 * 1024 * 1024)) +} + +atf_init_test_cases() { + atf_add_test_case reference + atf_add_test_case rounddown + atf_add_test_case roundup + atf_add_test_case negative +} diff -r 03d10f218fce -r 18356b65cc89 tests/usr.bin/truncate/t_shrink.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/usr.bin/truncate/t_shrink.sh Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,100 @@ +# $NetBSD$ +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +server="rump_server -lrumpvfs -lrumpdev_disk -lrumpfs_ffs -d key=/dev/dk,hostpath=ffs.img,size=host" + +export RUMP_SERVER=unix://ffs + +exmount='rumpfs on / type rumpfs (local) +/dev/dk on /mnt type ffs (local) +' + +t_init() +{ + atf_check -s exit:0 -o ignore -e ignore newfs -b 32k -f 4k -F -s 10M ffs.img + atf_check -s exit:0 -o ignore -e ignore ${server} ${RUMP_SERVER} + + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 -o ignore -e ignore mkdir /rump/mnt + export RUMPHIJACK=vfs=all,blanket=/dev/dk:/mnt + atf_check -s exit:0 -o ignore -e ignore mount /dev/dk /mnt + atf_check -s exit:0 -o inline:"${exmount}" mount +} + +atf_test_case t_cleanup cleanup +t_cleanup() +{ + rump.halt +} + +atf_test_case abs_shrink +abs_shrink_head() +{ + atf_set "descr" "Tests that truncate can shrink a file to absolute length" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +abs_shrink_body() +{ + t_init + + # truncate should allocate the first and the last block only + atf_check -s exit:0 -o ignore -e ignore truncate -s 10M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((10 * 1024 * 1024)) + + # truncate should allocate only one block post shrink + atf_check -s exit:0 -o ignore -e ignore truncate -s 5M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 64 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((5 * 1024 * 1024)) +} + +atf_test_case rela_shrink +rela_shrink_head() +{ + atf_set "descr" "Tests that truncate can shrink a file to relative length" + atf_set "use.fs" "true" + atf_set "require.progs" "ls awk du" +} +rela_shrink_body() +{ + t_init + + # truncate should allocate the first and the last block only + atf_check -s exit:0 -o ignore -e ignore truncate -s 10M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 128 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((10 * 1024 * 1024)) + + # truncate should allocate only one block post shrink + atf_check -s exit:0 -o ignore -e ignore truncate -s -5M /mnt/test_file + atf_check_equal $(du -s /mnt/test_file | awk '{print $1}') 64 + atf_check_equal $(ls -l /mnt/test_file | awk '{print $5}') $((5 * 1024 * 1024)) +} + +atf_init_test_cases() { + atf_add_test_case abs_shrink + atf_add_test_case rela_shrink +} diff -r 03d10f218fce -r 18356b65cc89 usr.bin/Makefile --- a/usr.bin/Makefile Sun Oct 19 18:56:19 2025 +0000 +++ b/usr.bin/Makefile Tue Oct 21 22:07:50 2025 +0200 @@ -29,7 +29,7 @@ spell split stat su sys_info systat \ tabs tail talk tcopy tee telnet tftp tic time timeout tip touch \ tput \ - tr true tset tsort tty ul uname unexpand unifdef \ + tr truncate true tset tsort tty ul uname unexpand unifdef \ uniq units unvis unzip usbhidaction usbhidctl users utoppya \ uudecode uuencode uuidgen vacation vgrind videoctl vis \ vmstat vndcompress w \ diff -r 03d10f218fce -r 18356b65cc89 usr.bin/truncate/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr.bin/truncate/Makefile Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,8 @@ +# $NetBSD$ + +PROG= truncate + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.include <bsd.prog.mk> diff -r 03d10f218fce -r 18356b65cc89 usr.bin/truncate/truncate Binary file usr.bin/truncate/truncate has changed diff -r 03d10f218fce -r 18356b65cc89 usr.bin/truncate/truncate.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr.bin/truncate/truncate.1 Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,249 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2000 Sheldon Hearn <sheldonh%FreeBSD.org@localhost>. +.\" All rights reserved. +.\" Copyright (c) 2021 The FreeBSD Foundation +.\" +.\" Portions of this manual page were written by Ka Ho Ng <khng%FreeBSD.org@localhost> +.\" 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: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 19, 2021 +.Dt TRUNCATE 1 +.Os +.Sh NAME +.Nm truncate +.Nd truncate, extend the length of files, or perform space management in files +.Sh SYNOPSIS +.Nm +.Op Fl c +.Bk -words +.Fl s Xo +.Sm off +.Op Cm + | - | % | / +.Ar size +.Op Cm SUFFIX +.Sm on +.Xc +.Ek +.Ar +.Nm +.Op Fl c +.Bk -words +.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, 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 +.It Fl c +Do not create files if they do not exist. +The +.Nm +utility does not treat this as an error. +No error messages are displayed +and the exit value is not affected. +.It Fl r Ar rfile +Truncate or extend files to the length of the file +.Ar rfile . +.It Fl s Xo +.Sm off +.Op Cm + | - | % | / +.Ar size +.Op Cm SUFFIX +.Sm on +.Xc +If the +.Ar size +argument is preceded by a plus sign +.Pq Cm + , +files will be extended by this number of bytes. +If the +.Ar size +argument is preceded by a dash +.Pq Cm - , +file lengths will be reduced by no more than this number of bytes, +to a minimum length of zero bytes. +If the +.Ar size +argument is preceded by a percent sign +.Pq Cm % , +files will be round up to a multiple of this number of bytes. +If the +.Ar size +argument is preceded by a slash sign +.Pq Cm / , +files will be round down to a multiple of this number of bytes, +to a minimum length of zero bytes. +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 , +.Ar offset +and +.Ar length +arguments may be suffixed with one of +.Cm K , +.Cm M , +.Cm G +or +.Cm T +(either upper or lower case) to indicate a multiple of +Kilobytes, Megabytes, Gigabytes or Terabytes +respectively. +.Pp +Exactly one of the +.Fl r , +.Fl s +and +.Fl d +options must be specified. +.Pp +If a file is made smaller, its extra data is lost. +If a file is made larger, +it will be extended as if by writing bytes with the value zero. +If the file does not exist, +it is created unless the +.Fl c +option is specified. +.Pp +Note that, +while truncating a file causes space on disk to be freed, +extending a file does not cause space to be allocated. +To extend a file and actually allocate the space, +it is necessary to explicitly write data to it, +using (for example) the shell's +.Ql >> +redirection syntax, or +.Xr dd 1 . +.Sh EXIT STATUS +.Ex -std +If the operation fails for an argument, +.Nm +will issue a diagnostic +and continue processing the remaining arguments. +.Sh EXAMPLES +Adjust the size of the file +.Pa test_file +to 10 Megabytes but do not create it if it does not exist: +.Bd -literal -offset indent +truncate -c -s +10M test_file +.Ed +.Pp +Same as above but create the file if it does not exist: +.Bd -literal -offset indent +truncate -s +10M test_file +ls -l test_file +-rw-r--r-- 1 root wheel 10485760 Jul 22 18:48 test_file +.Ed +.Pp +Adjust the size of +.Pa test_file +to the size of the kernel and create another file +.Pa test_file2 +with the same size: +.Bd -literal -offset indent +truncate -r /boot/kernel/kernel test_file test_file2 +ls -l /boot/kernel/kernel test_file* +-r-xr-xr-x 1 root wheel 31352552 May 15 14:18 /boot/kernel/kernel* +-rw-r--r-- 1 root wheel 31352552 Jul 22 19:15 test_file +-rw-r--r-- 1 root wheel 31352552 Jul 22 19:15 test_file2 +.Ed +.Pp +Downsize +.Pa test_file +in 5 Megabytes: +.Bd -literal -offset indent +# truncate -s -5M test_file +ls -l test_file* +-rw-r--r-- 1 root wheel 26109672 Jul 22 19:17 test_file +-rw-r--r-- 1 root wheel 31352552 Jul 22 19:15 test_file2 +.Ed +.Sh SEE ALSO +.Xr dd 1 , +.Xr touch 1 , +.Xr fspacectl 2 , +.Xr truncate 2 +.Sh STANDARDS +The +.Nm +utility conforms to no known standards. +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 4.2 . +.Sh AUTHORS +The +.Nm +utility was written by +.An Sheldon Hearn Aq Mt sheldonh%starjuice.net@localhost . +Hole-punching support of this +utility was developed by +.An Ka Ho Ng Aq Mt khng%FreeBSD.org@localhost . diff -r 03d10f218fce -r 18356b65cc89 usr.bin/truncate/truncate.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr.bin/truncate/truncate.c Tue Oct 21 22:07:50 2025 +0200 @@ -0,0 +1,222 @@ +/* $NetBSD$ */ +/* + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2000 Sheldon Hearn <[email protected]>. + * All rights reserved. + * + * Copyright (c) 2021 The FreeBSD Foundation + * + * Portions of this software were developed by Ka Ho Ng <[email protected]> + * 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <util.h> + +__dead static void usage(void); + +int +main(int argc, char **argv) +{ + struct stat sb; + mode_t omode; + off_t oflow, rsize, sz, tsize, round, off, len; + int64_t usz; + 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; + + fd = -1; + 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, "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; + break; + case 's': + if (*optarg == '+' || *optarg == '-') { + do_relative = 1; + } else if (*optarg == '%' || *optarg == '/') { + do_round = 1; + } + if (dehumanize_number(do_relative || + do_round ? optarg + 1 : optarg, &usz) == -1 || + usz <= 0) + errx(EXIT_FAILURE, + "invalid size argument `%s'", optarg); + + sz = (*optarg == '-' || *optarg == '/') ? + -usz : usz; + got_size = 1; + break; + case 'o': + if (dehumanize_number(optarg, &usz) == -1 || + usz < 0) + errx(EXIT_FAILURE, + "invalid offset argument `%s'", optarg); + + off = usz; + break; + case 'l': + if (dehumanize_number(optarg, &usz) == -1 || + usz <= 0) + errx(EXIT_FAILURE, + "invalid length argument `%s'", optarg); + + len = usz; + break; + default: + usage(); + /* NOTREACHED */ + } + + argv += optind; + argc -= optind; + + /* + * 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 (argc < 1 || do_refer + got_size + do_dealloc != 1 || + (do_dealloc == 1 && len == -1)) + usage(); + if (do_refer == 1) { + if (stat(rname, &sb) == -1) + err(EXIT_FAILURE, "%s", rname); + tsize = sb.st_size; + } else if (do_relative == 1 || do_round == 1) + rsize = sz; + else if (do_dealloc == 0) + tsize = sz; + + if (no_create) + oflags = O_WRONLY; + else + oflags = O_WRONLY | O_CREAT; + omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + while ((fname = *argv++) != NULL) { + if (fd != -1) + close(fd); + if ((fd = open(fname, oflags, omode)) == -1) { + if (errno != ENOENT) { + warn("%s", fname); + error++; + } + continue; + } + if (do_relative == 1) { + if (fstat(fd, &sb) == -1) { + warn("%s", fname); + error++; + continue; + } + oflow = sb.st_size + rsize; + if (oflow < (sb.st_size + rsize)) { + errno = EFBIG; + warn("%s", fname); + error++; + continue; + } + tsize = oflow; + } + if (do_round == 1) { + if (fstat(fd, &sb) == -1) { + warn("%s", fname); + error++; + continue; + } + sz = rsize; + if (sz < 0) + sz = -sz; + if (sb.st_size % sz) { + round = sb.st_size / sz; + if (rsize > 0) + round++; + tsize = round * sz; + } else + tsize = sb.st_size; + } + if (tsize < 0) + tsize = 0; + + if (do_dealloc == 1) { + r = fdiscard(fd, off, len); + } + if (do_truncate == 1) + r = ftruncate(fd, tsize); + if (r == -1) { + warn("%s", fname); + error++; + } + } + if (fd != -1) + close(fd); + + return (error ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void +usage(void) +{ + const char *pnam = getprogname(); + fprintf(stderr, "usage:\t%s %s\n\t%s %s\n\t%s %s\n", + pnam, "[-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...", + pnam, "[-c] -r rfile file ...", + pnam, "[-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); +}
