dd: fix a bug where we don't report write errors

testsuite: small cleanup

full_write_or_warn                                    38      40      +2
write_and_stats                                       66      67      +1
dd_main                                             1358    1335     -23
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 3/-23)             Total: -20 bytes
This commit is contained in:
Denis Vlasenko 2007-12-02 01:44:42 +00:00
parent 1796e2c495
commit e2532ab5f2
2 changed files with 62 additions and 69 deletions

View File

@ -14,6 +14,11 @@
/* This is a NOEXEC applet. Be very careful! */ /* This is a NOEXEC applet. Be very careful! */
enum {
ifd = STDIN_FILENO,
ofd = STDOUT_FILENO,
};
static const struct suffix_mult dd_suffixes[] = { static const struct suffix_mult dd_suffixes[] = {
{ "c", 1 }, { "c", 1 },
{ "w", 2 }, { "w", 2 },
@ -45,19 +50,19 @@ static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
G.out_full, G.out_part); G.out_full, G.out_part);
} }
static ssize_t full_write_or_warn(int fd, const void *buf, size_t len, static ssize_t full_write_or_warn(const void *buf, size_t len,
const char *const filename) const char *const filename)
{ {
ssize_t n = full_write(fd, buf, len); ssize_t n = full_write(ofd, buf, len);
if (n < 0) if (n < 0)
bb_perror_msg("writing '%s'", filename); bb_perror_msg("writing '%s'", filename);
return n; return n;
} }
static bool write_and_stats(int fd, const void *buf, size_t len, size_t obs, static bool write_and_stats(const void *buf, size_t len, size_t obs,
const char *filename) const char *filename)
{ {
ssize_t n = full_write_or_warn(fd, buf, len, filename); ssize_t n = full_write_or_warn(buf, len, filename);
if (n < 0) if (n < 0)
return 1; return 1;
if (n == obs) if (n == obs)
@ -105,13 +110,13 @@ int dd_main(int argc, char **argv)
OP_conv_noerror, OP_conv_noerror,
#endif #endif
}; };
int exitcode = EXIT_FAILURE;
size_t ibs = 512, obs = 512; size_t ibs = 512, obs = 512;
ssize_t n, w; ssize_t n, w;
char *ibuf, *obuf; char *ibuf, *obuf;
/* And these are all zeroed at once! */ /* And these are all zeroed at once! */
struct { struct {
int flags; int flags;
int ifd, ofd;
size_t oc; size_t oc;
off_t count; off_t count;
off_t seek, skip; off_t seek, skip;
@ -121,8 +126,6 @@ int dd_main(int argc, char **argv)
#endif #endif
} Z; } Z;
#define flags (Z.flags ) #define flags (Z.flags )
#define ifd (Z.ifd )
#define ofd (Z.ofd )
#define oc (Z.oc ) #define oc (Z.oc )
#define count (Z.count ) #define count (Z.count )
#define seek (Z.seek ) #define seek (Z.seek )
@ -133,6 +136,7 @@ int dd_main(int argc, char **argv)
memset(&Z, 0, sizeof(Z)); memset(&Z, 0, sizeof(Z));
INIT_G(); INIT_G();
//fflush(NULL); - is this needed because of NOEXEC?
#if ENABLE_FEATURE_DD_SIGNAL_HANDLING #if ENABLE_FEATURE_DD_SIGNAL_HANDLING
sigact.sa_handler = dd_output_status; sigact.sa_handler = dd_output_status;
@ -227,9 +231,8 @@ int dd_main(int argc, char **argv)
obuf = xmalloc(obs); obuf = xmalloc(obs);
} }
if (infile != NULL) if (infile != NULL)
ifd = xopen(infile, O_RDONLY); xmove_fd(xopen(infile, O_RDONLY), ifd);
else { else {
/* ifd = STDIN_FILENO; - it's zero and it's already there */
infile = bb_msg_standard_input; infile = bb_msg_standard_input;
} }
if (outfile != NULL) { if (outfile != NULL) {
@ -238,7 +241,7 @@ int dd_main(int argc, char **argv)
if (!seek && !(flags & FLAG_NOTRUNC)) if (!seek && !(flags & FLAG_NOTRUNC))
oflag |= O_TRUNC; oflag |= O_TRUNC;
ofd = xopen(outfile, oflag); xmove_fd(xopen(outfile, oflag), ofd);
if (seek && !(flags & FLAG_NOTRUNC)) { if (seek && !(flags & FLAG_NOTRUNC)) {
if (ftruncate(ofd, seek * obs) < 0) { if (ftruncate(ofd, seek * obs) < 0) {
@ -250,7 +253,6 @@ int dd_main(int argc, char **argv)
} }
} }
} else { } else {
ofd = STDOUT_FILENO;
outfile = bb_msg_standard_output; outfile = bb_msg_standard_output;
} }
if (skip) { if (skip) {
@ -276,11 +278,10 @@ int dd_main(int argc, char **argv)
if (n == 0) if (n == 0)
break; break;
if (n < 0) { if (n < 0) {
if (flags & FLAG_NOERROR) { if (!(flags & FLAG_NOERROR))
n = ibs;
bb_simple_perror_msg(infile);
} else
goto die_infile; goto die_infile;
n = ibs;
bb_simple_perror_msg(infile);
} }
if ((size_t)n == ibs) if ((size_t)n == ibs)
G.in_full++; G.in_full++;
@ -303,17 +304,17 @@ int dd_main(int argc, char **argv)
tmp += d; tmp += d;
oc += d; oc += d;
if (oc == obs) { if (oc == obs) {
if (write_and_stats(ofd, obuf, obs, obs, outfile)) if (write_and_stats(obuf, obs, obs, outfile))
goto out_status; goto out_status;
oc = 0; oc = 0;
} }
} }
} else if (write_and_stats(ofd, ibuf, n, obs, outfile)) } else if (write_and_stats(ibuf, n, obs, outfile))
goto out_status; goto out_status;
} }
if (ENABLE_FEATURE_DD_IBS_OBS && oc) { if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
w = full_write_or_warn(ofd, obuf, oc, outfile); w = full_write_or_warn(obuf, oc, outfile);
if (w < 0) goto out_status; if (w < 0) goto out_status;
if (w > 0) if (w > 0)
G.out_part++; G.out_part++;
@ -327,8 +328,10 @@ int dd_main(int argc, char **argv)
die_outfile: die_outfile:
bb_simple_perror_msg_and_die(outfile); bb_simple_perror_msg_and_die(outfile);
} }
exitcode = EXIT_SUCCESS;
out_status: out_status:
dd_output_status(0); dd_output_status(0);
return EXIT_SUCCESS; return exitcode;
} }

