Generated code for 'equal' command.
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 1 May 2009 11:16:08 +0000 (12:16 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 1 May 2009 11:16:08 +0000 (12:16 +0100)
22 files changed:
daemon/actions.h
daemon/stubs.c
fish/cmds.c
fish/completion.c
guestfish-actions.pod
guestfs-actions.pod
java/com/redhat/et/libguestfs/GuestFS.java
java/com_redhat_et_libguestfs_GuestFS.c
ocaml/guestfs.ml
ocaml/guestfs.mli
ocaml/guestfs_c_actions.c
perl/Guestfs.xs
perl/lib/Sys/Guestfs.pm
python/guestfs-py.c
python/guestfs.py
ruby/ext/guestfs/_guestfs.c
src/guestfs-actions.c
src/guestfs-actions.h
src/guestfs_protocol.c
src/guestfs_protocol.h
src/guestfs_protocol.x
tests.c

index 2dc8537..ac57a12 100644 (file)
@@ -113,3 +113,4 @@ extern int do_mv (const char *src, const char *dest);
 extern int do_drop_caches (int whattodrop);
 extern char *do_dmesg (void);
 extern int do_ping_daemon (void);
+extern int do_equal (const char *file1, const char *file2);
index f992bdf..fd9c7ea 100644 (file)
@@ -2284,6 +2284,34 @@ static void ping_daemon_stub (XDR *xdr_in)
 done: ;
 }
 
+static void equal_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_equal_args args;
+  const char *file1;
+  const char *file2;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_equal_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "equal");
+    return;
+  }
+  file1 = args.file1;
+  file2 = args.file2;
+
+  r = do_equal (file1, file2);
+  if (r == -1)
+    /* do_equal has already called reply_with_error */
+    goto done;
+
+  struct guestfs_equal_ret ret;
+  ret.equality = r;
+  reply ((xdrproc_t) &xdr_guestfs_equal_ret, (char *) &ret);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_equal_args, (char *) &args);
+}
+
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
@@ -2563,6 +2591,9 @@ void dispatch_incoming_message (XDR *xdr_in)
     case GUESTFS_PROC_PING_DAEMON:
       ping_daemon_stub (xdr_in);
       break;
+    case GUESTFS_PROC_EQUAL:
+      equal_stub (xdr_in);
+      break;
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
   }
index 7264e7d..0597738 100644 (file)
@@ -69,6 +69,7 @@ void list_commands (void)
   printf ("%-20s %s\n", "dmesg", "return kernel messages");
   printf ("%-20s %s\n", "download", "download a file to the local machine");
   printf ("%-20s %s\n", "drop-caches", "drop kernel page cache, dentries and inodes");
+  printf ("%-20s %s\n", "equal", "test if two files have equal contents");
   printf ("%-20s %s\n", "exists", "test if file or directory exists");
   printf ("%-20s %s\n", "file", "determine file type");
   printf ("%-20s %s\n", "fsck", "run the filesystem checker");
@@ -476,6 +477,9 @@ void display_command (const char *cmd)
   if (strcasecmp (cmd, "ping_daemon") == 0 || strcasecmp (cmd, "ping-daemon") == 0)
     pod2text ("ping-daemon - ping the guest daemon", " ping-daemon\n\nThis is a test probe into the guestfs daemon running inside\nthe qemu subprocess.  Calling this function checks that the\ndaemon responds to the ping message, without affecting the daemon\nor attached block device(s) in any other way.");
   else
+  if (strcasecmp (cmd, "equal") == 0)
+    pod2text ("equal - test if two files have equal contents", " equal <file1> <file2>\n\nThis compares the two files C<file1> and C<file2> and returns\ntrue if their content is exactly equal, or false otherwise.\n\nThe external L<cmp(1)> program is used for the comparison.");
+  else
     display_builtin_command (cmd);
 }
 
@@ -2315,6 +2319,24 @@ static int run_ping_daemon (const char *cmd, int argc, char *argv[])
   return r;
 }
 
+static int run_equal (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *file1;
+  const char *file2;
+  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;
+  }
+  file1 = argv[0];
+  file2 = argv[1];
+  r = guestfs_equal (g, file1, file2);
+  if (r == -1) return -1;
+  if (r) printf ("true\n"); else printf ("false\n");
+  return 0;
+}
+
 int run_action (const char *cmd, int argc, char *argv[])
 {
   if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0)
@@ -2647,6 +2669,9 @@ int run_action (const char *cmd, int argc, char *argv[])
   if (strcasecmp (cmd, "ping_daemon") == 0 || strcasecmp (cmd, "ping-daemon") == 0)
     return run_ping_daemon (cmd, argc, argv);
   else
