guestfish: Use xstrtol to parse integers (RHBZ#557655).

Current code uses atoi to parse the generator Int type and
atoll to parse the generator Int64 type.  The problem with the
ato* functions is that they don't cope with errors very well,
and they cannot parse numbers that begin with 0.. or 0x..
for octal and hexadecimal respectively.

This replaces the atoi call with a call to Gnulib xstrtol
and the atoll call with a call to Gnulib xstrtoll.

The generated code looks like this for all Int arguments:

  {
    strtol_error xerr;
    long r;

    xerr = xstrtol (argv[0], NULL, 0, &r, "");
    if (xerr != LONGINT_OK) {
      fprintf (stderr,
               _("%s: %s: invalid integer parameter (%s returned %d)\n"),
               cmd, "memsize", "xstrtol", xerr);
      return -1;
    }
    /* The Int type in the generator is a signed 31 bit int. */
    if (r < (-(2LL<<30)) || r > ((2LL<<30)-1)) {
      fprintf (stderr, _("%s: %s: integer out of range\n"), cmd, "memsize");
      return -1;
    }
    /* The check above should ensure this assignment does not overflow. */
    memsize = r;
  }

and like this for all Int64 arguments (note we don't need the
range check for these):

  {
    strtol_error xerr;
    long long r;

    xerr = xstrtoll (argv[1], NULL, 0, &r, "");
    if (xerr != LONGINT_OK) {
      fprintf (stderr,
               _("%s: %s: invalid integer parameter (%s returned %d)\n"),
               cmd, "size", "xstrtoll", xerr);
      return -1;
    }
    size = r;
  }

Note this also fixes an unrelated bug in guestfish handling of
RBufferOut.  We were using 'fwrite' without checking the return
value, and this could have caused silent failures, eg. in the case
where there was not enough disk space to store the resulting file,
or even if the program was interrupted (but continued) during the
write.

Replace this with Gnulib 'full-write', and check the return value
and report errors.
This commit is contained in:
Richard Jones
2010-01-22 11:01:06 +00:00
parent 306077ac96
commit 58abe782bf
8 changed files with 194 additions and 5 deletions

1
.gitignore vendored
View File

@@ -204,6 +204,7 @@ python/guestfs.py
python/guestfs-py.c
python/guestfs.pyc
regressions/test1.img
regressions/test.out
ruby/bindtests.rb
ruby/ext/guestfs/extconf.h
ruby/ext/guestfs/_guestfs.c

View File

@@ -60,6 +60,7 @@ modules='
arpa_inet
c-ctype
closeout
full-write
gitlog-to-changelog
gnu-make
gnumakefile
@@ -77,6 +78,8 @@ strndup
vasprintf
vc-list-files
warnings
xstrtol
xstrtoll
'
$gnulib_tool \

View File

@@ -296,6 +296,25 @@ must be escaped with a backslash.
command "/bin/echo 'foo bar'"
command "/bin/echo \'foo\'"
=head1 NUMBERS
Commands which take integers as parameters use the C convention which
is to use C<0> to prefix an octal number or C<0x> to prefix a
hexadecimal number. For example:
1234 decimal number 1234
02322 octal number, equivalent to decimal 1234
0x4d2 hexadecimal number, equivalent to decimal 1234
When using the C<chmod> command, you almost always want to specify an
octal number for the mode, and you must prefix it with C<0> (unlike
the Unix L<chmod(1)> program):
chmod 0777 /public # OK
chmod 777 /public # WRONG! This is mode 777 decimal = 01411 octal.
Commands that return numbers currently always print them in decimal.
=head1 WILDCARDS AND GLOBBING
Neither guestfish nor the underlying guestfs API performs

13
m4/.gitignore vendored
View File

@@ -130,3 +130,16 @@ xsize.m4
/yield.m4
/fcntl-o.m4
/warn-on-use.m4
/getopt.m4
/stat.m4
/symlink.m4
/time_h.m4
/xstrtol.m4
/safe-read.m4
/safe-write.m4
/ssize_t.m4
/write.m4
/strtol.m4
/strtoll.m4
/strtoul.m4
/strtoull.m4

View File

@@ -26,6 +26,7 @@ include $(top_srcdir)/subdir-rules.mk
TESTS = \
rhbz503169c10.sh \
rhbz503169c13.sh \
rhbz557655.sh \
test-cancellation-download-librarycancels.sh \
test-cancellation-upload-daemoncancels.sh \
test-find0.sh \
@@ -55,4 +56,5 @@ TESTS_ENVIRONMENT = \
EXTRA_DIST = \
$(FAILING_TESTS) \
$(SKIPPED_TESTS) \
$(TESTS)
$(TESTS) \
rhbz557655-expected.out

View File

@@ -0,0 +1,22 @@
0
16
8
-1073741824
1073741823
set-memsize: memsize: integer out of range
set-memsize: memsize: integer out of range
set-memsize: memsize: integer out of range
set-memsize: memsize: integer out of range
set-memsize: memsize: invalid integer parameter (xstrtol returned 4)
set-memsize: memsize: invalid integer parameter (xstrtol returned 2)
set-memsize: memsize: invalid integer parameter (xstrtol returned 2)
set-memsize: memsize: invalid integer parameter (xstrtol returned 2)
1234
1234
1234
libguestfs: error: truncate_size: ftruncate: /test: File too large
truncate-size: size: invalid integer parameter (xstrtoll returned 1)
truncate-size: size: invalid integer parameter (xstrtoll returned 4)
truncate-size: size: invalid integer parameter (xstrtoll returned 2)
truncate-size: size: invalid integer parameter (xstrtoll returned 2)
truncate-size: size: invalid integer parameter (xstrtoll returned 2)

83
regressions/rhbz557655.sh Executable file
View File

@@ -0,0 +1,83 @@
#!/bin/bash -
# libguestfs
# Copyright (C) 2009 Red Hat 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 2 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, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# Regression test for:
# https://bugzilla.redhat.com/show_bug.cgi?id=557655
# "guestfish number parsing should not use atoi, should support '0...' for octal and '0x...' for hexadecimal"
set -e
rm -f test.out
export LANG=C
../fish/guestfish >> test.out 2>&1 <<EOF
# set-memsize is just a convenient non-daemon function that
# takes a single integer argument.
set-memsize 0
get-memsize
set-memsize 0x10
get-memsize
set-memsize 010
get-memsize
set-memsize -1073741824
get-memsize
set-memsize 1073741823
get-memsize
# the following should all provoke error messages:
-set-memsize -9000000000000000
-set-memsize 9000000000000000
-set-memsize 0x900000000000
-set-memsize 07777770000000000000
-set-memsize ABC
-set-memsize 09
-set-memsize 123K
-set-memsize 123L
EOF
../fish/guestfish >> test.out 2>&1 <<EOF
alloc test1.img 10M
run
part-disk /dev/sda mbr
mkfs ext2 /dev/sda1
mount /dev/sda1 /
touch /test
# truncate-size takes an Int64 argument
truncate-size /test 1234
filesize /test
truncate-size /test 0x4d2
filesize /test
truncate-size /test 02322
filesize /test
# should parse OK, but underlying filesystem will reject it:
-truncate-size /test 0x7fffffffffffffff
# larger than 64 bits, should be an error:
-truncate-size /test 0x10000000000000000
# these should all provoke parse errors:
-truncate-size /test ABC
-truncate-size /test 09
-truncate-size /test 123K
-truncate-size /test 123L
EOF
diff -u test.out rhbz557655-expected.out
rm test.out test1.img

View File

@@ -1326,7 +1326,11 @@ as necessary. This is like the C<mkdir -p> shell command.");
"change file mode",
"\
Change the mode (permissions) of C<path> to C<mode>. Only
numeric modes are supported.");
numeric modes are supported.
I<Note>: When using this command from guestfish, C<mode>
by default would be decimal, unless you prefix it with
C<0> to get octal, ie. use C<0700> not C<700>.");
("chown", (RErr, [Int "owner"; Int "group"; Pathname "path"]), 35, [],
[], (* XXX Need stat command to test *)
@@ -6861,6 +6865,8 @@ and generate_fish_cmds () =
fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
) all_functions_sorted in
pr "#include <config.h>\n";
pr "\n";
pr "#include <stdio.h>\n";
pr "#include <stdlib.h>\n";
pr "#include <string.h>\n";
@@ -6868,6 +6874,8 @@ and generate_fish_cmds () =
pr "\n";
pr "#include <guestfs.h>\n";
pr "#include \"c-ctype.h\"\n";
pr "#include \"full-write.h\"\n";
pr "#include \"xstrtol.h\"\n";
pr "#include \"fish.h\"\n";
pr "\n";
@@ -7079,6 +7087,34 @@ and generate_fish_cmds () =
pr " fprintf (stderr, _(\"type 'help %%s' for help on %%s\\n\"), cmd, cmd);\n";
pr " return -1;\n";
pr " }\n";
let parse_integer fn fntyp rtyp range name i =
pr " {\n";
pr " strtol_error xerr;\n";
pr " %s r;\n" fntyp;
pr "\n";
pr " xerr = %s (argv[%d], NULL, 0, &r, \"\");\n" fn i;
pr " if (xerr != LONGINT_OK) {\n";
pr " fprintf (stderr,\n";
pr " _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n";
pr " cmd, \"%s\", \"%s\", xerr);\n" name fn;
pr " return -1;\n";
pr " }\n";
(match range with
| None -> ()
| Some (min, max, comment) ->
pr " /* %s */\n" comment;
pr " if (r < %s || r > %s) {\n" min max;
pr " fprintf (stderr, _(\"%%s: %%s: integer out of range\\n\"), cmd, \"%s\");\n"
name;
pr " return -1;\n";
pr " }\n";
pr " /* The check above should ensure this assignment does not overflow. */\n";
);
pr " %s = r;\n" name;
pr " }\n";
in
iteri (
fun i ->
function
@@ -7104,9 +7140,15 @@ and generate_fish_cmds () =
| Bool name ->
pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i
| Int name ->
pr " %s = atoi (argv[%d]);\n" name i
let range =
let min = "(-(2LL<<30))"
and max = "((2LL<<30)-1)"
and comment =
"The Int type in the generator is a signed 31 bit int." in
Some (min, max, comment) in
parse_integer "xstrtol" "long" "int" range name i
| Int64 name ->
pr " %s = atoll (argv[%d]);\n" name i
parse_integer "xstrtoll" "long long" "int64_t" None name i
) (snd style);
(* Call C API function. *)
@@ -7177,7 +7219,11 @@ and generate_fish_cmds () =
pr " return 0;\n"
| RBufferOut _ ->
pr " if (r == NULL) return -1;\n";
pr " fwrite (r, size, 1, stdout);\n";
pr " if (full_write (1, r, size) != size) {\n";
pr " perror (\"write\");\n";
pr " free (r);\n";
pr " return -1;\n";
pr " }\n";
pr " free (r);\n";
pr " return 0;\n"
);