mirror of
https://git.savannah.gnu.org/git/coreutils.git
synced 2025-09-10 07:59:52 +02:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dc5394183 | ||
|
|
ace9ef2a94 | ||
|
|
403e8e3b89 | ||
|
|
501ce81b6a | ||
|
|
53ee267945 | ||
|
|
ce0f286cce | ||
|
|
1487204385 | ||
|
|
4db3c6b5ea | ||
|
|
b80e2c7c25 | ||
|
|
9288068ff8 | ||
|
|
e6f4c34167 | ||
|
|
b2dfc3b5e5 | ||
|
|
80be785c95 | ||
|
|
324571ea4a | ||
|
|
18f5a8537c | ||
|
|
8b2bedadb5 | ||
|
|
a71537366e | ||
|
|
4e23ca47ac | ||
|
|
aec3e1e107 | ||
|
|
40f71538b6 | ||
|
|
1bc76356d2 | ||
|
|
6227c8e8de | ||
|
|
8d0b79afa5 | ||
|
|
03583f8d64 | ||
|
|
1c3654cb1f | ||
|
|
45b332f7ab | ||
|
|
e08b3e7c08 | ||
|
|
bfdb6b585b | ||
|
|
b56b53bd70 | ||
|
|
594292a1d8 | ||
|
|
0ec711b5c4 | ||
|
|
f69876e558 | ||
|
|
17a7e45927 | ||
|
|
fde07ff436 | ||
|
|
99679fff62 | ||
|
|
437fcf713c | ||
|
|
b3f6b33e4b | ||
|
|
3f31ec950b | ||
|
|
2a3a094e0d | ||
|
|
c20d0e17c3 | ||
|
|
8e5292dfb8 | ||
|
|
7a804b9af4 | ||
|
|
4f591fdd0b | ||
|
|
8e4e1d484f | ||
|
|
0ddfd54b08 | ||
|
|
56e8714e2b | ||
|
|
e0245ae0ae | ||
|
|
3ed0384c41 | ||
|
|
194c1e89a4 | ||
|
|
5bf48c1186 | ||
|
|
5f3115535b | ||
|
|
433a7c614f | ||
|
|
7525860fb9 | ||
|
|
e1788d9ed9 | ||
|
|
caaf2899f6 | ||
|
|
7cfd12c78e | ||
|
|
a132e03507 | ||
|
|
269665866d | ||
|
|
2895f44e89 | ||
|
|
e6067bcb04 | ||
|
|
43066fe2ca | ||
|
|
0f56019db8 | ||
|
|
275c831a3c | ||
|
|
6f95025c9d | ||
|
|
15571e0c8f | ||
|
|
c91b2a3c3b | ||
|
|
ee6c466ba8 | ||
|
|
5c3fd50a75 | ||
|
|
480c0dc9e7 | ||
|
|
beaf631292 | ||
|
|
b9fc790ddc | ||
|
|
2e636af1ef | ||
|
|
9f61806875 | ||
|
|
661413d8c3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -75,6 +75,7 @@
|
||||
/src/version.h
|
||||
/stamp-h1
|
||||
/tests/*/*.log
|
||||
/tests/.built-programs
|
||||
/tests/t?
|
||||
/tests/test-suite.log
|
||||
ID
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.9
|
||||
8.10
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
^build-aux/check\.mk$
|
||||
@@ -1,7 +0,0 @@
|
||||
^gl/lib/randint\.c$
|
||||
^gl/tests/test-di-set\.c$
|
||||
^gl/tests/test-fadvise\.c$
|
||||
^gl/tests/test-ino-map\.c$
|
||||
^gl/tests/test-mbsalign\.c$
|
||||
^gl/tests/test-rand-isaac\.c$
|
||||
^lib/euidaccess-stat\.c$
|
||||
@@ -1 +0,0 @@
|
||||
build-aux/cvsu
|
||||
@@ -1,5 +0,0 @@
|
||||
ChangeLog
|
||||
NEWS
|
||||
src/df.c
|
||||
src/stat.c
|
||||
tests/misc/df-P
|
||||
@@ -1 +0,0 @@
|
||||
ChangeLog
|
||||
@@ -1 +0,0 @@
|
||||
^gl/
|
||||
@@ -1,3 +0,0 @@
|
||||
gl/lib/randint.c
|
||||
lib/euidaccess-stat.c
|
||||
gl/tests/.*\.c
|
||||
@@ -1 +0,0 @@
|
||||
src/seq.c
|
||||
@@ -1,2 +0,0 @@
|
||||
^m4/stat-prog\.m4$
|
||||
ChangeLog
|
||||
@@ -1 +0,0 @@
|
||||
^lib/euidaccess-stat\.c$
|
||||
@@ -1 +0,0 @@
|
||||
^tests/pr/
|
||||
@@ -1,4 +0,0 @@
|
||||
\.mk$
|
||||
/Makefile\.am$
|
||||
^tests/init\.sh$
|
||||
^tests/test-lib\.sh$
|
||||
@@ -1,2 +0,0 @@
|
||||
configure.ac
|
||||
ChangeLog*
|
||||
@@ -1,4 +0,0 @@
|
||||
src/system.h
|
||||
tests/du/2g
|
||||
old/fileutils/ChangeLog-1997
|
||||
ChangeLog-2005
|
||||
@@ -1 +0,0 @@
|
||||
ChangeLog
|
||||
@@ -1,7 +0,0 @@
|
||||
^GNUMakefile$
|
||||
Makefile\.am$
|
||||
\.mk$
|
||||
^tests/pr/
|
||||
ChangeLog.*
|
||||
^man/help2man$
|
||||
^gl/lib/.*\.c\.diff$
|
||||
@@ -1,9 +0,0 @@
|
||||
^lib/buffer-lcm\.c$
|
||||
^src/false\.c$
|
||||
^src/lbracket\.c$
|
||||
^src/ls-dir\.c$
|
||||
^src/ls-ls\.c$
|
||||
^src/ls-vdir\.c$
|
||||
^src/tac-pipe\.c$
|
||||
^src/uname-arch\.c$
|
||||
^src/uname-uname\.c$
|
||||
@@ -1,9 +0,0 @@
|
||||
^lib/buffer-lcm\.c$
|
||||
^src/false\.c$
|
||||
^src/lbracket\.c$
|
||||
^src/ls-dir\.c$
|
||||
^src/ls-ls\.c$
|
||||
^src/ls-vdir\.c$
|
||||
^src/tac-pipe\.c$
|
||||
^src/uname-arch\.c$
|
||||
^src/uname-uname\.c$
|
||||
@@ -1,12 +0,0 @@
|
||||
config(ure|\.(guess|sub))
|
||||
tests/pr
|
||||
lib/regex.c
|
||||
config-log
|
||||
tests/misc/nl
|
||||
po/de.po
|
||||
m4/lib-ld.m4
|
||||
m4/lib-prefix.m4
|
||||
m4/po.m4
|
||||
aclocal.m4
|
||||
src/c99-to-c89.diff
|
||||
^gl/lib/.*\.c\.diff$
|
||||
@@ -1,2 +0,0 @@
|
||||
config-log
|
||||
build-aux/config.guess
|
||||
@@ -1,3 +0,0 @@
|
||||
^src/libstdbuf\.c$
|
||||
^src/system\.h$
|
||||
^src/copy\.h$
|
||||
@@ -1 +0,0 @@
|
||||
^tests/pr/
|
||||
@@ -1,2 +0,0 @@
|
||||
^lib/xstrtol\.h$
|
||||
^build-aux/cvsu$
|
||||
@@ -1,13 +0,0 @@
|
||||
^build-aux/config.guess
|
||||
^configure
|
||||
^lib/alloca.c
|
||||
^lib/fts.c
|
||||
^lib/getdate.c
|
||||
^lib/getloadavg.c
|
||||
^lib/gettext.h
|
||||
^lib/getusershell.c
|
||||
^lib/mbswidth.c
|
||||
^lib/strtod.c
|
||||
^lib/xstrtol.c
|
||||
^m4/
|
||||
^tests/misc/pwd-unreadable-parent
|
||||
@@ -9086,7 +9086,7 @@
|
||||
|
||||
split's --verbose option did nothing [broken in 4.5.10 and 5.0]
|
||||
* src/split.c (longopts): Use `1', not `0' as the value for
|
||||
for &verbose. Reported by Keith Thompson.
|
||||
&verbose. Reported by Keith Thompson.
|
||||
|
||||
Test for the above fix.
|
||||
* tests/misc/split-a: Also use --verbose and compare stderr
|
||||
|
||||
27
Makefile.am
27
Makefile.am
@@ -39,35 +39,8 @@ changelog_etc = \
|
||||
old/textutils/NEWS \
|
||||
po/ChangeLog-2007
|
||||
|
||||
syntax_check_exceptions = \
|
||||
.x-sc_GPL_version \
|
||||
.x-sc_bindtextdomain \
|
||||
.x-sc_error_message_uppercase \
|
||||
.x-sc_file_system \
|
||||
.x-sc_obsolete_symbols \
|
||||
.x-sc_po_check \
|
||||
.x-sc_program_name \
|
||||
.x-sc_prohibit_always-defined_macros \
|
||||
.x-sc_prohibit_atoi_atof \
|
||||
.x-sc_prohibit_empty_lines_at_EOF \
|
||||
.x-sc_prohibit_fail_0 \
|
||||
.x-sc_prohibit_magic_number_exit \
|
||||
.x-sc_prohibit_stat_st_blocks \
|
||||
.x-sc_prohibit_strcmp \
|
||||
.x-sc_prohibit_tab_based_indentation \
|
||||
.x-sc_require_config_h \
|
||||
.x-sc_require_config_h_first \
|
||||
.x-sc_space_tab \
|
||||
.x-sc_sun_os_names \
|
||||
.x-sc_system_h_headers \
|
||||
.x-sc_trailing_blank \
|
||||
.x-sc_prohibit_always_true_header_tests \
|
||||
.x-sc_unmarked_diagnostics \
|
||||
.x-sc_useless_cpp_parens
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(changelog_etc) \
|
||||
$(syntax_check_exceptions) \
|
||||
.mailmap \
|
||||
.prev-version \
|
||||
.version \
|
||||
|
||||
60
NEWS
60
NEWS
@@ -1,5 +1,61 @@
|
||||
GNU coreutils NEWS -*- outline -*-
|
||||
|
||||
* Noteworthy changes in release 8.11 (2011-04-13) [stable]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
cp -a --link would not create a hardlink to a symlink, instead
|
||||
copying the symlink and then not preserving its timestamp.
|
||||
[bug introduced in coreutils-8.0]
|
||||
|
||||
cp now avoids FIEMAP issues with BTRFS before Linux 2.6.38,
|
||||
which could result in corrupt copies of sparse files.
|
||||
[bug introduced in coreutils-8.10]
|
||||
|
||||
cut could segfault when invoked with a user-specified output
|
||||
delimiter and an unbounded range like "-f1234567890-".
|
||||
[bug introduced in coreutils-5.3.0]
|
||||
|
||||
du would infloop when given --files0-from=DIR
|
||||
[bug introduced in coreutils-7.1]
|
||||
|
||||
sort no longer spawns 7 worker threads to sort 16 lines
|
||||
[bug introduced in coreutils-8.6]
|
||||
|
||||
touch built on Solaris 9 would segfault when run on Solaris 10
|
||||
[bug introduced in coreutils-8.8]
|
||||
|
||||
wc would dereference a NULL pointer upon an early out-of-memory error
|
||||
[bug introduced in coreutils-7.1]
|
||||
|
||||
** New features
|
||||
|
||||
dd now accepts the 'nocache' flag to the iflag and oflag options,
|
||||
which will discard any cache associated with the files, or
|
||||
processed portion thereof.
|
||||
|
||||
dd now warns that 'iflag=fullblock' should be used,
|
||||
in various cases where partial reads can cause issues.
|
||||
|
||||
** Changes in behavior
|
||||
|
||||
cp now avoids syncing files when possible, when doing a FIEMAP copy.
|
||||
The sync is only needed on Linux kernels before 2.6.39.
|
||||
[The sync was introduced in coreutils-8.10]
|
||||
|
||||
cp now copies empty extents efficiently, when doing a FIEMAP copy.
|
||||
It no longer reads the zero bytes from the input, and also can efficiently
|
||||
create a hole in the output file when --sparse=always is specified.
|
||||
|
||||
df now aligns columns consistently, and no longer wraps entries
|
||||
with longer device identifiers, over two lines.
|
||||
|
||||
install now rejects its long-deprecated --preserve_context option.
|
||||
Use --preserve-context instead.
|
||||
|
||||
test now accepts "==" as a synonym for "="
|
||||
|
||||
|
||||
* Noteworthy changes in release 8.10 (2011-02-04) [stable]
|
||||
|
||||
** Bug fixes
|
||||
@@ -367,7 +423,7 @@ GNU coreutils NEWS -*- outline -*-
|
||||
if it uses helper processes for compression and its parent
|
||||
ignores CHLD signals. [bug introduced in coreutils-6.9]
|
||||
|
||||
tail without -f no longer access uninitialized memory
|
||||
tail without -f no longer accesses uninitialized memory
|
||||
[bug introduced in coreutils-7.6]
|
||||
|
||||
timeout is now immune to the signal handling of its parent.
|
||||
@@ -496,7 +552,7 @@ GNU coreutils NEWS -*- outline -*-
|
||||
|
||||
ls -LR exits with status 2, not 0, when it encounters a cycle
|
||||
|
||||
ls -is is now consistent with ls -lis in ignoring values returned
|
||||
"ls -is" is now consistent with ls -lis in ignoring values returned
|
||||
from a failed stat/lstat. For example ls -Lis now prints "?", not "0",
|
||||
for the inode number and allocated size of a dereferenced dangling symlink.
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ Barry Kelly http://barrkel.blogspot.com/
|
||||
Bauke Jan Douma bjdouma@xs4all.nl
|
||||
Ben Elliston bje@air.net.au
|
||||
Ben Harris bjh21@netbsd.org
|
||||
Ben Walton bwalton@artsci.utoronto.ca
|
||||
Bengt Martensson bengt@mathematik.uni-Bremen.de
|
||||
Benjamin Cutler cutlerbc@simla.colostate.edu
|
||||
Bernard Giroud bernard.giroud@creditlyonnais.ch
|
||||
@@ -206,7 +207,6 @@ Geoff Whale geoffw@cse.unsw.EDU.AU
|
||||
Gerald Pfeifer gerald@pfeifer.com
|
||||
Gerhard Poul gpoul@gnu.org
|
||||
Germano Leichsenring germano@jedi.cs.kobe-u.ac.jp
|
||||
Gilles Espinasse g.esp@free.fr
|
||||
Glen Lenker glen.lenker@gmail.com
|
||||
Göran Uddeborg goeran@uddeborg.se
|
||||
Guochun Shi gshi@ncsa.uiuc.edu
|
||||
@@ -524,6 +524,7 @@ Soeren Sonnenburg sonnenburg@informatik.hu-berlin.de
|
||||
Solar Designer solar@owl.openwall.com
|
||||
Stanislav Ievlev inger@altlinux.ru
|
||||
Stavros Passas stabat@ics.forth.gr
|
||||
Stefan Vargyas stvar@yahoo.com
|
||||
Stéphane Chazelas Stephane_CHAZELAS@yahoo.fr
|
||||
Stephen Depooter sbdep@myrealbox.com
|
||||
Stephen Eglen eglen@pcg.wustl.edu
|
||||
|
||||
90
bootstrap
90
bootstrap
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-11-20.03; # UTC
|
||||
scriptversion=2011-03-03.12; # UTC
|
||||
|
||||
# Bootstrap this package from checked-out sources.
|
||||
|
||||
@@ -42,30 +42,32 @@ local_gl_dir=gl
|
||||
bt='._bootmp'
|
||||
bt_regex=`echo "$bt"| sed 's/\./[.]/g'`
|
||||
bt2=${bt}2
|
||||
me=$0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION]...
|
||||
Usage: $me [OPTION]...
|
||||
Bootstrap this package from the checked-out sources.
|
||||
|
||||
Options:
|
||||
--gnulib-srcdir=DIRNAME Specify the local directory where gnulib
|
||||
--gnulib-srcdir=DIRNAME specify the local directory where gnulib
|
||||
sources reside. Use this if you already
|
||||
have gnulib sources on your machine, and
|
||||
do not want to waste your bandwidth downloading
|
||||
them again. Defaults to \$GNULIB_SRCDIR.
|
||||
--bootstrap-sync If this bootstrap script is not identical to
|
||||
them again. Defaults to \$GNULIB_SRCDIR
|
||||
--bootstrap-sync if this bootstrap script is not identical to
|
||||
the version in the local gnulib sources,
|
||||
update this script, and then restart it with
|
||||
/bin/sh or the shell \$CONFIG_SHELL if
|
||||
defined.
|
||||
--no-bootstrap-sync Do not check whether bootstrap is out of sync.
|
||||
--copy Copy files instead of creating symbolic links.
|
||||
--force Attempt to bootstrap even if the sources seem
|
||||
not to have been checked out.
|
||||
--skip-po Do not download po files.
|
||||
/bin/sh or the shell \$CONFIG_SHELL
|
||||
--no-bootstrap-sync do not check whether bootstrap is out of sync
|
||||
--copy copy files instead of creating symbolic links
|
||||
--force attempt to bootstrap even if the sources seem
|
||||
not to have been checked out
|
||||
--no-git do not use git to update gnulib. Requires that
|
||||
--gnulib-srcdir point to a correct gnulib snapshot
|
||||
--skip-po do not download po files
|
||||
|
||||
If the file $0.conf exists in the same directory as this script, its
|
||||
If the file $me.conf exists in the same directory as this script, its
|
||||
contents are read as shell variables to configure the bootstrap.
|
||||
|
||||
For build prerequisites, environment variables like \$AUTOCONF and \$AMTAR
|
||||
@@ -178,6 +180,9 @@ vc_ignore=auto
|
||||
# default.
|
||||
bootstrap_sync=false
|
||||
|
||||
# Use git to update gnulib sources
|
||||
use_git=true
|
||||
|
||||
# find_tool ENVVAR NAMES...
|
||||
# -------------------------
|
||||
# Search for a required program. Use the value of ENVVAR, if set,
|
||||
@@ -202,11 +207,11 @@ find_tool ()
|
||||
find_tool_error_prefix="\$$find_tool_envvar: "
|
||||
fi
|
||||
if test x"$find_tool_res" = x; then
|
||||
echo >&2 "$0: one of these is required: $find_tool_names"
|
||||
echo >&2 "$me: one of these is required: $find_tool_names"
|
||||
exit 1
|
||||
fi
|
||||
($find_tool_res --version </dev/null) >/dev/null 2>&1 || {
|
||||
echo >&2 "$0: ${find_tool_error_prefix}cannot run $find_tool_res --version"
|
||||
echo >&2 "$me: ${find_tool_error_prefix}cannot run $find_tool_res --version"
|
||||
exit 1
|
||||
}
|
||||
eval "$find_tool_envvar=\$find_tool_res"
|
||||
@@ -253,12 +258,21 @@ do
|
||||
bootstrap_sync=true;;
|
||||
--no-bootstrap-sync)
|
||||
bootstrap_sync=false;;
|
||||
--no-git)
|
||||
use_git=false;;
|
||||
*)
|
||||
echo >&2 "$0: $option: unknown option"
|
||||
exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if $use_git || test -d "$GNULIB_SRCDIR"; then
|
||||
:
|
||||
else
|
||||
echo "$0: Error: --no-git requires --gnulib-srcdir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
|
||||
echo "$0: Bootstrapping from a non-checked-out distribution is risky." >&2
|
||||
exit 1
|
||||
@@ -384,18 +398,22 @@ check_versions() {
|
||||
if test "$app" = libtool; then
|
||||
app=libtoolize
|
||||
fi
|
||||
# Exempt git if --no-git is in effect.
|
||||
if test "$app" = git; then
|
||||
$use_git || continue
|
||||
fi
|
||||
# Honor $APP variables ($TAR, $AUTOCONF, etc.)
|
||||
appvar=`echo $app | tr '[a-z]-' '[A-Z]_'`
|
||||
test "$appvar" = TAR && appvar=AMTAR
|
||||
eval "app=\${$appvar-$app}"
|
||||
inst_ver=$(get_version $app)
|
||||
if [ ! "$inst_ver" ]; then
|
||||
echo "Error: '$app' not found" >&2
|
||||
echo "$me: Error: '$app' not found" >&2
|
||||
ret=1
|
||||
elif [ ! "$req_ver" = "-" ]; then
|
||||
latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2)
|
||||
if [ ! "$latest_ver" = "$inst_ver" ]; then
|
||||
echo "Error: '$app' version == $inst_ver is too old" >&2
|
||||
echo "$me: Error: '$app' version == $inst_ver is too old" >&2
|
||||
echo " '$app' version >= $req_ver is required" >&2
|
||||
ret=1
|
||||
fi
|
||||
@@ -428,9 +446,9 @@ fi
|
||||
if ! printf "$buildreq" | check_versions; then
|
||||
echo >&2
|
||||
if test -f README-prereq; then
|
||||
echo "See README-prereq for how to get the prerequisite programs" >&2
|
||||
echo "$0: See README-prereq for how to get the prerequisite programs" >&2
|
||||
else
|
||||
echo "Please install the prerequisite programs" >&2
|
||||
echo "$0: Please install the prerequisite programs" >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
@@ -442,11 +460,11 @@ if test -d .git && (git --version) >/dev/null 2>/dev/null ; then
|
||||
if git config merge.merge-changelog.driver >/dev/null ; then
|
||||
:
|
||||
elif (git-merge-changelog --version) >/dev/null 2>/dev/null ; then
|
||||
echo "initializing git-merge-changelog driver"
|
||||
echo "$0: initializing git-merge-changelog driver"
|
||||
git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
|
||||
git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
|
||||
else
|
||||
echo "consider installing git-merge-changelog from gnulib"
|
||||
echo "$0: consider installing git-merge-changelog from gnulib"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -462,7 +480,7 @@ git_modules_config () {
|
||||
}
|
||||
|
||||
gnulib_path=`git_modules_config submodule.gnulib.path`
|
||||
: ${gnulib_path=gnulib}
|
||||
test -z "$gnulib_path" && gnulib_path=gnulib
|
||||
|
||||
# Get gnulib files.
|
||||
|
||||
@@ -533,7 +551,7 @@ gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
|
||||
download_po_files() {
|
||||
subdir=$1
|
||||
domain=$2
|
||||
echo "$0: getting translations into $subdir for $domain..."
|
||||
echo "$me: getting translations into $subdir for $domain..."
|
||||
cmd=`printf "$po_download_command_format" "$domain" "$subdir"`
|
||||
eval "$cmd"
|
||||
}
|
||||
@@ -567,7 +585,7 @@ update_po_files() {
|
||||
! test -f "$po_dir/$po.po" ||
|
||||
! $SHA1SUM -c --status "$cksum_file" \
|
||||
< "$new_po" > /dev/null; then
|
||||
echo "updated $po_dir/$po.po..."
|
||||
echo "$me: updated $po_dir/$po.po..."
|
||||
cp "$new_po" "$po_dir/$po.po" \
|
||||
&& $SHA1SUM < "$new_po" > "$cksum_file"
|
||||
fi
|
||||
@@ -612,13 +630,13 @@ symlink_to_dir()
|
||||
if $copy; then
|
||||
{
|
||||
test ! -h "$dst" || {
|
||||
echo "$0: rm -f $dst" &&
|
||||
echo "$me: rm -f $dst" &&
|
||||
rm -f "$dst"
|
||||
}
|
||||
} &&
|
||||
test -f "$dst" &&
|
||||
cmp -s "$src" "$dst" || {
|
||||
echo "$0: cp -fp $src $dst" &&
|
||||
echo "$me: cp -fp $src $dst" &&
|
||||
cp -fp "$src" "$dst"
|
||||
}
|
||||
else
|
||||
@@ -632,7 +650,7 @@ symlink_to_dir()
|
||||
*)
|
||||
case /$dst/ in
|
||||
*//* | */../* | */./* | /*/*/*/*/*/)
|
||||
echo >&2 "$0: invalid symlink calculation: $src -> $dst"
|
||||
echo >&2 "$me: invalid symlink calculation: $src -> $dst"
|
||||
exit 1;;
|
||||
/*/*/*/*/) dot_dots=../../../;;
|
||||
/*/*/*/) dot_dots=../../;;
|
||||
@@ -640,7 +658,7 @@ symlink_to_dir()
|
||||
esac;;
|
||||
esac
|
||||
|
||||
echo "$0: ln -fs $dot_dots$src $dst" &&
|
||||
echo "$me: ln -fs $dot_dots$src $dst" &&
|
||||
ln -fs "$dot_dots$src" "$dst"
|
||||
}
|
||||
fi
|
||||
@@ -673,7 +691,7 @@ cp_mark_as_generated()
|
||||
cmp -s "$cp_src" "$cp_dst" || {
|
||||
# Copy the file first to get proper permissions if it
|
||||
# doesn't already exist. Then overwrite the copy.
|
||||
echo "$0: cp -f $cp_src $cp_dst" &&
|
||||
echo "$me: cp -f $cp_src $cp_dst" &&
|
||||
rm -f "$cp_dst" &&
|
||||
cp "$cp_src" "$cp_dst-t" &&
|
||||
sed "s!$bt_regex/!!g" "$cp_src" > "$cp_dst-t" &&
|
||||
@@ -691,7 +709,7 @@ cp_mark_as_generated()
|
||||
if cmp -s "$cp_dst-t" "$cp_dst"; then
|
||||
rm -f "$cp_dst-t"
|
||||
else
|
||||
echo "$0: cp $cp_src $cp_dst # with edits" &&
|
||||
echo "$me: cp $cp_src $cp_dst # with edits" &&
|
||||
mv -f "$cp_dst-t" "$cp_dst"
|
||||
fi
|
||||
fi
|
||||
@@ -710,7 +728,7 @@ version_controlled_file() {
|
||||
elif test -d .svn; then
|
||||
svn log -r HEAD "$dir/$file" > /dev/null 2>&1 && found=yes
|
||||
else
|
||||
echo "$0: no version control for $dir/$file?" >&2
|
||||
echo "$me: no version control for $dir/$file?" >&2
|
||||
fi
|
||||
test $found = yes
|
||||
}
|
||||
@@ -734,18 +752,18 @@ slurp() {
|
||||
remove_intl='/^[^#].*\/intl/s/^/#/;'"s!$bt_regex/!!g"
|
||||
sed "$remove_intl" $1/$dir/$file |
|
||||
cmp - $dir/$gnulib_mk > /dev/null || {
|
||||
echo "$0: Copying $1/$dir/$file to $dir/$gnulib_mk ..." &&
|
||||
echo "$me: Copying $1/$dir/$file to $dir/$gnulib_mk ..." &&
|
||||
rm -f $dir/$gnulib_mk &&
|
||||
sed "$remove_intl" $1/$dir/$file >$dir/$gnulib_mk &&
|
||||
gnulib_mk_hook $dir/$gnulib_mk
|
||||
}
|
||||
elif { test "${2+set}" = set && test -r $2/$dir/$file; } ||
|
||||
version_controlled_file $dir $file; then
|
||||
echo "$0: $dir/$file overrides $1/$dir/$file"
|
||||
echo "$me: $dir/$file overrides $1/$dir/$file"
|
||||
else
|
||||
copied=$copied$sep$file; sep=$nl
|
||||
if test $file = gettext.m4; then
|
||||
echo "$0: patching m4/gettext.m4 to remove need for intl/* ..."
|
||||
echo "$me: patching m4/gettext.m4 to remove need for intl/* ..."
|
||||
rm -f $dir/$file
|
||||
sed '
|
||||
/^AC_DEFUN(\[AM_INTL_SUBDIR],/,/^]/c\
|
||||
@@ -856,7 +874,7 @@ grep -E '^[ ]*AC_CONFIG_HEADERS?\>' configure.ac >/dev/null ||
|
||||
|
||||
for command in \
|
||||
libtool \
|
||||
"${ACLOCAL-aclocal} --force -I m4 $ACLOCAL_FLAGS" \
|
||||
"${ACLOCAL-aclocal} --force -I '$m4_base' $ACLOCAL_FLAGS" \
|
||||
"${AUTOCONF-autoconf} --force" \
|
||||
"${AUTOHEADER-autoheader} --force" \
|
||||
"${AUTOMAKE-automake} --add-missing --copy --force-missing"
|
||||
@@ -867,7 +885,7 @@ do
|
||||
command="${LIBTOOLIZE-libtoolize} -c -f"
|
||||
fi
|
||||
echo "$0: $command ..."
|
||||
$command || exit
|
||||
eval "$command" || exit
|
||||
done
|
||||
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ gnulib_modules="
|
||||
verror
|
||||
version-etc-fsf
|
||||
wcwidth
|
||||
wcswidth
|
||||
winsz-ioctl
|
||||
winsz-termios
|
||||
write-any-file
|
||||
@@ -299,7 +300,6 @@ if test $gettext_external = 1; then
|
||||
excluded_files='
|
||||
m4/glibc2.m4
|
||||
m4/intdiv0.m4
|
||||
m4/lcmessage.m4
|
||||
m4/uintmax_t.m4
|
||||
m4/ulonglong.m4
|
||||
m4/visibility.m4
|
||||
|
||||
63
cfg.mk
63
cfg.mk
@@ -27,7 +27,7 @@ bootstrap-tools = autoconf,automake,gnulib,bison
|
||||
# Now that we have better tests, make this the default.
|
||||
export VERBOSE = yes
|
||||
|
||||
old_NEWS_hash = f2ebf9f1f16209f7a4b9927a755956fa
|
||||
old_NEWS_hash = 99b06e7bb289add96b10127fceced4e9
|
||||
|
||||
# Add an exemption for sc_makefile_at_at_check.
|
||||
_makefile_at_at_check_exceptions = ' && !/^cu_install_program =/'
|
||||
@@ -39,8 +39,8 @@ _hv_file ?= $(srcdir)/tests/misc/help-version
|
||||
dd = $(srcdir)/src/dd.c
|
||||
sc_dd_O_FLAGS:
|
||||
@rm -f $@.1 $@.2
|
||||
@{ echo O_FULLBLOCK; perl -nle '/^ +\| (O_\w*)$$/ and print $$1' \
|
||||
$(dd); } | sort > $@.1
|
||||
@{ echo O_FULLBLOCK; echo O_NOCACHE; \
|
||||
perl -nle '/^ +\| (O_\w*)$$/ and print $$1' $(dd); } | sort > $@.1
|
||||
@{ echo O_NOFOLLOW; perl -nle '/{"[a-z]+",\s*(O_\w+)},/ and print $$1' \
|
||||
$(dd); } | sort > $@.2
|
||||
@diff -u $@.1 $@.2 || diff=1 || diff=; \
|
||||
@@ -93,19 +93,6 @@ sc_root_tests:
|
||||
exit 1; } || :; \
|
||||
fi
|
||||
|
||||
# Ensure that the syntax_check_exceptions file list in Makefile.am
|
||||
# stays in sync with corresponding files in the repository.
|
||||
sce = syntax_check_exceptions
|
||||
sc_x_sc_dist_check:
|
||||
@test "$$( ($(VC_LIST) | sed -n '/\.x-sc_/p' \
|
||||
| sed 's|^$(_dot_escaped_srcdir)/||'; \
|
||||
sed -n '/^$(sce) =[ ]*\\$$/,/[^\]$$/p' \
|
||||
$(srcdir)/Makefile.am \
|
||||
| sed 's/^ *//;/^$(sce) =/d' \
|
||||
| tr -s '\012\\' ' ' | fmt -1 \
|
||||
) | sort | uniq -u)" \
|
||||
&& { echo 'Makefile.am: $(sce) mismatch' >&2; exit 1; } || :;
|
||||
|
||||
# Create a list of regular expressions matching the names
|
||||
# of files included from system.h. Exclude a couple.
|
||||
.re-list:
|
||||
@@ -186,7 +173,7 @@ sc_NEWS_two_empty_lines:
|
||||
@sed -n 4,/Noteworthy/p $(srcdir)/NEWS \
|
||||
| perl -n0e '/(^|\n)\n\n\* Noteworthy/ or exit 1' \
|
||||
|| { echo '$(ME): use two empty lines to separate NEWS sections' \
|
||||
1>&2; exit 1; } || :; \
|
||||
1>&2; exit 1; } || :
|
||||
|
||||
# Perl-based tests used to exec perl from a #!/bin/sh script.
|
||||
# Now they all start with #!/usr/bin/perl and the portability
|
||||
@@ -328,6 +315,15 @@ sc_space_before_open_paren:
|
||||
else :; \
|
||||
fi
|
||||
|
||||
# Similar to the gnulib maint.mk rule for sc_prohibit_strcmp
|
||||
# Use STREQ_LEN or STRPREFIX rather than comparing strncmp == 0, or != 0.
|
||||
sc_prohibit_strncmp:
|
||||
@grep -nE '! *str''ncmp *\(|\<str''ncmp *\(.+\) *[!=]=' \
|
||||
$$($(VC_LIST_EXCEPT)) \
|
||||
| grep -vE ':# *define STR(N?EQ_LEN|PREFIX)\(' && \
|
||||
{ echo '$(ME): use STREQ_LEN or STRPREFIX instead of str''ncmp' \
|
||||
1>&2; exit 1; } || :
|
||||
|
||||
# Override the default Cc: used in generating an announcement.
|
||||
announcement_Cc_ = $(translation_project_), \
|
||||
coreutils@gnu.org, coreutils-announce@gnu.org
|
||||
@@ -337,3 +333,36 @@ include $(srcdir)/dist-check.mk
|
||||
update-copyright-env = \
|
||||
UPDATE_COPYRIGHT_USE_INTERVALS=1 \
|
||||
UPDATE_COPYRIGHT_MAX_LINE_LENGTH=79
|
||||
|
||||
# List syntax-check exemptions.
|
||||
exclude_file_name_regexp--sc_space_tab = \
|
||||
^(tests/pr/|tests/misc/nl$$|gl/.*\.diff$$)
|
||||
exclude_file_name_regexp--sc_bindtextdomain = ^(gl/.*|lib/euidaccess-stat)\.c$$
|
||||
exclude_file_name_regexp--sc_unmarked_diagnostics = ^build-aux/cvsu$$
|
||||
exclude_file_name_regexp--sc_error_message_uppercase = ^build-aux/cvsu$$
|
||||
exclude_file_name_regexp--sc_trailing_blank = ^tests/pr/
|
||||
exclude_file_name_regexp--sc_system_h_headers = \
|
||||
^src/((system|copy)\.h|libstdbuf\.c)$$
|
||||
|
||||
_src = (false|lbracket|ls-(dir|ls|vdir)|tac-pipe|uname-(arch|uname))
|
||||
exclude_file_name_regexp--sc_require_config_h_first = \
|
||||
(^lib/buffer-lcm\.c|src/$(_src)\.c)$$
|
||||
exclude_file_name_regexp--sc_require_config_h = \
|
||||
$(exclude_file_name_regexp--sc_require_config_h_first)
|
||||
|
||||
exclude_file_name_regexp--sc_po_check = ^gl/
|
||||
exclude_file_name_regexp--sc_prohibit_always-defined_macros = ^src/seq\.c$$
|
||||
exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = ^tests/pr/
|
||||
exclude_file_name_regexp--sc_program_name = ^(gl/.*|lib/euidaccess-stat)\.c$$
|
||||
exclude_file_name_regexp--sc_file_system = \
|
||||
NEWS|^(tests/init\.cfg|src/df\.c|tests/misc/df-P)$$
|
||||
exclude_file_name_regexp--sc_prohibit_always_true_header_tests = \
|
||||
^m4/stat-prog\.m4$$
|
||||
exclude_file_name_regexp--sc_prohibit_fail_0 = \
|
||||
(^tests/init\.sh|Makefile\.am|\.mk)$$
|
||||
exclude_file_name_regexp--sc_prohibit_atoi_atof = ^lib/euidaccess-stat\.c$$
|
||||
exclude_file_name_regexp--sc_prohibit_tab_based_indentation = \
|
||||
^tests/pr/|(^gl/lib/reg.*\.c\.diff|Makefile(\.am)?|\.mk|^man/help2man)$$
|
||||
|
||||
exclude_file_name_regexp--sc_prohibit_stat_st_blocks = \
|
||||
^(src/system\.h|tests/du/2g)$$
|
||||
|
||||
10
configure.ac
10
configure.ac
@@ -162,18 +162,18 @@ int main()
|
||||
time_t now = time ((time_t *) 0);
|
||||
int hour_GMT0, hour_unset;
|
||||
if (putenv ("TZ=GMT0") != 0)
|
||||
exit (1);
|
||||
return 1;
|
||||
hour_GMT0 = localtime (&now)->tm_hour;
|
||||
unset_TZ ();
|
||||
hour_unset = localtime (&now)->tm_hour;
|
||||
if (putenv ("TZ=PST8") != 0)
|
||||
exit (1);
|
||||
return 1;
|
||||
if (localtime (&now)->tm_hour == hour_GMT0)
|
||||
exit (1);
|
||||
return 1;
|
||||
unset_TZ ();
|
||||
if (localtime (&now)->tm_hour != hour_unset)
|
||||
exit (1);
|
||||
exit (0);
|
||||
return 1;
|
||||
return 0;
|
||||
}]])],
|
||||
[utils_cv_localtime_cache=no],
|
||||
[utils_cv_localtime_cache=yes],
|
||||
|
||||
@@ -1455,10 +1455,11 @@ The @sc{gnu} utilities normally conform to the version of @acronym{POSIX}
|
||||
that is standard for your system. To cause them to conform to a
|
||||
different version of @acronym{POSIX}, define the @env{_POSIX2_VERSION}
|
||||
environment variable to a value of the form @var{yyyymm} specifying
|
||||
the year and month the standard was adopted. Two values are currently
|
||||
the year and month the standard was adopted. Three values are currently
|
||||
supported for @env{_POSIX2_VERSION}: @samp{199209} stands for
|
||||
@acronym{POSIX} 1003.2-1992, and @samp{200112} stands for @acronym{POSIX}
|
||||
1003.1-2001. For example, if you have a newer system but are running software
|
||||
@acronym{POSIX} 1003.2-1992, @samp{200112} stands for @acronym{POSIX}
|
||||
1003.1-2001, and @samp{200809} stands for @acronym{POSIX} 1003.1-2008.
|
||||
For example, if you have a newer system but are running software
|
||||
that assumes an older version of @acronym{POSIX} and uses @samp{sort +1}
|
||||
or @samp{tail +10}, you can work around any compatibility problems by setting
|
||||
@samp{_POSIX2_VERSION=199209} in your environment.
|
||||
@@ -7971,8 +7972,8 @@ Set both input and output block sizes to @var{bytes}.
|
||||
This makes @command{dd} read and write @var{bytes} per block,
|
||||
overriding any @samp{ibs} and @samp{obs} settings.
|
||||
In addition, if no data-transforming @option{conv} option is specified,
|
||||
each input block is copied to the output as a single block,
|
||||
without aggregating short reads.
|
||||
input is copied to the output as soon as it's read,
|
||||
even if it is smaller than the block size.
|
||||
|
||||
@item cbs=@var{bytes}
|
||||
@opindex cbs
|
||||
@@ -8062,22 +8063,29 @@ Swap every pair of input bytes. @sc{gnu} @command{dd}, unlike others, works
|
||||
when an odd number of bytes are read---the last byte is simply copied
|
||||
(since there is nothing to swap it with).
|
||||
|
||||
@item noerror
|
||||
@opindex noerror
|
||||
@cindex read errors, ignoring
|
||||
Continue after read errors.
|
||||
@item sync
|
||||
@opindex sync @r{(padding with @acronym{ASCII} @sc{nul}s)}
|
||||
Pad every input block to size of @samp{ibs} with trailing zero bytes.
|
||||
When used with @samp{block} or @samp{unblock}, pad with spaces instead of
|
||||
zero bytes.
|
||||
|
||||
@item nocreat
|
||||
@opindex nocreat
|
||||
@cindex creating output file, avoiding
|
||||
Do not create the output file; the output file must already exist.
|
||||
@end table
|
||||
|
||||
The following ``conversions'' are really file flags
|
||||
and don't affect internal processing:
|
||||
|
||||
@table @samp
|
||||
@item excl
|
||||
@opindex excl
|
||||
@cindex creating output file, requiring
|
||||
Fail if the output file already exists; @command{dd} must create the
|
||||
output file itself.
|
||||
|
||||
@item nocreat
|
||||
@opindex nocreat
|
||||
@cindex creating output file, avoiding
|
||||
Do not create the output file; the output file must already exist.
|
||||
|
||||
The @samp{excl} and @samp{nocreat} conversions are mutually exclusive.
|
||||
|
||||
@item notrunc
|
||||
@@ -8085,11 +8093,10 @@ The @samp{excl} and @samp{nocreat} conversions are mutually exclusive.
|
||||
@cindex truncating output file, avoiding
|
||||
Do not truncate the output file.
|
||||
|
||||
@item sync
|
||||
@opindex sync @r{(padding with @acronym{ASCII} @sc{nul}s)}
|
||||
Pad every input block to size of @samp{ibs} with trailing zero bytes.
|
||||
When used with @samp{block} or @samp{unblock}, pad with spaces instead of
|
||||
zero bytes.
|
||||
@item noerror
|
||||
@opindex noerror
|
||||
@cindex read errors, ignoring
|
||||
Continue after read errors.
|
||||
|
||||
@item fdatasync
|
||||
@opindex fdatasync
|
||||
@@ -8168,6 +8175,31 @@ last-access and last-modified time) is not necessarily synchronized.
|
||||
@cindex synchronized data and metadata I/O
|
||||
Use synchronized I/O for both data and metadata.
|
||||
|
||||
@item nocache
|
||||
@opindex nocache
|
||||
@cindex discarding file cache
|
||||
Discard the data cache for a file.
|
||||
When count=0 all cache is discarded,
|
||||
otherwise the cache is dropped for the processed
|
||||
portion of the file. Also when count=0
|
||||
failure to discard the cache is diagnosed
|
||||
and reflected in the exit status.
|
||||
Here as some usage examples:
|
||||
|
||||
@example
|
||||
# Advise to drop cache for whole file
|
||||
dd if=ifile iflag=nocache count=0
|
||||
|
||||
# Ensure drop cache for the whole file
|
||||
dd of=ofile oflag=nocache conv=notrunc,fdatasync count=0
|
||||
|
||||
# Drop cache for part of file
|
||||
dd if=ifile iflag=nocache skip=10 count=10 of=/dev/null
|
||||
|
||||
# Stream data using just the read-ahead cache
|
||||
dd if=ifile of=ofile iflag=nocache oflag=nocache
|
||||
@end example
|
||||
|
||||
@item nonblock
|
||||
@opindex nonblock
|
||||
@cindex nonblocking I/O
|
||||
@@ -11504,7 +11536,7 @@ Exit status:
|
||||
* File type tests:: -[bcdfhLpSt]
|
||||
* Access permission tests:: -[gkruwxOG]
|
||||
* File characteristic tests:: -e -s -nt -ot -ef
|
||||
* String tests:: -z -n = !=
|
||||
* String tests:: -z -n = == !=
|
||||
* Numeric tests:: -eq -ne -lt -le -gt -ge
|
||||
* Connectives for test:: ! -a -o
|
||||
@end menu
|
||||
@@ -11695,6 +11727,11 @@ True if the length of @var{string} is nonzero.
|
||||
@cindex equal string check
|
||||
True if the strings are equal.
|
||||
|
||||
@item @var{string1} == @var{string2}
|
||||
@opindex ==
|
||||
@cindex equal string check
|
||||
True if the strings are equal (synonym for =).
|
||||
|
||||
@item @var{string1} != @var{string2}
|
||||
@opindex !=
|
||||
@cindex not-equal string check
|
||||
|
||||
237
gl/lib/di-set.c
237
gl/lib/di-set.c
@@ -1,237 +0,0 @@
|
||||
/* Set operations for device-inode pairs stored in a space-efficient manner.
|
||||
|
||||
Copyright 2009-2011 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Paul Eggert and Jim Meyering */
|
||||
|
||||
#include <config.h>
|
||||
#include "di-set.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "ino-map.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The hash package hashes "void *", but this package wants to hash
|
||||
integers. Use integers that are as large as possible, but no
|
||||
larger than void *, so that they can be cast to void * and back
|
||||
without losing information. */
|
||||
typedef size_t hashint;
|
||||
#define HASHINT_MAX ((hashint) -1)
|
||||
|
||||
/* Integers represent inode numbers. Integers in the range
|
||||
1..(LARGE_INO_MIN-1) represent inode numbers directly. (The hash
|
||||
package does not work with null pointers, so inode 0 cannot be used
|
||||
as a key.) To find the representations of other inode numbers, map
|
||||
them through INO_MAP. */
|
||||
#define LARGE_INO_MIN (HASHINT_MAX / 2)
|
||||
|
||||
/* Set operations for device-inode pairs stored in a space-efficient
|
||||
manner. Use a two-level hash table. The top level hashes by
|
||||
device number, as there are typically a small number of devices.
|
||||
The lower level hashes by mapped inode numbers. In the typical
|
||||
case where the inode number is positive and small, the inode number
|
||||
maps to itself, masquerading as a void * value; otherwise, its
|
||||
value is the result of hashing the inode value through INO_MAP. */
|
||||
|
||||
/* A pair that maps a device number to a set of inode numbers. */
|
||||
struct di_ent
|
||||
{
|
||||
dev_t dev;
|
||||
struct hash_table *ino_set;
|
||||
};
|
||||
|
||||
/* A two-level hash table that manages and indexes these pairs. */
|
||||
struct di_set
|
||||
{
|
||||
/* Map device numbers to sets of inode number representatives. */
|
||||
struct hash_table *dev_map;
|
||||
|
||||
/* If nonnull, map large inode numbers to their small
|
||||
representatives. If null, there are no large inode numbers in
|
||||
this set. */
|
||||
struct ino_map *ino_map;
|
||||
|
||||
/* Cache of the most recently allocated and otherwise-unused storage
|
||||
for probing this table. */
|
||||
struct di_ent *probe;
|
||||
};
|
||||
|
||||
/* Hash a device-inode-set entry. */
|
||||
static size_t
|
||||
di_ent_hash (void const *x, size_t table_size)
|
||||
{
|
||||
struct di_ent const *p = x;
|
||||
dev_t dev = p->dev;
|
||||
|
||||
/* When DEV is wider than size_t, exclusive-OR the words of DEV into H.
|
||||
This avoids loss of info, without applying % to the wider type,
|
||||
which could be quite slow on some systems. */
|
||||
size_t h = dev;
|
||||
unsigned int i;
|
||||
unsigned int n_words = sizeof dev / sizeof h + (sizeof dev % sizeof h != 0);
|
||||
for (i = 1; i < n_words; i++)
|
||||
h ^= dev >> CHAR_BIT * sizeof h * i;
|
||||
|
||||
return h % table_size;
|
||||
}
|
||||
|
||||
/* Return true if two device-inode-set entries are the same. */
|
||||
static bool
|
||||
di_ent_compare (void const *x, void const *y)
|
||||
{
|
||||
struct di_ent const *a = x;
|
||||
struct di_ent const *b = y;
|
||||
return a->dev == b->dev;
|
||||
}
|
||||
|
||||
/* Free a device-inode-set entry. */
|
||||
static void
|
||||
di_ent_free (void *v)
|
||||
{
|
||||
struct di_ent *a = v;
|
||||
hash_free (a->ino_set);
|
||||
free (a);
|
||||
}
|
||||
|
||||
/* Create a set of device-inode pairs. Return NULL on allocation failure. */
|
||||
struct di_set *
|
||||
di_set_alloc (void)
|
||||
{
|
||||
struct di_set *dis = malloc (sizeof *dis);
|
||||
if (dis)
|
||||
{
|
||||
enum { INITIAL_DEV_MAP_SIZE = 11 };
|
||||
dis->dev_map = hash_initialize (INITIAL_DEV_MAP_SIZE, NULL,
|
||||
di_ent_hash, di_ent_compare,
|
||||
di_ent_free);
|
||||
if (! dis->dev_map)
|
||||
{
|
||||
free (dis);
|
||||
return NULL;
|
||||
}
|
||||
dis->ino_map = NULL;
|
||||
dis->probe = NULL;
|
||||
}
|
||||
|
||||
return dis;
|
||||
}
|
||||
|
||||
/* Free a set of device-inode pairs. */
|
||||
void
|
||||
di_set_free (struct di_set *dis)
|
||||
{
|
||||
hash_free (dis->dev_map);
|
||||
free (dis->ino_map);
|
||||
free (dis->probe);
|
||||
free (dis);
|
||||
}
|
||||
|
||||
/* Hash an encoded inode number I. */
|
||||
static size_t
|
||||
di_ino_hash (void const *i, size_t table_size)
|
||||
{
|
||||
return (hashint) i % table_size;
|
||||
}
|
||||
|
||||
/* Using the DIS table, map a device to a hash table that represents
|
||||
a set of inode numbers. Return NULL on error. */
|
||||
static struct hash_table *
|
||||
map_device (struct di_set *dis, dev_t dev)
|
||||
{
|
||||
/* Find space for the probe, reusing the cache if available. */
|
||||
struct di_ent *ent;
|
||||
struct di_ent *probe = dis->probe;
|
||||
if (probe)
|
||||
{
|
||||
/* If repeating a recent query, return the cached result. */
|
||||
if (probe->dev == dev)
|
||||
return probe->ino_set;
|
||||
}
|
||||
else
|
||||
{
|
||||
dis->probe = probe = malloc (sizeof *probe);
|
||||
if (! probe)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Probe for the device. */
|
||||
probe->dev = dev;
|
||||
ent = hash_insert (dis->dev_map, probe);
|
||||
if (! ent)
|
||||
return NULL;
|
||||
|
||||
if (ent != probe)
|
||||
{
|
||||
/* Use the existing entry. */
|
||||
probe->ino_set = ent->ino_set;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum { INITIAL_INO_SET_SIZE = 1021 };
|
||||
|
||||
/* Prepare to allocate a new probe next time; this one is in use. */
|
||||
dis->probe = NULL;
|
||||
|
||||
/* DEV is new; allocate an inode set for it. */
|
||||
probe->ino_set = hash_initialize (INITIAL_INO_SET_SIZE, NULL,
|
||||
di_ino_hash, NULL, NULL);
|
||||
}
|
||||
|
||||
return probe->ino_set;
|
||||
}
|
||||
|
||||
/* Using the DIS table, map an inode number to a mapped value.
|
||||
Return INO_MAP_INSERT_FAILURE on error. */
|
||||
static hashint
|
||||
map_inode_number (struct di_set *dis, ino_t ino)
|
||||
{
|
||||
if (0 < ino && ino < LARGE_INO_MIN)
|
||||
return ino;
|
||||
|
||||
if (! dis->ino_map)
|
||||
{
|
||||
dis->ino_map = ino_map_alloc (LARGE_INO_MIN);
|
||||
if (! dis->ino_map)
|
||||
return INO_MAP_INSERT_FAILURE;
|
||||
}
|
||||
|
||||
return ino_map_insert (dis->ino_map, ino);
|
||||
}
|
||||
|
||||
/* Attempt to insert the DEV,INO pair into the set DIS.
|
||||
If it matches a pair already in DIS, keep that pair and return 0.
|
||||
Otherwise, if insertion is successful, return 1.
|
||||
Upon any failure return -1. */
|
||||
int
|
||||
di_set_insert (struct di_set *dis, dev_t dev, ino_t ino)
|
||||
{
|
||||
hashint i;
|
||||
|
||||
/* Map the device number to a set of inodes. */
|
||||
struct hash_table *ino_set = map_device (dis, dev);
|
||||
if (! ino_set)
|
||||
return -1;
|
||||
|
||||
/* Map the inode number to a small representative I. */
|
||||
i = map_inode_number (dis, ino);
|
||||
if (i == INO_MAP_INSERT_FAILURE)
|
||||
return -1;
|
||||
|
||||
/* Put I into the inode set. */
|
||||
return hash_insert0 (ino_set, (void *) i, NULL);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#undef _ATTRIBUTE_NONNULL_
|
||||
#if __GNUC__ == 3 && __GNUC_MINOR__ >= 3 || 3 < __GNUC__
|
||||
# define _ATTRIBUTE_NONNULL_(m) __attribute__ ((__nonnull__ (m)))
|
||||
#else
|
||||
# define _ATTRIBUTE_NONNULL_(m)
|
||||
#endif
|
||||
|
||||
struct di_set *di_set_alloc (void);
|
||||
int di_set_insert (struct di_set *, dev_t, ino_t) _ATTRIBUTE_NONNULL_ (1);
|
||||
void di_set_free (struct di_set *) _ATTRIBUTE_NONNULL_ (1);
|
||||
164
gl/lib/ino-map.c
164
gl/lib/ino-map.c
@@ -1,164 +0,0 @@
|
||||
/* Map an ino_t inode number to a small integer.
|
||||
|
||||
Copyright 2009-2011 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Paul Eggert and Jim Meyering */
|
||||
|
||||
#include <config.h>
|
||||
#include "ino-map.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "verify.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* A pair that maps an inode number to a mapped inode number; the
|
||||
latter is a small unique ID for the former. */
|
||||
struct ino_map_ent
|
||||
{
|
||||
ino_t ino;
|
||||
size_t mapped_ino;
|
||||
};
|
||||
|
||||
/* A table that manages and indexes these pairs. */
|
||||
struct ino_map
|
||||
{
|
||||
/* A table of KEY,VAL pairs, where KEY is the raw ino_t value and
|
||||
VAL is the small number that it maps to. */
|
||||
struct hash_table *map;
|
||||
|
||||
/* The next mapped inode number to hand out. */
|
||||
size_t next_mapped_ino;
|
||||
|
||||
/* Cache of the most recently allocated and otherwise-unused storage
|
||||
for probing the table. */
|
||||
struct ino_map_ent *probe;
|
||||
};
|
||||
|
||||
/* Hash an inode map entry. */
|
||||
static size_t
|
||||
ino_hash (void const *x, size_t table_size)
|
||||
{
|
||||
struct ino_map_ent const *p = x;
|
||||
ino_t ino = p->ino;
|
||||
|
||||
/* When INO is wider than size_t, exclusive-OR the words of INO into H.
|
||||
This avoids loss of info, without applying % to the wider type,
|
||||
which could be quite slow on some systems. */
|
||||
size_t h = ino;
|
||||
unsigned int i;
|
||||
unsigned int n_words = sizeof ino / sizeof h + (sizeof ino % sizeof h != 0);
|
||||
for (i = 1; i < n_words; i++)
|
||||
h ^= ino >> CHAR_BIT * sizeof h * i;
|
||||
|
||||
return h % table_size;
|
||||
}
|
||||
|
||||
/* Return true if two inode map entries are the same. */
|
||||
static bool
|
||||
ino_compare (void const *x, void const *y)
|
||||
{
|
||||
struct ino_map_ent const *a = x;
|
||||
struct ino_map_ent const *b = y;
|
||||
return a->ino == b->ino;
|
||||
}
|
||||
|
||||
/* Allocate an inode map that will hand out integers starting with
|
||||
NEXT_MAPPED_INO. Return NULL if memory is exhausted. */
|
||||
struct ino_map *
|
||||
ino_map_alloc (size_t next_mapped_ino)
|
||||
{
|
||||
struct ino_map *im = malloc (sizeof *im);
|
||||
|
||||
if (im)
|
||||
{
|
||||
enum { INITIAL_INO_MAP_TABLE_SIZE = 1021 };
|
||||
im->map = hash_initialize (INITIAL_INO_MAP_TABLE_SIZE, NULL,
|
||||
ino_hash, ino_compare, free);
|
||||
if (! im->map)
|
||||
{
|
||||
free (im);
|
||||
return NULL;
|
||||
}
|
||||
im->next_mapped_ino = next_mapped_ino;
|
||||
im->probe = NULL;
|
||||
}
|
||||
|
||||
return im;
|
||||
}
|
||||
|
||||
/* Free an inode map. */
|
||||
void
|
||||
ino_map_free (struct ino_map *map)
|
||||
{
|
||||
hash_free (map->map);
|
||||
free (map->probe);
|
||||
free (map);
|
||||
}
|
||||
|
||||
|
||||
/* Insert into MAP the inode number INO if it's not there already,
|
||||
and return its nonnegative mapped inode number.
|
||||
If INO is already in MAP, return the existing mapped inode number.
|
||||
Return INO_MAP_INSERT_FAILURE on memory or counter exhaustion. */
|
||||
size_t
|
||||
ino_map_insert (struct ino_map *im, ino_t ino)
|
||||
{
|
||||
struct ino_map_ent *ent;
|
||||
|
||||
/* Find space for the probe, reusing the cache if available. */
|
||||
struct ino_map_ent *probe = im->probe;
|
||||
if (probe)
|
||||
{
|
||||
/* If repeating a recent query, return the cached result. */
|
||||
if (probe->ino == ino)
|
||||
return probe->mapped_ino;
|
||||
}
|
||||
else
|
||||
{
|
||||
im->probe = probe = malloc (sizeof *probe);
|
||||
if (! probe)
|
||||
return INO_MAP_INSERT_FAILURE;
|
||||
}
|
||||
|
||||
probe->ino = ino;
|
||||
ent = hash_insert (im->map, probe);
|
||||
if (! ent)
|
||||
return INO_MAP_INSERT_FAILURE;
|
||||
|
||||
if (ent != probe)
|
||||
{
|
||||
/* Use the existing entry. */
|
||||
probe->mapped_ino = ent->mapped_ino;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If adding 1 to map->next_mapped_ino would cause it to
|
||||
overflow to zero, then it must equal INO_MAP_INSERT_FAILURE,
|
||||
which is the value that should be returned in that case.
|
||||
Verify that this works. */
|
||||
verify (INO_MAP_INSERT_FAILURE + 1 == 0);
|
||||
|
||||
/* Prepare to allocate a new probe next time; this one is in use. */
|
||||
im->probe = NULL;
|
||||
|
||||
/* INO is new; allocate a mapped inode number for it. */
|
||||
probe->mapped_ino = im->next_mapped_ino++;
|
||||
}
|
||||
|
||||
return probe->mapped_ino;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#undef _ATTRIBUTE_NONNULL_
|
||||
#if __GNUC__ == 3 && __GNUC_MINOR__ >= 3 || 3 < __GNUC__
|
||||
# define _ATTRIBUTE_NONNULL_(m) __attribute__ ((__nonnull__ (m)))
|
||||
#else
|
||||
# define _ATTRIBUTE_NONNULL_(m)
|
||||
#endif
|
||||
|
||||
#define INO_MAP_INSERT_FAILURE ((size_t) -1)
|
||||
|
||||
struct ino_map *ino_map_alloc (size_t);
|
||||
void ino_map_free (struct ino_map *) _ATTRIBUTE_NONNULL_ (1);
|
||||
size_t ino_map_insert (struct ino_map *, ino_t) _ATTRIBUTE_NONNULL_ (1);
|
||||
@@ -79,27 +79,6 @@ wc_truncate (wchar_t *wc, size_t width)
|
||||
return cells;
|
||||
}
|
||||
|
||||
/* FIXME: move this function to gnulib as it's missing on:
|
||||
OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS */
|
||||
|
||||
static int
|
||||
rpl_wcswidth (const wchar_t *s, size_t n)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (n-- > 0 && *s != L'\0')
|
||||
{
|
||||
int nwidth = wcwidth (*s++);
|
||||
if (nwidth == -1) /* non printable */
|
||||
return -1;
|
||||
if (ret > (INT_MAX - nwidth)) /* overflow */
|
||||
return -1;
|
||||
ret += nwidth;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write N_SPACES space characters to DEST while ensuring
|
||||
nothing is written beyond DEST_END. A terminating NUL
|
||||
is always added to DEST.
|
||||
@@ -171,7 +150,7 @@ mbsalign (const char *src, char *dest, size_t dest_size,
|
||||
str_wc[src_chars - 1] = L'\0';
|
||||
wc_enabled = true;
|
||||
conversion = wc_ensure_printable (str_wc);
|
||||
n_cols = rpl_wcswidth (str_wc, src_chars);
|
||||
n_cols = wcswidth (str_wc, src_chars);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t;
|
||||
|
||||
enum {
|
||||
/* Use unibyte mode for invalid multibyte strings or
|
||||
/* Use unibyte mode for invalid multibyte strings
|
||||
or when heap memory is exhausted. */
|
||||
MBA_UNIBYTE_FALLBACK = 0x0001
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
Description:
|
||||
manipulate sets of device-inode pairs efficiently
|
||||
|
||||
Files:
|
||||
lib/di-set.c
|
||||
lib/di-set.h
|
||||
|
||||
Depends-on:
|
||||
ino-map
|
||||
hash
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
lib_SOURCES += di-set.c di-set.h
|
||||
|
||||
Include:
|
||||
"di-set.h"
|
||||
|
||||
License
|
||||
GPL
|
||||
|
||||
Maintainer:
|
||||
Jim Meyering
|
||||
@@ -1,10 +0,0 @@
|
||||
Files:
|
||||
tests/test-di-set.c
|
||||
|
||||
Depends-on:
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
TESTS += test-di-set
|
||||
check_PROGRAMS += test-di-set
|
||||
@@ -1,24 +0,0 @@
|
||||
Description:
|
||||
maintain a mapping of ino_t numbers to small integers
|
||||
|
||||
Files:
|
||||
lib/ino-map.c
|
||||
lib/ino-map.h
|
||||
|
||||
Depends-on:
|
||||
hash
|
||||
verify
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
lib_SOURCES += ino-map.c ino-map.h
|
||||
|
||||
Include:
|
||||
"ino-map.h"
|
||||
|
||||
License
|
||||
GPL
|
||||
|
||||
Maintainer:
|
||||
Jim Meyering
|
||||
@@ -1,10 +0,0 @@
|
||||
Files:
|
||||
tests/test-ino-map.c
|
||||
|
||||
Depends-on:
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
TESTS += test-ino-map
|
||||
check_PROGRAMS += test-ino-map
|
||||
@@ -1,5 +1,6 @@
|
||||
Files:
|
||||
tests/test-rand-isaac.c
|
||||
tests/macros.h
|
||||
|
||||
Depends-on:
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* Test the di-set module.
|
||||
Copyright (C) 2010-2011 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering. */
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define ASSERT(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
|
||||
fflush (stderr); \
|
||||
abort (); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#include "di-set.h"
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
struct di_set *dis = di_set_alloc ();
|
||||
ASSERT (dis);
|
||||
|
||||
ASSERT (di_set_insert (dis, 2, 5) == 1); /* first insertion succeeds */
|
||||
ASSERT (di_set_insert (dis, 2, 5) == 0); /* duplicate fails */
|
||||
ASSERT (di_set_insert (dis, 3, 5) == 1); /* diff dev, duplicate inode is ok */
|
||||
ASSERT (di_set_insert (dis, 2, 8) == 1); /* same dev, different inode is ok */
|
||||
|
||||
/* very large (or negative) inode number */
|
||||
ASSERT (di_set_insert (dis, 5, (ino_t) -1) == 1);
|
||||
ASSERT (di_set_insert (dis, 5, (ino_t) -1) == 0); /* dup */
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < 3000; i++)
|
||||
ASSERT (di_set_insert (dis, 9, i) == 1);
|
||||
for (i = 0; i < 3000; i++)
|
||||
ASSERT (di_set_insert (dis, 9, i) == 0); /* duplicate fails */
|
||||
|
||||
di_set_free (dis);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/* Test the ino-map module.
|
||||
Copyright (C) 2010-2011 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Jim Meyering. */
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* FIXME: once/if in gnulib, use #include "macros.h" in place of this */
|
||||
#define ASSERT(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
|
||||
fflush (stderr); \
|
||||
abort (); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#include "ino-map.h"
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
enum { INO_MAP_INIT = 123 };
|
||||
struct ino_map *ino_map = ino_map_alloc (INO_MAP_INIT);
|
||||
ASSERT (ino_map != NULL);
|
||||
|
||||
ASSERT (ino_map_insert (ino_map, 42) == INO_MAP_INIT);
|
||||
ASSERT (ino_map_insert (ino_map, 42) == INO_MAP_INIT);
|
||||
ASSERT (ino_map_insert (ino_map, 398) == INO_MAP_INIT + 1);
|
||||
ASSERT (ino_map_insert (ino_map, 398) == INO_MAP_INIT + 1);
|
||||
ASSERT (ino_map_insert (ino_map, 0) == INO_MAP_INIT + 2);
|
||||
ASSERT (ino_map_insert (ino_map, 0) == INO_MAP_INIT + 2);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
ASSERT (ino_map_insert (ino_map, 10000 + i) == INO_MAP_INIT + 3 + i);
|
||||
}
|
||||
|
||||
ino_map_free (ino_map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -23,22 +23,9 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* FIXME: once/if in gnulib, use #include "macros.h" in place of this */
|
||||
#define ASSERT(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
|
||||
fflush (stderr); \
|
||||
abort (); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
#include "macros.h"
|
||||
|
||||
/* This expected output was generated by running the programs in
|
||||
<http://burtleburtle.net/bob/rand/isaacafa.html>, as last modified
|
||||
|
||||
2
gnulib
2
gnulib
Submodule gnulib updated: a036b7684f...a81348d2e1
@@ -707,8 +707,8 @@ sc_check-AUTHORS: $(all_programs)
|
||||
|
||||
# Most functions in src/*.c should have static scope.
|
||||
# Any that don't must be marked with `extern', but `main'
|
||||
# and `usage' are exceptions. They're always extern, but
|
||||
# don't need to be marked. Also functions starting with __
|
||||
# and `usage' are exceptions: they're always extern, but
|
||||
# do not need to be marked. Also functions starting with __
|
||||
# are exempted due to possibly being added by the compiler
|
||||
# (when compiled as a shared library for example).
|
||||
#
|
||||
@@ -717,9 +717,9 @@ sc_check-AUTHORS: $(all_programs)
|
||||
sc_tight_scope: $(bin_PROGRAMS)
|
||||
@t=exceptions-$$$$; \
|
||||
trap 's=$$?; rm -f $$t; exit $$s' 0; \
|
||||
am__exit='(exit $s); exit $s'; \
|
||||
trap "s=129; $$am__exit" 1; trap "s=130; $$am__exit" 2; \
|
||||
trap "s=141; $$am__exit" 13; trap "s=143; $$am__exit" 15; \
|
||||
for sig in 1 2 3 13 15; do \
|
||||
eval "trap 'v=`expr $$sig + 128`; (exit $$v); exit $$v' $$sig"; \
|
||||
done; \
|
||||
src=`for f in $(SOURCES); do \
|
||||
test -f $$f && d= || d=$(srcdir)/; echo $$d$$f; done`; \
|
||||
hdr=`for f in $(noinst_HEADERS); do \
|
||||
@@ -727,7 +727,8 @@ sc_tight_scope: $(bin_PROGRAMS)
|
||||
( printf 'main\nusage\n_.*\n'; \
|
||||
grep -h -A1 '^extern .*[^;]$$' $$src \
|
||||
| grep -vE '^(extern |--)' | sed 's/ .*//'; \
|
||||
perl -ne '/^extern (?:enum )?\S+ (\S*) \(/ and print "$$1\n"' $$hdr; \
|
||||
perl -lne '/^extern (?:enum )?\S+ \**(\S*) \(/ and print $$1' \
|
||||
$$hdr; \
|
||||
) | $(ASSORT) -u | sed 's/^/^/;s/$$/$$/' > $$t; \
|
||||
nm -e *.$(OBJEXT) \
|
||||
| sed -n 's/.* T //p' \
|
||||
@@ -736,7 +737,7 @@ sc_tight_scope: $(bin_PROGRAMS)
|
||||
{ echo 'the above functions should have static scope' 1>&2; \
|
||||
exit 1; } || : ; \
|
||||
( printf '^program_name$$\n'; \
|
||||
perl -ne '/^extern .*?\**(\w+);/ and print "^$$1\$$\n"' \
|
||||
perl -lne '/^extern .*?\**(\w+);/ and print "^$$1\$$"' \
|
||||
$$hdr *.h ) | $(ASSORT) -u > $$t; \
|
||||
nm -e *.$(OBJEXT) \
|
||||
| sed -n 's/.* [BD] //p' \
|
||||
|
||||
176
src/copy.c
176
src/copy.c
@@ -39,6 +39,7 @@
|
||||
#include "extent-scan.h"
|
||||
#include "error.h"
|
||||
#include "fcntl--.h"
|
||||
#include "fiemap.h"
|
||||
#include "file-set.h"
|
||||
#include "filemode.h"
|
||||
#include "filenamecat.h"
|
||||
@@ -233,21 +234,6 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the file ends with a `hole' (i.e., if sparse_copy set wrote_hole_at_eof),
|
||||
call this function to record the length of the output file. */
|
||||
static bool
|
||||
sparse_copy_finalize (int dest_fd, char const *dst_name)
|
||||
{
|
||||
off_t len = lseek (dest_fd, 0, SEEK_CUR);
|
||||
if (0 <= len && ftruncate (dest_fd, len) < 0)
|
||||
{
|
||||
error (0, errno, _("truncating %s"), quote (dst_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Perform the O(1) btrfs clone operation, if possible.
|
||||
Upon success, return 0. Otherwise, return -1 and set errno. */
|
||||
static inline int
|
||||
@@ -309,7 +295,7 @@ write_zeros (int fd, uint64_t n_bytes)
|
||||
return false. */
|
||||
static bool
|
||||
extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
|
||||
off_t src_total_size, bool make_holes,
|
||||
off_t src_total_size, enum Sparse_type sparse_mode,
|
||||
char const *src_name, char const *dst_name,
|
||||
bool *require_normal_copy)
|
||||
{
|
||||
@@ -345,56 +331,105 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < scan.ei_count; i++)
|
||||
bool empty_extent = false;
|
||||
for (i = 0; i < scan.ei_count || empty_extent; i++)
|
||||
{
|
||||
off_t ext_start = scan.ext_info[i].ext_logical;
|
||||
uint64_t ext_len = scan.ext_info[i].ext_length;
|
||||
off_t ext_start;
|
||||
uint64_t ext_len;
|
||||
uint64_t hole_size;
|
||||
|
||||
if (lseek (src_fd, ext_start, SEEK_SET) < 0)
|
||||
if (i < scan.ei_count)
|
||||
{
|
||||
error (0, errno, _("cannot lseek %s"), quote (src_name));
|
||||
fail:
|
||||
extent_scan_free (&scan);
|
||||
return false;
|
||||
ext_start = scan.ext_info[i].ext_logical;
|
||||
ext_len = scan.ext_info[i].ext_length;
|
||||
}
|
||||
else /* empty extent at EOF. */
|
||||
{
|
||||
i--;
|
||||
ext_start = last_ext_start + scan.ext_info[i].ext_length;
|
||||
ext_len = 0;
|
||||
}
|
||||
|
||||
if (make_holes)
|
||||
hole_size = ext_start - last_ext_start - last_ext_len;
|
||||
|
||||
wrote_hole_at_eof = false;
|
||||
|
||||
if (hole_size)
|
||||
{
|
||||
if (lseek (dest_fd, ext_start, SEEK_SET) < 0)
|
||||
if (lseek (src_fd, ext_start, SEEK_SET) < 0)
|
||||
{
|
||||
error (0, errno, _("cannot lseek %s"), quote (dst_name));
|
||||
goto fail;
|
||||
error (0, errno, _("cannot lseek %s"), quote (src_name));
|
||||
fail:
|
||||
extent_scan_free (&scan);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* When not inducing holes and when there is a hole between
|
||||
the end of the previous extent and the beginning of the
|
||||
current one, write zeros to the destination file. */
|
||||
if (last_ext_start + last_ext_len < ext_start)
|
||||
|
||||
if ((empty_extent && sparse_mode == SPARSE_ALWAYS)
|
||||
|| (!empty_extent && sparse_mode != SPARSE_NEVER))
|
||||
{
|
||||
uint64_t hole_size = (ext_start
|
||||
- last_ext_start
|
||||
- last_ext_len);
|
||||
if (! write_zeros (dest_fd, hole_size))
|
||||
if (lseek (dest_fd, ext_start, SEEK_SET) < 0)
|
||||
{
|
||||
error (0, errno, _("cannot lseek %s"), quote (dst_name));
|
||||
goto fail;
|
||||
}
|
||||
wrote_hole_at_eof = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* When not inducing holes and when there is a hole between
|
||||
the end of the previous extent and the beginning of the
|
||||
current one, write zeros to the destination file. */
|
||||
off_t nzeros = hole_size;
|
||||
if (empty_extent)
|
||||
nzeros = MIN (src_total_size - dest_pos, hole_size);
|
||||
|
||||
if (! write_zeros (dest_fd, nzeros))
|
||||
{
|
||||
error (0, errno, _("%s: write failed"), quote (dst_name));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dest_pos = MIN (src_total_size, ext_start);
|
||||
}
|
||||
}
|
||||
|
||||
last_ext_start = ext_start;
|
||||
last_ext_len = ext_len;
|
||||
|
||||
off_t n_read;
|
||||
if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
|
||||
make_holes, src_name, dst_name,
|
||||
ext_len, &n_read,
|
||||
&wrote_hole_at_eof))
|
||||
return false;
|
||||
/* Treat an unwritten but allocated extent much like a hole.
|
||||
I.E. don't read, but don't convert to a hole in the destination,
|
||||
unless SPARSE_ALWAYS. */
|
||||
if (scan.ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN)
|
||||
{
|
||||
empty_extent = true;
|
||||
last_ext_len = 0;
|
||||
if (ext_len == 0) /* The last extent is empty and processed. */
|
||||
empty_extent = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
off_t n_read;
|
||||
empty_extent = false;
|
||||
last_ext_len = ext_len;
|
||||
|
||||
dest_pos = ext_start + n_read;
|
||||
if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
|
||||
sparse_mode == SPARSE_ALWAYS,
|
||||
src_name, dst_name, ext_len, &n_read,
|
||||
&wrote_hole_at_eof))
|
||||
goto fail;
|
||||
|
||||
dest_pos = ext_start + n_read;
|
||||
}
|
||||
|
||||
/* If the file ends with unwritten extents not accounted for in the
|
||||
size, then skip processing them, and the associated redundant
|
||||
read() calls which will always return 0. We will need to
|
||||
remove this when we add fallocate() so that we can maintain
|
||||
extents beyond the apparent size. */
|
||||
if (dest_pos == src_total_size)
|
||||
{
|
||||
scan.hit_final_extent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the space allocated to scan->ext_info. */
|
||||
@@ -412,7 +447,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
|
||||
just converted them to a hole in the destination, we must call ftruncate
|
||||
here in order to record the proper length in the destination. */
|
||||
if ((dest_pos < src_total_size || wrote_hole_at_eof)
|
||||
&& (make_holes
|
||||
&& (sparse_mode != SPARSE_NEVER
|
||||
? ftruncate (dest_fd, src_total_size)
|
||||
: ! write_zeros (dest_fd, src_total_size - dest_pos)))
|
||||
{
|
||||
@@ -531,8 +566,11 @@ copy_attr (char const *src_path ATTRIBUTE_UNUSED,
|
||||
DST_NAME_IN is a directory that was created previously in the
|
||||
recursion. SRC_SB and ANCESTORS describe SRC_NAME_IN.
|
||||
Set *COPY_INTO_SELF if SRC_NAME_IN is a parent of
|
||||
FIRST_DIR_CREATED_PER_COMMAND_LINE_ARG FIXME
|
||||
(or the same as) DST_NAME_IN; otherwise, clear it.
|
||||
Propagate *FIRST_DIR_CREATED_PER_COMMAND_LINE_ARG from
|
||||
caller to each invocation of copy_internal. Be careful to
|
||||
pass the address of a temporary, and to update
|
||||
*FIRST_DIR_CREATED_PER_COMMAND_LINE_ARG only upon completion.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
@@ -561,16 +599,18 @@ copy_dir (char const *src_name_in, char const *dst_name_in, bool new_dst,
|
||||
if (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
|
||||
non_command_line_options.dereference = DEREF_NEVER;
|
||||
|
||||
bool new_first_dir_created = false;
|
||||
namep = name_space;
|
||||
while (*namep != '\0')
|
||||
{
|
||||
bool local_copy_into_self;
|
||||
char *src_name = file_name_concat (src_name_in, namep, NULL);
|
||||
char *dst_name = file_name_concat (dst_name_in, namep, NULL);
|
||||
bool first_dir_created = *first_dir_created_per_command_line_arg;
|
||||
|
||||
ok &= copy_internal (src_name, dst_name, new_dst, src_sb->st_dev,
|
||||
ancestors, &non_command_line_options, false,
|
||||
first_dir_created_per_command_line_arg,
|
||||
&first_dir_created,
|
||||
&local_copy_into_self, NULL);
|
||||
*copy_into_self |= local_copy_into_self;
|
||||
|
||||
@@ -583,9 +623,12 @@ copy_dir (char const *src_name_in, char const *dst_name_in, bool new_dst,
|
||||
if (local_copy_into_self)
|
||||
break;
|
||||
|
||||
new_first_dir_created |= first_dir_created;
|
||||
namep += strlen (namep) + 1;
|
||||
}
|
||||
free (name_space);
|
||||
*first_dir_created_per_command_line_arg = new_first_dir_created;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -983,7 +1026,8 @@ copy_reg (char const *src_name, char const *dst_name,
|
||||
'--sparse=never' option is specified, write all data but use
|
||||
any extents to read more efficiently. */
|
||||
if (extent_copy (source_desc, dest_desc, buf, buf_size,
|
||||
src_open_sb.st_size, make_holes,
|
||||
src_open_sb.st_size,
|
||||
S_ISREG (sb.st_mode) ? x->sparse_mode : SPARSE_NEVER,
|
||||
src_name, dst_name, &normal_copy_required))
|
||||
goto preserve_metadata;
|
||||
|
||||
@@ -1000,8 +1044,9 @@ copy_reg (char const *src_name, char const *dst_name,
|
||||
UINTMAX_MAX, &n_read,
|
||||
&wrote_hole_at_eof)
|
||||
|| (wrote_hole_at_eof &&
|
||||
! sparse_copy_finalize (dest_desc, dst_name)))
|
||||
ftruncate (dest_desc, n_read) < 0))
|
||||
{
|
||||
error (0, errno, _("failed to extend %s"), quote (dst_name));
|
||||
return_val = false;
|
||||
goto close_src_and_dst_desc;
|
||||
}
|
||||
@@ -2184,13 +2229,24 @@ copy_internal (char const *src_name, char const *dst_name,
|
||||
}
|
||||
}
|
||||
|
||||
/* cp, invoked with `--link --no-dereference', should not follow the
|
||||
link; we guarantee this with gnulib's linkat module (on systems
|
||||
where link(2) follows the link, gnulib creates a symlink with
|
||||
identical contents, which is good enough for our purposes). */
|
||||
/* POSIX 2008 states that it is implementation-defined whether
|
||||
link() on a symlink creates a hard-link to the symlink, or only
|
||||
to the referent (effectively dereferencing the symlink) (POSIX
|
||||
2001 required the latter behavior, although many systems provided
|
||||
the former). Yet cp, invoked with `--link --no-dereference',
|
||||
should not follow the link. We can approximate the desired
|
||||
behavior by skipping this hard-link creating block and instead
|
||||
copying the symlink, via the `S_ISLNK'- copying code below.
|
||||
LINK_FOLLOWS_SYMLINKS is tri-state; if it is -1, we don't know
|
||||
how link() behaves, so we use the fallback case for safety.
|
||||
|
||||
Note gnulib's linkat module, guarantees that the symlink is not
|
||||
dereferenced. However its emulation currently doesn't maintain
|
||||
timestamps or ownership so we only call it when we know the
|
||||
emulation will not be needed. */
|
||||
else if (x->hard_link
|
||||
&& (!S_ISLNK (src_mode)
|
||||
|| x->dereference != DEREF_NEVER))
|
||||
&& !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
|
||||
&& x->dereference == DEREF_NEVER))
|
||||
{
|
||||
if (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, 0))
|
||||
{
|
||||
@@ -2313,7 +2369,9 @@ copy_internal (char const *src_name, char const *dst_name,
|
||||
|
||||
/* If we've just created a hard-link due to cp's --link option,
|
||||
we're done. */
|
||||
if (x->hard_link && ! S_ISDIR (src_mode))
|
||||
if (x->hard_link && ! S_ISDIR (src_mode)
|
||||
&& !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
|
||||
&& x->dereference == DEREF_NEVER))
|
||||
return delayed_ok;
|
||||
|
||||
if (copied_as_regular)
|
||||
|
||||
@@ -496,6 +496,8 @@ set_fields (const char *fieldstr)
|
||||
if (rp[i].hi > max_range_endpoint)
|
||||
max_range_endpoint = rp[i].hi;
|
||||
}
|
||||
if (max_range_endpoint < eol_range_start)
|
||||
max_range_endpoint = eol_range_start;
|
||||
|
||||
/* Allocate an array large enough so that it may be indexed by
|
||||
the field numbers corresponding to all finite ranges
|
||||
|
||||
227
src/dd.c
227
src/dd.c
@@ -199,6 +199,9 @@ static int input_seek_errno;
|
||||
static uintmax_t input_offset;
|
||||
static bool input_offset_overflow;
|
||||
|
||||
/* True if a partial read should be diagnosed. */
|
||||
static bool warn_partial_read;
|
||||
|
||||
/* Records truncated by conv=block. */
|
||||
static uintmax_t r_truncate = 0;
|
||||
|
||||
@@ -225,6 +228,9 @@ static sig_atomic_t volatile interrupt_signal;
|
||||
/* A count of the number of pending info signals that have been received. */
|
||||
static sig_atomic_t volatile info_signal_count;
|
||||
|
||||
/* Whether to discard cache for input or output. */
|
||||
static bool i_nocache, o_nocache;
|
||||
|
||||
/* Function used for read (to handle iflag=fullblock parameter). */
|
||||
static ssize_t (*iread_fnc) (int fd, char *buf, size_t size);
|
||||
|
||||
@@ -259,6 +265,7 @@ static struct symbol_value const conversions[] =
|
||||
{"", 0}
|
||||
};
|
||||
|
||||
#define FFS_MASK(x) ((x) ^ ((x) & ((x) - 1)))
|
||||
enum
|
||||
{
|
||||
/* Compute a value that's bitwise disjoint from the union
|
||||
@@ -278,17 +285,23 @@ enum
|
||||
| O_SYNC
|
||||
| O_TEXT
|
||||
),
|
||||
/* Use its lowest bit. */
|
||||
O_FULLBLOCK = v ^ (v & (v - 1))
|
||||
|
||||
/* Use its lowest bits for private flags. */
|
||||
O_FULLBLOCK = FFS_MASK (v),
|
||||
v2 = v ^ O_FULLBLOCK,
|
||||
|
||||
O_NOCACHE = FFS_MASK (v2)
|
||||
};
|
||||
|
||||
/* Ensure that we got something. */
|
||||
verify (O_FULLBLOCK != 0);
|
||||
verify (O_NOCACHE != 0);
|
||||
|
||||
#define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0)
|
||||
|
||||
/* Ensure that this is a single-bit value. */
|
||||
verify ( ! MULTIPLE_BITS_SET (O_FULLBLOCK));
|
||||
verify ( ! MULTIPLE_BITS_SET (O_NOCACHE));
|
||||
|
||||
/* Flags, for iflag="..." and oflag="...". */
|
||||
static struct symbol_value const flags[] =
|
||||
@@ -300,6 +313,7 @@ static struct symbol_value const flags[] =
|
||||
{"directory", O_DIRECTORY},
|
||||
{"dsync", O_DSYNC},
|
||||
{"noatime", O_NOATIME},
|
||||
{"nocache", O_NOCACHE}, /* Discard cache. */
|
||||
{"noctty", O_NOCTTY},
|
||||
{"nofollow", HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0},
|
||||
{"nolinks", O_NOLINKS},
|
||||
@@ -467,11 +481,11 @@ Usage: %s [OPERAND]...\n\
|
||||
fputs (_("\
|
||||
Copy a file, converting and formatting according to the operands.\n\
|
||||
\n\
|
||||
bs=BYTES read and write BYTES bytes at a time (also see ibs=,obs=)\n\
|
||||
bs=BYTES read and write up to BYTES bytes at a time\n\
|
||||
cbs=BYTES convert BYTES bytes at a time\n\
|
||||
conv=CONVS convert the file as per the comma separated symbol list\n\
|
||||
count=BLOCKS copy only BLOCKS input blocks\n\
|
||||
ibs=BYTES read BYTES bytes at a time (default: 512)\n\
|
||||
ibs=BYTES read up to BYTES bytes at a time (default: 512)\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
if=FILE read from FILE instead of stdin\n\
|
||||
@@ -499,18 +513,16 @@ Each CONV symbol may be:\n\
|
||||
block pad newline-terminated records with spaces to cbs-size\n\
|
||||
unblock replace trailing spaces in cbs-size records with newline\n\
|
||||
lcase change upper case to lower case\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
nocreat do not create the output file\n\
|
||||
excl fail if the output file already exists\n\
|
||||
notrunc do not truncate the output file\n\
|
||||
ucase change lower case to upper case\n\
|
||||
swab swap every pair of input bytes\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
noerror continue after read errors\n\
|
||||
sync pad every input block with NULs to ibs-size; when used\n\
|
||||
with block or unblock, pad with spaces rather than NULs\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
excl fail if the output file already exists\n\
|
||||
nocreat do not create the output file\n\
|
||||
notrunc do not truncate the output file\n\
|
||||
noerror continue after read errors\n\
|
||||
fdatasync physically write output file data before finishing\n\
|
||||
fsync likewise, but also write metadata\n\
|
||||
"), stdout);
|
||||
@@ -536,6 +548,10 @@ Each FLAG symbol may be:\n\
|
||||
fputs (_(" nonblock use non-blocking I/O\n"), stdout);
|
||||
if (O_NOATIME)
|
||||
fputs (_(" noatime do not update access time\n"), stdout);
|
||||
#if HAVE_POSIX_FADVISE
|
||||
if (O_NOCACHE)
|
||||
fputs (_(" nocache discard cached data\n"), stdout);
|
||||
#endif
|
||||
if (O_NOCTTY)
|
||||
fputs (_(" noctty do not assign controlling terminal from file\n"),
|
||||
stdout);
|
||||
@@ -789,6 +805,91 @@ process_signals (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return LEN rounded down to a multiple of PAGE_SIZE
|
||||
while storing the remainder internally per FD.
|
||||
Pass LEN == 0 to get the current remainder. */
|
||||
|
||||
static off_t
|
||||
cache_round (int fd, off_t len)
|
||||
{
|
||||
static off_t i_pending, o_pending;
|
||||
off_t *pending = (fd == STDIN_FILENO ? &i_pending : &o_pending);
|
||||
|
||||
if (len)
|
||||
{
|
||||
off_t c_pending = *pending + len;
|
||||
*pending = c_pending % page_size;
|
||||
if (c_pending > *pending)
|
||||
len = c_pending - *pending;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
len = *pending;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Discard the cache from the current offset of either
|
||||
STDIN_FILENO or STDOUT_FILENO.
|
||||
Return true on success. */
|
||||
|
||||
static bool
|
||||
invalidate_cache (int fd, off_t len)
|
||||
{
|
||||
int adv_ret = -1;
|
||||
|
||||
/* Minimize syscalls. */
|
||||
off_t clen = cache_round (fd, len);
|
||||
if (len && !clen)
|
||||
return true; /* Don't advise this time. */
|
||||
if (!len && !clen && max_records)
|
||||
return true; /* Nothing pending. */
|
||||
off_t pending = len ? cache_round (fd, 0) : 0;
|
||||
|
||||
if (fd == STDIN_FILENO)
|
||||
{
|
||||
if (input_seekable)
|
||||
{
|
||||
/* Note we're being careful here to only invalidate what
|
||||
we've read, so as not to dump any read ahead cache. */
|
||||
#if HAVE_POSIX_FADVISE
|
||||
adv_ret = posix_fadvise (fd, input_offset - clen - pending, clen,
|
||||
POSIX_FADV_DONTNEED);
|
||||
#else
|
||||
errno = ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
errno = ESPIPE;
|
||||
}
|
||||
else if (fd == STDOUT_FILENO)
|
||||
{
|
||||
static off_t output_offset = -2;
|
||||
|
||||
if (output_offset != -1)
|
||||
{
|
||||
if (0 > output_offset)
|
||||
{
|
||||
output_offset = lseek (fd, 0, SEEK_CUR);
|
||||
output_offset -= clen + pending;
|
||||
}
|
||||
if (0 <= output_offset)
|
||||
{
|
||||
#if HAVE_POSIX_FADVISE
|
||||
adv_ret = posix_fadvise (fd, output_offset, clen,
|
||||
POSIX_FADV_DONTNEED);
|
||||
#else
|
||||
errno = ENOTSUP;
|
||||
#endif
|
||||
output_offset += clen + pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adv_ret != -1 ? true : false;
|
||||
}
|
||||
|
||||
/* Read from FD into the buffer BUF of size SIZE, processing any
|
||||
signals that arrive before bytes are read. Return the number of
|
||||
bytes read if successful, -1 (setting errno) on failure. */
|
||||
@@ -796,14 +897,35 @@ process_signals (void)
|
||||
static ssize_t
|
||||
iread (int fd, char *buf, size_t size)
|
||||
{
|
||||
while (true)
|
||||
ssize_t nread;
|
||||
|
||||
do
|
||||
{
|
||||
ssize_t nread;
|
||||
process_signals ();
|
||||
nread = read (fd, buf, size);
|
||||
if (! (nread < 0 && errno == EINTR))
|
||||
return nread;
|
||||
}
|
||||
while (nread < 0 && errno == EINTR);
|
||||
|
||||
if (0 < nread && warn_partial_read)
|
||||
{
|
||||
static ssize_t prev_nread;
|
||||
|
||||
if (0 < prev_nread && prev_nread < size)
|
||||
{
|
||||
uintmax_t prev = prev_nread;
|
||||
error (0, 0, ngettext (("warning: partial read (%"PRIuMAX" byte); "
|
||||
"suggest iflag=fullblock"),
|
||||
("warning: partial read (%"PRIuMAX" bytes); "
|
||||
"suggest iflag=fullblock"),
|
||||
select_plural (prev)),
|
||||
prev);
|
||||
warn_partial_read = false;
|
||||
}
|
||||
|
||||
prev_nread = nread;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
/* Wrapper around iread function to accumulate full blocks. */
|
||||
@@ -849,9 +971,7 @@ iwrite (int fd, char const *buf, size_t size)
|
||||
posix_fadvise to tell the system not to pollute the buffer
|
||||
cache with this data. Don't bother to diagnose lseek or
|
||||
posix_fadvise failure. */
|
||||
off_t off = lseek (STDOUT_FILENO, 0, SEEK_CUR);
|
||||
if (0 <= off)
|
||||
fdadvise (STDOUT_FILENO, off, 0, FADVISE_DONTNEED);
|
||||
invalidate_cache (STDOUT_FILENO, 0);
|
||||
|
||||
/* Attempt to ensure that that final block is committed
|
||||
to disk as quickly as possible. */
|
||||
@@ -880,6 +1000,9 @@ iwrite (int fd, char const *buf, size_t size)
|
||||
total_written += nwritten;
|
||||
}
|
||||
|
||||
if (o_nocache && total_written)
|
||||
invalidate_cache (fd, total_written);
|
||||
|
||||
return total_written;
|
||||
}
|
||||
|
||||
@@ -1070,7 +1193,7 @@ scanargs (int argc, char *const *argv)
|
||||
input_blocksize = output_blocksize = blocksize;
|
||||
else
|
||||
{
|
||||
/* POSIX says dd aggregates short reads into
|
||||
/* POSIX says dd aggregates partial reads into
|
||||
output_blocksize if bs= is not specified. */
|
||||
conversions_mask |= C_TWOBUFS;
|
||||
}
|
||||
@@ -1090,6 +1213,17 @@ scanargs (int argc, char *const *argv)
|
||||
error (0, 0, "%s: %s", _("invalid output flag"), "'fullblock'");
|
||||
usage (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Warn about partial reads if bs=SIZE is given and iflag=fullblock
|
||||
is not, and if counting or skipping bytes or using direct I/O.
|
||||
This helps to avoid confusion with miscounts, and to avoid issues
|
||||
with direct I/O on GNU/Linux. */
|
||||
warn_partial_read =
|
||||
(! (conversions_mask & C_TWOBUFS) && ! (input_flags & O_FULLBLOCK)
|
||||
&& (skip_records
|
||||
|| (0 < max_records && max_records < (uintmax_t) -1)
|
||||
|| (input_flags | output_flags) & O_DIRECT));
|
||||
|
||||
iread_fnc = ((input_flags & O_FULLBLOCK)
|
||||
? iread_fullblock
|
||||
: iread);
|
||||
@@ -1103,6 +1237,20 @@ scanargs (int argc, char *const *argv)
|
||||
error (EXIT_FAILURE, 0, _("cannot combine lcase and ucase"));
|
||||
if (multiple_bits_set (conversions_mask & (C_EXCL | C_NOCREAT)))
|
||||
error (EXIT_FAILURE, 0, _("cannot combine excl and nocreat"));
|
||||
if (multiple_bits_set (input_flags & (O_DIRECT | O_NOCACHE))
|
||||
|| multiple_bits_set (output_flags & (O_DIRECT | O_NOCACHE)))
|
||||
error (EXIT_FAILURE, 0, _("cannot combine direct and nocache"));
|
||||
|
||||
if (input_flags & O_NOCACHE)
|
||||
{
|
||||
i_nocache = true;
|
||||
input_flags &= ~O_NOCACHE;
|
||||
}
|
||||
if (output_flags & O_NOCACHE)
|
||||
{
|
||||
o_nocache = true;
|
||||
output_flags &= ~O_NOCACHE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix up translation table. */
|
||||
@@ -1639,7 +1787,7 @@ dd_copy (void)
|
||||
There are 3 reasons why there might be unskipped blocks/bytes:
|
||||
1. file is too small
|
||||
2. pipe has not enough data
|
||||
3. short reads */
|
||||
3. partial reads */
|
||||
if (us_blocks || (!input_offset_overflow && us_bytes))
|
||||
{
|
||||
error (0, 0,
|
||||
@@ -1685,6 +1833,9 @@ dd_copy (void)
|
||||
|
||||
nread = iread_fnc (STDIN_FILENO, ibuf, input_blocksize);
|
||||
|
||||
if (nread >= 0 && i_nocache)
|
||||
invalidate_cache (STDIN_FILENO, nread);
|
||||
|
||||
if (nread == 0)
|
||||
break; /* EOF. */
|
||||
|
||||
@@ -1694,8 +1845,14 @@ dd_copy (void)
|
||||
if (conversions_mask & C_NOERROR)
|
||||
{
|
||||
print_stats ();
|
||||
size_t bad_portion = input_blocksize - partread;
|
||||
|
||||
/* We already know this data is not cached,
|
||||
but call this so that correct offsets are maintained. */
|
||||
invalidate_cache (STDIN_FILENO, bad_portion);
|
||||
|
||||
/* Seek past the bad block if possible. */
|
||||
if (!advance_input_after_read_error (input_blocksize - partread))
|
||||
if (!advance_input_after_read_error (bad_portion))
|
||||
{
|
||||
exit_status = EXIT_FAILURE;
|
||||
|
||||
@@ -1950,5 +2107,31 @@ main (int argc, char **argv)
|
||||
|
||||
exit_status = dd_copy ();
|
||||
|
||||
if (max_records == 0)
|
||||
{
|
||||
/* Special case to invalidate cache to end of file. */
|
||||
if (i_nocache && !invalidate_cache (STDIN_FILENO, 0))
|
||||
{
|
||||
error (0, errno, _("failed to discard cache for: %s"),
|
||||
quote (input_file));
|
||||
exit_status = EXIT_FAILURE;
|
||||
}
|
||||
if (o_nocache && !invalidate_cache (STDOUT_FILENO, 0))
|
||||
{
|
||||
error (0, errno, _("failed to discard cache for: %s"),
|
||||
quote (output_file));
|
||||
exit_status = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (max_records != (uintmax_t) -1)
|
||||
{
|
||||
/* Invalidate any pending region less that page size,
|
||||
in case the kernel might round up. */
|
||||
if (i_nocache)
|
||||
invalidate_cache (STDIN_FILENO, 0);
|
||||
if (o_nocache)
|
||||
invalidate_cache (STDOUT_FILENO, 0);
|
||||
}
|
||||
|
||||
quit (exit_status);
|
||||
}
|
||||
|
||||
470
src/df.c
470
src/df.c
@@ -22,11 +22,14 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "system.h"
|
||||
#include "error.h"
|
||||
#include "fsusage.h"
|
||||
#include "human.h"
|
||||
#include "mbsalign.h"
|
||||
#include "mbswidth.h"
|
||||
#include "mountlist.h"
|
||||
#include "quote.h"
|
||||
#include "find-mount-point.h"
|
||||
@@ -112,6 +115,52 @@ static bool print_grand_total;
|
||||
/* Grand total data. */
|
||||
static struct fs_usage grand_fsu;
|
||||
|
||||
/* Display modes. */
|
||||
enum { DEFAULT_MODE, INODES_MODE, HUMAN_MODE, POSIX_MODE, NMODES };
|
||||
static int header_mode = DEFAULT_MODE;
|
||||
|
||||
/* Displayable fields. */
|
||||
enum
|
||||
{
|
||||
DEV_FIELD, /* file system */
|
||||
TYPE_FIELD, /* FS type */
|
||||
TOTAL_FIELD, /* blocks or inodes */
|
||||
USED_FIELD, /* ditto */
|
||||
FREE_FIELD, /* ditto */
|
||||
PCENT_FIELD, /* percent used */
|
||||
MNT_FIELD, /* mount point */
|
||||
NFIELDS
|
||||
};
|
||||
|
||||
/* Header strings for the above fields in each mode.
|
||||
NULL means to use the header for the default mode. */
|
||||
static const char *headers[NFIELDS][NMODES] = {
|
||||
/* DEFAULT_MODE INODES_MODE HUMAN_MODE POSIX_MODE */
|
||||
{ N_("Filesystem"), NULL, NULL, NULL },
|
||||
{ N_("Type"), NULL, NULL, NULL },
|
||||
{ N_("blocks"), N_("Inodes"), N_("Size"), NULL },
|
||||
{ N_("Used"), N_("IUsed"), NULL, NULL },
|
||||
{ N_("Available"), N_("IFree"), N_("Avail"), NULL },
|
||||
{ N_("Use%"), N_("IUse%"), NULL, N_("Capacity") },
|
||||
{ N_("Mounted on"), NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Alignments for the 3 textual and 4 numeric fields. */
|
||||
static mbs_align_t alignments[NFIELDS] = {
|
||||
MBS_ALIGN_LEFT, MBS_ALIGN_LEFT,
|
||||
MBS_ALIGN_RIGHT, MBS_ALIGN_RIGHT, MBS_ALIGN_RIGHT, MBS_ALIGN_RIGHT,
|
||||
MBS_ALIGN_LEFT
|
||||
};
|
||||
|
||||
/* Auto adjusted (up) widths used to align columns. */
|
||||
static size_t widths[NFIELDS] = { 14, 4, 5, 5, 5, 4, 0 };
|
||||
|
||||
/* Storage for pointers for each string (cell of table). */
|
||||
static char ***table;
|
||||
|
||||
/* The current number of processed rows (including header). */
|
||||
static size_t nrows;
|
||||
|
||||
/* For long options that have no equivalent short option, use a
|
||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
||||
enum
|
||||
@@ -141,70 +190,133 @@ static struct option const long_options[] =
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* Dynamically allocate a row of pointers in TABLE, which
|
||||
can then be accessed with standard 2D array notation. */
|
||||
|
||||
static void
|
||||
print_header (void)
|
||||
alloc_table_row (void)
|
||||
{
|
||||
char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
|
||||
nrows++;
|
||||
table = xnrealloc (table, nrows, sizeof (char *));
|
||||
table[nrows-1] = xnmalloc (NFIELDS, sizeof (char *));
|
||||
}
|
||||
|
||||
if (print_type)
|
||||
/* TRANSLATORS:
|
||||
For best results (df header/column alignment), ensure that
|
||||
your translation has the same length as the original. */
|
||||
fputs (_("Filesystem Type"), stdout);
|
||||
else
|
||||
fputs (_("Filesystem "), stdout);
|
||||
/* Output each cell in the table, accounting for the
|
||||
alignment and max width of each column. */
|
||||
|
||||
if (inode_format)
|
||||
/* TRANSLATORS:
|
||||
For best results (df header/column alignment), ensure that
|
||||
your translation has the same length as the original.
|
||||
Also, each column name translation should end at the same
|
||||
column as the corresponding original. */
|
||||
fputs (_(" Inodes IUsed IFree IUse%"), stdout);
|
||||
else if (human_output_opts & human_autoscale)
|
||||
static void
|
||||
print_table (void)
|
||||
{
|
||||
size_t field, row;
|
||||
|
||||
for (row = 0; row < nrows; row ++)
|
||||
{
|
||||
if (human_output_opts & human_base_1024)
|
||||
fputs (_(" Size Used Avail Use%"), stdout);
|
||||
else
|
||||
fputs (_(" Size Used Avail Use%"), stdout);
|
||||
}
|
||||
else if (posix_format)
|
||||
printf (_(" %s-blocks Used Available Capacity"),
|
||||
umaxtostr (output_block_size, buf));
|
||||
else
|
||||
{
|
||||
int opts = (human_suppress_point_zero
|
||||
| human_autoscale | human_SI
|
||||
| (human_output_opts
|
||||
& (human_group_digits | human_base_1024 | human_B)));
|
||||
|
||||
/* Prefer the base that makes the human-readable value more exact,
|
||||
if there is a difference. */
|
||||
|
||||
uintmax_t q1000 = output_block_size;
|
||||
uintmax_t q1024 = output_block_size;
|
||||
bool divisible_by_1000;
|
||||
bool divisible_by_1024;
|
||||
|
||||
do
|
||||
for (field = 0; field < NFIELDS; field++)
|
||||
{
|
||||
divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
|
||||
divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
|
||||
size_t width = widths[field];
|
||||
char *cell = table[row][field];
|
||||
if (!cell)
|
||||
continue;
|
||||
|
||||
/* Note the DEV_FIELD used to be displayed on it's own line
|
||||
if (!posix_format && mbswidth (cell) > 20), but that
|
||||
functionality is probably more problematic than helpful. */
|
||||
if (field != 0)
|
||||
putchar (' ');
|
||||
if (field == MNT_FIELD) /* The last one. */
|
||||
fputs (cell, stdout);
|
||||
else
|
||||
{
|
||||
cell = ambsalign (table[row][field], &width,
|
||||
alignments[field], MBA_UNIBYTE_FALLBACK);
|
||||
fputs (cell, stdout);
|
||||
free (cell);
|
||||
}
|
||||
IF_LINT (free (table[row][field]));
|
||||
}
|
||||
while (divisible_by_1000 & divisible_by_1024);
|
||||
|
||||
if (divisible_by_1000 < divisible_by_1024)
|
||||
opts |= human_base_1024;
|
||||
if (divisible_by_1024 < divisible_by_1000)
|
||||
opts &= ~human_base_1024;
|
||||
if (! (opts & human_base_1024))
|
||||
opts |= human_B;
|
||||
|
||||
printf (_(" %4s-blocks Used Available Use%%"),
|
||||
human_readable (output_block_size, buf, opts, 1, 1));
|
||||
putchar ('\n');
|
||||
IF_LINT (free (table[row]));
|
||||
}
|
||||
|
||||
fputs (_(" Mounted on\n"), stdout);
|
||||
IF_LINT (free (table));
|
||||
}
|
||||
|
||||
/* Optain the appropriate header entries. */
|
||||
|
||||
static void
|
||||
get_header (void)
|
||||
{
|
||||
size_t field;
|
||||
|
||||
alloc_table_row ();
|
||||
|
||||
for (field = 0; field < NFIELDS; field++)
|
||||
{
|
||||
if (field == TYPE_FIELD && !print_type)
|
||||
{
|
||||
table[nrows-1][field] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
char *cell = NULL;
|
||||
char const *header = _(headers[field][header_mode]);
|
||||
if (!header)
|
||||
header = _(headers[field][DEFAULT_MODE]);
|
||||
|
||||
if (header_mode == DEFAULT_MODE && field == TOTAL_FIELD)
|
||||
{
|
||||
char buf[LONGEST_HUMAN_READABLE + 1];
|
||||
|
||||
int opts = (human_suppress_point_zero
|
||||
| human_autoscale | human_SI
|
||||
| (human_output_opts
|
||||
& (human_group_digits | human_base_1024 | human_B)));
|
||||
|
||||
/* Prefer the base that makes the human-readable value more exact,
|
||||
if there is a difference. */
|
||||
|
||||
uintmax_t q1000 = output_block_size;
|
||||
uintmax_t q1024 = output_block_size;
|
||||
bool divisible_by_1000;
|
||||
bool divisible_by_1024;
|
||||
|
||||
do
|
||||
{
|
||||
divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
|
||||
divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
|
||||
}
|
||||
while (divisible_by_1000 & divisible_by_1024);
|
||||
|
||||
if (divisible_by_1000 < divisible_by_1024)
|
||||
opts |= human_base_1024;
|
||||
if (divisible_by_1024 < divisible_by_1000)
|
||||
opts &= ~human_base_1024;
|
||||
if (! (opts & human_base_1024))
|
||||
opts |= human_B;
|
||||
|
||||
char *num = human_readable (output_block_size, buf, opts, 1, 1);
|
||||
|
||||
if (asprintf (&cell, "%s-%s", num, header) == -1)
|
||||
cell = NULL;
|
||||
}
|
||||
else if (header_mode == POSIX_MODE && field == TOTAL_FIELD)
|
||||
{
|
||||
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
|
||||
char *num = umaxtostr (output_block_size, buf);
|
||||
|
||||
if (asprintf (&cell, "%s-%s", num, header) == -1)
|
||||
cell = NULL;
|
||||
}
|
||||
else
|
||||
cell = strdup (header);
|
||||
|
||||
if (!cell)
|
||||
xalloc_die ();
|
||||
|
||||
table[nrows-1][field] = cell;
|
||||
|
||||
widths[field] = MAX (widths[field], mbswidth (cell, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/* Is FSTYPE a type of file system that should be listed? */
|
||||
@@ -305,7 +417,7 @@ add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg,
|
||||
*dest = -*dest;
|
||||
}
|
||||
|
||||
/* Display a space listing for the disk device with absolute file name DISK.
|
||||
/* Optain a space listing for the disk device with absolute file name DISK.
|
||||
If MOUNT_POINT is non-NULL, it is the name of the root of the
|
||||
file system on DISK.
|
||||
If STAT_FILE is non-null, it is the name of a file within the file
|
||||
@@ -319,16 +431,13 @@ add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg,
|
||||
ME_DUMMY and ME_REMOTE are the mount entry flags. */
|
||||
|
||||
static void
|
||||
show_dev (char const *disk, char const *mount_point,
|
||||
char const *stat_file, char const *fstype,
|
||||
bool me_dummy, bool me_remote,
|
||||
const struct fs_usage *force_fsu)
|
||||
get_dev (char const *disk, char const *mount_point,
|
||||
char const *stat_file, char const *fstype,
|
||||
bool me_dummy, bool me_remote,
|
||||
const struct fs_usage *force_fsu)
|
||||
{
|
||||
struct fs_usage fsu;
|
||||
char buf[3][LONGEST_HUMAN_READABLE + 2];
|
||||
int width;
|
||||
int col1_adjustment = 0;
|
||||
int use_width;
|
||||
char buf[LONGEST_HUMAN_READABLE + 2];
|
||||
uintmax_t input_units;
|
||||
uintmax_t output_units;
|
||||
uintmax_t total;
|
||||
@@ -338,6 +447,8 @@ show_dev (char const *disk, char const *mount_point,
|
||||
uintmax_t used;
|
||||
bool negate_used;
|
||||
double pct = -1;
|
||||
char* cell;
|
||||
size_t field;
|
||||
|
||||
if (me_remote && show_local_fs)
|
||||
return;
|
||||
@@ -370,39 +481,18 @@ show_dev (char const *disk, char const *mount_point,
|
||||
if (! file_systems_processed)
|
||||
{
|
||||
file_systems_processed = true;
|
||||
print_header ();
|
||||
get_header ();
|
||||
}
|
||||
|
||||
alloc_table_row ();
|
||||
|
||||
if (! disk)
|
||||
disk = "-"; /* unknown */
|
||||
if (! fstype)
|
||||
fstype = "-"; /* unknown */
|
||||
|
||||
/* df.c reserved 5 positions for fstype,
|
||||
but that does not suffice for type iso9660 */
|
||||
if (print_type)
|
||||
{
|
||||
size_t disk_name_len = strlen (disk);
|
||||
size_t fstype_len = strlen (fstype);
|
||||
if (disk_name_len + fstype_len < 18)
|
||||
printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype);
|
||||
else if (!posix_format)
|
||||
printf ("%s\n%18s ", disk, fstype);
|
||||
else
|
||||
printf ("%s %s", disk, fstype);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen (disk) > 20 && !posix_format)
|
||||
printf ("%s\n%20s", disk, "");
|
||||
else
|
||||
printf ("%-20s", disk);
|
||||
}
|
||||
|
||||
if (inode_format)
|
||||
{
|
||||
width = 7;
|
||||
use_width = 5;
|
||||
input_units = output_units = 1;
|
||||
total = fsu.fsu_files;
|
||||
available = fsu.fsu_ffree;
|
||||
@@ -416,22 +506,6 @@ show_dev (char const *disk, char const *mount_point,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (human_output_opts & human_autoscale)
|
||||
width = 5 + ! (human_output_opts & human_base_1024);
|
||||
else
|
||||
{
|
||||
width = 9;
|
||||
if (posix_format)
|
||||
{
|
||||
uintmax_t b;
|
||||
col1_adjustment = -3;
|
||||
for (b = output_block_size; 9 < b; b /= 10)
|
||||
col1_adjustment++;
|
||||
}
|
||||
}
|
||||
use_width = ((posix_format
|
||||
&& ! (human_output_opts & human_autoscale))
|
||||
? 8 : 4);
|
||||
input_units = fsu.fsu_blocksize;
|
||||
output_units = output_block_size;
|
||||
total = fsu.fsu_blocks;
|
||||
@@ -458,73 +532,110 @@ show_dev (char const *disk, char const *mount_point,
|
||||
negate_used = (total < available_to_root);
|
||||
}
|
||||
|
||||
printf (" %*s %*s %*s ",
|
||||
width + col1_adjustment,
|
||||
df_readable (false, total,
|
||||
buf[0], input_units, output_units),
|
||||
width, df_readable (negate_used, used,
|
||||
buf[1], input_units, output_units),
|
||||
width, df_readable (negate_available, available,
|
||||
buf[2], input_units, output_units));
|
||||
|
||||
if (! known_value (used) || ! known_value (available))
|
||||
;
|
||||
else if (!negate_used
|
||||
&& used <= TYPE_MAXIMUM (uintmax_t) / 100
|
||||
&& used + available != 0
|
||||
&& (used + available < used) == negate_available)
|
||||
for (field = 0; field < NFIELDS; field++)
|
||||
{
|
||||
uintmax_t u100 = used * 100;
|
||||
uintmax_t nonroot_total = used + available;
|
||||
pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The calculation cannot be done easily with integer
|
||||
arithmetic. Fall back on floating point. This can suffer
|
||||
from minor rounding errors, but doing it exactly requires
|
||||
multiple precision arithmetic, and it's not worth the
|
||||
aggravation. */
|
||||
double u = negate_used ? - (double) - used : used;
|
||||
double a = negate_available ? - (double) - available : available;
|
||||
double nonroot_total = u + a;
|
||||
if (nonroot_total)
|
||||
switch (field)
|
||||
{
|
||||
long int lipct = pct = u * 100 / nonroot_total;
|
||||
double ipct = lipct;
|
||||
case DEV_FIELD:
|
||||
cell = xstrdup (disk);
|
||||
break;
|
||||
|
||||
/* Like `pct = ceil (dpct);', but avoid ceil so that
|
||||
the math library needn't be linked. */
|
||||
if (ipct - 1 < pct && pct <= ipct + 1)
|
||||
pct = ipct + (ipct < pct);
|
||||
}
|
||||
}
|
||||
case TYPE_FIELD:
|
||||
cell = print_type ? xstrdup (fstype) : NULL;
|
||||
break;
|
||||
|
||||
if (0 <= pct)
|
||||
printf ("%*.0f%%", use_width - 1, pct);
|
||||
else
|
||||
printf ("%*s", use_width, "- ");
|
||||
case TOTAL_FIELD:
|
||||
cell = xstrdup (df_readable (false, total, buf,
|
||||
input_units, output_units));
|
||||
break;
|
||||
case USED_FIELD:
|
||||
cell = xstrdup (df_readable (negate_used, used, buf,
|
||||
input_units, output_units));
|
||||
break;
|
||||
case FREE_FIELD:
|
||||
cell = xstrdup (df_readable (negate_available, available, buf,
|
||||
input_units, output_units));
|
||||
break;
|
||||
|
||||
if (mount_point)
|
||||
{
|
||||
case PCENT_FIELD:
|
||||
if (! known_value (used) || ! known_value (available))
|
||||
;
|
||||
else if (!negate_used
|
||||
&& used <= TYPE_MAXIMUM (uintmax_t) / 100
|
||||
&& used + available != 0
|
||||
&& (used + available < used) == negate_available)
|
||||
{
|
||||
uintmax_t u100 = used * 100;
|
||||
uintmax_t nonroot_total = used + available;
|
||||
pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The calculation cannot be done easily with integer
|
||||
arithmetic. Fall back on floating point. This can suffer
|
||||
from minor rounding errors, but doing it exactly requires
|
||||
multiple precision arithmetic, and it's not worth the
|
||||
aggravation. */
|
||||
double u = negate_used ? - (double) - used : used;
|
||||
double a = negate_available ? - (double) - available : available;
|
||||
double nonroot_total = u + a;
|
||||
if (nonroot_total)
|
||||
{
|
||||
long int lipct = pct = u * 100 / nonroot_total;
|
||||
double ipct = lipct;
|
||||
|
||||
/* Like `pct = ceil (dpct);', but avoid ceil so that
|
||||
the math library needn't be linked. */
|
||||
if (ipct - 1 < pct && pct <= ipct + 1)
|
||||
pct = ipct + (ipct < pct);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 <= pct)
|
||||
{
|
||||
if (asprintf (&cell, "%.0f%%", pct) == -1)
|
||||
cell = NULL;
|
||||
}
|
||||
else
|
||||
cell = strdup ("-");
|
||||
|
||||
if (!cell)
|
||||
xalloc_die ();
|
||||
|
||||
break;
|
||||
|
||||
case MNT_FIELD:
|
||||
if (mount_point)
|
||||
{
|
||||
#ifdef HIDE_AUTOMOUNT_PREFIX
|
||||
/* Don't print the first directory name in MOUNT_POINT if it's an
|
||||
artifact of an automounter. This is a bit too aggressive to be
|
||||
the default. */
|
||||
if (strncmp ("/auto/", mount_point, 6) == 0)
|
||||
mount_point += 5;
|
||||
else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
|
||||
mount_point += 8;
|
||||
/* Don't print the first directory name in MOUNT_POINT if it's an
|
||||
artifact of an automounter. This is a bit too aggressive to be
|
||||
the default. */
|
||||
if (STRNCMP_LIT (mount_point, "/auto/") == 0)
|
||||
mount_point += 5;
|
||||
else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0)
|
||||
mount_point += 8;
|
||||
#endif
|
||||
printf (" %s", mount_point);
|
||||
cell = xstrdup (mount_point);
|
||||
}
|
||||
else
|
||||
cell = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (!"unhandled field");
|
||||
}
|
||||
|
||||
if (cell)
|
||||
widths[field] = MAX (widths[field], mbswidth (cell, 0));
|
||||
table[nrows-1][field] = cell;
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
/* If DISK corresponds to a mount point, show its usage
|
||||
and return true. Otherwise, return false. */
|
||||
static bool
|
||||
show_disk (char const *disk)
|
||||
get_disk (char const *disk)
|
||||
{
|
||||
struct mount_entry const *me;
|
||||
struct mount_entry const *best_match = NULL;
|
||||
@@ -535,9 +646,9 @@ show_disk (char const *disk)
|
||||
|
||||
if (best_match)
|
||||
{
|
||||
show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
|
||||
best_match->me_type, best_match->me_dummy,
|
||||
best_match->me_remote, NULL);
|
||||
get_dev (best_match->me_devname, best_match->me_mountdir, NULL,
|
||||
best_match->me_type, best_match->me_dummy,
|
||||
best_match->me_remote, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -548,7 +659,7 @@ show_disk (char const *disk)
|
||||
and show its disk usage.
|
||||
STATP must be the result of `stat (POINT, STATP)'. */
|
||||
static void
|
||||
show_point (const char *point, const struct stat *statp)
|
||||
get_point (const char *point, const struct stat *statp)
|
||||
{
|
||||
struct stat disk_stats;
|
||||
struct mount_entry *me;
|
||||
@@ -571,7 +682,7 @@ show_point (const char *point, const struct stat *statp)
|
||||
if (best_match_len <= len && len <= resolved_len
|
||||
&& (len == 1 /* root file system */
|
||||
|| ((len == resolved_len || resolved[len] == '/')
|
||||
&& strncmp (me->me_mountdir, resolved, len) == 0)))
|
||||
&& STREQ_LEN (me->me_mountdir, resolved, len))))
|
||||
{
|
||||
best_match = me;
|
||||
best_match_len = len;
|
||||
@@ -621,9 +732,9 @@ show_point (const char *point, const struct stat *statp)
|
||||
}
|
||||
|
||||
if (best_match)
|
||||
show_dev (best_match->me_devname, best_match->me_mountdir, point,
|
||||
best_match->me_type, best_match->me_dummy, best_match->me_remote,
|
||||
NULL);
|
||||
get_dev (best_match->me_devname, best_match->me_mountdir, point,
|
||||
best_match->me_type, best_match->me_dummy, best_match->me_remote,
|
||||
NULL);
|
||||
else
|
||||
{
|
||||
/* We couldn't find the mount entry corresponding to POINT. Go ahead and
|
||||
@@ -634,7 +745,7 @@ show_point (const char *point, const struct stat *statp)
|
||||
char *mp = find_mount_point (point, statp);
|
||||
if (mp)
|
||||
{
|
||||
show_dev (NULL, mp, NULL, NULL, false, false, NULL);
|
||||
get_dev (NULL, mp, NULL, NULL, false, false, NULL);
|
||||
free (mp);
|
||||
}
|
||||
}
|
||||
@@ -644,25 +755,25 @@ show_point (const char *point, const struct stat *statp)
|
||||
for it. STATP is the results of `stat' on NAME. */
|
||||
|
||||
static void
|
||||
show_entry (char const *name, struct stat const *statp)
|
||||
get_entry (char const *name, struct stat const *statp)
|
||||
{
|
||||
if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
|
||||
&& show_disk (name))
|
||||
&& get_disk (name))
|
||||
return;
|
||||
|
||||
show_point (name, statp);
|
||||
get_point (name, statp);
|
||||
}
|
||||
|
||||
/* Show all mounted file systems, except perhaps those that are of
|
||||
an unselected type or are empty. */
|
||||
|
||||
static void
|
||||
show_all_entries (void)
|
||||
get_all_entries (void)
|
||||
{
|
||||
struct mount_entry *me;
|
||||
|
||||
for (me = mount_list; me; me = me->me_next)
|
||||
show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
|
||||
get_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
|
||||
me->me_dummy, me->me_remote, NULL);
|
||||
}
|
||||
|
||||
@@ -862,6 +973,13 @@ main (int argc, char **argv)
|
||||
&human_output_opts, &output_block_size);
|
||||
}
|
||||
|
||||
if (inode_format)
|
||||
header_mode = INODES_MODE;
|
||||
else if (human_output_opts & human_autoscale)
|
||||
header_mode = HUMAN_MODE;
|
||||
else if (posix_format)
|
||||
header_mode = POSIX_MODE;
|
||||
|
||||
/* Fail if the same file system type was both selected and excluded. */
|
||||
{
|
||||
bool match = false;
|
||||
@@ -939,18 +1057,20 @@ main (int argc, char **argv)
|
||||
|
||||
for (i = optind; i < argc; ++i)
|
||||
if (argv[i])
|
||||
show_entry (argv[i], &stats[i - optind]);
|
||||
get_entry (argv[i], &stats[i - optind]);
|
||||
}
|
||||
else
|
||||
show_all_entries ();
|
||||
get_all_entries ();
|
||||
|
||||
if (print_grand_total)
|
||||
{
|
||||
if (inode_format)
|
||||
grand_fsu.fsu_blocks = 1;
|
||||
show_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu);
|
||||
get_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu);
|
||||
}
|
||||
|
||||
print_table ();
|
||||
|
||||
if (! file_systems_processed)
|
||||
error (EXIT_FAILURE, 0, _("no file systems processed"));
|
||||
|
||||
|
||||
15
src/du.c
15
src/du.c
@@ -926,19 +926,19 @@ main (int argc, char **argv)
|
||||
bool skip_file = false;
|
||||
enum argv_iter_err ai_err;
|
||||
char *file_name = argv_iter (ai, &ai_err);
|
||||
if (ai_err == AI_ERR_EOF)
|
||||
break;
|
||||
if (!file_name)
|
||||
{
|
||||
switch (ai_err)
|
||||
{
|
||||
case AI_ERR_EOF:
|
||||
goto argv_iter_done;
|
||||
case AI_ERR_READ:
|
||||
error (0, errno, _("%s: read error"), quote (files_from));
|
||||
continue;
|
||||
|
||||
error (0, errno, _("%s: read error"),
|
||||
quotearg_colon (files_from));
|
||||
ok = false;
|
||||
goto argv_iter_done;
|
||||
case AI_ERR_MEM:
|
||||
xalloc_die ();
|
||||
|
||||
default:
|
||||
assert (!"unexpected error code from argv_iter");
|
||||
}
|
||||
@@ -985,11 +985,12 @@ main (int argc, char **argv)
|
||||
ok &= du_files (temp_argv, bit_flags);
|
||||
}
|
||||
}
|
||||
argv_iter_done:
|
||||
|
||||
argv_iter_free (ai);
|
||||
di_set_free (di_set);
|
||||
|
||||
if (files_from && (ferror (stdin) || fclose (stdin) != 0))
|
||||
if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok)
|
||||
error (EXIT_FAILURE, 0, _("error reading %s"), quote (files_from));
|
||||
|
||||
if (print_grand_total)
|
||||
|
||||
@@ -17,18 +17,48 @@
|
||||
Written by Jie Liu (jeff.liu@oracle.com). */
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "system.h"
|
||||
#include "extent-scan.h"
|
||||
#include "xstrtol.h"
|
||||
|
||||
#ifndef HAVE_FIEMAP
|
||||
# include "fiemap.h"
|
||||
#endif
|
||||
|
||||
/* Work around Linux kernel issues on BTRFS and EXT4 before 2.6.39.
|
||||
FIXME: remove in 2013, or whenever we're pretty confident
|
||||
that the offending, unpatched kernels are no longer in use. */
|
||||
static bool
|
||||
extent_need_sync (void)
|
||||
{
|
||||
static int need_sync = -1;
|
||||
|
||||
if (need_sync == -1)
|
||||
{
|
||||
struct utsname name;
|
||||
need_sync = 0; /* No workaround by default. */
|
||||
|
||||
#ifdef __linux__
|
||||
if (uname (&name) != -1 && STRNCMP_LIT (name.release, "2.6.") == 0)
|
||||
{
|
||||
unsigned long val;
|
||||
if (xstrtoul (name.release + 4, NULL, 10, &val, NULL) == LONGINT_OK)
|
||||
{
|
||||
if (val < 39)
|
||||
need_sync = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return need_sync;
|
||||
}
|
||||
|
||||
/* Allocate space for struct extent_scan, initialize the entries if
|
||||
necessary and return it as the input argument of extent_scan_read(). */
|
||||
extern void
|
||||
@@ -36,9 +66,11 @@ extent_scan_init (int src_fd, struct extent_scan *scan)
|
||||
{
|
||||
scan->fd = src_fd;
|
||||
scan->ei_count = 0;
|
||||
scan->ext_info = NULL;
|
||||
scan->scan_start = 0;
|
||||
scan->initial_scan_failed = false;
|
||||
scan->hit_final_extent = false;
|
||||
scan->fm_flags = extent_need_sync () ? FIEMAP_FLAG_SYNC : 0;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -50,60 +82,126 @@ extent_scan_init (int src_fd, struct extent_scan *scan)
|
||||
extern bool
|
||||
extent_scan_read (struct extent_scan *scan)
|
||||
{
|
||||
union { struct fiemap f; char c[4096]; } fiemap_buf;
|
||||
struct fiemap *fiemap = &fiemap_buf.f;
|
||||
struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
|
||||
enum { count = (sizeof fiemap_buf - sizeof *fiemap) / sizeof *fm_extents };
|
||||
verify (count != 0);
|
||||
unsigned int si = 0;
|
||||
struct extent_info *last_ei IF_LINT ( = scan->ext_info);
|
||||
|
||||
/* This is required at least to initialize fiemap->fm_start,
|
||||
but also serves (in mid 2010) to appease valgrind, which
|
||||
appears not to know the semantics of the FIEMAP ioctl. */
|
||||
memset (&fiemap_buf, 0, sizeof fiemap_buf);
|
||||
|
||||
fiemap->fm_start = scan->scan_start;
|
||||
fiemap->fm_flags = FIEMAP_FLAG_SYNC;
|
||||
fiemap->fm_extent_count = count;
|
||||
fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
|
||||
|
||||
/* Fall back to the standard copy if call ioctl(2) failed for the
|
||||
the first time. */
|
||||
if (ioctl (scan->fd, FS_IOC_FIEMAP, fiemap) < 0)
|
||||
while (true)
|
||||
{
|
||||
if (scan->scan_start == 0)
|
||||
scan->initial_scan_failed = true;
|
||||
return false;
|
||||
union { struct fiemap f; char c[4096]; } fiemap_buf;
|
||||
struct fiemap *fiemap = &fiemap_buf.f;
|
||||
struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
|
||||
enum { count = (sizeof fiemap_buf - sizeof *fiemap)/sizeof *fm_extents };
|
||||
verify (count > 1);
|
||||
|
||||
/* This is required at least to initialize fiemap->fm_start,
|
||||
but also serves (in mid 2010) to appease valgrind, which
|
||||
appears not to know the semantics of the FIEMAP ioctl. */
|
||||
memset (&fiemap_buf, 0, sizeof fiemap_buf);
|
||||
|
||||
fiemap->fm_start = scan->scan_start;
|
||||
fiemap->fm_flags = scan->fm_flags;
|
||||
fiemap->fm_extent_count = count;
|
||||
fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
|
||||
|
||||
/* Fall back to the standard copy if call ioctl(2) failed for
|
||||
the first time. */
|
||||
if (ioctl (scan->fd, FS_IOC_FIEMAP, fiemap) < 0)
|
||||
{
|
||||
if (scan->scan_start == 0)
|
||||
scan->initial_scan_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If 0 extents are returned, then no more scans are needed. */
|
||||
if (fiemap->fm_mapped_extents == 0)
|
||||
{
|
||||
scan->hit_final_extent = true;
|
||||
return scan->scan_start != 0;
|
||||
}
|
||||
|
||||
assert (scan->ei_count <= SIZE_MAX - fiemap->fm_mapped_extents);
|
||||
scan->ei_count += fiemap->fm_mapped_extents;
|
||||
scan->ext_info = xnrealloc (scan->ext_info, scan->ei_count,
|
||||
sizeof (struct extent_info));
|
||||
|
||||
unsigned int i = 0;
|
||||
for (i = 0; i < fiemap->fm_mapped_extents; i++)
|
||||
{
|
||||
assert (fm_extents[i].fe_logical <=
|
||||
OFF_T_MAX - fm_extents[i].fe_length);
|
||||
|
||||
if (si && last_ei->ext_flags ==
|
||||
(fm_extents[i].fe_flags & ~FIEMAP_EXTENT_LAST)
|
||||
&& (last_ei->ext_logical + last_ei->ext_length
|
||||
== fm_extents[i].fe_logical))
|
||||
{
|
||||
/* Merge previous with last. */
|
||||
last_ei->ext_length += fm_extents[i].fe_length;
|
||||
/* Copy flags in case different. */
|
||||
last_ei->ext_flags = fm_extents[i].fe_flags;
|
||||
}
|
||||
else if ((si == 0 && scan->scan_start > fm_extents[i].fe_logical)
|
||||
|| (si && last_ei->ext_logical + last_ei->ext_length >
|
||||
fm_extents[i].fe_logical))
|
||||
{
|
||||
/* BTRFS before 2.6.38 could return overlapping extents
|
||||
for sparse files. We adjust the returned extents
|
||||
rather than failing, as otherwise it would be inefficient
|
||||
to detect this on the initial scan. */
|
||||
uint64_t new_logical;
|
||||
uint64_t length_adjust;
|
||||
if (si == 0)
|
||||
new_logical = scan->scan_start;
|
||||
else
|
||||
{
|
||||
/* We could return here if scan->scan_start == 0
|
||||
but don't so as to minimize special cases. */
|
||||
new_logical = last_ei->ext_logical + last_ei->ext_length;
|
||||
}
|
||||
length_adjust = new_logical - fm_extents[i].fe_logical;
|
||||
/* If an extent is contained within the previous one, fail. */
|
||||
if (length_adjust < fm_extents[i].fe_length)
|
||||
{
|
||||
if (scan->scan_start == 0)
|
||||
scan->initial_scan_failed = true;
|
||||
return false;
|
||||
}
|
||||
fm_extents[i].fe_logical = new_logical;
|
||||
fm_extents[i].fe_length -= length_adjust;
|
||||
/* Process the adjusted extent again. */
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_ei = scan->ext_info + si;
|
||||
last_ei->ext_logical = fm_extents[i].fe_logical;
|
||||
last_ei->ext_length = fm_extents[i].fe_length;
|
||||
last_ei->ext_flags = fm_extents[i].fe_flags;
|
||||
si++;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_ei->ext_flags & FIEMAP_EXTENT_LAST)
|
||||
scan->hit_final_extent = true;
|
||||
|
||||
/* If we have enough extents, discard the last as it might
|
||||
be merged with one from the next scan. */
|
||||
if (si > count && !scan->hit_final_extent)
|
||||
last_ei = scan->ext_info + --si - 1;
|
||||
|
||||
/* We don't bother reallocating any trailing slots. */
|
||||
scan->ei_count = si;
|
||||
|
||||
if (scan->hit_final_extent)
|
||||
break;
|
||||
else
|
||||
scan->scan_start = last_ei->ext_logical + last_ei->ext_length;
|
||||
|
||||
if (si >= count)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If 0 extents are returned, then more get_extent_table() are not needed. */
|
||||
if (fiemap->fm_mapped_extents == 0)
|
||||
{
|
||||
scan->hit_final_extent = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
scan->ei_count = fiemap->fm_mapped_extents;
|
||||
scan->ext_info = xnmalloc (scan->ei_count, sizeof (struct extent_info));
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < scan->ei_count; i++)
|
||||
{
|
||||
assert (fm_extents[i].fe_logical <= OFF_T_MAX);
|
||||
|
||||
scan->ext_info[i].ext_logical = fm_extents[i].fe_logical;
|
||||
scan->ext_info[i].ext_length = fm_extents[i].fe_length;
|
||||
scan->ext_info[i].ext_flags = fm_extents[i].fe_flags;
|
||||
}
|
||||
|
||||
i--;
|
||||
if (scan->ext_info[i].ext_flags & FIEMAP_EXTENT_LAST)
|
||||
{
|
||||
scan->hit_final_extent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
scan->scan_start = fm_extents[i].fe_logical + fm_extents[i].fe_length;
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -41,6 +41,9 @@ struct extent_scan
|
||||
/* Next scan start offset. */
|
||||
off_t scan_start;
|
||||
|
||||
/* Flags to use for scan. */
|
||||
uint32_t fm_flags;
|
||||
|
||||
/* How many extent info returned for a scan. */
|
||||
uint32_t ei_count;
|
||||
|
||||
@@ -63,6 +66,8 @@ static inline void
|
||||
extent_scan_free (struct extent_scan *scan)
|
||||
{
|
||||
free (scan->ext_info);
|
||||
scan->ext_info = NULL;
|
||||
scan->ei_count = 0;
|
||||
}
|
||||
|
||||
#endif /* EXTENT_SCAN_H */
|
||||
|
||||
@@ -75,7 +75,7 @@ struct fiemap
|
||||
/* Location still pending, Sets EXTENT_UNKNOWN. */
|
||||
# define FIEMAP_EXTENT_DELALLOC 0x00000004
|
||||
|
||||
/* Data can not be read while fs is unmounted. */
|
||||
/* Data cannot be read while fs is unmounted. */
|
||||
# define FIEMAP_EXTENT_ENCODED 0x00000008
|
||||
|
||||
/* Data is encrypted by fs. Sets EXTENT_NO_BYPASS. */
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
Give a diagnostic and return NULL if unable to determine the mount point.
|
||||
Exit if unable to restore current working directory. */
|
||||
extern char *
|
||||
find_mount_point (const char *file, const struct stat *file_stat)
|
||||
find_mount_point (char const *file, struct stat const *file_stat)
|
||||
{
|
||||
struct saved_cwd cwd;
|
||||
struct stat last_stat;
|
||||
char *mp = NULL; /* The malloced mount point. */
|
||||
char *mp = NULL; /* The malloc'd mount point. */
|
||||
|
||||
if (save_cwd (&cwd) != 0)
|
||||
{
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
extern char* find_mount_point (const char *, const struct stat *);
|
||||
extern char *find_mount_point (char const *, struct stat const *);
|
||||
|
||||
679
src/install.c
679
src/install.c
@@ -68,23 +68,6 @@ static bool use_default_selinux_context = true;
|
||||
# define matchpathcon_init_prefix(a, p) /* empty */
|
||||
#endif
|
||||
|
||||
static bool change_timestamps (struct stat const *from_sb, char const *to);
|
||||
static bool change_attributes (char const *name);
|
||||
static bool copy_file (const char *from, const char *to,
|
||||
const struct cp_options *x);
|
||||
static bool install_file_in_file_parents (char const *from, char *to,
|
||||
struct cp_options *x);
|
||||
static bool install_file_in_dir (const char *from, const char *to_dir,
|
||||
const struct cp_options *x);
|
||||
static bool install_file_in_file (const char *from, const char *to,
|
||||
const struct cp_options *x);
|
||||
static void get_ids (void);
|
||||
static void strip (char const *name);
|
||||
static void announce_mkdir (char const *dir, void *options);
|
||||
static int make_ancestor (char const *dir, char const *component,
|
||||
void *options);
|
||||
void usage (int status);
|
||||
|
||||
/* The user name that will own the files, or NULL to make the owner
|
||||
the current user ID. */
|
||||
static char *owner_name;
|
||||
@@ -131,7 +114,6 @@ static char const *strip_program = "strip";
|
||||
enum
|
||||
{
|
||||
PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1,
|
||||
PRESERVE_CONTEXT_OPTION_DEPRECATED,
|
||||
STRIP_PROGRAM_OPTION
|
||||
};
|
||||
|
||||
@@ -147,9 +129,6 @@ static struct option const long_options[] =
|
||||
{"owner", required_argument, NULL, 'o'},
|
||||
{"preserve-timestamps", no_argument, NULL, 'p'},
|
||||
{"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
|
||||
/* --preserve_context was silently supported until Apr 2009.
|
||||
FIXME: disable altogether in a year or so. */
|
||||
{"preserve_context", no_argument, NULL, PRESERVE_CONTEXT_OPTION_DEPRECATED},
|
||||
{"strip", no_argument, NULL, 's'},
|
||||
{"strip-program", required_argument, NULL, STRIP_PROGRAM_OPTION},
|
||||
{"suffix", required_argument, NULL, 'S'},
|
||||
@@ -407,6 +386,27 @@ target_directory_operand (char const *file)
|
||||
return is_a_dir;
|
||||
}
|
||||
|
||||
/* Report that directory DIR was made, if OPTIONS requests this. */
|
||||
static void
|
||||
announce_mkdir (char const *dir, void *options)
|
||||
{
|
||||
struct cp_options const *x = options;
|
||||
if (x->verbose)
|
||||
prog_fprintf (stdout, _("creating directory %s"), quote (dir));
|
||||
}
|
||||
|
||||
/* Make ancestor directory DIR, whose last file name component is
|
||||
COMPONENT, with options OPTIONS. Assume the working directory is
|
||||
COMPONENT's parent. */
|
||||
static int
|
||||
make_ancestor (char const *dir, char const *component, void *options)
|
||||
{
|
||||
int r = mkdir (component, DEFAULT_MODE);
|
||||
if (r == 0)
|
||||
announce_mkdir (dir, options);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Process a command-line file name, for the -d option. */
|
||||
static int
|
||||
process_dir (char *dir, struct savewd *wd, void *options)
|
||||
@@ -419,6 +419,312 @@ process_dir (char *dir, struct savewd *wd, void *options)
|
||||
: EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Copy file FROM onto file TO, creating TO if necessary.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
copy_file (const char *from, const char *to, const struct cp_options *x)
|
||||
{
|
||||
bool copy_into_self;
|
||||
|
||||
if (copy_only_if_needed && !need_copy (from, to, x))
|
||||
return true;
|
||||
|
||||
/* Allow installing from non-regular files like /dev/null.
|
||||
Charles Karney reported that some Sun version of install allows that
|
||||
and that sendmail's installation process relies on the behavior.
|
||||
However, since !x->recursive, the call to "copy" will fail if FROM
|
||||
is a directory. */
|
||||
|
||||
return copy (from, to, false, x, ©_into_self, NULL);
|
||||
}
|
||||
|
||||
/* Set the attributes of file or directory NAME.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
change_attributes (char const *name)
|
||||
{
|
||||
bool ok = false;
|
||||
/* chown must precede chmod because on some systems,
|
||||
chown clears the set[ug]id bits for non-superusers,
|
||||
resulting in incorrect permissions.
|
||||
On System V, users can give away files with chown and then not
|
||||
be able to chmod them. So don't give files away.
|
||||
|
||||
We don't normally ignore errors from chown because the idea of
|
||||
the install command is that the file is supposed to end up with
|
||||
precisely the attributes that the user specified (or defaulted).
|
||||
If the file doesn't end up with the group they asked for, they'll
|
||||
want to know. */
|
||||
|
||||
if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1)
|
||||
&& lchown (name, owner_id, group_id) != 0)
|
||||
error (0, errno, _("cannot change ownership of %s"), quote (name));
|
||||
else if (chmod (name, mode) != 0)
|
||||
error (0, errno, _("cannot change permissions of %s"), quote (name));
|
||||
else
|
||||
ok = true;
|
||||
|
||||
if (use_default_selinux_context)
|
||||
setdefaultfilecon (name);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Set the timestamps of file DEST to match those of SRC_SB.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
change_timestamps (struct stat const *src_sb, char const *dest)
|
||||
{
|
||||
struct timespec timespec[2];
|
||||
timespec[0] = get_stat_atime (src_sb);
|
||||
timespec[1] = get_stat_mtime (src_sb);
|
||||
|
||||
if (utimens (dest, timespec))
|
||||
{
|
||||
error (0, errno, _("cannot set time stamps for %s"), quote (dest));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Strip the symbol table from the file NAME.
|
||||
We could dig the magic number out of the file first to
|
||||
determine whether to strip it, but the header files and
|
||||
magic numbers vary so much from system to system that making
|
||||
it portable would be very difficult. Not worth the effort. */
|
||||
|
||||
static void
|
||||
strip (char const *name)
|
||||
{
|
||||
int status;
|
||||
pid_t pid = fork ();
|
||||
|
||||
switch (pid)
|
||||
{
|
||||
case -1:
|
||||
error (EXIT_FAILURE, errno, _("fork system call failed"));
|
||||
break;
|
||||
case 0: /* Child. */
|
||||
execlp (strip_program, strip_program, name, NULL);
|
||||
error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program);
|
||||
break;
|
||||
default: /* Parent. */
|
||||
if (waitpid (pid, &status, 0) < 0)
|
||||
error (EXIT_FAILURE, errno, _("waiting for strip"));
|
||||
else if (! WIFEXITED (status) || WEXITSTATUS (status))
|
||||
error (EXIT_FAILURE, 0, _("strip process terminated abnormally"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the user and group ownership of the files to install. */
|
||||
|
||||
static void
|
||||
get_ids (void)
|
||||
{
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
|
||||
if (owner_name)
|
||||
{
|
||||
pw = getpwnam (owner_name);
|
||||
if (pw == NULL)
|
||||
{
|
||||
unsigned long int tmp;
|
||||
if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
|
||||
|| UID_T_MAX < tmp)
|
||||
error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name));
|
||||
owner_id = tmp;
|
||||
}
|
||||
else
|
||||
owner_id = pw->pw_uid;
|
||||
endpwent ();
|
||||
}
|
||||
else
|
||||
owner_id = (uid_t) -1;
|
||||
|
||||
if (group_name)
|
||||
{
|
||||
gr = getgrnam (group_name);
|
||||
if (gr == NULL)
|
||||
{
|
||||
unsigned long int tmp;
|
||||
if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
|
||||
|| GID_T_MAX < tmp)
|
||||
error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name));
|
||||
group_id = tmp;
|
||||
}
|
||||
else
|
||||
group_id = gr->gr_gid;
|
||||
endgrent ();
|
||||
}
|
||||
else
|
||||
group_id = (gid_t) -1;
|
||||
}
|
||||
|
||||
void
|
||||
usage (int status)
|
||||
{
|
||||
if (status != EXIT_SUCCESS)
|
||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
||||
program_name);
|
||||
else
|
||||
{
|
||||
printf (_("\
|
||||
Usage: %s [OPTION]... [-T] SOURCE DEST\n\
|
||||
or: %s [OPTION]... SOURCE... DIRECTORY\n\
|
||||
or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
|
||||
or: %s [OPTION]... -d DIRECTORY...\n\
|
||||
"),
|
||||
program_name, program_name, program_name, program_name);
|
||||
fputs (_("\
|
||||
\n\
|
||||
This install program copies files (often just compiled) into destination\n\
|
||||
locations you choose. If you want to download and install a ready-to-use\n\
|
||||
package on a GNU/Linux system, you should instead be using a package manager\n\
|
||||
like yum(1) or apt-get(1).\n\
|
||||
\n\
|
||||
In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
|
||||
the existing DIRECTORY, while setting permission modes and owner/group.\n\
|
||||
In the 4th form, create all components of the given DIRECTORY(ies).\n\
|
||||
\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
--backup[=CONTROL] make a backup of each existing destination file\n\
|
||||
-b like --backup but does not accept an argument\n\
|
||||
-c (ignored)\n\
|
||||
-C, --compare compare each pair of source and destination files, and\n\
|
||||
in some cases, do not modify the destination at all\n\
|
||||
-d, --directory treat all arguments as directory names; create all\n\
|
||||
components of the specified directories\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
-D create all leading components of DEST except the last,\n\
|
||||
then copy SOURCE to DEST\n\
|
||||
-g, --group=GROUP set group ownership, instead of process' current group\n\
|
||||
-m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
|
||||
-o, --owner=OWNER set ownership (super-user only)\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
-p, --preserve-timestamps apply access/modification times of SOURCE files\n\
|
||||
to corresponding destination files\n\
|
||||
-s, --strip strip symbol tables\n\
|
||||
--strip-program=PROGRAM program used to strip binaries\n\
|
||||
-S, --suffix=SUFFIX override the usual backup suffix\n\
|
||||
-t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
|
||||
-T, --no-target-directory treat DEST as a normal file\n\
|
||||
-v, --verbose print the name of each directory as it is created\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
--preserve-context preserve SELinux security context\n\
|
||||
-Z, --context=CONTEXT set SELinux security context of files and directories\
|
||||
\n\
|
||||
"), stdout);
|
||||
|
||||
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
||||
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
||||
fputs (_("\
|
||||
\n\
|
||||
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
|
||||
The version control method may be selected via the --backup option or through\n\
|
||||
the VERSION_CONTROL environment variable. Here are the values:\n\
|
||||
\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
none, off never make backups (even if --backup is given)\n\
|
||||
numbered, t make numbered backups\n\
|
||||
existing, nil numbered if numbered backups exist, simple otherwise\n\
|
||||
simple, never always make simple backups\n\
|
||||
"), stdout);
|
||||
emit_ancillary_info ();
|
||||
}
|
||||
exit (status);
|
||||
}
|
||||
|
||||
/* Copy file FROM onto file TO and give TO the appropriate
|
||||
attributes.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
install_file_in_file (const char *from, const char *to,
|
||||
const struct cp_options *x)
|
||||
{
|
||||
struct stat from_sb;
|
||||
if (x->preserve_timestamps && stat (from, &from_sb) != 0)
|
||||
{
|
||||
error (0, errno, _("cannot stat %s"), quote (from));
|
||||
return false;
|
||||
}
|
||||
if (! copy_file (from, to, x))
|
||||
return false;
|
||||
if (strip_files)
|
||||
strip (to);
|
||||
if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
|
||||
&& ! change_timestamps (&from_sb, to))
|
||||
return false;
|
||||
return change_attributes (to);
|
||||
}
|
||||
|
||||
/* Copy file FROM onto file TO, creating any missing parent directories of TO.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
install_file_in_file_parents (char const *from, char *to,
|
||||
struct cp_options *x)
|
||||
{
|
||||
bool save_working_directory =
|
||||
! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
|
||||
int status = EXIT_SUCCESS;
|
||||
|
||||
struct savewd wd;
|
||||
savewd_init (&wd);
|
||||
if (! save_working_directory)
|
||||
savewd_finish (&wd);
|
||||
|
||||
if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
|
||||
{
|
||||
error (0, errno, _("cannot create directory %s"), to);
|
||||
status = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (save_working_directory)
|
||||
{
|
||||
int restore_result = savewd_restore (&wd, status);
|
||||
int restore_errno = errno;
|
||||
savewd_finish (&wd);
|
||||
if (EXIT_SUCCESS < restore_result)
|
||||
return false;
|
||||
if (restore_result < 0 && status == EXIT_SUCCESS)
|
||||
{
|
||||
error (0, restore_errno, _("cannot create directory %s"), to);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
|
||||
}
|
||||
|
||||
/* Copy file FROM into directory TO_DIR, keeping its same name,
|
||||
and give the copy the appropriate attributes.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
install_file_in_dir (const char *from, const char *to_dir,
|
||||
const struct cp_options *x)
|
||||
{
|
||||
const char *from_base = last_component (from);
|
||||
char *to = file_name_concat (to_dir, from_base, NULL);
|
||||
bool ret = install_file_in_file (from, to, x);
|
||||
free (to);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
@@ -529,10 +835,6 @@ main (int argc, char **argv)
|
||||
no_target_directory = true;
|
||||
break;
|
||||
|
||||
case PRESERVE_CONTEXT_OPTION_DEPRECATED:
|
||||
error (0, 0, _("WARNING: --preserve_context is deprecated; "
|
||||
"use --preserve-context instead"));
|
||||
/* fall through */
|
||||
case PRESERVE_CONTEXT_OPTION:
|
||||
if ( ! selinux_enabled)
|
||||
{
|
||||
@@ -681,330 +983,3 @@ main (int argc, char **argv)
|
||||
|
||||
exit (exit_status);
|
||||
}
|
||||
|
||||
/* Copy file FROM onto file TO, creating any missing parent directories of TO.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
install_file_in_file_parents (char const *from, char *to,
|
||||
struct cp_options *x)
|
||||
{
|
||||
bool save_working_directory =
|
||||
! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
|
||||
int status = EXIT_SUCCESS;
|
||||
|
||||
struct savewd wd;
|
||||
savewd_init (&wd);
|
||||
if (! save_working_directory)
|
||||
savewd_finish (&wd);
|
||||
|
||||
if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
|
||||
{
|
||||
error (0, errno, _("cannot create directory %s"), to);
|
||||
status = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (save_working_directory)
|
||||
{
|
||||
int restore_result = savewd_restore (&wd, status);
|
||||
int restore_errno = errno;
|
||||
savewd_finish (&wd);
|
||||
if (EXIT_SUCCESS < restore_result)
|
||||
return false;
|
||||
if (restore_result < 0 && status == EXIT_SUCCESS)
|
||||
{
|
||||
error (0, restore_errno, _("cannot create directory %s"), to);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
|
||||
}
|
||||
|
||||
/* Copy file FROM onto file TO and give TO the appropriate
|
||||
attributes.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
install_file_in_file (const char *from, const char *to,
|
||||
const struct cp_options *x)
|
||||
{
|
||||
struct stat from_sb;
|
||||
if (x->preserve_timestamps && stat (from, &from_sb) != 0)
|
||||
{
|
||||
error (0, errno, _("cannot stat %s"), quote (from));
|
||||
return false;
|
||||
}
|
||||
if (! copy_file (from, to, x))
|
||||
return false;
|
||||
if (strip_files)
|
||||
strip (to);
|
||||
if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
|
||||
&& ! change_timestamps (&from_sb, to))
|
||||
return false;
|
||||
return change_attributes (to);
|
||||
}
|
||||
|
||||
/* Copy file FROM into directory TO_DIR, keeping its same name,
|
||||
and give the copy the appropriate attributes.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
install_file_in_dir (const char *from, const char *to_dir,
|
||||
const struct cp_options *x)
|
||||
{
|
||||
const char *from_base = last_component (from);
|
||||
char *to = file_name_concat (to_dir, from_base, NULL);
|
||||
bool ret = install_file_in_file (from, to, x);
|
||||
free (to);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Copy file FROM onto file TO, creating TO if necessary.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
copy_file (const char *from, const char *to, const struct cp_options *x)
|
||||
{
|
||||
bool copy_into_self;
|
||||
|
||||
if (copy_only_if_needed && !need_copy (from, to, x))
|
||||
return true;
|
||||
|
||||
/* Allow installing from non-regular files like /dev/null.
|
||||
Charles Karney reported that some Sun version of install allows that
|
||||
and that sendmail's installation process relies on the behavior.
|
||||
However, since !x->recursive, the call to "copy" will fail if FROM
|
||||
is a directory. */
|
||||
|
||||
return copy (from, to, false, x, ©_into_self, NULL);
|
||||
}
|
||||
|
||||
/* Set the attributes of file or directory NAME.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
change_attributes (char const *name)
|
||||
{
|
||||
bool ok = false;
|
||||
/* chown must precede chmod because on some systems,
|
||||
chown clears the set[ug]id bits for non-superusers,
|
||||
resulting in incorrect permissions.
|
||||
On System V, users can give away files with chown and then not
|
||||
be able to chmod them. So don't give files away.
|
||||
|
||||
We don't normally ignore errors from chown because the idea of
|
||||
the install command is that the file is supposed to end up with
|
||||
precisely the attributes that the user specified (or defaulted).
|
||||
If the file doesn't end up with the group they asked for, they'll
|
||||
want to know. */
|
||||
|
||||
if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1)
|
||||
&& lchown (name, owner_id, group_id) != 0)
|
||||
error (0, errno, _("cannot change ownership of %s"), quote (name));
|
||||
else if (chmod (name, mode) != 0)
|
||||
error (0, errno, _("cannot change permissions of %s"), quote (name));
|
||||
else
|
||||
ok = true;
|
||||
|
||||
if (use_default_selinux_context)
|
||||
setdefaultfilecon (name);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Set the timestamps of file TO to match those of file FROM.
|
||||
Return true if successful. */
|
||||
|
||||
static bool
|
||||
change_timestamps (struct stat const *from_sb, char const *to)
|
||||
{
|
||||
struct timespec timespec[2];
|
||||
timespec[0] = get_stat_atime (from_sb);
|
||||
timespec[1] = get_stat_mtime (from_sb);
|
||||
|
||||
if (utimens (to, timespec))
|
||||
{
|
||||
error (0, errno, _("cannot set time stamps for %s"), quote (to));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Strip the symbol table from the file NAME.
|
||||
We could dig the magic number out of the file first to
|
||||
determine whether to strip it, but the header files and
|
||||
magic numbers vary so much from system to system that making
|
||||
it portable would be very difficult. Not worth the effort. */
|
||||
|
||||
static void
|
||||
strip (char const *name)
|
||||
{
|
||||
int status;
|
||||
pid_t pid = fork ();
|
||||
|
||||
switch (pid)
|
||||
{
|
||||
case -1:
|
||||
error (EXIT_FAILURE, errno, _("fork system call failed"));
|
||||
break;
|
||||
case 0: /* Child. */
|
||||
execlp (strip_program, strip_program, name, NULL);
|
||||
error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program);
|
||||
break;
|
||||
default: /* Parent. */
|
||||
if (waitpid (pid, &status, 0) < 0)
|
||||
error (EXIT_FAILURE, errno, _("waiting for strip"));
|
||||
else if (! WIFEXITED (status) || WEXITSTATUS (status))
|
||||
error (EXIT_FAILURE, 0, _("strip process terminated abnormally"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the user and group ownership of the files to install. */
|
||||
|
||||
static void
|
||||
get_ids (void)
|
||||
{
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
|
||||
if (owner_name)
|
||||
{
|
||||
pw = getpwnam (owner_name);
|
||||
if (pw == NULL)
|
||||
{
|
||||
unsigned long int tmp;
|
||||
if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
|
||||
|| UID_T_MAX < tmp)
|
||||
error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name));
|
||||
owner_id = tmp;
|
||||
}
|
||||
else
|
||||
owner_id = pw->pw_uid;
|
||||
endpwent ();
|
||||
}
|
||||
else
|
||||
owner_id = (uid_t) -1;
|
||||
|
||||
if (group_name)
|
||||
{
|
||||
gr = getgrnam (group_name);
|
||||
if (gr == NULL)
|
||||
{
|
||||
unsigned long int tmp;
|
||||
if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
|
||||
|| GID_T_MAX < tmp)
|
||||
error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name));
|
||||
group_id = tmp;
|
||||
}
|
||||
else
|
||||
group_id = gr->gr_gid;
|
||||
endgrent ();
|
||||
}
|
||||
else
|
||||
group_id = (gid_t) -1;
|
||||
}
|
||||
|
||||
/* Report that directory DIR was made, if OPTIONS requests this. */
|
||||
static void
|
||||
announce_mkdir (char const *dir, void *options)
|
||||
{
|
||||
struct cp_options const *x = options;
|
||||
if (x->verbose)
|
||||
prog_fprintf (stdout, _("creating directory %s"), quote (dir));
|
||||
}
|
||||
|
||||
/* Make ancestor directory DIR, whose last file name component is
|
||||
COMPONENT, with options OPTIONS. Assume the working directory is
|
||||
COMPONENT's parent. */
|
||||
static int
|
||||
make_ancestor (char const *dir, char const *component, void *options)
|
||||
{
|
||||
int r = mkdir (component, DEFAULT_MODE);
|
||||
if (r == 0)
|
||||
announce_mkdir (dir, options);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
usage (int status)
|
||||
{
|
||||
if (status != EXIT_SUCCESS)
|
||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
||||
program_name);
|
||||
else
|
||||
{
|
||||
printf (_("\
|
||||
Usage: %s [OPTION]... [-T] SOURCE DEST\n\
|
||||
or: %s [OPTION]... SOURCE... DIRECTORY\n\
|
||||
or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
|
||||
or: %s [OPTION]... -d DIRECTORY...\n\
|
||||
"),
|
||||
program_name, program_name, program_name, program_name);
|
||||
fputs (_("\
|
||||
\n\
|
||||
This install program copies files (often just compiled) into destination\n\
|
||||
locations you choose. If you want to download and install a ready-to-use\n\
|
||||
package on a GNU/Linux system, you should instead be using a package manager\n\
|
||||
like yum(1) or apt-get(1).\n\
|
||||
\n\
|
||||
In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
|
||||
the existing DIRECTORY, while setting permission modes and owner/group.\n\
|
||||
In the 4th form, create all components of the given DIRECTORY(ies).\n\
|
||||
\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
--backup[=CONTROL] make a backup of each existing destination file\n\
|
||||
-b like --backup but does not accept an argument\n\
|
||||
-c (ignored)\n\
|
||||
-C, --compare compare each pair of source and destination files, and\n\
|
||||
in some cases, do not modify the destination at all\n\
|
||||
-d, --directory treat all arguments as directory names; create all\n\
|
||||
components of the specified directories\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
-D create all leading components of DEST except the last,\n\
|
||||
then copy SOURCE to DEST\n\
|
||||
-g, --group=GROUP set group ownership, instead of process' current group\n\
|
||||
-m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
|
||||
-o, --owner=OWNER set ownership (super-user only)\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
-p, --preserve-timestamps apply access/modification times of SOURCE files\n\
|
||||
to corresponding destination files\n\
|
||||
-s, --strip strip symbol tables\n\
|
||||
--strip-program=PROGRAM program used to strip binaries\n\
|
||||
-S, --suffix=SUFFIX override the usual backup suffix\n\
|
||||
-t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
|
||||
-T, --no-target-directory treat DEST as a normal file\n\
|
||||
-v, --verbose print the name of each directory as it is created\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
--preserve-context preserve SELinux security context\n\
|
||||
-Z, --context=CONTEXT set SELinux security context of files and directories\
|
||||
\n\
|
||||
"), stdout);
|
||||
|
||||
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
||||
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
||||
fputs (_("\
|
||||
\n\
|
||||
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
|
||||
The version control method may be selected via the --backup option or through\n\
|
||||
the VERSION_CONTROL environment variable. Here are the values:\n\
|
||||
\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
none, off never make backups (even if --backup is given)\n\
|
||||
numbered, t make numbered backups\n\
|
||||
existing, nil numbered if numbered backups exist, simple otherwise\n\
|
||||
simple, never always make simple backups\n\
|
||||
"), stdout);
|
||||
emit_ancillary_info ();
|
||||
}
|
||||
exit (status);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "system.h"
|
||||
#include "verify.h"
|
||||
|
||||
/* Note currently for glibc (2.3.5) the following call does not change the
|
||||
/* Note currently for glibc (2.3.5) the following call does not change
|
||||
the buffer size, and more problematically does not give any indication
|
||||
that the new size request was ignored:
|
||||
|
||||
|
||||
14
src/ls.c
14
src/ls.c
@@ -1131,8 +1131,8 @@ is_colored (enum indicator_no type)
|
||||
size_t len = color_indicator[type].len;
|
||||
char const *s = color_indicator[type].string;
|
||||
return ! (len == 0
|
||||
|| (len == 1 && strncmp (s, "0", 1) == 0)
|
||||
|| (len == 2 && strncmp (s, "00", 2) == 0));
|
||||
|| (len == 1 && STRNCMP_LIT (s, "0") == 0)
|
||||
|| (len == 2 && STRNCMP_LIT (s, "00") == 0));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1996,7 +1996,7 @@ decode_switches (int argc, char **argv)
|
||||
if (! (style = getenv ("TIME_STYLE")))
|
||||
style = bad_cast ("locale");
|
||||
|
||||
while (strncmp (style, posix_prefix, sizeof posix_prefix - 1) == 0)
|
||||
while (STREQ_LEN (style, posix_prefix, sizeof posix_prefix - 1))
|
||||
{
|
||||
if (! hard_locale (LC_TIME))
|
||||
return optind;
|
||||
@@ -2388,7 +2388,7 @@ parse_ls_color (void)
|
||||
}
|
||||
|
||||
if (color_indicator[C_LINK].len == 6
|
||||
&& !strncmp (color_indicator[C_LINK].string, "target", 6))
|
||||
&& !STRNCMP_LIT (color_indicator[C_LINK].string, "target"))
|
||||
color_symlink_as_referent = true;
|
||||
}
|
||||
|
||||
@@ -4178,7 +4178,7 @@ print_color_indicator (const struct fileinfo *f, bool symlink_target)
|
||||
}
|
||||
else if (S_ISLNK (mode))
|
||||
type = ((!linkok
|
||||
&& (!strncmp (color_indicator[C_LINK].string, "target", 6)
|
||||
&& (!STRNCMP_LIT (color_indicator[C_LINK].string, "target")
|
||||
|| color_indicator[C_ORPHAN].string))
|
||||
? C_ORPHAN : C_LINK);
|
||||
else if (S_ISFIFO (mode))
|
||||
@@ -4209,8 +4209,8 @@ print_color_indicator (const struct fileinfo *f, bool symlink_target)
|
||||
for (ext = color_ext_list; ext != NULL; ext = ext->next)
|
||||
{
|
||||
if (ext->ext.len <= len
|
||||
&& strncmp (name - ext->ext.len, ext->ext.string,
|
||||
ext->ext.len) == 0)
|
||||
&& STREQ_LEN (name - ext->ext.len, ext->ext.string,
|
||||
ext->ext.len))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ split_3 (char *s, size_t s_len,
|
||||
|
||||
/* Check for BSD-style checksum line. */
|
||||
algo_name_len = strlen (DIGEST_TYPE_STRING);
|
||||
if (strncmp (s + i, DIGEST_TYPE_STRING, algo_name_len) == 0)
|
||||
if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
|
||||
{
|
||||
if (s[i + algo_name_len] == ' ')
|
||||
++i;
|
||||
|
||||
24
src/sort.c
24
src/sort.c
@@ -109,12 +109,16 @@ struct rlimit { size_t rlim_cur; };
|
||||
and is responsible for merging TOTAL lines. */
|
||||
#define MAX_MERGE(total, level) (((total) >> (2 * ((level) + 1))) + 1)
|
||||
|
||||
/* Heuristic value for the number of lines for which it is worth
|
||||
creating a subthread, during an internal merge sort, on a machine
|
||||
that has processors galore. Currently this number is just a guess.
|
||||
This value must be at least 4. We don't know of any machine where
|
||||
this number has any practical effect. */
|
||||
enum { SUBTHREAD_LINES_HEURISTIC = 4 };
|
||||
/* Heuristic value for the number of lines for which it is worth creating
|
||||
a subthread, during an internal merge sort. I.e., it is a small number
|
||||
of "average" lines for which sorting via two threads is faster than
|
||||
sorting via one on an "average" system. On an dual-core 2.0 GHz i686
|
||||
system with 3GB of RAM and 2MB of L2 cache, a file containing 128K
|
||||
lines of gensort -a output is sorted slightly faster with --parallel=2
|
||||
than with --parallel=1. By contrast, using --parallel=1 is about 10%
|
||||
faster than using --parallel=2 with a 64K-line input. */
|
||||
enum { SUBTHREAD_LINES_HEURISTIC = 128 * 1024 };
|
||||
verify (4 <= SUBTHREAD_LINES_HEURISTIC);
|
||||
|
||||
/* The number of threads after which there are
|
||||
diminishing performance gains. */
|
||||
@@ -315,8 +319,12 @@ static size_t merge_buffer_size = MAX (MIN_MERGE_BUFFER_SIZE, 256 * 1024);
|
||||
specified by the user. Zero if the user has not specified a size. */
|
||||
static size_t sort_size;
|
||||
|
||||
/* The guessed size for non-regular files. */
|
||||
#define INPUT_FILE_SIZE_GUESS (1024 * 1024)
|
||||
/* The initial allocation factor for non-regular files.
|
||||
This is used, e.g., when reading from a pipe.
|
||||
Don't make it too big, since it is multiplied by ~130 to
|
||||
obtain the size of the actual buffer sort will allocate.
|
||||
Also, there may be 8 threads all doing this at the same time. */
|
||||
#define INPUT_FILE_SIZE_GUESS (128 * 1024)
|
||||
|
||||
/* Array of directory names in which any temporary files are to be created. */
|
||||
static char const **temp_dirs;
|
||||
|
||||
@@ -895,12 +895,12 @@ main (int argc, char **argv)
|
||||
/* skip any whitespace */
|
||||
while (isspace (to_uchar (*optarg)))
|
||||
optarg++;
|
||||
if (strncmp (optarg, "r/", 2) == 0)
|
||||
if (STRNCMP_LIT (optarg, "r/") == 0)
|
||||
{
|
||||
split_type = type_rr;
|
||||
optarg += 2;
|
||||
}
|
||||
else if (strncmp (optarg, "l/", 2) == 0)
|
||||
else if (STRNCMP_LIT (optarg, "l/") == 0)
|
||||
{
|
||||
split_type = type_chunk_lines;
|
||||
optarg += 2;
|
||||
|
||||
@@ -209,7 +209,7 @@ set_LD_PRELOAD (void)
|
||||
char const *const *path = search_path;
|
||||
char *libstdbuf;
|
||||
|
||||
do
|
||||
while (true)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
@@ -224,8 +224,11 @@ set_LD_PRELOAD (void)
|
||||
if (stat (libstdbuf, &sb) == 0) /* file_exists */
|
||||
break;
|
||||
free (libstdbuf);
|
||||
|
||||
++path;
|
||||
if ( ! *path)
|
||||
error (EXIT_CANCELED, 0, _("failed to find %s"), quote (LIB_NAME));
|
||||
}
|
||||
while (*++path);
|
||||
|
||||
/* FIXME: Do we need to support libstdbuf.dll, c:, '\' separators etc? */
|
||||
|
||||
|
||||
@@ -257,6 +257,13 @@ select_plural (uintmax_t n)
|
||||
}
|
||||
|
||||
#define STREQ(a, b) (strcmp (a, b) == 0)
|
||||
#define STREQ_LEN(a, b, n) (strncmp (a, b, n) == 0)
|
||||
#define STRPREFIX(a, b) (strncmp(a, b, strlen (b)) == 0)
|
||||
|
||||
/* Just like strncmp, but the first argument must be a literal string
|
||||
and you don't specify the length. */
|
||||
#define STRNCMP_LIT(s, literal) \
|
||||
strncmp (s, "" literal "", sizeof (literal) - 1)
|
||||
|
||||
#if !HAVE_DECL_GETLOGIN
|
||||
char *getlogin ();
|
||||
@@ -607,7 +614,7 @@ emit_ancillary_info (void)
|
||||
/* Don't output this redundant message for English locales.
|
||||
Note we still output for 'C' so that it gets included in the man page. */
|
||||
const char *lc_messages = setlocale (LC_MESSAGES, NULL);
|
||||
if (lc_messages && strncmp (lc_messages, "en_", 3))
|
||||
if (lc_messages && STRNCMP_LIT (lc_messages, "en_"))
|
||||
{
|
||||
/* TRANSLATORS: Replace LANG_CODE in this URL with your language code
|
||||
<http://translationproject.org/team/LANG_CODE.html> to form one of
|
||||
|
||||
@@ -173,7 +173,8 @@ get_mtime (char const *filename, struct timespec *mtime)
|
||||
static bool
|
||||
binop (char const *s)
|
||||
{
|
||||
return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
|
||||
return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "==")) ||
|
||||
(STREQ (s, "-nt")) ||
|
||||
(STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
|
||||
(STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
|
||||
(STREQ (s, "-gt")) || (STREQ (s, "-ge")));
|
||||
@@ -360,7 +361,8 @@ binary_operator (bool l_is_l)
|
||||
test_syntax_error (_("unknown binary operator"), argv[op]);
|
||||
}
|
||||
|
||||
if (argv[op][0] == '=' && !argv[op][1])
|
||||
if (argv[op][0] == '=' && (!argv[op][1] ||
|
||||
((argv[op][1] == '=') && !argv[op][2])))
|
||||
{
|
||||
bool value = STREQ (argv[pos], argv[pos + 2]);
|
||||
pos += 3;
|
||||
|
||||
2
src/tr.c
2
src/tr.c
@@ -548,7 +548,7 @@ look_up_char_class (char const *class_str, size_t len)
|
||||
enum Char_class i;
|
||||
|
||||
for (i = 0; i < ARRAY_CARDINALITY (char_class_name); i++)
|
||||
if (strncmp (class_str, char_class_name[i], len) == 0
|
||||
if (STREQ_LEN (class_str, char_class_name[i], len)
|
||||
&& strlen (char_class_name[i]) == len)
|
||||
return i;
|
||||
return CC_NO_CLASS;
|
||||
|
||||
@@ -333,7 +333,7 @@ main (int argc, char **argv)
|
||||
|
||||
/* Hack "safely" around the ppc vs. powerpc return value. */
|
||||
if (cputype == CPU_TYPE_POWERPC
|
||||
&& strncmp (element, "ppc", 3) == 0)
|
||||
&& STRNCMP_LIT (element, "ppc") == 0)
|
||||
element = "powerpc";
|
||||
}
|
||||
# endif
|
||||
|
||||
@@ -151,11 +151,7 @@ print_uptime (size_t n, const STRUCT_UTMP *this)
|
||||
printf (ngettext ("%lu user", "%lu users", entries),
|
||||
(unsigned long int) entries);
|
||||
|
||||
#if defined HAVE_GETLOADAVG || defined C_GETLOADAVG
|
||||
loads = getloadavg (avg, 3);
|
||||
#else
|
||||
loads = -1;
|
||||
#endif
|
||||
|
||||
if (loads == -1)
|
||||
putchar ('\n');
|
||||
|
||||
15
src/wc.c
15
src/wc.c
@@ -709,6 +709,9 @@ main (int argc, char **argv)
|
||||
ai = argv_iter_init_argv (files);
|
||||
}
|
||||
|
||||
if (!ai)
|
||||
xalloc_die ();
|
||||
|
||||
fstatus = get_input_fstatus (nfiles, files);
|
||||
number_width = compute_number_width (nfiles, fstatus);
|
||||
|
||||
@@ -719,16 +722,17 @@ main (int argc, char **argv)
|
||||
bool skip_file = false;
|
||||
enum argv_iter_err ai_err;
|
||||
char *file_name = argv_iter (ai, &ai_err);
|
||||
if (ai_err == AI_ERR_EOF)
|
||||
break;
|
||||
if (!file_name)
|
||||
{
|
||||
switch (ai_err)
|
||||
{
|
||||
case AI_ERR_EOF:
|
||||
goto argv_iter_done;
|
||||
case AI_ERR_READ:
|
||||
error (EXIT_FAILURE, errno, _("%s: read error"),
|
||||
quote (files_from));
|
||||
continue;
|
||||
error (0, errno, _("%s: read error"),
|
||||
quotearg_colon (files_from));
|
||||
ok = false;
|
||||
goto argv_iter_done;
|
||||
case AI_ERR_MEM:
|
||||
xalloc_die ();
|
||||
default:
|
||||
@@ -770,6 +774,7 @@ main (int argc, char **argv)
|
||||
else
|
||||
ok &= wc_file (file_name, &fstatus[nfiles ? i : 0]);
|
||||
}
|
||||
argv_iter_done:
|
||||
|
||||
/* No arguments on the command line is fine. That means read from stdin.
|
||||
However, no arguments on the --files0-from input stream is an error
|
||||
|
||||
@@ -584,7 +584,7 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf)
|
||||
ttyname_b = ttyname (STDIN_FILENO);
|
||||
if (!ttyname_b)
|
||||
return;
|
||||
if (strncmp (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH, DEV_DIR_LEN) == 0)
|
||||
if (STRNCMP_LIT (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH) == 0)
|
||||
ttyname_b += DEV_DIR_LEN; /* Discard /dev/ prefix. */
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ EXTRA_DIST = \
|
||||
check.mk \
|
||||
envvar-check \
|
||||
filefrag-extent-compare \
|
||||
fiemap-capable \
|
||||
init.cfg \
|
||||
init.sh \
|
||||
lang-default \
|
||||
@@ -26,7 +27,7 @@ root_tests = \
|
||||
cp/special-bits \
|
||||
cp/cp-mv-enotsup-xattr \
|
||||
cp/capability \
|
||||
cp/sparse-fiemap \
|
||||
cp/sparse-fiemap \
|
||||
dd/skip-seek-past-dev \
|
||||
install/install-C-root \
|
||||
ls/capability \
|
||||
@@ -320,6 +321,7 @@ TESTS = \
|
||||
cp/dir-vs-file \
|
||||
cp/existing-perm-race \
|
||||
cp/fail-perm \
|
||||
cp/fiemap-empty \
|
||||
cp/fiemap-perf \
|
||||
cp/fiemap-2 \
|
||||
cp/file-perm-race \
|
||||
@@ -327,6 +329,7 @@ TESTS = \
|
||||
cp/link \
|
||||
cp/link-no-deref \
|
||||
cp/link-preserve \
|
||||
cp/link-symlink \
|
||||
cp/no-deref-link1 \
|
||||
cp/no-deref-link2 \
|
||||
cp/no-deref-link3 \
|
||||
@@ -351,6 +354,7 @@ TESTS = \
|
||||
df/unreadable \
|
||||
dd/direct \
|
||||
dd/misc \
|
||||
dd/nocache \
|
||||
dd/not-rewound \
|
||||
dd/reblock \
|
||||
dd/skip-seek \
|
||||
@@ -369,6 +373,7 @@ TESTS = \
|
||||
du/exclude \
|
||||
du/fd-leak \
|
||||
du/files0-from \
|
||||
du/files0-from-dir \
|
||||
du/hard-link \
|
||||
du/inacc-dest \
|
||||
du/inacc-dir \
|
||||
|
||||
@@ -40,8 +40,13 @@ vc_exe_in_TESTS: Makefile
|
||||
check: vc_exe_in_TESTS
|
||||
.PHONY: vc_exe_in_TESTS
|
||||
|
||||
built_programs = \
|
||||
(cd $(top_builddir)/src && MAKEFLAGS= $(MAKE) -s built_programs.list)
|
||||
CLEANFILES =
|
||||
CLEANFILES += .built-programs
|
||||
check-am: .built-programs
|
||||
.built-programs:
|
||||
$(AM_V_GEN)(cd $(top_builddir)/src \
|
||||
&& MAKEFLAGS= $(MAKE) -s built_programs.list) \
|
||||
> $@-t && mv $@-t $@
|
||||
|
||||
# Note that the first lines are statements. They ensure that environment
|
||||
# variables that can perturb tests are unset or set to expected values.
|
||||
@@ -76,7 +81,7 @@ TESTS_ENVIRONMENT = \
|
||||
abs_top_builddir='$(abs_top_builddir)' \
|
||||
abs_top_srcdir='$(abs_top_srcdir)' \
|
||||
abs_srcdir='$(abs_srcdir)' \
|
||||
built_programs="`$(built_programs)`" \
|
||||
built_programs="`cat .built-programs`" \
|
||||
host_os=$(host_os) \
|
||||
host_triplet='$(host_triplet)' \
|
||||
srcdir='$(srcdir)' \
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
print_ver_ cp
|
||||
|
||||
# Require a fiemap-enabled FS.
|
||||
fiemap_capable_ . \
|
||||
touch fiemap_chk # check a file rather than current dir for best coverage
|
||||
fiemap_capable_ fiemap_chk \
|
||||
|| skip_ "this file system lacks FIEMAP support"
|
||||
|
||||
# Exercise the code that handles a file ending in a hole.
|
||||
|
||||
96
tests/cp/fiemap-empty
Executable file
96
tests/cp/fiemap-empty
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh
|
||||
# Test cp reads unwritten extents efficiently
|
||||
|
||||
# Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ cp
|
||||
|
||||
touch fiemap_chk
|
||||
fiemap_capable_ fiemap_chk ||
|
||||
skip_test_ 'this file system lacks FIEMAP support'
|
||||
rm fiemap_chk
|
||||
|
||||
# TODO: rather than requiring `fallocate`, possible add
|
||||
# this functionality to truncate --alloc
|
||||
fallocate --help >/dev/null || skip_test_ 'The fallocate utility is required'
|
||||
fallocate -l 1 -n falloc.test ||
|
||||
skip_test_ 'this file system lacks FALLOCATE support'
|
||||
rm falloc.test
|
||||
|
||||
# Require more space than we'll actually use, so that
|
||||
# tests run in parallel do not run out of space.
|
||||
# Otherwise, with inadequate space, simply running the following
|
||||
# fallocate command would induce a temporary disk-full condition,
|
||||
# which would cause failure of unrelated tests run in parallel.
|
||||
require_file_system_bytes_free_ 800000000
|
||||
|
||||
fallocate -l 600MiB space.test ||
|
||||
skip_test_ 'this test needs at least 600MiB free space'
|
||||
|
||||
# Disable this test on old BTRFS (e.g. Fedora 14)
|
||||
# which reports ordinary extents for unwritten ones.
|
||||
filefrag space.test || skip_test_ 'the `filefrag` utility is missing'
|
||||
filefrag -v space.test | grep -F 'unwritten' > /dev/null ||
|
||||
skip_test_ 'this file system does not report empty extents as "unwritten"'
|
||||
|
||||
rm space.test
|
||||
|
||||
# Ensure we read a large empty file quickly
|
||||
fallocate -l 300MiB empty.big || framework_failure
|
||||
timeout 3 cp --sparse=always empty.big cp.test || fail=1
|
||||
test $(stat -c %s empty.big) = $(stat -c %s cp.test) || fail=1
|
||||
rm empty.big cp.test
|
||||
|
||||
# Ensure we handle extents beyond file size correctly.
|
||||
# Note until we support fallocate, we will not maintain
|
||||
# the file allocation. FIXME: amend this test when fallocate is supported.
|
||||
fallocate -l 10MiB -n unwritten.withdata || framework_failure
|
||||
dd count=10 if=/dev/urandom conv=notrunc iflag=fullblock of=unwritten.withdata
|
||||
cp unwritten.withdata cp.test || fail=1
|
||||
test $(stat -c %s unwritten.withdata) = $(stat -c %s cp.test) || fail=1
|
||||
cmp unwritten.withdata cp.test || fail=1
|
||||
rm unwritten.withdata cp.test
|
||||
|
||||
# The following to generate unaccounted extents followed by a hole, is not
|
||||
# supported by ext4 at least. The ftruncate discards all extents not
|
||||
# accounted for in the size.
|
||||
# fallocate -l 10MiB -n unacc.withholes
|
||||
# dd count=10 if=/dev/urandom conv=notrunc iflag=fullblock of=unacc.withholes
|
||||
# truncate -s20M unacc.withholes
|
||||
|
||||
# Ensure we handle a hole after empty extents correctly.
|
||||
# Since all extents are accounted for in the size,
|
||||
# we can maintain the allocation independently from
|
||||
# fallocate() support.
|
||||
fallocate -l 10MiB empty.withholes
|
||||
truncate -s 20M empty.withholes
|
||||
sectors_per_block=$(expr $(stat -c %o .) / 512)
|
||||
cp empty.withholes cp.test || fail=1
|
||||
test $(stat -c %s empty.withholes) = $(stat -c %s cp.test) || fail=1
|
||||
# These are usually equal but can vary by an IO block due to alignment
|
||||
alloc_diff=$(expr $(stat -c %b empty.withholes) - $(stat -c %b cp.test))
|
||||
alloc_diff=$(echo $alloc_diff | tr -d -- -) # abs()
|
||||
test $alloc_diff -le $sectors_per_block || fail=1
|
||||
# Again with SPARSE_ALWAYS
|
||||
cp --sparse=always empty.withholes cp.test || fail=1
|
||||
test $(stat -c %s empty.withholes) = $(stat -c %s cp.test) || fail=1
|
||||
# cp.test should take 0 space, but allowing for some systems
|
||||
# that store default extended attributes in data blocks
|
||||
test $(stat -c %b cp.test) -le $sectors_per_block || fail=1
|
||||
rm empty.withholes cp.test
|
||||
|
||||
Exit $fail
|
||||
@@ -20,12 +20,24 @@
|
||||
print_ver_ cp
|
||||
|
||||
# Require a fiemap-enabled FS.
|
||||
fiemap_capable_ . \
|
||||
|| skip_ "this file system lacks FIEMAP support"
|
||||
touch fiemap_chk
|
||||
fiemap_capable_ fiemap_chk ||
|
||||
skip_ "this file system lacks FIEMAP support"
|
||||
|
||||
# Exclude ext3 (or unknown fs types)
|
||||
# as the emulated extent scanning is slow
|
||||
df -t ext3 . >/dev/null &&
|
||||
skip_ "ext3 has known slow FIEMAP scanning"
|
||||
|
||||
# Create a large-but-sparse file.
|
||||
timeout 10 truncate -s1T f || framework_failure_
|
||||
|
||||
# Disable this test on old BTRFS (e.g. Fedora 14)
|
||||
# which reports (unwritten) extents for holes.
|
||||
filefrag f || skip_test_ 'the `filefrag` utility is missing'
|
||||
filefrag f | grep -F ': 0 extents found' > /dev/null ||
|
||||
skip_test_ 'this file system reports extents for holes'
|
||||
|
||||
# Nothing can read (much less write) that many bytes in so little time.
|
||||
timeout 10 cp f f2 || fail=1
|
||||
|
||||
|
||||
41
tests/cp/link-symlink
Executable file
41
tests/cp/link-symlink
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
# Ensure that cp -a --link maintains timestamps if possible
|
||||
|
||||
# Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ cp
|
||||
|
||||
# Check that the timestamps of the symlink are copied
|
||||
# if we're using hardlink to symlink emulation.
|
||||
touch file
|
||||
ln -s file link || framework_failure
|
||||
touch -m -h -d 2011-01-01 link ||
|
||||
skip_test_ "Your system doesn't support updating symlink timestamps"
|
||||
case `stat --format=%y link` in
|
||||
2011-01-01*) ;;
|
||||
*) skip_test_ "Your system doesn't support updating symlink timestamps" ;;
|
||||
esac
|
||||
|
||||
# link.cp is probably a hardlink, but may also be a symlink
|
||||
# In either case the timestamp should match the original.
|
||||
cp -al link link.cp
|
||||
case `stat --format=%y link.cp` in
|
||||
2011-01-01*) ;;
|
||||
*) fail=1 ;;
|
||||
esac
|
||||
|
||||
Exit $fail
|
||||
@@ -52,14 +52,6 @@ t0() {
|
||||
fi
|
||||
}
|
||||
|
||||
t1() {
|
||||
f=$1; shift
|
||||
u=$1; shift
|
||||
g=$1; shift
|
||||
t0 "$f" "$u" "$g" setuidgid -g "$nameless_gid1,$nameless_gid2" \
|
||||
"$nameless_uid" "$@"
|
||||
}
|
||||
|
||||
nameless_uid=`$PERL -le '
|
||||
foreach my $i (1000..16*1024-1)
|
||||
{
|
||||
@@ -105,6 +97,30 @@ t0 b1 "$nameless_uid" "$nameless_gid2" cp -p
|
||||
t0 c0 0 "$nameless_gid1" cp -p
|
||||
t0 c1 0 "$nameless_gid2" cp -p
|
||||
|
||||
# For the remaining tests, we need a cp binary that is accessible to a user
|
||||
# with UID of $nameless_uid. The build directory may not be accessible,
|
||||
# so create a temporary directory and copy cp into it, ensure that
|
||||
# $nameless_uid can access it and then make that directory the search path.
|
||||
tmp_path=
|
||||
cleanup_() { rm -rf "$tmp_path"; }
|
||||
|
||||
# Cause mktemp to create a directory directly under /tmp.
|
||||
# Setting TMPDIR explicitly is required here, in case $TMPDIR
|
||||
# is not readable by our nameless IDs.
|
||||
test -d /tmp && TMPDIR=/tmp
|
||||
tmp_path=$(mktemp -d) || fail_ "failed to create temporary directory"
|
||||
cp "$abs_path_dir_/cp" "$tmp_path"
|
||||
chmod -R a+rx "$tmp_path"
|
||||
|
||||
t1() {
|
||||
f=$1; shift
|
||||
u=$1; shift
|
||||
g=$1; shift
|
||||
t0 "$f" "$u" "$g" \
|
||||
setuidgid -g "$nameless_gid1,$nameless_gid2" \
|
||||
"$nameless_uid" env PATH="$tmp_path" "$@"
|
||||
}
|
||||
|
||||
t1 a0 "$nameless_uid" "$nameless_gid1" cp
|
||||
t1 b0 "$nameless_uid" "$nameless_gid1" cp
|
||||
t1 b1 "$nameless_uid" "$nameless_gid1" cp
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ cp
|
||||
$PERL -e 1 || skip_test_ 'you lack perl'
|
||||
|
||||
if fiemap_capable_ . ; then
|
||||
: # Current dir is on a partition with working extents. Good!
|
||||
# The test was seen to fail on ext3 so exclude that type
|
||||
# (or any file system where the type can't be determined)
|
||||
touch fiemap_chk
|
||||
if fiemap_capable_ fiemap_chk && ! df -t ext3 . >/dev/null; then
|
||||
: # Current partition has working extents. Good!
|
||||
else
|
||||
# It's not; we need to create one, hence we need root access.
|
||||
require_root_
|
||||
@@ -49,8 +53,6 @@ fi
|
||||
# in which it calls ioctl (fd, FS_IOC_FIEMAP,...
|
||||
# This also verifies that non-trivial extents are preserved.
|
||||
|
||||
$PERL -e 1 || skip_test_ 'skipping part of this test; you lack perl'
|
||||
|
||||
# Extract logical block number and length pairs from filefrag -v output.
|
||||
# The initial sed is to remove the "eof" from the normally-empty "flags" field.
|
||||
# Similarly, remove flags values like "unknown,delalloc,eof".
|
||||
@@ -66,39 +68,41 @@ for i in $(seq 1 2 21); do
|
||||
$PERL -e 'BEGIN { $n = '$i' * 1024; *F = *STDOUT }' \
|
||||
-e 'for (1..'$j') { sysseek (*F, $n, 1)' \
|
||||
-e '&& syswrite (*F, chr($_)x$n) or die "$!"}' > j1 || fail=1
|
||||
# sync
|
||||
|
||||
# Note there is an implicit sync performed by cp on Linux kernels
|
||||
# before 2.6.39 to work around bugs in EXT4 and BTRFS.
|
||||
# Note also the -s parameter to the filefrag commands below
|
||||
# for the same reasons.
|
||||
cp --sparse=always j1 j2 || fail=1
|
||||
# sync
|
||||
# Technically we may need the 'sync' uses above, but
|
||||
# uncommenting them makes this test take much longer.
|
||||
|
||||
cmp j1 j2 || fail=1
|
||||
filefrag -v j1 | grep extent \
|
||||
|| skip_test_ 'skipping part of this test; you lack filefrag'
|
||||
if ! filefrag -vs j1 | grep -F extent >/dev/null; then
|
||||
test $skip != 1 && warn_ 'skipping part; you lack filefrag'
|
||||
skip=1
|
||||
else
|
||||
# Here is sample filefrag output:
|
||||
# $ perl -e 'BEGIN{$n=16*1024; *F=*STDOUT}' \
|
||||
# -e 'for (1..5) { sysseek(*F,$n,1)' \
|
||||
# -e '&& syswrite *F,"."x$n or die "$!"}' > j
|
||||
# $ filefrag -v j
|
||||
# File system type is: ef53
|
||||
# File size of j is 163840 (40 blocks, blocksize 4096)
|
||||
# ext logical physical expected length flags
|
||||
# 0 4 6258884 4
|
||||
# 1 12 6258892 6258887 4
|
||||
# 2 20 6258900 6258895 4
|
||||
# 3 28 6258908 6258903 4
|
||||
# 4 36 6258916 6258911 4 eof
|
||||
# j: 6 extents found
|
||||
|
||||
# Here is sample filefrag output:
|
||||
# $ perl -e 'BEGIN{$n=16*1024; *F=*STDOUT}' \
|
||||
# -e 'for (1..5) { sysseek(*F,$n,1)' \
|
||||
# -e '&& syswrite *F,"."x$n or die "$!"}' > j
|
||||
# $ filefrag -v j
|
||||
# File system type is: ef53
|
||||
# File size of j is 163840 (40 blocks, blocksize 4096)
|
||||
# ext logical physical expected length flags
|
||||
# 0 4 6258884 4
|
||||
# 1 12 6258892 6258887 4
|
||||
# 2 20 6258900 6258895 4
|
||||
# 3 28 6258908 6258903 4
|
||||
# 4 36 6258916 6258911 4 eof
|
||||
# j: 6 extents found
|
||||
|
||||
# exclude the physical block numbers; they always differ
|
||||
filefrag -v j1 > ff1 || fail=1
|
||||
filefrag -v j2 > ff2 || fail=1
|
||||
{ f ff1; f ff2; } \
|
||||
| $PERL $abs_top_srcdir/tests/filefrag-extent-compare \
|
||||
|| { fail=1; break; }
|
||||
# exclude the physical block numbers; they always differ
|
||||
filefrag -v j1 > ff1 || framework_failure
|
||||
filefrag -vs j2 > ff2 || framework_failure
|
||||
{ f ff1; f ff2; } | $PERL $abs_top_srcdir/tests/filefrag-extent-compare ||
|
||||
fail=1
|
||||
fi
|
||||
test $fail = 1 && break 2
|
||||
done
|
||||
test $fail = 1 && break
|
||||
done
|
||||
|
||||
Exit $fail
|
||||
|
||||
@@ -26,6 +26,10 @@ timeout 10 cat pipe > copy &
|
||||
|
||||
truncate -s1M sparse || framework_failure_
|
||||
cp sparse pipe || fail=1
|
||||
|
||||
# Ensure that the cat has completed before comparing.
|
||||
wait
|
||||
|
||||
cmp sparse copy || fail=1
|
||||
|
||||
Exit $fail
|
||||
|
||||
58
tests/dd/nocache
Executable file
58
tests/dd/nocache
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
# Ensure dd handles the 'nocache' flag
|
||||
|
||||
# Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ dd
|
||||
|
||||
# This should not call posix_fadvise
|
||||
dd iflag=nocache oflag=nocache if=/dev/null of=/dev/null || fail=1
|
||||
|
||||
# We should get an error for trying to process a pipe
|
||||
dd count=0 | dd iflag=nocache count=0 && fail=1
|
||||
|
||||
# O_DIRECT is orthogonal to drop cache so mutually exclusive
|
||||
dd iflag=nocache,direct if=/dev/null && fail=1
|
||||
|
||||
# The rest ensure that the documented uses cases
|
||||
# proceed without error
|
||||
for f in ifile ofile; do
|
||||
dd if=/dev/zero of=$f conv=fdatasync count=100 || framework_failure
|
||||
done
|
||||
|
||||
# Advise to drop cache for whole file
|
||||
if ! dd if=ifile iflag=nocache count=0 2>err; then
|
||||
if grep -F 'Operation not supported' err >/dev/null; then
|
||||
warn_ 'skipping part; this file system lacks support for posix_fadvise()'
|
||||
skip=1
|
||||
else
|
||||
fail=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$skip" != 1; then
|
||||
# Ensure drop cache for whole file
|
||||
dd of=ofile oflag=nocache conv=notrunc,fdatasync count=0 || fail=1
|
||||
|
||||
# Drop cache for part of file
|
||||
dd if=ifile iflag=nocache skip=10 count=10 of=/dev/null || fail=1
|
||||
|
||||
# Stream data just using readahead cache
|
||||
dd if=ifile of=ofile iflag=nocache oflag=nocache || fail=1
|
||||
fi
|
||||
|
||||
Exit $fail
|
||||
39
tests/du/files0-from-dir
Executable file
39
tests/du/files0-from-dir
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
# ensure that du and wc handle --files0-from=DIR
|
||||
|
||||
# Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ du wc
|
||||
|
||||
mkdir dir
|
||||
|
||||
# Skip this test if reading from a directory succeeds.
|
||||
# In that case, using --files0-from=dir would yield garbage,
|
||||
# interpreting the directory entry as a sequence of
|
||||
# NUL-separated file names.
|
||||
cat dir > /dev/null && skip_ "cat dir/ succeeds"
|
||||
|
||||
for prog in du wc; do
|
||||
$prog --files0-from=dir > /dev/null 2>err && fail=1
|
||||
printf "$prog: dir:\n" > exp || fail=1
|
||||
# The diagnostic string is usually "Is a directory" (ENOTDIR),
|
||||
# but accept a different string or errno value.
|
||||
sed 's/dir:.*/dir:/' err > k; mv k err
|
||||
compare err exp || fail=1
|
||||
done
|
||||
|
||||
Exit $fail
|
||||
16
tests/fiemap-capable
Normal file
16
tests/fiemap-capable
Normal file
@@ -0,0 +1,16 @@
|
||||
import struct, fcntl, sys, os
|
||||
|
||||
def sizeof(t): return struct.calcsize(t)
|
||||
IOCPARM_MASK = 0x7f
|
||||
IOC_OUT = 0x40000000
|
||||
IOC_IN = 0x80000000
|
||||
IOC_INOUT = (IOC_IN|IOC_OUT)
|
||||
def _IOWR(x,y,t): return (IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|y)
|
||||
|
||||
try:
|
||||
fd = os.open (len (sys.argv) == 2 and sys.argv[1] or '.', os.O_RDONLY)
|
||||
struct_fiemap = '=qqllll'
|
||||
FS_IOC_FIEMAP = _IOWR (ord ('f'), 11, struct_fiemap)
|
||||
fcntl.ioctl (fd, FS_IOC_FIEMAP, struct.pack(struct_fiemap, 0,~0,0,0,0,0))
|
||||
except:
|
||||
sys.exit (1)
|
||||
@@ -28,30 +28,47 @@ my @b;
|
||||
foreach my $i (0..@A/2-1) { $a[$i] = { L_BLK => $A[2*$i], LEN => $A[2*$i+1] } };
|
||||
foreach my $i (0..@B/2-1) { $b[$i] = { L_BLK => $B[2*$i], LEN => $B[2*$i+1] } };
|
||||
|
||||
# Merge adjacent extents in array E.
|
||||
sub merge_extents($)
|
||||
{
|
||||
my ($e) = @_;
|
||||
|
||||
my $i = 0;
|
||||
while (1)
|
||||
{
|
||||
!defined $e->[$i+1]
|
||||
and last;
|
||||
$e->[$i]->{L_BLK} + $e->[$i]->{LEN} != $e->[$i+1]->{L_BLK}
|
||||
and ++$i, next;
|
||||
|
||||
$e->[$i]->{LEN} += $e->[$i+1]->{LEN};
|
||||
# Remove $e->[$i+1]
|
||||
splice @$e, $i+1, 1;
|
||||
}
|
||||
}
|
||||
|
||||
merge_extents \@a;
|
||||
merge_extents \@b;
|
||||
|
||||
@a == @b
|
||||
or die "$ME: extent counts differ, even after adjustment\n";
|
||||
|
||||
my $i = 0;
|
||||
my $j = 0;
|
||||
while (1)
|
||||
while (defined $a[$i])
|
||||
{
|
||||
!defined $a[$i] && !defined $b[$j]
|
||||
and exit 0;
|
||||
defined $a[$i] && defined $b[$j]
|
||||
or die "\@a and \@b have different lengths, even after adjustment\n";
|
||||
($a[$i]->{L_BLK} == $b[$j]->{L_BLK}
|
||||
&& $a[$i]->{LEN} == $b[$j]->{LEN})
|
||||
and next;
|
||||
($a[$i]->{LEN} < $b[$j]->{LEN}
|
||||
&& exists $a[$i+1] && $a[$i]->{LEN} + $a[$i+1]->{LEN} == $b[$j]->{LEN})
|
||||
and ++$i, next;
|
||||
exists $b[$j+1] && $a[$i]->{LEN} == $b[$i]->{LEN} + $b[$i+1]->{LEN}
|
||||
and ++$j, next;
|
||||
die "differing extent:\n"
|
||||
. " [$i]=$a[$i]->{L_BLK} $a[$i]->{LEN}\n"
|
||||
. " [$j]=$b[$j]->{L_BLK} $b[$j]->{LEN}\n"
|
||||
}
|
||||
continue
|
||||
{
|
||||
++$i;
|
||||
++$j;
|
||||
my $start_match = $a[$i]->{L_BLK} == $b[$i]->{L_BLK};
|
||||
my $len_match = $a[$i]->{LEN} == $b[$i]->{LEN};
|
||||
if ( ! ($start_match && ($len_match || $i == (@a - 1))))
|
||||
{
|
||||
# On XFS on Linux kernel 2.6.38, it was seen that the size of the
|
||||
# last extent can vary, and can extend beyond the length of the file.
|
||||
# So we ignore the length of the last extent, because if the
|
||||
# file is the wrong length we'll get failures elsewhere.
|
||||
die "$ME: differing extent:\n"
|
||||
. " [$i]=$a[$i]->{L_BLK} $a[$i]->{LEN}\n"
|
||||
. " [$i]=$b[$i]->{L_BLK} $b[$i]->{LEN}\n";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
### Setup "GNU" style for perl-mode and cperl-mode.
|
||||
|
||||
@@ -151,6 +151,14 @@ require_built_()
|
||||
test $skip_ = yes && skip_test_ "required program(s) not built"
|
||||
}
|
||||
|
||||
require_file_system_bytes_free_()
|
||||
{
|
||||
local req=$1
|
||||
local expr=$(stat -f --printf "$req / %S <= %a" .)
|
||||
awk "BEGIN{ exit !($expr) }" \
|
||||
|| skip_test_ "this test needs at least $req bytes of free space"
|
||||
}
|
||||
|
||||
uid_is_privileged_()
|
||||
{
|
||||
# Make sure id -u succeeds.
|
||||
@@ -216,6 +224,13 @@ skip_if_()
|
||||
|
||||
require_selinux_()
|
||||
{
|
||||
# When in a chroot of an SELinux-enabled system, but with a mock-simulated
|
||||
# SELinux-*disabled* system, recognize that SELinux is disabled system wide:
|
||||
grep 'selinuxfs$' /proc/filesystems > /dev/null \
|
||||
|| skip_test_ "this system lacks SELinux support"
|
||||
|
||||
# Independent of whether SELinux is enabled system-wide,
|
||||
# the current file system may lack SELinux support.
|
||||
case `ls -Zd .` in
|
||||
'? .'|'unlabeled .')
|
||||
skip_test_ "this system (or maybe just" \
|
||||
@@ -295,13 +310,16 @@ require_proc_pid_status_()
|
||||
kill $pid
|
||||
}
|
||||
|
||||
# Return nonzero if the specified directory is on a file system for
|
||||
# which FIEMAP support exists, and the file system type is new enough
|
||||
# (unlike ext2 and ext3) that it is hard to find an instance *without*
|
||||
# FIEMAP support.
|
||||
# Return nonzero if the specified path is on a file system for
|
||||
# which FIEMAP support exists. Note some file systems (like ext3 and btrfs)
|
||||
# only support FIEMAP for files, not directories.
|
||||
fiemap_capable_()
|
||||
{
|
||||
df -T -t btrfs -t xfs -t ext4 -t ocfs2 -t gfs2 "$@"
|
||||
if ! python < /dev/null; then
|
||||
warn_ 'fiemap_capable_: python missing: assuming not fiemap capable'
|
||||
return 1
|
||||
fi
|
||||
python $abs_srcdir/fiemap-capable "$@"
|
||||
}
|
||||
|
||||
# Does the current (working-dir) file system support sparse files?
|
||||
|
||||
@@ -74,10 +74,10 @@ Exit () { set +e; (exit $1); exit $1; }
|
||||
# the reason for skip/failure to console, rather than to the .log files.
|
||||
: ${stderr_fileno_=2}
|
||||
|
||||
warn_() { echo "$@" 1>&$stderr_fileno_; }
|
||||
fail_() { warn_ "$ME_: failed test: $@"; Exit 1; }
|
||||
skip_() { warn_ "$ME_: skipped test: $@"; Exit 77; }
|
||||
framework_failure_() { warn_ "$ME_: set-up failure: $@"; Exit 99; }
|
||||
warn_ () { echo "$@" 1>&$stderr_fileno_; }
|
||||
fail_ () { warn_ "$ME_: failed test: $@"; Exit 1; }
|
||||
skip_ () { warn_ "$ME_: skipped test: $@"; Exit 77; }
|
||||
framework_failure_ () { warn_ "$ME_: set-up failure: $@"; Exit 99; }
|
||||
|
||||
# Sanitize this shell to POSIX mode, if possible.
|
||||
DUALCASE=1; export DUALCASE
|
||||
@@ -193,7 +193,7 @@ fi
|
||||
test -n "$EXEEXT" && shopt -s expand_aliases
|
||||
|
||||
# Enable glibc's malloc-perturbing option.
|
||||
# This is cheap and useful for exposing code that depends on the fact that
|
||||
# This is useful for exposing code that depends on the fact that
|
||||
# malloc-related functions often return memory that is mostly zeroed.
|
||||
# If you have the time and cycles, use valgrind to do an even better job.
|
||||
: ${MALLOC_PERTURB_=87}
|
||||
@@ -202,22 +202,22 @@ export MALLOC_PERTURB_
|
||||
# This is a stub function that is run upon trap (upon regular exit and
|
||||
# interrupt). Override it with a per-test function, e.g., to unmount
|
||||
# a partition, or to undo any other global state changes.
|
||||
cleanup_() { :; }
|
||||
cleanup_ () { :; }
|
||||
|
||||
if ( diff --version < /dev/null 2>&1 | grep GNU ) > /dev/null 2>&1; then
|
||||
compare() { diff -u "$@"; }
|
||||
compare () { diff -u "$@"; }
|
||||
elif ( cmp --version < /dev/null 2>&1 | grep GNU ) > /dev/null 2>&1; then
|
||||
compare() { cmp -s "$@"; }
|
||||
compare () { cmp -s "$@"; }
|
||||
else
|
||||
compare() { cmp "$@"; }
|
||||
compare () { cmp "$@"; }
|
||||
fi
|
||||
|
||||
# An arbitrary prefix to help distinguish test directories.
|
||||
testdir_prefix_() { printf gt; }
|
||||
testdir_prefix_ () { printf gt; }
|
||||
|
||||
# Run the user-overridable cleanup_ function, remove the temporary
|
||||
# directory and exit with the incoming value of $?.
|
||||
remove_tmp_()
|
||||
remove_tmp_ ()
|
||||
{
|
||||
__st=$?
|
||||
cleanup_
|
||||
@@ -233,7 +233,7 @@ remove_tmp_()
|
||||
# contains only the specified bytes (see the case stmt below), then print
|
||||
# a space-separated list of those names and return 0. Otherwise, don't
|
||||
# print anything and return 1. Naming constraints apply also to DIR.
|
||||
find_exe_basenames_()
|
||||
find_exe_basenames_ ()
|
||||
{
|
||||
feb_dir_=$1
|
||||
feb_fail_=0
|
||||
@@ -266,7 +266,7 @@ find_exe_basenames_()
|
||||
# PROG that simply invokes PROG.exe, then return 0. If any selected
|
||||
# file name or the directory name, $1, contains an unexpected character,
|
||||
# define no alias and return 1.
|
||||
create_exe_shims_()
|
||||
create_exe_shims_ ()
|
||||
{
|
||||
case $EXEEXT in
|
||||
'') return 0 ;;
|
||||
@@ -288,7 +288,7 @@ create_exe_shims_()
|
||||
|
||||
# Use this function to prepend to PATH an absolute name for each
|
||||
# specified, possibly-$initial_cwd_-relative, directory.
|
||||
path_prepend_()
|
||||
path_prepend_ ()
|
||||
{
|
||||
while test $# != 0; do
|
||||
path_dir_=$1
|
||||
@@ -311,7 +311,7 @@ path_prepend_()
|
||||
export PATH
|
||||
}
|
||||
|
||||
setup_()
|
||||
setup_ ()
|
||||
{
|
||||
if test "$VERBOSE" = yes; then
|
||||
# Test whether set -x may cause the selected shell to corrupt an
|
||||
@@ -364,7 +364,7 @@ setup_()
|
||||
# - make only $MAX_TRIES_ attempts
|
||||
|
||||
# Helper function. Print $N pseudo-random bytes from a-zA-Z0-9.
|
||||
rand_bytes_()
|
||||
rand_bytes_ ()
|
||||
{
|
||||
n_=$1
|
||||
|
||||
@@ -396,7 +396,7 @@ rand_bytes_()
|
||||
| LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
|
||||
}
|
||||
|
||||
mktempd_()
|
||||
mktempd_ ()
|
||||
{
|
||||
case $# in
|
||||
2);;
|
||||
|
||||
@@ -27,7 +27,7 @@ umask 22
|
||||
# Output time as something constant
|
||||
export TIME_STYLE="+norm"
|
||||
|
||||
# helper to to strip ls columns up to "norm" time
|
||||
# helper to strip ls columns up to "norm" time
|
||||
qls() { sed 's/-r.*norm/norm/'; }
|
||||
|
||||
touch exe || framework_failure
|
||||
|
||||
@@ -150,6 +150,10 @@ my @Tests =
|
||||
{ERR=>$no_endpoint}],
|
||||
['inval5', '-f', '1-,-', {IN=>''}, {OUT=>''}, {EXIT=>1}, {ERR=>$no_endpoint}],
|
||||
['inval6', '-f', '-1,-', {IN=>''}, {OUT=>''}, {EXIT=>1}, {ERR=>$no_endpoint}],
|
||||
# This would evoke a segfault from 5.3.0..6.10
|
||||
['big-unbounded-b', '--output-d=:', '-b1234567890-', {IN=>''}, {OUT=>''}],
|
||||
['big-unbounded-c', '--output-d=:', '-c1234567890-', {IN=>''}, {OUT=>''}],
|
||||
['big-unbounded-f', '--output-d=:', '-f1234567890-', {IN=>''}, {OUT=>''}],
|
||||
);
|
||||
|
||||
@Tests = triple_test \@Tests;
|
||||
|
||||
@@ -226,7 +226,7 @@ id_setup () { args=-u; }
|
||||
|
||||
# Use env to avoid invoking built-in sleep of Solaris 11's /bin/sh.
|
||||
kill_setup () {
|
||||
env sleep 10m &
|
||||
env sleep 31.5 &
|
||||
args=$!
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ retry_delay_ stdbuf_unbuffer .1 6 || fail=1
|
||||
|
||||
# Ensure block buffering stdout takes effect
|
||||
# We don't currently test block buffering failures as
|
||||
# this doesn't work on on GLIBC-2.7 or GLIBC-2.9 at least.
|
||||
# this doesn't work on GLIBC-2.7 or GLIBC-2.9 at least.
|
||||
# stdbuf_blockbuffer()
|
||||
# {
|
||||
# local delay="$1"
|
||||
|
||||
@@ -132,6 +132,8 @@ my @Tests =
|
||||
|
||||
['streq-1', qw(t = t)],
|
||||
['streq-2', qw(t = f), {EXIT=>1}],
|
||||
['streqeq-1', qw(t == t)],
|
||||
['streqeq-2', qw(t == f), {EXIT=>1}],
|
||||
['streq-3', qw(! = !)],
|
||||
['streq-4', qw(= = =)],
|
||||
['streq-5', "'(' = '('"],
|
||||
|
||||
@@ -47,8 +47,7 @@ version=`
|
||||
`
|
||||
case $version in
|
||||
$PACKAGE_VERSION) ;;
|
||||
*) echo "$0: cannot access just-built mv as user $NON_ROOT_USERNAME" 1>&2
|
||||
fail=1 ;;
|
||||
*) skip_ "cannot access just-built mv as user $NON_ROOT_USERNAME";;
|
||||
esac
|
||||
|
||||
setuidgid $NON_ROOT_USERNAME env PATH="$PATH" \
|
||||
|
||||
@@ -24,12 +24,12 @@ fi
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
expensive_
|
||||
|
||||
# Wait up to 10 seconds for grep REGEXP FILE to succeed.
|
||||
# Wait several seconds for grep REGEXP FILE to succeed.
|
||||
# Usage: grep_timeout REGEXP FILE
|
||||
grep_timeout()
|
||||
{
|
||||
local j
|
||||
for j in $(seq 100); do
|
||||
for j in $(seq 150); do
|
||||
grep $1 $2 > /dev/null && return 0
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user