From 0232e722826cfda0f6042da983f9eb871f24e946 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 20 Apr 2009 15:54:22 +0100 Subject: [PATCH] Added tar-in, tar-out, tgz-in, tgz-out commands. --- HACKING | 2 + daemon/Makefile.am | 1 + daemon/actions.h | 4 + daemon/stubs.c | 108 +++++++++++++ daemon/tar.c | 279 ++++++++++++++++++++++++++++++++++ fish/cmds.c | 92 +++++++++++ fish/completion.c | 4 + guestfish-actions.pod | 44 ++++++ guestfs-actions.pod | 52 +++++++ images/Makefile.am | 5 +- images/helloworld.tar | Bin 0 -> 10240 bytes images/helloworld.tar.gz | Bin 0 -> 190 bytes ocaml/guestfs.ml | 4 + ocaml/guestfs.mli | 12 ++ ocaml/guestfs_c_actions.c | 96 ++++++++++++ perl/Guestfs.xs | 48 ++++++ perl/lib/Sys/Guestfs.pm | 28 ++++ python/guestfs-py.c | 108 +++++++++++++ python/guestfs.py | 32 ++++ ruby/ext/guestfs/_guestfs.c | 108 +++++++++++++ src/generator.ml | 108 ++++++++----- src/guestfs-actions.c | 360 ++++++++++++++++++++++++++++++++++++++++++++ src/guestfs-actions.h | 4 + src/guestfs_protocol.c | 40 +++++ src/guestfs_protocol.h | 34 ++++- src/guestfs_protocol.x | 20 +++ tests.c | 214 +++++++++++++++++++++----- 27 files changed, 1734 insertions(+), 73 deletions(-) create mode 100644 daemon/tar.c create mode 100644 images/helloworld.tar create mode 100644 images/helloworld.tar.gz diff --git a/HACKING b/HACKING index 1c2b20c..85d094b 100644 --- a/HACKING +++ b/HACKING @@ -44,6 +44,8 @@ images/ Some guest images to test against. These are gzipped to save space. You have to unzip them before use. + Also contains some files used by the test suite. + m4/ M4 macros used by autoconf. diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 1886639..4f7ce24 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -36,6 +36,7 @@ guestfsd_SOURCES = \ stat.c \ stubs.c \ sync.c \ + tar.c \ tune2fs.c \ upload.c \ ../src/guestfs_protocol.h \ diff --git a/daemon/actions.h b/daemon/actions.h index cffd8ef..d55f493 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -89,3 +89,7 @@ extern int do_blockdev_rereadpt (const char *device); extern int do_upload (const char *remotefilename); extern int do_download (const char *remotefilename); extern char *do_checksum (const char *csumtype, const char *path); +extern int do_tar_in (const char *directory); +extern int do_tar_out (const char *directory); +extern int do_tgz_in (const char *directory); +extern int do_tgz_out (const char *directory); diff --git a/daemon/stubs.c b/daemon/stubs.c index 3039404..8201641 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -1661,6 +1661,102 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_checksum_args, (char *) &args); } +static void tar_in_stub (XDR *xdr_in) +{ + int r; + struct guestfs_tar_in_args args; + const char *directory; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_tar_in_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "tar_in"); + return; + } + directory = args.directory; + + r = do_tar_in (directory); + if (r == -1) + /* do_tar_in has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_tar_in_args, (char *) &args); +} + +static void tar_out_stub (XDR *xdr_in) +{ + int r; + struct guestfs_tar_out_args args; + const char *directory; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_tar_out_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "tar_out"); + return; + } + directory = args.directory; + + r = do_tar_out (directory); + if (r == -1) + /* do_tar_out has already called reply_with_error */ + goto done; + + /* do_tar_out has already sent a reply */ +done: + xdr_free ((xdrproc_t) xdr_guestfs_tar_out_args, (char *) &args); +} + +static void tgz_in_stub (XDR *xdr_in) +{ + int r; + struct guestfs_tgz_in_args args; + const char *directory; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_tgz_in_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "tgz_in"); + return; + } + directory = args.directory; + + r = do_tgz_in (directory); + if (r == -1) + /* do_tgz_in has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_tgz_in_args, (char *) &args); +} + +static void tgz_out_stub (XDR *xdr_in) +{ + int r; + struct guestfs_tgz_out_args args; + const char *directory; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_tgz_out_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "tgz_out"); + return; + } + directory = args.directory; + + r = do_tgz_out (directory); + if (r == -1) + /* do_tgz_out has already called reply_with_error */ + goto done; + + /* do_tgz_out has already sent a reply */ +done: + xdr_free ((xdrproc_t) xdr_guestfs_tgz_out_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -1868,6 +1964,18 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_CHECKSUM: checksum_stub (xdr_in); break; + case GUESTFS_PROC_TAR_IN: + tar_in_stub (xdr_in); + break; + case GUESTFS_PROC_TAR_OUT: + tar_out_stub (xdr_in); + break; + case GUESTFS_PROC_TGZ_IN: + tgz_in_stub (xdr_in); + break; + case GUESTFS_PROC_TGZ_OUT: + tgz_out_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); } diff --git a/daemon/tar.c b/daemon/tar.c new file mode 100644 index 0000000..ecf919d --- /dev/null +++ b/daemon/tar.c @@ -0,0 +1,279 @@ +/* libguestfs - the guestfsd daemon + * 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. + */ + +#include + +#include +#include +#include +#include + +#include "../src/guestfs_protocol.h" +#include "daemon.h" +#include "actions.h" + +static int +fwrite_cb (void *fp_ptr, const void *buf, int len) +{ + FILE *fp = *(FILE **)fp_ptr; + return fwrite (buf, len, 1, fp) == 1 ? 0 : -1; +} + +/* Has one FileIn parameter. */ +int +do_tar_in (const char *dir) +{ + int err, r, len; + FILE *fp; + char *cmd; + + if (!root_mounted || dir[0] != '/') { + cancel_receive (); + reply_with_error ("tar-in: root must be mounted and path must be absolute"); + return -1; + } + + /* "tar -C /sysroot%s -xf -" but we have to quote the dir. */ + len = 2 * strlen (dir) + 32; + cmd = malloc (len); + if (!cmd) { + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("malloc"); + return -1; + } + strcpy (cmd, "tar -C /sysroot"); + shell_quote (cmd+15, len-15, dir); + strcat (cmd, " -xf -"); + + fp = popen (cmd, "w"); + if (fp == NULL) { + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("%s", cmd); + return -1; + } + + r = receive_file (fwrite_cb, &fp); + if (r == -1) { /* write error */ + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("write: %s", dir); + pclose (fp); + return -1; + } + if (r == -2) { /* cancellation from library */ + pclose (fp); + /* Do NOT send any error. */ + return -1; + } + + if (pclose (fp) == -1) { + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("pclose: %s", dir); + return -1; + } + + return 0; +} + +/* Has one FileOut parameter. */ +int +do_tar_out (const char *dir) +{ + int r, len; + FILE *fp; + char *cmd; + char buf[GUESTFS_MAX_CHUNK_SIZE]; + + NEED_ROOT (-1); + ABS_PATH (dir, -1); + + /* "tar -C /sysroot%s -cf - ." but we have to quote the dir. */ + len = 2 * strlen (dir) + 32; + cmd = malloc (len); + if (!cmd) { + reply_with_perror ("malloc"); + return -1; + } + strcpy (cmd, "tar -C /sysroot"); + shell_quote (cmd+15, len-15, dir); + strcat (cmd, " -cf - ."); + + fp = popen (cmd, "r"); + if (fp == NULL) { + reply_with_perror ("%s", cmd); + return -1; + } + + /* Now we must send the reply message, before the file contents. After + * this there is no opportunity in the protocol to send any error + * message back. Instead we can only cancel the transfer. + */ + reply (NULL, NULL); + + while ((r = fread (buf, 1, sizeof buf, fp)) > 0) { + if (send_file_write (buf, r) < 0) { + pclose (fp); + return -1; + } + } + + if (r == -1) { + perror (dir); + send_file_end (1); /* Cancel. */ + pclose (fp); + return -1; + } + + if (pclose (fp) == -1) { + perror (dir); + send_file_end (1); /* Cancel. */ + return -1; + } + + send_file_end (0); /* Normal end of file. */ + return 0; +} + +/* Has one FileIn parameter. */ +int +do_tgz_in (const char *dir) +{ + int err, r, len; + FILE *fp; + char *cmd; + + if (!root_mounted || dir[0] != '/') { + cancel_receive (); + reply_with_error ("tar-in: root must be mounted and path must be absolute"); + return -1; + } + + /* "tar -C /sysroot%s -zxf -" but we have to quote the dir. */ + len = 2 * strlen (dir) + 32; + cmd = malloc (len); + if (!cmd) { + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("malloc"); + return -1; + } + strcpy (cmd, "tar -C /sysroot"); + shell_quote (cmd+15, len-15, dir); + strcat (cmd, " -zxf -"); + + fp = popen (cmd, "w"); + if (fp == NULL) { + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("%s", cmd); + return -1; + } + + r = receive_file (fwrite_cb, &fp); + if (r == -1) { /* write error */ + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("write: %s", dir); + pclose (fp); + return -1; + } + if (r == -2) { /* cancellation from library */ + pclose (fp); + /* Do NOT send any error. */ + return -1; + } + + if (pclose (fp) == -1) { + err = errno; + cancel_receive (); + errno = err; + reply_with_perror ("pclose: %s", dir); + return -1; + } + + return 0; +} + +/* Has one FileOut parameter. */ +int +do_tgz_out (const char *dir) +{ + int r, len; + FILE *fp; + char *cmd; + char buf[GUESTFS_MAX_CHUNK_SIZE]; + + NEED_ROOT (-1); + ABS_PATH (dir, -1); + + /* "tar -C /sysroot%s -zcf - ." but we have to quote the dir. */ + len = 2 * strlen (dir) + 32; + cmd = malloc (len); + if (!cmd) { + reply_with_perror ("malloc"); + return -1; + } + strcpy (cmd, "tar -C /sysroot"); + shell_quote (cmd+15, len-15, dir); + strcat (cmd, " -zcf - ."); + + fp = popen (cmd, "r"); + if (fp == NULL) { + reply_with_perror ("%s", cmd); + return -1; + } + + /* Now we must send the reply message, before the file contents. After + * this there is no opportunity in the protocol to send any error + * message back. Instead we can only cancel the transfer. + */ + reply (NULL, NULL); + + while ((r = fread (buf, 1, sizeof buf, fp)) > 0) { + if (send_file_write (buf, r) < 0) { + pclose (fp); + return -1; + } + } + + if (r == -1) { + perror (dir); + send_file_end (1); /* Cancel. */ + pclose (fp); + return -1; + } + + if (pclose (fp) == -1) { + perror (dir); + send_file_end (1); /* Cancel. */ + return -1; + } + + send_file_end (0); /* Normal end of file. */ + return 0; +} diff --git a/fish/cmds.c b/fish/cmds.c index dd574f2..82be261 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -106,6 +106,10 @@ void list_commands (void) printf ("%-20s %s\n", "stat", "get file information"); printf ("%-20s %s\n", "statvfs", "get file system statistics"); printf ("%-20s %s\n", "sync", "sync disks, writes are flushed through to the disk image"); + printf ("%-20s %s\n", "tar-in", "unpack tarfile to directory"); + printf ("%-20s %s\n", "tar-out", "pack directory into tarfile"); + printf ("%-20s %s\n", "tgz-in", "unpack compressed tarball to directory"); + printf ("%-20s %s\n", "tgz-out", "pack directory into compressed tarball"); printf ("%-20s %s\n", "touch", "update file timestamps or create a new file"); printf ("%-20s %s\n", "tune2fs-l", "get ext2/ext3 superblock details"); printf ("%-20s %s\n", "umount", "unmount a filesystem"); @@ -372,6 +376,18 @@ void display_command (const char *cmd) if (strcasecmp (cmd, "checksum") == 0) pod2text ("checksum - compute MD5, SHAx or CRC checksum of file", " checksum \n\nThis call computes the MD5, SHAx or CRC checksum of the\nfile named C.\n\nThe type of checksum to compute is given by the C\nparameter which must have one of the following values:\n\n=over 4\n\n=item C\n\nCompute the cyclic redundancy check (CRC) specified by POSIX\nfor the C command.\n\n=item C\n\nCompute the MD5 hash (using the C program).\n\n=item C\n\nCompute the SHA1 hash (using the C program).\n\n=item C\n\nCompute the SHA224 hash (using the C program).\n\n=item C\n\nCompute the SHA256 hash (using the C program).\n\n=item C\n\nCompute the SHA384 hash (using the C program).\n\n=item C\n\nCompute the SHA512 hash (using the C program).\n\n=back\n\nThe checksum is returned as a printable string."); else + if (strcasecmp (cmd, "tar_in") == 0 || strcasecmp (cmd, "tar-in") == 0) + pod2text ("tar-in - unpack tarfile to directory", " tar-in \n\nThis command uploads and unpacks local file C (an\nI tar file) into C.\n\nTo upload a compressed tarball, use C."); + else + if (strcasecmp (cmd, "tar_out") == 0 || strcasecmp (cmd, "tar-out") == 0) + pod2text ("tar-out - pack directory into tarfile", " tar-out \n\nThis command packs the contents of C and downloads\nit to local file C.\n\nTo download a compressed tarball, use C."); + else + if (strcasecmp (cmd, "tgz_in") == 0 || strcasecmp (cmd, "tgz-in") == 0) + pod2text ("tgz-in - unpack compressed tarball to directory", " tgz-in \n\nThis command uploads and unpacks local file C (a\nI tar file) into C.\n\nTo upload an uncompressed tarball, use C."); + else + if (strcasecmp (cmd, "tgz_out") == 0 || strcasecmp (cmd, "tgz-out") == 0) + pod2text ("tgz-out - pack directory into compressed tarball", " tgz-out \n\nThis command packs the contents of C and downloads\nit to local file C.\n\nTo download an uncompressed tarball, use C."); + else display_builtin_command (cmd); } @@ -1801,6 +1817,70 @@ static int run_checksum (const char *cmd, int argc, char *argv[]) return 0; } +static int run_tar_in (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *tarfile; + const char *directory; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + tarfile = strcmp (argv[0], "-") != 0 ? argv[0] : "/dev/stdin"; + directory = argv[1]; + r = guestfs_tar_in (g, tarfile, directory); + return r; +} + +static int run_tar_out (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *directory; + const char *tarfile; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + directory = argv[0]; + tarfile = strcmp (argv[1], "-") != 0 ? argv[1] : "/dev/stdout"; + r = guestfs_tar_out (g, directory, tarfile); + return r; +} + +static int run_tgz_in (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *tarball; + const char *directory; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + tarball = strcmp (argv[0], "-") != 0 ? argv[0] : "/dev/stdin"; + directory = argv[1]; + r = guestfs_tgz_in (g, tarball, directory); + return r; +} + +static int run_tgz_out (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *directory; + const char *tarball; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + directory = argv[0]; + tarball = strcmp (argv[1], "-") != 0 ? argv[1] : "/dev/stdout"; + r = guestfs_tgz_out (g, directory, tarball); + return r; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -2055,6 +2135,18 @@ int run_action (const char *cmd, int argc, char *argv[]) if (strcasecmp (cmd, "checksum") == 0) return run_checksum (cmd, argc, argv); else + if (strcasecmp (cmd, "tar_in") == 0 || strcasecmp (cmd, "tar-in") == 0) + return run_tar_in (cmd, argc, argv); + else + if (strcasecmp (cmd, "tar_out") == 0 || strcasecmp (cmd, "tar-out") == 0) + return run_tar_out (cmd, argc, argv); + else + if (strcasecmp (cmd, "tgz_in") == 0 || strcasecmp (cmd, "tgz-in") == 0) + return run_tgz_in (cmd, argc, argv); + else + if (strcasecmp (cmd, "tgz_out") == 0 || strcasecmp (cmd, "tgz-out") == 0) + return run_tgz_out (cmd, argc, argv); + else { fprintf (stderr, "%s: unknown command\n", cmd); return -1; diff --git a/fish/completion.c b/fish/completion.c index 49f912b..44cfb9e 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -114,6 +114,10 @@ static const char *commands[] = { "stat", "statvfs", "sync", + "tar-in", + "tar-out", + "tgz-in", + "tgz-out", "touch", "tune2fs-l", "umount", diff --git a/guestfish-actions.pod b/guestfish-actions.pod index 78cb4b7..da4bbae 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -825,6 +825,50 @@ underlying disk image. You should always call this if you have modified a disk image, before closing the handle. +=head2 tar-in + + tar-in (tarfile|-) directory + +This command uploads and unpacks local file C (an +I tar file) into C. + +To upload a compressed tarball, use C. + +Use C<-> instead of a filename to read/write from stdin/stdout. + +=head2 tar-out + + tar-out directory (tarfile|-) + +This command packs the contents of C and downloads +it to local file C. + +To download a compressed tarball, use C. + +Use C<-> instead of a filename to read/write from stdin/stdout. + +=head2 tgz-in + + tgz-in (tarball|-) directory + +This command uploads and unpacks local file C (a +I tar file) into C. + +To upload an uncompressed tarball, use C. + +Use C<-> instead of a filename to read/write from stdin/stdout. + +=head2 tgz-out + + tgz-out directory (tarball|-) + +This command packs the contents of C and downloads +it to local file C. + +To download an uncompressed tarball, use C. + +Use C<-> instead of a filename to read/write from stdin/stdout. + =head2 touch touch path diff --git a/guestfs-actions.pod b/guestfs-actions.pod index b901919..dcffc83 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -1115,6 +1115,58 @@ closing the handle. This function returns 0 on success or -1 on error. +=head2 guestfs_tar_in + + int guestfs_tar_in (guestfs_h *handle, + const char *tarfile, + const char *directory); + +This command uploads and unpacks local file C (an +I tar file) into C. + +To upload a compressed tarball, use C. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_tar_out + + int guestfs_tar_out (guestfs_h *handle, + const char *directory, + const char *tarfile); + +This command packs the contents of C and downloads +it to local file C. + +To download a compressed tarball, use C. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_tgz_in + + int guestfs_tgz_in (guestfs_h *handle, + const char *tarball, + const char *directory); + +This command uploads and unpacks local file C (a +I tar file) into C. + +To upload an uncompressed tarball, use C. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_tgz_out + + int guestfs_tgz_out (guestfs_h *handle, + const char *directory, + const char *tarball); + +This command packs the contents of C and downloads +it to local file C. + +To download an uncompressed tarball, use C. + +This function returns 0 on success or -1 on error. + =head2 guestfs_touch int guestfs_touch (guestfs_h *handle, diff --git a/images/Makefile.am b/images/Makefile.am index e9ea2f4..d5272e9 100644 --- a/images/Makefile.am +++ b/images/Makefile.am @@ -15,6 +15,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -EXTRA_DIST = mbr-ext2-empty.img.gz +EXTRA_DIST = \ + helloworld.tar \ + helloworld.tar.gz \ + mbr-ext2-empty.img.gz CLEANFILES = *~ diff --git a/images/helloworld.tar b/images/helloworld.tar new file mode 100644 index 0000000000000000000000000000000000000000..191a31b2d668b4152445cbeb4aa037293cf54846 GIT binary patch literal 10240 zcmeIy!3u*g41nRB_Y{2sZLMveXE0$5nV=hc{ng@Sf^3(Fus@fg!6tkmR<)>y&5=@& za>n-KoZsaT=UnnOwT*AYIvZ^g6VyA)1)kI6JQ^e8ZMb*ScExY`?Jp0Vsp{7-_7~M2 z=K;sn+|%+r=W=#6e-}d#V^zCaF8}lSm+7DOfTqR~1*-bm_5GmQTG{Yl_rHnx|L>x2 z*?-lcESHz*v;TWgQv(465I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|000A|D EH*PXM5C8xG literal 0 HcmV?d00001 diff --git a/images/helloworld.tar.gz b/images/helloworld.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..9d36cc97a0ebbf8a7e801ac53f797ab481c0b88f GIT binary patch literal 190 zcmV;v073sBiwFp?c7 zaM>57q@4P5JG}q*>3yTLx%H(xRNe6mI4yFY=bsk&yA-1+TlK2RpXWcOzgznXtVNq+ s+jV`_9bfg|r?LM{p#Q%xrceI?0000000000007vuJEweVu>dFl06e~2W&i*H literal 0 HcmV?d00001 diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index 337555c..3ca33f7 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -202,3 +202,7 @@ external blockdev_rereadpt : t -> string -> unit = "ocaml_guestfs_blockdev_rerea external upload : t -> string -> string -> unit = "ocaml_guestfs_upload" external download : t -> string -> string -> unit = "ocaml_guestfs_download" external checksum : t -> string -> string -> string = "ocaml_guestfs_checksum" +external tar_in : t -> string -> string -> unit = "ocaml_guestfs_tar_in" +external tar_out : t -> string -> string -> unit = "ocaml_guestfs_tar_out" +external tgz_in : t -> string -> string -> unit = "ocaml_guestfs_tgz_in" +external tgz_out : t -> string -> string -> unit = "ocaml_guestfs_tgz_out" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 4fd05da..629e443 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -385,3 +385,15 @@ val download : t -> string -> string -> unit val checksum : t -> string -> string -> string (** compute MD5, SHAx or CRC checksum of file *) +val tar_in : t -> string -> string -> unit +(** unpack tarfile to directory *) + +val tar_out : t -> string -> string -> unit +(** pack directory into tarfile *) + +val tgz_in : t -> string -> string -> unit +(** unpack compressed tarball to directory *) + +val tgz_out : t -> string -> string -> unit +(** pack directory into compressed tarball *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index 882d505..cfcf4e4 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -2392,3 +2392,99 @@ ocaml_guestfs_checksum (value gv, value csumtypev, value pathv) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_tar_in (value gv, value tarfilev, value directoryv) +{ + CAMLparam3 (gv, tarfilev, directoryv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("tar_in: used handle after closing it"); + + const char *tarfile = String_val (tarfilev); + const char *directory = String_val (directoryv); + int r; + + caml_enter_blocking_section (); + r = guestfs_tar_in (g, tarfile, directory); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "tar_in"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_tar_out (value gv, value directoryv, value tarfilev) +{ + CAMLparam3 (gv, directoryv, tarfilev); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("tar_out: used handle after closing it"); + + const char *directory = String_val (directoryv); + const char *tarfile = String_val (tarfilev); + int r; + + caml_enter_blocking_section (); + r = guestfs_tar_out (g, directory, tarfile); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "tar_out"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_tgz_in (value gv, value tarballv, value directoryv) +{ + CAMLparam3 (gv, tarballv, directoryv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("tgz_in: used handle after closing it"); + + const char *tarball = String_val (tarballv); + const char *directory = String_val (directoryv); + int r; + + caml_enter_blocking_section (); + r = guestfs_tgz_in (g, tarball, directory); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "tgz_in"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_tgz_out (value gv, value directoryv, value tarballv) +{ + CAMLparam3 (gv, directoryv, tarballv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("tgz_out: used handle after closing it"); + + const char *directory = String_val (directoryv); + const char *tarball = String_val (tarballv); + int r; + + caml_enter_blocking_section (); + r = guestfs_tgz_out (g, directory, tarball); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "tgz_out"); + + rv = Val_unit; + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index 7d9b3df..63dce74 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -1357,3 +1357,51 @@ PREINIT: OUTPUT: RETVAL +void +tar_in (g, tarfile, directory) + guestfs_h *g; + char *tarfile; + char *directory; +PREINIT: + int r; + PPCODE: + r = guestfs_tar_in (g, tarfile, directory); + if (r == -1) + croak ("tar_in: %s", guestfs_last_error (g)); + +void +tar_out (g, directory, tarfile) + guestfs_h *g; + char *directory; + char *tarfile; +PREINIT: + int r; + PPCODE: + r = guestfs_tar_out (g, directory, tarfile); + if (r == -1) + croak ("tar_out: %s", guestfs_last_error (g)); + +void +tgz_in (g, tarball, directory) + guestfs_h *g; + char *tarball; + char *directory; +PREINIT: + int r; + PPCODE: + r = guestfs_tgz_in (g, tarball, directory); + if (r == -1) + croak ("tgz_in: %s", guestfs_last_error (g)); + +void +tgz_out (g, directory, tarball) + guestfs_h *g; + char *directory; + char *tarball; +PREINIT: + int r; + PPCODE: + r = guestfs_tgz_out (g, directory, tarball); + if (r == -1) + croak ("tgz_out: %s", guestfs_last_error (g)); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index ad20088..d16077e 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -780,6 +780,34 @@ underlying disk image. You should always call this if you have modified a disk image, before closing the handle. +=item $h->tar_in ($tarfile, $directory); + +This command uploads and unpacks local file C (an +I tar file) into C. + +To upload a compressed tarball, use C<$h-Etgz_in>. + +=item $h->tar_out ($directory, $tarfile); + +This command packs the contents of C and downloads +it to local file C. + +To download a compressed tarball, use C<$h-Etgz_out>. + +=item $h->tgz_in ($tarball, $directory); + +This command uploads and unpacks local file C (a +I tar file) into C. + +To upload an uncompressed tarball, use C<$h-Etar_in>. + +=item $h->tgz_out ($directory, $tarball); + +This command packs the contents of C and downloads +it to local file C. + +To download an uncompressed tarball, use C<$h-Etar_out>. + =item $h->touch ($path); Touch acts like the L command. It can be used to diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 4d31d9f..800d21b 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -2562,6 +2562,110 @@ py_guestfs_checksum (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_tar_in (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *tarfile; + const char *directory; + + if (!PyArg_ParseTuple (args, (char *) "Oss:guestfs_tar_in", + &py_g, &tarfile, &directory)) + return NULL; + g = get_handle (py_g); + + r = guestfs_tar_in (g, tarfile, directory); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + +static PyObject * +py_guestfs_tar_out (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *directory; + const char *tarfile; + + if (!PyArg_ParseTuple (args, (char *) "Oss:guestfs_tar_out", + &py_g, &directory, &tarfile)) + return NULL; + g = get_handle (py_g); + + r = guestfs_tar_out (g, directory, tarfile); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + +static PyObject * +py_guestfs_tgz_in (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *tarball; + const char *directory; + + if (!PyArg_ParseTuple (args, (char *) "Oss:guestfs_tgz_in", + &py_g, &tarball, &directory)) + return NULL; + g = get_handle (py_g); + + r = guestfs_tgz_in (g, tarball, directory); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + +static PyObject * +py_guestfs_tgz_out (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *directory; + const char *tarball; + + if (!PyArg_ParseTuple (args, (char *) "Oss:guestfs_tgz_out", + &py_g, &directory, &tarball)) + return NULL; + g = get_handle (py_g); + + r = guestfs_tgz_out (g, directory, tarball); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -2652,6 +2756,10 @@ static PyMethodDef methods[] = { { (char *) "upload", py_guestfs_upload, METH_VARARGS, NULL }, { (char *) "download", py_guestfs_download, METH_VARARGS, NULL }, { (char *) "checksum", py_guestfs_checksum, METH_VARARGS, NULL }, + { (char *) "tar_in", py_guestfs_tar_in, METH_VARARGS, NULL }, + { (char *) "tar_out", py_guestfs_tar_out, METH_VARARGS, NULL }, + { (char *) "tgz_in", py_guestfs_tgz_in, METH_VARARGS, NULL }, + { (char *) "tgz_out", py_guestfs_tgz_out, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index 4d71b23..6417caf 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -997,3 +997,35 @@ class GuestFS: """ return libguestfsmod.checksum (self._o, csumtype, path) + def tar_in (self, tarfile, directory): + u"""This command uploads and unpacks local file "tarfile" + (an *uncompressed* tar file) into "directory". + + To upload a compressed tarball, use "g.tgz_in". + """ + return libguestfsmod.tar_in (self._o, tarfile, directory) + + def tar_out (self, directory, tarfile): + u"""This command packs the contents of "directory" and + downloads it to local file "tarfile". + + To download a compressed tarball, use "g.tgz_out". + """ + return libguestfsmod.tar_out (self._o, directory, tarfile) + + def tgz_in (self, tarball, directory): + u"""This command uploads and unpacks local file "tarball" (a + *gzip compressed* tar file) into "directory". + + To upload an uncompressed tarball, use "g.tar_in". + """ + return libguestfsmod.tgz_in (self._o, tarball, directory) + + def tgz_out (self, directory, tarball): + u"""This command packs the contents of "directory" and + downloads it to local file "tarball". + + To download an uncompressed tarball, use "g.tar_out". + """ + return libguestfsmod.tgz_out (self._o, directory, tarball) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index 3861d9b..9971228 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -2093,6 +2093,106 @@ static VALUE ruby_guestfs_checksum (VALUE gv, VALUE csumtypev, VALUE pathv) return rv; } +static VALUE ruby_guestfs_tar_in (VALUE gv, VALUE tarfilev, VALUE directoryv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "tar_in"); + + const char *tarfile = StringValueCStr (tarfilev); + if (!tarfile) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "tarfile", "tar_in"); + const char *directory = StringValueCStr (directoryv); + if (!directory) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "directory", "tar_in"); + + int r; + + r = guestfs_tar_in (g, tarfile, directory); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + +static VALUE ruby_guestfs_tar_out (VALUE gv, VALUE directoryv, VALUE tarfilev) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "tar_out"); + + const char *directory = StringValueCStr (directoryv); + if (!directory) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "directory", "tar_out"); + const char *tarfile = StringValueCStr (tarfilev); + if (!tarfile) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "tarfile", "tar_out"); + + int r; + + r = guestfs_tar_out (g, directory, tarfile); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + +static VALUE ruby_guestfs_tgz_in (VALUE gv, VALUE tarballv, VALUE directoryv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "tgz_in"); + + const char *tarball = StringValueCStr (tarballv); + if (!tarball) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "tarball", "tgz_in"); + const char *directory = StringValueCStr (directoryv); + if (!directory) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "directory", "tgz_in"); + + int r; + + r = guestfs_tgz_in (g, tarball, directory); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + +static VALUE ruby_guestfs_tgz_out (VALUE gv, VALUE directoryv, VALUE tarballv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "tgz_out"); + + const char *directory = StringValueCStr (directoryv); + if (!directory) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "directory", "tgz_out"); + const char *tarball = StringValueCStr (tarballv); + if (!tarball) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "tarball", "tgz_out"); + + int r; + + r = guestfs_tgz_out (g, directory, tarball); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + /* Initialize the module. */ void Init__guestfs () { @@ -2277,4 +2377,12 @@ void Init__guestfs () ruby_guestfs_download, 2); rb_define_method (c_guestfs, "checksum", ruby_guestfs_checksum, 2); + rb_define_method (c_guestfs, "tar_in", + ruby_guestfs_tar_in, 2); + rb_define_method (c_guestfs, "tar_out", + ruby_guestfs_tar_out, 2); + rb_define_method (c_guestfs, "tgz_in", + ruby_guestfs_tgz_in, 2); + rb_define_method (c_guestfs, "tgz_out", + ruby_guestfs_tgz_out, 2); } diff --git a/src/generator.ml b/src/generator.ml index ae1dfe9..bdd032c 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -1369,6 +1369,46 @@ Compute the SHA512 hash (using the C program). The checksum is returned as a printable string."); + ("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [], + [InitBasicFS, TestOutput ( + [["tar_in"; "images/helloworld.tar"; "/"]; + ["cat"; "/hello"]], "hello\n")], + "unpack tarfile to directory", + "\ +This command uploads and unpacks local file C (an +I tar file) into C. + +To upload a compressed tarball, use C."); + + ("tar_out", (RErr, [String "directory"; FileOut "tarfile"]), 70, [], + [], + "pack directory into tarfile", + "\ +This command packs the contents of C and downloads +it to local file C. + +To download a compressed tarball, use C."); + + ("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [], + [InitBasicFS, TestOutput ( + [["tgz_in"; "images/helloworld.tar.gz"; "/"]; + ["cat"; "/hello"]], "hello\n")], + "unpack compressed tarball to directory", + "\ +This command uploads and unpacks local file C (a +I tar file) into C. + +To upload an uncompressed tarball, use C."); + + ("tgz_out", (RErr, [String "directory"; FileOut "tarball"]), 72, [], + [], + "pack directory into compressed tarball", + "\ +This command packs the contents of C and downloads +it to local file C. + +To download an uncompressed tarball, use C."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -2856,8 +2896,8 @@ int main (int argc, char *argv[]) char c = 0; int failed = 0; const char *srcdir; + const char *filename; int fd; - char buf[256]; int nr_tests, test_num = 0; no_test_warnings (); @@ -2872,89 +2912,90 @@ int main (int argc, char *argv[]) srcdir = getenv (\"srcdir\"); if (!srcdir) srcdir = \".\"; - guestfs_set_path (g, srcdir); + chdir (srcdir); + guestfs_set_path (g, \".\"); - snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir); - fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + filename = \"test1.img\"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); if (fd == -1) { - perror (buf); + perror (filename); exit (1); } if (lseek (fd, %d, SEEK_SET) == -1) { perror (\"lseek\"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (write (fd, &c, 1) == -1) { perror (\"write\"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (close (fd) == -1) { - perror (buf); - unlink (buf); + perror (filename); + unlink (filename); exit (1); } - if (guestfs_add_drive (g, buf) == -1) { - printf (\"guestfs_add_drive %%s FAILED\\n\", buf); + if (guestfs_add_drive (g, filename) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", filename); exit (1); } - snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir); - fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + filename = \"test2.img\"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); if (fd == -1) { - perror (buf); + perror (filename); exit (1); } if (lseek (fd, %d, SEEK_SET) == -1) { perror (\"lseek\"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (write (fd, &c, 1) == -1) { perror (\"write\"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (close (fd) == -1) { - perror (buf); - unlink (buf); + perror (filename); + unlink (filename); exit (1); } - if (guestfs_add_drive (g, buf) == -1) { - printf (\"guestfs_add_drive %%s FAILED\\n\", buf); + if (guestfs_add_drive (g, filename) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", filename); exit (1); } - snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir); - fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + filename = \"test3.img\"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); if (fd == -1) { - perror (buf); + perror (filename); exit (1); } if (lseek (fd, %d, SEEK_SET) == -1) { perror (\"lseek\"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (write (fd, &c, 1) == -1) { perror (\"write\"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (close (fd) == -1) { - perror (buf); - unlink (buf); + perror (filename); + unlink (filename); exit (1); } - if (guestfs_add_drive (g, buf) == -1) { - printf (\"guestfs_add_drive %%s FAILED\\n\", buf); + if (guestfs_add_drive (g, filename) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", filename); exit (1); } @@ -2983,12 +3024,9 @@ int main (int argc, char *argv[]) pr "\n"; pr " guestfs_close (g);\n"; - pr " snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);\n"; - pr " unlink (buf);\n"; - pr " snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);\n"; - pr " unlink (buf);\n"; - pr " snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);\n"; - pr " unlink (buf);\n"; + pr " unlink (\"test1.img\");\n"; + pr " unlink (\"test2.img\");\n"; + pr " unlink (\"test3.img\");\n"; pr "\n"; pr " if (failed > 0) {\n"; diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index 5985e41..efc556e 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -5777,3 +5777,363 @@ char *guestfs_checksum (guestfs_h *g, return ctx.ret.checksum; /* caller will free */ } +struct tar_in_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = send called, + * 1001 = reply called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void tar_in_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct tar_in_ctx *ctx = (struct tar_in_ctx *) data; + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_tar_in"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_tar_in"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1001; +} + +int guestfs_tar_in (guestfs_h *g, + const char *tarfile, + const char *directory) +{ + struct guestfs_tar_in_args args; + struct tar_in_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_tar_in") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.directory = (char *) directory; + serial = guestfs__send_sync (g, GUESTFS_PROC_TAR_IN, + (xdrproc_t) xdr_guestfs_tar_in_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return -1; + } + + { + int r; + + r = guestfs__send_file_sync (g, tarfile); + if (r == -1) { + guestfs_set_ready (g); + return -1; + } + if (r == -2) /* daemon cancelled */ + goto read_reply; + } + + read_reply: + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, tar_in_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1001) { + error (g, "%s reply failed, see earlier error messages", "guestfs_tar_in"); + guestfs_set_ready (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_TAR_IN, serial) == -1) { + guestfs_set_ready (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return -1; + } + + guestfs_set_ready (g); + return 0; +} + +struct tar_out_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = send called, + * 1001 = reply called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void tar_out_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct tar_out_ctx *ctx = (struct tar_out_ctx *) data; + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_tar_out"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_tar_out"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1001; +} + +int guestfs_tar_out (guestfs_h *g, + const char *directory, + const char *tarfile) +{ + struct guestfs_tar_out_args args; + struct tar_out_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_tar_out") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.directory = (char *) directory; + serial = guestfs__send_sync (g, GUESTFS_PROC_TAR_OUT, + (xdrproc_t) xdr_guestfs_tar_out_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, tar_out_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1001) { + error (g, "%s reply failed, see earlier error messages", "guestfs_tar_out"); + guestfs_set_ready (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_TAR_OUT, serial) == -1) { + guestfs_set_ready (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return -1; + } + + if (guestfs__receive_file_sync (g, tarfile) == -1) { + guestfs_set_ready (g); + return -1; + } + + guestfs_set_ready (g); + return 0; +} + +struct tgz_in_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = send called, + * 1001 = reply called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void tgz_in_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct tgz_in_ctx *ctx = (struct tgz_in_ctx *) data; + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_tgz_in"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_tgz_in"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1001; +} + +int guestfs_tgz_in (guestfs_h *g, + const char *tarball, + const char *directory) +{ + struct guestfs_tgz_in_args args; + struct tgz_in_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_tgz_in") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.directory = (char *) directory; + serial = guestfs__send_sync (g, GUESTFS_PROC_TGZ_IN, + (xdrproc_t) xdr_guestfs_tgz_in_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return -1; + } + + { + int r; + + r = guestfs__send_file_sync (g, tarball); + if (r == -1) { + guestfs_set_ready (g); + return -1; + } + if (r == -2) /* daemon cancelled */ + goto read_reply; + } + + read_reply: + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, tgz_in_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1001) { + error (g, "%s reply failed, see earlier error messages", "guestfs_tgz_in"); + guestfs_set_ready (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_TGZ_IN, serial) == -1) { + guestfs_set_ready (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return -1; + } + + guestfs_set_ready (g); + return 0; +} + +struct tgz_out_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = send called, + * 1001 = reply called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void tgz_out_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct tgz_out_ctx *ctx = (struct tgz_out_ctx *) data; + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_tgz_out"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_tgz_out"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1001; +} + +int guestfs_tgz_out (guestfs_h *g, + const char *directory, + const char *tarball) +{ + struct guestfs_tgz_out_args args; + struct tgz_out_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_tgz_out") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.directory = (char *) directory; + serial = guestfs__send_sync (g, GUESTFS_PROC_TGZ_OUT, + (xdrproc_t) xdr_guestfs_tgz_out_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, tgz_out_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1001) { + error (g, "%s reply failed, see earlier error messages", "guestfs_tgz_out"); + guestfs_set_ready (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_TGZ_OUT, serial) == -1) { + guestfs_set_ready (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return -1; + } + + if (guestfs__receive_file_sync (g, tarball) == -1) { + guestfs_set_ready (g); + return -1; + } + + guestfs_set_ready (g); + return 0; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index c694ddd..ae54d3a 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -106,3 +106,7 @@ extern int guestfs_blockdev_rereadpt (guestfs_h *handle, const char *device); extern int guestfs_upload (guestfs_h *handle, const char *filename, const char *remotefilename); extern int guestfs_download (guestfs_h *handle, const char *remotefilename, const char *filename); extern char *guestfs_checksum (guestfs_h *handle, const char *csumtype, const char *path); +extern int guestfs_tar_in (guestfs_h *handle, const char *tarfile, const char *directory); +extern int guestfs_tar_out (guestfs_h *handle, const char *directory, const char *tarfile); +extern int guestfs_tgz_in (guestfs_h *handle, const char *tarball, const char *directory); +extern int guestfs_tgz_out (guestfs_h *handle, const char *directory, const char *tarball); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index fc2eae9..8b88ad9 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1229,6 +1229,46 @@ xdr_guestfs_checksum_ret (XDR *xdrs, guestfs_checksum_ret *objp) } bool_t +xdr_guestfs_tar_in_args (XDR *xdrs, guestfs_tar_in_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->directory, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tar_out_args (XDR *xdrs, guestfs_tar_out_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->directory, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tgz_in_args (XDR *xdrs, guestfs_tgz_in_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->directory, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tgz_out_args (XDR *xdrs, guestfs_tgz_out_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->directory, ~0)) + return FALSE; + return TRUE; +} + +bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { register int32_t *buf; diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 05bd40b..55e6241 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -637,6 +637,26 @@ struct guestfs_checksum_ret { }; typedef struct guestfs_checksum_ret guestfs_checksum_ret; +struct guestfs_tar_in_args { + char *directory; +}; +typedef struct guestfs_tar_in_args guestfs_tar_in_args; + +struct guestfs_tar_out_args { + char *directory; +}; +typedef struct guestfs_tar_out_args guestfs_tar_out_args; + +struct guestfs_tgz_in_args { + char *directory; +}; +typedef struct guestfs_tgz_in_args guestfs_tgz_in_args; + +struct guestfs_tgz_out_args { + char *directory; +}; +typedef struct guestfs_tgz_out_args guestfs_tgz_out_args; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -706,7 +726,11 @@ enum guestfs_procedure { GUESTFS_PROC_UPLOAD = 66, GUESTFS_PROC_DOWNLOAD = 67, GUESTFS_PROC_CHECKSUM = 68, - GUESTFS_PROC_NR_PROCS = 68 + 1, + GUESTFS_PROC_TAR_IN = 69, + GUESTFS_PROC_TAR_OUT = 70, + GUESTFS_PROC_TGZ_IN = 71, + GUESTFS_PROC_TGZ_OUT = 72, + GUESTFS_PROC_NR_PROCS = 72 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -853,6 +877,10 @@ extern bool_t xdr_guestfs_upload_args (XDR *, guestfs_upload_args*); extern bool_t xdr_guestfs_download_args (XDR *, guestfs_download_args*); extern bool_t xdr_guestfs_checksum_args (XDR *, guestfs_checksum_args*); extern bool_t xdr_guestfs_checksum_ret (XDR *, guestfs_checksum_ret*); +extern bool_t xdr_guestfs_tar_in_args (XDR *, guestfs_tar_in_args*); +extern bool_t xdr_guestfs_tar_out_args (XDR *, guestfs_tar_out_args*); +extern bool_t xdr_guestfs_tgz_in_args (XDR *, guestfs_tgz_in_args*); +extern bool_t xdr_guestfs_tgz_out_args (XDR *, guestfs_tgz_out_args*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -958,6 +986,10 @@ extern bool_t xdr_guestfs_upload_args (); extern bool_t xdr_guestfs_download_args (); extern bool_t xdr_guestfs_checksum_args (); extern bool_t xdr_guestfs_checksum_ret (); +extern bool_t xdr_guestfs_tar_in_args (); +extern bool_t xdr_guestfs_tar_out_args (); +extern bool_t xdr_guestfs_tgz_in_args (); +extern bool_t xdr_guestfs_tgz_out_args (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index ca2d0ce..7f98269 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -492,6 +492,22 @@ struct guestfs_checksum_ret { string checksum<>; }; +struct guestfs_tar_in_args { + string directory<>; +}; + +struct guestfs_tar_out_args { + string directory<>; +}; + +struct guestfs_tgz_in_args { + string directory<>; +}; + +struct guestfs_tgz_out_args { + string directory<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -561,6 +577,10 @@ enum guestfs_procedure { GUESTFS_PROC_UPLOAD = 66, GUESTFS_PROC_DOWNLOAD = 67, GUESTFS_PROC_CHECKSUM = 68, + GUESTFS_PROC_TAR_IN = 69, + GUESTFS_PROC_TAR_OUT = 70, + GUESTFS_PROC_TGZ_IN = 71, + GUESTFS_PROC_TGZ_OUT = 72, GUESTFS_PROC_NR_PROCS }; diff --git a/tests.c b/tests.c index 876cc29..1c86269 100644 --- a/tests.c +++ b/tests.c @@ -101,6 +101,138 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_command_lines\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_tune2fs_l\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_blockdev_setbsz\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_tar_out\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_tgz_out\" has no tests\n"); +} + +static int test_tgz_in_0 (void) +{ + /* InitBasicFS for tgz_in (0): create ext2 on /dev/sda1 */ + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + { + char *lines[] = { + ",", + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_mkfs (g, "ext2", "/dev/sda1"); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_mount (g, "/dev/sda1", "/"); + if (r == -1) + return -1; + } + /* TestOutput for tgz_in (0) */ + { + int r; + suppress_error = 0; + r = guestfs_tgz_in (g, "images/helloworld.tar.gz", "/"); + if (r == -1) + return -1; + } + { + char *r; + suppress_error = 0; + r = guestfs_cat (g, "/hello"); + if (r == NULL) + return -1; + if (strcmp (r, "hello\n") != 0) { + fprintf (stderr, "test_tgz_in_0: expected \"hello\n\" but got \"%s\"\n", r); + return -1; + } + free (r); + } + return 0; +} + +static int test_tar_in_0 (void) +{ + /* InitBasicFS for tar_in (0): create ext2 on /dev/sda1 */ + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + { + char *lines[] = { + ",", + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_mkfs (g, "ext2", "/dev/sda1"); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_mount (g, "/dev/sda1", "/"); + if (r == -1) + return -1; + } + /* TestOutput for tar_in (0) */ + { + int r; + suppress_error = 0; + r = guestfs_tar_in (g, "images/helloworld.tar", "/"); + if (r == -1) + return -1; + } + { + char *r; + suppress_error = 0; + r = guestfs_cat (g, "/hello"); + if (r == NULL) + return -1; + if (strcmp (r, "hello\n") != 0) { + fprintf (stderr, "test_tar_in_0: expected \"hello\n\" but got \"%s\"\n", r); + return -1; + } + free (r); + } + return 0; } static int test_checksum_0 (void) @@ -5048,8 +5180,8 @@ int main (int argc, char *argv[]) char c = 0; int failed = 0; const char *srcdir; + const char *filename; int fd; - char buf[256]; int nr_tests, test_num = 0; no_test_warnings (); @@ -5064,89 +5196,90 @@ int main (int argc, char *argv[]) srcdir = getenv ("srcdir"); if (!srcdir) srcdir = "."; - guestfs_set_path (g, srcdir); + chdir (srcdir); + guestfs_set_path (g, "."); - snprintf (buf, sizeof buf, "%s/test1.img", srcdir); - fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + filename = "test1.img"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); if (fd == -1) { - perror (buf); + perror (filename); exit (1); } if (lseek (fd, 524288000, SEEK_SET) == -1) { perror ("lseek"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (write (fd, &c, 1) == -1) { perror ("write"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (close (fd) == -1) { - perror (buf); - unlink (buf); + perror (filename); + unlink (filename); exit (1); } - if (guestfs_add_drive (g, buf) == -1) { - printf ("guestfs_add_drive %s FAILED\n", buf); + if (guestfs_add_drive (g, filename) == -1) { + printf ("guestfs_add_drive %s FAILED\n", filename); exit (1); } - snprintf (buf, sizeof buf, "%s/test2.img", srcdir); - fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + filename = "test2.img"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); if (fd == -1) { - perror (buf); + perror (filename); exit (1); } if (lseek (fd, 52428800, SEEK_SET) == -1) { perror ("lseek"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (write (fd, &c, 1) == -1) { perror ("write"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (close (fd) == -1) { - perror (buf); - unlink (buf); + perror (filename); + unlink (filename); exit (1); } - if (guestfs_add_drive (g, buf) == -1) { - printf ("guestfs_add_drive %s FAILED\n", buf); + if (guestfs_add_drive (g, filename) == -1) { + printf ("guestfs_add_drive %s FAILED\n", filename); exit (1); } - snprintf (buf, sizeof buf, "%s/test3.img", srcdir); - fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); + filename = "test3.img"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666); if (fd == -1) { - perror (buf); + perror (filename); exit (1); } if (lseek (fd, 10485760, SEEK_SET) == -1) { perror ("lseek"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (write (fd, &c, 1) == -1) { perror ("write"); close (fd); - unlink (buf); + unlink (filename); exit (1); } if (close (fd) == -1) { - perror (buf); - unlink (buf); + perror (filename); + unlink (filename); exit (1); } - if (guestfs_add_drive (g, buf) == -1) { - printf ("guestfs_add_drive %s FAILED\n", buf); + if (guestfs_add_drive (g, filename) == -1) { + printf ("guestfs_add_drive %s FAILED\n", filename); exit (1); } @@ -5159,9 +5292,21 @@ int main (int argc, char *argv[]) exit (1); } - nr_tests = 73; + nr_tests = 75; test_num++; + printf ("%3d/%3d test_tgz_in_0\n", test_num, nr_tests); + if (test_tgz_in_0 () == -1) { + printf ("test_tgz_in_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_tar_in_0\n", test_num, nr_tests); + if (test_tar_in_0 () == -1) { + printf ("test_tar_in_0 FAILED\n"); + failed++; + } + test_num++; printf ("%3d/%3d test_checksum_0\n", test_num, nr_tests); if (test_checksum_0 () == -1) { printf ("test_checksum_0 FAILED\n"); @@ -5601,12 +5746,9 @@ int main (int argc, char *argv[]) } guestfs_close (g); - snprintf (buf, sizeof buf, "%s/test1.img", srcdir); - unlink (buf); - snprintf (buf, sizeof buf, "%s/test2.img", srcdir); - unlink (buf); - snprintf (buf, sizeof buf, "%s/test3.img", srcdir); - unlink (buf); + unlink ("test1.img"); + unlink ("test2.img"); + unlink ("test3.img"); if (failed > 0) { printf ("***** %d / %d tests FAILED *****\n", failed, nr_tests); -- 1.8.3.1