View File

@ -1,20 +1,20 @@
#!/bin/sh #!/bin/sh
# Run old-style test. # Run one old-style test.
# Tests are stored in applet/testcase shell scripts.
# They are run using "sh -x -e applet/testcase".
# Option -e will make testcase stop on the first failed command.
run_applet_testcase() run_applet_testcase()
{ {
local applet=$1 local applet=$1
local testcase=$2 local testcase=$2
local status=0 local status
local RES=
local uc_applet=$(echo $applet | tr a-z A-Z) local uc_applet=$(echo $applet | tr a-z A-Z)
local testname=$(basename $testcase) local testname=$(basename $testcase)
if grep -q "^# CONFIG_${uc_applet} is not set$" $bindir/.config; then if grep -q "^# CONFIG_${uc_applet} is not set$" $bindir/.config; then
echo UNTESTED: $testname echo "UNTESTED: $testname"
return 0 return 0
fi fi
@ -22,72 +22,67 @@ run_applet_testcase()
local feature=`sed -ne 's/^# FEATURE: //p' $testcase` local feature=`sed -ne 's/^# FEATURE: //p' $testcase`
if grep -q "^# ${feature} is not set$" $bindir/.config; then if grep -q "^# ${feature} is not set$" $bindir/.config; then
echo UNTESTED: $testname echo "UNTESTED: $testname"
return 0 return 0
fi fi
fi fi
rm -rf tmp rm -rf ".tmpdir.$applet"
mkdir -p tmp mkdir -p ".tmpdir.$applet"
pushd tmp > /dev/null cd ".tmpdir.$applet" || return 1
# echo Running testcase $testcase # echo "Running testcase $testcase"
d=$tsdir sh -x -e $testcase >.logfile.txt 2>&1 || status=$? d="$tsdir" sh -x -e "$testcase" >"$testname.stdout.txt" 2>&1
status=$?
if [ $status -ne 0 ]; then if [ $status != 0 ]; then
echo FAIL: $testname echo "FAIL: $testname"
if [ $verbose -gt 0 ]; then if [ x"$VERBOSE" != x ]; then
cat .logfile.txt cat "$testname.stdout.txt"
fi fi
status=$?
else else
echo PASS: $testname echo "PASS: $testname"
rm -f .logfile.txt
status=$?
fi fi
popd > /dev/null cd ..
rm -rf tmp rm -rf ".tmpdir.$applet"
return $status return $status
} }
# Run all old-style tests for given applet
run_applet_tests() run_applet_tests()
{ {
local applet=$1 local applet=$1
local status=0 local status=0
for testcase in $tsdir/$applet/*; do for testcase in $tsdir/$applet/*; do
if [ "$testcase" = "$tsdir/$applet/CVS" ]; then if [ "$testcase" = "$tsdir/$applet/CVS" ]; then
continue continue
fi fi
if ! run_applet_testcase $applet $testcase; then run_applet_testcase $applet $testcase
status=1 test $? = 0 || status=1
fi
done done
return $status return $status
} }
status=0
verbose=0
[ -n "$tsdir" ] || tsdir=$(pwd) [ -n "$tsdir" ] || tsdir=$(pwd)
[ -n "$bindir" ] || bindir=$(dirname $(pwd)) [ -n "$bindir" ] || bindir=$(dirname $(pwd))
PATH="$bindir:$PATH" PATH="$bindir:$PATH"
if [ x"$VERBOSE" = x ]; then
export VERBOSE=
fi
if [ x"$1" = x"-v" ]; then if [ x"$1" = x"-v" ]; then
verbose=1 export VERBOSE=1
export VERBOSE=$verbose
shift shift
fi fi
implemented=$( implemented=$(
$bindir/busybox 2>&1 | $bindir/busybox 2>&1 |
while read line; do while read line; do
if test x"$line" = x"Currently defined functions:"; then if [ x"$line" = x"Currently defined functions:" ]; then
xargs | sed 's/,//g' xargs | sed 's/,//g'
break break
fi fi
@ -109,39 +104,34 @@ for i in $implemented; do
done done
# Set up option flags so tests can be selective. # Set up option flags so tests can be selective.
export OPTIONFLAGS=:$(sed -nr 's/^CONFIG_//p' $bindir/.config | sed 's/=.*//' | xargs | sed 's/ /:/g')
configfile=${bindir}/.config status=0
export OPTIONFLAGS=:$(sed -nr 's/^CONFIG_(.*)=.*/\1/p' $configfile | xargs | sed 's/ /:/g')
for applet in $applets; do for applet in $applets; do
if [ "$applet" = "links" ]; then continue; fi if [ "$applet" = "links" ]; then continue; fi
# Any old-style tests for this applet?
if [ "$applet" != "CVS" -a -d "$tsdir/$applet" ]; then if [ "$applet" != "CVS" -a -d "$tsdir/$applet" ]; then
if ! run_applet_tests $applet; then run_applet_tests "$applet"
status=1 test $? = 0 || status=1
fi
fi fi
# Is this a new-style test? # Is this a new-style test?
if [ -f ${applet}.tests ]; then if [ -f "${applet}.tests" ]; then
if [ ! -h "$LINKSDIR/$applet" ] && [ "${applet:0:4}" != "all_" ]; then if [ ! -h "$LINKSDIR/$applet" ] && [ "${applet:0:4}" != "all_" ]; then
echo "SKIPPED: $applet (not built)" echo "SKIPPED: $applet (not built)"
continue continue
fi fi
if PATH="$LINKSDIR:$tsdir:$bindir:$PATH" \ # echo "Running test ${tsdir:-.}/${applet}.tests"
"${tsdir:-.}/$applet".tests PATH="$LINKSDIR:$tsdir:$bindir:$PATH" "${tsdir:-.}/${applet}.tests"
then test $? = 0 || status=1
:
else
status=1
fi
fi fi
done done
# Leaving the dir makes it somewhat easier to run failed test by hand # Leaving the dir makes it somewhat easier to run failed test by hand
#rm -rf "$LINKSDIR" #rm -rf "$LINKSDIR"
if [ $status != 0 -a x"$VERBOSE" = x ]; then if [ $status != 0 -a x"$VERBOSE" = x ]; then
echo "Failures detected, running with VERBOSE=1 will give more info" echo "Failures detected, running with -v (verbose) will give more info"
fi fi
exit $status exit $status