+  if (strcasecmp (cmd, "equal") == 0)
+    return run_equal (cmd, argc, argv);
+  else
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
       return -1;
index 095a335..640c369 100644 (file)
@@ -75,6 +75,7 @@ static const char *const commands[] = {
   "dmesg",
   "download",
   "drop-caches",
+  "equal",
   "exists",
   "file",
   "fsck",
index 2d9c5e8..f1b624b 100644 (file)
@@ -469,6 +469,15 @@ Setting C<whattodrop> to 3 should drop everything.
 This automatically calls L<sync(2)> before the operation,
 so that the maximum guest memory is freed.
 
+=head2 equal
+
+ equal file1 file2
+
+This compares the two files C<file1> and C<file2> and returns
+true if their content is exactly equal, or false otherwise.
+
+The external L<cmp(1)> program is used for the comparison.
+
 =head2 exists
 
  exists path
index 9d094c9..871ad3c 100644 (file)
@@ -609,6 +609,19 @@ so that the maximum guest memory is freed.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_equal
+
+ int guestfs_equal (guestfs_h *handle,
+               const char *file1,
+               const char *file2);
+
+This compares the two files C<file1> and C<file2> and returns
+true if their content is exactly equal, or false otherwise.
+
+The external L<cmp(1)> program is used for the comparison.
+
+This function returns a C truth value on success or -1 on error.
+
 =head2 guestfs_exists
 
  int guestfs_exists (guestfs_h *handle,
index 5ae2b10..0725317 100644 (file)
@@ -2557,4 +2557,25 @@ public class GuestFS {
   private native void _ping_daemon (long g)
     throws LibGuestFSException;
 
+  /**
+   * test if two files have equal contents
+   *
+   * This compares the two files "file1" and "file2" and
+   * returns true if their content is exactly equal, or false
+   * otherwise.
+   * 
+   * The external cmp(1) program is used for the comparison.
+   * 
+   * @throws LibGuestFSException
+   */
+  public boolean equal (String file1, String file2)
+    throws LibGuestFSException
+  {
+    if (g == 0)
+      throw new LibGuestFSException ("equal: handle is closed");
+    return _equal (g, file1, file2);
+  }
+  private native boolean _equal (long g, String file1, String file2)
+    throws LibGuestFSException;
+
 }
index b689a6c..da287b1 100644 (file)
@@ -2565,3 +2565,24 @@ Java_com_redhat_et_libguestfs_GuestFS__1ping_1daemon
   }
 }
 
+JNIEXPORT jboolean JNICALL
+Java_com_redhat_et_libguestfs_GuestFS__1equal
+  (JNIEnv *env, jobject obj, jlong jg, jstring jfile1, jstring jfile2)
+{
+  guestfs_h *g = (guestfs_h *) (long) jg;
+  int r;
+  const char *file1;
+  const char *file2;
+
+  file1 = (*env)->GetStringUTFChars (env, jfile1, NULL);
+  file2 = (*env)->GetStringUTFChars (env, jfile2, NULL);
+  r = guestfs_equal (g, file1, file2);
+  (*env)->ReleaseStringUTFChars (env, jfile1, file1);
+  (*env)->ReleaseStringUTFChars (env, jfile2, file2);
+  if (r == -1) {
+    throw_exception (env, guestfs_last_error (g));
+    return 0;
+  }
+  return (jboolean) r;
+}
+
index e4916b0..99d5903 100644 (file)
@@ -228,3 +228,4 @@ external mv : t -> string -> string -> unit = "ocaml_guestfs_mv"
 external drop_caches : t -> int -> unit = "ocaml_guestfs_drop_caches"
 external dmesg : t -> string = "ocaml_guestfs_dmesg"
 external ping_daemon : t -> unit = "ocaml_guestfs_ping_daemon"
+external equal : t -> string -> string -> bool = "ocaml_guestfs_equal"
index 125fa97..40d1d2a 100644 (file)
@@ -463,3 +463,6 @@ val dmesg : t -> string
 val ping_daemon : t -> unit
 (** ping the guest daemon *)
 
+val equal : t -> string -> string -> bool
+(** test if two files have equal contents *)
+
index a640e0f..7732e1c 100644 (file)
@@ -3010,3 +3010,27 @@ ocaml_guestfs_ping_daemon (value gv)
   CAMLreturn (rv);
 }
 
+CAMLprim value
+ocaml_guestfs_equal (value gv, value file1v, value file2v)
+{
+  CAMLparam3 (gv, file1v, file2v);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("equal: used handle after closing it");
+
+  const char *file1 = String_val (file1v);
+  const char *file2 = String_val (file2v);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_equal (g, file1, file2);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "equal");
+
+  rv = Val_bool (r);
+  CAMLreturn (rv);
+}
+
index af543ec..d29d5a0 100644 (file)
@@ -1682,3 +1682,18 @@ PREINIT:
       if (r == -1)
         croak ("ping_daemon: %s", guestfs_last_error (g));
 
+SV *
+equal (g, file1, file2)
+      guestfs_h *g;
+      char *file1;
+      char *file2;
+PREINIT:
+      int equality;
+   CODE:
+      equality = guestfs_equal (g, file1, file2);
+      if (equality == -1)
+        croak ("equal: %s", guestfs_last_error (g));
+      RETVAL = newSViv (equality);
+ OUTPUT:
+      RETVAL
+
index a13c441..7331053 100644 (file)
@@ -484,6 +484,13 @@ Setting C<whattodrop> to 3 should drop everything.
 This automatically calls L<sync(2)> before the operation,
 so that the maximum guest memory is freed.
 
+=item $equality = $h->equal ($file1, $file2);
+
+This compares the two files C<file1> and C<file2> and returns
+true if their content is exactly equal, or false otherwise.
+
+The external L<cmp(1)> program is used for the comparison.
+
 =item $existsflag = $h->exists ($path);
 
 This returns C<true> if and only if there is a file, directory
index 6a807e3..918e2ba 100644 (file)
@@ -3229,6 +3229,31 @@ py_guestfs_ping_daemon (PyObject *self, PyObject *args)
   return py_r;
 }
 
+static PyObject *
+py_guestfs_equal (PyObject *self, PyObject *args)
+{
+  PyObject *py_g;
+  guestfs_h *g;
+  PyObject *py_r;
+  int r;
+  const char *file1;
+  const char *file2;
+
+  if (!PyArg_ParseTuple (args, (char *) "Oss:guestfs_equal",
+                         &py_g, &file1, &file2))
+    return NULL;
+  g = get_handle (py_g);
+
+  r = guestfs_equal (g, file1, file2);
+  if (r == -1) {
+    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));
+    return NULL;
+  }
+
+  py_r = PyInt_FromLong ((long) r);
+  return py_r;
+}
+
 static PyMethodDef methods[] = {
   { (char *) "create", py_guestfs_create, METH_VARARGS, NULL },
   { (char *) "close", py_guestfs_close, METH_VARARGS, NULL },
@@ -3345,6 +3370,7 @@ static PyMethodDef methods[] = {
   { (char *) "drop_caches", py_guestfs_drop_caches, METH_VARARGS, NULL },
   { (char *) "dmesg", py_guestfs_dmesg, METH_VARARGS, NULL },
   { (char *) "ping_daemon", py_guestfs_ping_daemon, METH_VARARGS, NULL },
+  { (char *) "equal", py_guestfs_equal, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
 };
 
index 1596bcd..d03a7a9 100644 (file)
@@ -1242,3 +1242,12 @@ class GuestFS:
         """
         return libguestfsmod.ping_daemon (self._o)
 
+    def equal (self, file1, file2):
+        u"""This compares the two files "file1" and "file2" and
+        returns true if their content is exactly equal, or false
+        otherwise.
+        
+        The external cmp(1) program is used for the comparison.
+        """
+        return libguestfsmod.equal (self._o, file1, file2)
+
index eed0b80..83486e7 100644 (file)
@@ -2715,6 +2715,31 @@ static VALUE ruby_guestfs_ping_daemon (VALUE gv)
   return Qnil;
 }
 
+static VALUE ruby_guestfs_equal (VALUE gv, VALUE file1v, VALUE file2v)
+{
+  guestfs_h *g;
+  Data_Get_Struct (gv, guestfs_h, g);
+  if (!g)
+    rb_raise (rb_eArgError, "%s: used handle after closing it", "equal");
+
+  const char *file1 = StringValueCStr (file1v);
+  if (!file1)
+    rb_raise (rb_eTypeError, "expected string for parameter %s of %s",
+              "file1", "equal");
+  const char *file2 = StringValueCStr (file2v);
+  if (!file2)
+    rb_raise (rb_eTypeError, "expected string for parameter %s of %s",
+              "file2", "equal");
+
+  int r;
+
+  r = guestfs_equal (g, file1, file2);
+  if (r == -1)
+    rb_raise (e_Error, "%s", guestfs_last_error (g));
+
+  return INT2NUM (r);
+}
+
 /* Initialize the module. */
 void Init__guestfs ()
 {
@@ -2951,4 +2976,6 @@ void Init__guestfs ()
         ruby_guestfs_dmesg, 0);
   rb_define_method (c_guestfs, "ping_daemon",
         ruby_guestfs_ping_daemon, 0);
+  rb_define_method (c_guestfs, "equal",
+        ruby_guestfs_equal, 2);
 }
index c0762df..3da1b42 100644 (file)
@@ -8335,3 +8335,96 @@ int guestfs_ping_daemon (guestfs_h *g)
   return 0;
 }
 
+struct equal_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 = reply_cb called.
+   */
+  int cb_sequence;
+  struct guestfs_message_header hdr;
+  struct guestfs_message_error err;
+  struct guestfs_equal_ret ret;
+};
+
+static void equal_reply_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  struct equal_ctx *ctx = (struct equal_ctx *) data;
+
+  /* This should definitely not happen. */
+  if (ctx->cb_sequence != 0) {
+    ctx->cb_sequence = 9999;
+    error (g, "%s: internal error: reply callback called twice", "guestfs_equal");
+    return;
+  }
+
+  ml->main_loop_quit (ml, g);
+
+  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {
+    error (g, "%s: failed to parse reply header", "guestfs_equal");
+    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_equal");
+      return;
+    }
+    goto done;
+  }
+  if (!xdr_guestfs_equal_ret (xdr, &ctx->ret)) {
+    error (g, "%s: failed to parse reply", "guestfs_equal");
+    return;
+  }
+ done:
+  ctx->cb_sequence = 1;
+}
+
+int guestfs_equal (guestfs_h *g,
+               const char *file1,
+               const char *file2)
+{
+  struct guestfs_equal_args args;
+  struct equal_ctx ctx;
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  int serial;
+
+  if (check_state (g, "guestfs_equal") == -1) return -1;
+  guestfs_set_busy (g);
+
+  memset (&ctx, 0, sizeof ctx);
+
+  args.file1 = (char *) file1;
+  args.file2 = (char *) file2;
+  serial = guestfs__send_sync (g, GUESTFS_PROC_EQUAL,
+        (xdrproc_t) xdr_guestfs_equal_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, equal_reply_cb, &ctx);
+  (void) ml->main_loop_run (ml, g);
+  guestfs_set_reply_callback (g, NULL, NULL);
+  if (ctx.cb_sequence != 1) {
+    error (g, "%s reply failed, see earlier error messages", "guestfs_equal");
+    guestfs_set_ready (g);
+    return -1;
+  }
+
+  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_EQUAL, 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 ctx.ret.equality;
+}
+
index f2218f4..0539981 100644 (file)
@@ -132,3 +132,4 @@ extern int guestfs_mv (guestfs_h *handle, const char *src, const char *dest);
 extern int guestfs_drop_caches (guestfs_h *handle, int whattodrop);
 extern char *guestfs_dmesg (guestfs_h *handle);
 extern int guestfs_ping_daemon (guestfs_h *handle);
+extern int guestfs_equal (guestfs_h *handle, const char *file1, const char *file2);
index 7a40e14..66889cc 100644 (file)
@@ -1528,6 +1528,28 @@ xdr_guestfs_dmesg_ret (XDR *xdrs, guestfs_dmesg_ret *objp)
 }
 
 bool_t
+xdr_guestfs_equal_args (XDR *xdrs, guestfs_equal_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->file1, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->file2, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_equal_ret (XDR *xdrs, guestfs_equal_ret *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bool (xdrs, &objp->equality))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
 {
        register int32_t *buf;
index e472524..06287c9 100644 (file)
@@ -789,6 +789,17 @@ struct guestfs_dmesg_ret {
 };
 typedef struct guestfs_dmesg_ret guestfs_dmesg_ret;
 
+struct guestfs_equal_args {
+       char *file1;
+       char *file2;
+};
+typedef struct guestfs_equal_args guestfs_equal_args;
+
+struct guestfs_equal_ret {
+       bool_t equality;
+};
+typedef struct guestfs_equal_ret guestfs_equal_ret;
+
 enum guestfs_procedure {
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
@@ -882,7 +893,8 @@ enum guestfs_procedure {
        GUESTFS_PROC_DROP_CACHES = 90,
        GUESTFS_PROC_DMESG = 91,
        GUESTFS_PROC_PING_DAEMON = 92,
-       GUESTFS_PROC_NR_PROCS = 92 + 1,
+       GUESTFS_PROC_EQUAL = 93,
+       GUESTFS_PROC_NR_PROCS = 93 + 1,
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
@@ -1056,6 +1068,8 @@ extern  bool_t xdr_guestfs_cp_a_args (XDR *, guestfs_cp_a_args*);
 extern  bool_t xdr_guestfs_mv_args (XDR *, guestfs_mv_args*);
 extern  bool_t xdr_guestfs_drop_caches_args (XDR *, guestfs_drop_caches_args*);
 extern  bool_t xdr_guestfs_dmesg_ret (XDR *, guestfs_dmesg_ret*);
+extern  bool_t xdr_guestfs_equal_args (XDR *, guestfs_equal_args*);
+extern  bool_t xdr_guestfs_equal_ret (XDR *, guestfs_equal_ret*);
 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*);
@@ -1188,6 +1202,8 @@ extern bool_t xdr_guestfs_cp_a_args ();
 extern bool_t xdr_guestfs_mv_args ();
 extern bool_t xdr_guestfs_drop_caches_args ();
 extern bool_t xdr_guestfs_dmesg_ret ();
+extern bool_t xdr_guestfs_equal_args ();
+extern bool_t xdr_guestfs_equal_ret ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
index e61d3fb..f6cab67 100644 (file)
@@ -614,6 +614,15 @@ struct guestfs_dmesg_ret {
   string kmsgs<>;
 };
 
+struct guestfs_equal_args {
+  string file1<>;
+  string file2<>;
+};
+
+struct guestfs_equal_ret {
+  bool equality;
+};
+
 enum guestfs_procedure {
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
@@ -707,6 +716,7 @@ enum guestfs_procedure {
   GUESTFS_PROC_DROP_CACHES = 90,
   GUESTFS_PROC_DMESG = 91,
   GUESTFS_PROC_PING_DAEMON = 92,
+  GUESTFS_PROC_EQUAL = 93,
   GUESTFS_PROC_NR_PROCS
 };
 
diff --git a/tests.c b/tests.c
index 3adf389..d1bccc5 100644 (file)
--- a/tests.c
+++ b/tests.c
@@ -112,6 +112,201 @@ static void no_test_warnings (void)
   fprintf (stderr, "warning: \"guestfs_get_e2uuid\" has no tests\n");
 }
 
+static int test_equal_0 (void)
+{
+  /* InitBasicFS for equal (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;
+  }
+  /* TestOutputTrue for equal (0) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/file1", "contents of a file", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_cp (g, "/file1", "/file2");
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_equal (g, "/file1", "/file2");
+    if (r == -1)
+      return -1;
+    if (!r) {
+      fprintf (stderr, "test_equal_0: expected true, got false\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_equal_1 (void)
+{
+  /* InitBasicFS for equal (1): 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;
+  }
+  /* TestOutputFalse for equal (1) */
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/file1", "contents of a file", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, "/file2", "contents of another file", 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    int r;
+    suppress_error = 0;
+    r = guestfs_equal (g, "/file1", "/file2");
+    if (r == -1)
+      return -1;
+    if (r) {
+      fprintf (stderr, "test_equal_1: expected false, got true\n");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int test_equal_2 (void)
+{
+  /* InitBasicFS for equal (2): 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;
+  }
+  /* TestLastFail for equal (2) */
+  {
+    int r;
+    suppress_error = 1;
+    r = guestfs_equal (g, "/file1", "/file2");
+    if (r != -1)
+      return -1;
+  }
+  return 0;
+}
+
 static int test_ping_daemon_0 (void)
 {
   /* InitEmpty for ping_daemon (0) */
@@ -7320,9 +7515,27 @@ int main (int argc, char *argv[])
     exit (1);
   }
 
-  nr_tests = 104;
+  nr_tests = 107;
 
   test_num++;
+  printf ("%3d/%3d test_equal_0\n", test_num, nr_tests);
+  if (test_equal_0 () == -1) {
+    printf ("test_equal_0 FAILED\n");
+    failed++;
+  }
+  test_num++;
+  printf ("%3d/%3d test_equal_1\n", test_num, nr_tests);
+  if (test_equal_1 () == -1) {
+    printf ("test_equal_1 FAILED\n");
+    failed++;
+  }
+  test_num++;
+  printf ("%3d/%3d test_equal_2\n", test_num, nr_tests);
+  if (test_equal_2 () == -1) {
+    printf ("test_equal_2 FAILED\n");
+    failed++;
+  }
+  test_num++;
   printf ("%3d/%3d test_ping_daemon_0\n", test_num, nr_tests);
   if (test_ping_daemon_0 () == -1) {
     printf ("test_ping_daemon_0 FAILED\n");