Generated code for 'scrub-*' commands.
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 23 Jun 2009 14:53:44 +0000 (15:53 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 23 Jun 2009 14:53:44 +0000 (15:53 +0100)
23 files changed:
capitests/tests.c
daemon/actions.h
daemon/stubs.c
fish/cmds.c
fish/completion.c
guestfish-actions.pod
guestfs-actions.pod
haskell/Guestfs.hs
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

index 309bac3..62d4805 100644 (file)
@@ -150,6 +150,153 @@ static void no_test_warnings (void)
   fprintf (stderr, "warning: \"guestfs_e2fsck_f\" has no tests\n");
   fprintf (stderr, "warning: \"guestfs_sh\" has no tests\n");
   fprintf (stderr, "warning: \"guestfs_sh_lines\" has no tests\n");
+  fprintf (stderr, "warning: \"guestfs_scrub_freespace\" has no tests\n");
+}
+
+static int test_scrub_file_0_skip (void)
+{
+  const char *str;
+
+  str = getenv ("SKIP_TEST_SCRUB_FILE_0");
+  if (str && strcmp (str, "1") == 0) return 1;
+  str = getenv ("SKIP_TEST_SCRUB_FILE");
+  if (str && strcmp (str, "1") == 0) return 1;
+  return 0;
+}
+
+static int test_scrub_file_0 (void)
+{
+  if (test_scrub_file_0_skip ()) {
+    printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_scrub_file_0");
+    return 0;
+  }
+
+  /* InitBasicFS for test_scrub_file_0: create ext2 on /dev/sda1 */
+  {
+    char device[] = "/dev/sda";
+    int r;
+    suppress_error = 0;
+    r = guestfs_blockdev_setrw (g, device);
+    if (r == -1)
+      return -1;
+  }
+  {
+    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 device[] = "/dev/sda";
+    char lines_0[] = ",";
+    char *lines[] = {
+      lines_0,
+      NULL
+    };
+    int r;
+    suppress_error = 0;
+    r = guestfs_sfdisk (g, device, 0, 0, 0, lines);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char fstype[] = "ext2";
+    char device[] = "/dev/sda1";
+    int r;
+    suppress_error = 0;
+    r = guestfs_mkfs (g, fstype, device);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char device[] = "/dev/sda1";
+    char mountpoint[] = "/";
+    int r;
+    suppress_error = 0;
+    r = guestfs_mount (g, device, mountpoint);
+    if (r == -1)
+      return -1;
+  }
+  /* TestRun for scrub_file (0) */
+  {
+    char path[] = "/file";
+    char content[] = "content";
+    int r;
+    suppress_error = 0;
+    r = guestfs_write_file (g, path, content, 0);
+    if (r == -1)
+      return -1;
+  }
+  {
+    char file[] = "/file";
+    int r;
+    suppress_error = 0;
+    r = guestfs_scrub_file (g, file);
+    if (r == -1)
+      return -1;
+  }
+  return 0;
+}
+
+static int test_scrub_device_0_skip (void)
+{
+  const char *str;
+
+  str = getenv ("SKIP_TEST_SCRUB_DEVICE_0");
+  if (str && strcmp (str, "1") == 0) return 1;
+  str = getenv ("SKIP_TEST_SCRUB_DEVICE");
+  if (str && strcmp (str, "1") == 0) return 1;
+  return 0;
+}
+
+static int test_scrub_device_0 (void)
+{
+  if (test_scrub_device_0_skip ()) {
+    printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_scrub_device_0");
+    return 0;
+  }
+
+  /* InitNone|InitEmpty for test_scrub_device_0 */
+  {
+    char device[] = "/dev/sda";
+    int r;
+    suppress_error = 0;
+    r = guestfs_blockdev_setrw (g, device);
+    if (r == -1)
+      return -1;
+  }
+  {
+    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;
+  }
+  /* TestRun for scrub_device (0) */
+  {
+    char device[] = "/dev/sdc";
+    int r;
+    suppress_error = 0;
+    r = guestfs_scrub_device (g, device);
+    if (r == -1)
+      return -1;
+  }
+  return 0;
 }
 
 static int test_glob_expand_0_skip (void)
@@ -16166,9 +16313,21 @@ int main (int argc, char *argv[])
   /* Cancel previous alarm. */
   alarm (0);
 
-  nr_tests = 149;
+  nr_tests = 151;
 
   test_num++;
+  printf ("%3d/%3d test_scrub_file_0\n", test_num, nr_tests);
+  if (test_scrub_file_0 () == -1) {
+    printf ("test_scrub_file_0 FAILED\n");
+    failed++;
+  }
+  test_num++;
+  printf ("%3d/%3d test_scrub_device_0\n", test_num, nr_tests);
+  if (test_scrub_device_0 () == -1) {
+    printf ("test_scrub_device_0 FAILED\n");
+    failed++;
+  }
+  test_num++;
   printf ("%3d/%3d test_glob_expand_0\n", test_num, nr_tests);
   if (test_glob_expand_0 () == -1) {
     printf ("test_glob_expand_0 FAILED\n");
index cc8bc78..17ec338 100644 (file)
@@ -134,3 +134,6 @@ extern int do_ntfs_3g_probe (int rw, char *device);
 extern char *do_sh (char *command);
 extern char **do_sh_lines (char *command);
 extern char **do_glob_expand (char *pattern);
+extern int do_scrub_device (char *device);
+extern int do_scrub_file (char *file);
+extern int do_scrub_freespace (char *dir);
index af87a99..be97a86 100644 (file)
@@ -2854,6 +2854,78 @@ done:
   xdr_free ((xdrproc_t) xdr_guestfs_glob_expand_args, (char *) &args);
 }
 
+static void scrub_device_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_scrub_device_args args;
+  char *device;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_scrub_device_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "scrub_device");
+    return;
+  }
+  device = args.device;
+
+  r = do_scrub_device (device);
+  if (r == -1)
+    /* do_scrub_device has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_scrub_device_args, (char *) &args);
+}
+
+static void scrub_file_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_scrub_file_args args;
+  char *file;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_scrub_file_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "scrub_file");
+    return;
+  }
+  file = args.file;
+
+  r = do_scrub_file (file);
+  if (r == -1)
+    /* do_scrub_file has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_scrub_file_args, (char *) &args);
+}
+
+static void scrub_freespace_stub (XDR *xdr_in)
+{
+  int r;
+  struct guestfs_scrub_freespace_args args;
+  char *dir;
+
+  memset (&args, 0, sizeof args);
+
+  if (!xdr_guestfs_scrub_freespace_args (xdr_in, &args)) {
+    reply_with_error ("%s: daemon failed to decode procedure arguments", "scrub_freespace");
+    return;
+  }
+  dir = args.dir;
+
+  r = do_scrub_freespace (dir);
+  if (r == -1)
+    /* do_scrub_freespace has already called reply_with_error */
+    goto done;
+
+  reply (NULL, NULL);
+done:
+  xdr_free ((xdrproc_t) xdr_guestfs_scrub_freespace_args, (char *) &args);
+}
+
 void dispatch_incoming_message (XDR *xdr_in)
 {
   switch (proc_nr) {
@@ -3196,6 +3268,15 @@ void dispatch_incoming_message (XDR *xdr_in)
     case GUESTFS_PROC_GLOB_EXPAND:
       glob_expand_stub (xdr_in);
       break;
+    case GUESTFS_PROC_SCRUB_DEVICE:
+      scrub_device_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_SCRUB_FILE:
+      scrub_file_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_SCRUB_FREESPACE:
+      scrub_freespace_stub (xdr_in);
+      break;
     default:
       reply_with_error ("dispatch_incoming_message: unknown procedure number %d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory", proc_nr);
   }
index 8c35eea..b94b322 100644 (file)
@@ -127,6 +127,9 @@ void list_commands (void)
   printf ("%-20s %s\n", "rm", "remove a file");
   printf ("%-20s %s\n", "rm-rf", "remove a file or directory recursively");
   printf ("%-20s %s\n", "rmdir", "remove a directory");
+  printf ("%-20s %s\n", "scrub-device", "scrub (securely wipe) a device");
+  printf ("%-20s %s\n", "scrub-file", "scrub (securely wipe) a file");
+  printf ("%-20s %s\n", "scrub-freespace", "scrub (securely wipe) free space");
   printf ("%-20s %s\n", "set-append", "add options to kernel command line");
   printf ("%-20s %s\n", "set-autosync", "set autosync mode");
   printf ("%-20s %s\n", "set-e2label", "set the ext2/3/4 filesystem label");
@@ -177,7 +180,7 @@ void display_command (const char *cmd)
     pod2text ("kill-subprocess - kill the qemu subprocess", " kill-subprocess\n\nThis kills the qemu subprocess.  You should never need to call this.");
   else
   if (strcasecmp (cmd, "add_drive") == 0 || strcasecmp (cmd, "add-drive") == 0 || strcasecmp (cmd, "add") == 0)
-    pod2text ("add-drive - add an image to examine or modify", " add-drive <filename>\n\nThis function adds a virtual machine disk image C<filename> to the\nguest.  The first time you call this function, the disk appears as IDE\ndisk 0 (C</dev/sda>) in the guest, the second time as C</dev/sdb>, and\nso on.\n\nYou don't necessarily need to be root when using libguestfs.  However\nyou obviously do need sufficient permissions to access the filename\nfor whatever operations you want to perform (ie. read access if you\njust want to read the image or write access if you want to modify the\nimage).\n\nThis is equivalent to the qemu parameter C<-drive file=filename>.\n\nNote that this call checks for the existence of C<filename>.  This\nstops you from specifying other types of drive which are supported\nby qemu such as C<nbd:> and C<http:> URLs.  To specify those, use\nthe general C<config> call instead.\n\nYou can use 'add' as an alias for this command.");
+    pod2text ("add-drive - add an image to examine or modify", " add-drive <filename>\n\nThis function adds a virtual machine disk image C<filename> to the\nguest.  The first time you call this function, the disk appears as IDE\ndisk 0 (C</dev/sda>) in the guest, the second time as C</dev/sdb>, and\nso on.\n\nYou don't necessarily need to be root when using libguestfs.  However\nyou obviously do need sufficient permissions to access the filename\nfor whatever operations you want to perform (ie. read access if you\njust want to read the image or write access if you want to modify the\nimage).\n\nThis is equivalent to the qemu parameter C<-drive file=filename,cache=off>.\n\nNote that this call checks for the existence of C<filename>.  This\nstops you from specifying other types of drive which are supported\nby qemu such as C<nbd:> and C<http:> URLs.  To specify those, use\nthe general C<config> call instead.\n\nYou can use 'add' as an alias for this command.");
   else
   if (strcasecmp (cmd, "add_cdrom") == 0 || strcasecmp (cmd, "add-cdrom") == 0 || strcasecmp (cmd, "cdrom") == 0)
     pod2text ("add-cdrom - add a CD-ROM disk image to examine", " add-cdrom <filename>\n\nThis function adds a virtual CD-ROM disk image to the guest.\n\nThis is equivalent to the qemu parameter C<-cdrom filename>.\n\nNote that this call checks for the existence of C<filename>.  This\nstops you from specifying other types of drive which are supported\nby qemu such as C<nbd:> and C<http:> URLs.  To specify those, use\nthe general C<config> call instead.\n\nYou can use 'cdrom' as an alias for this command.");
@@ -486,7 +489,7 @@ void display_command (const char *cmd)
     pod2text ("fsck - run the filesystem checker", " fsck <fstype> <device>\n\nThis runs the filesystem checker (fsck) on C<device> which\nshould have filesystem type C<fstype>.\n\nThe returned integer is the status.  See L<fsck(8)> for the\nlist of status codes from C<fsck>.\n\nNotes:\n\n=over 4\n\n=item *\n\nMultiple status codes can be summed together.\n\n=item *\n\nA non-zero return code can mean \"success\", for example if\nerrors have been corrected on the filesystem.\n\n=item *\n\nChecking or repairing NTFS volumes is not supported\n(by linux-ntfs).\n\n=back\n\nThis command is entirely equivalent to running C<fsck -a -t fstype device>.");
   else
   if (strcasecmp (cmd, "zero") == 0)
-    pod2text ("zero - write zeroes to the device", " zero <device>\n\nThis command writes zeroes over the first few blocks of C<device>.\n\nHow many blocks are zeroed isn't specified (but it's I<not> enough\nto securely wipe the device).  It should be sufficient to remove\nany partition tables, filesystem superblocks and so on.");
+    pod2text ("zero - write zeroes to the device", " zero <device>\n\nThis command writes zeroes over the first few blocks of C<device>.\n\nHow many blocks are zeroed isn't specified (but it's I<not> enough\nto securely wipe the device).  It should be sufficient to remove\nany partition tables, filesystem superblocks and so on.\n\nSee also: C<scrub_device>.");
   else
   if (strcasecmp (cmd, "grub_install") == 0 || strcasecmp (cmd, "grub-install") == 0)
     pod2text ("grub-install - install GRUB", " grub-install <root> <device>\n\nThis command installs GRUB (the Grand Unified Bootloader) on\nC<device>, with the root directory being C<root>.");
@@ -572,6 +575,15 @@ void display_command (const char *cmd)
   if (strcasecmp (cmd, "glob_expand") == 0 || strcasecmp (cmd, "glob-expand") == 0)
     pod2text ("glob-expand - expand a wildcard path", " glob-expand <pattern>\n\nThis command searches for all the pathnames matching\nC<pattern> according to the wildcard expansion rules\nused by the shell.\n\nIf no paths match, then this returns an empty list\n(note: not an error).\n\nIt is just a wrapper around the C L<glob(3)> function\nwith flags C<GLOB_MARK|GLOB_BRACE>.\nSee that manual page for more details.");
   else
+  if (strcasecmp (cmd, "scrub_device") == 0 || strcasecmp (cmd, "scrub-device") == 0)
+    pod2text ("scrub-device - scrub (securely wipe) a device", " scrub-device <device>\n\nThis command writes patterns over C<device> to make data retrieval\nmore difficult.\n\nIt is an interface to the L<scrub(1)> program.  See that\nmanual page for more details.\n\nB<This command is dangerous.  Without careful use you\ncan easily destroy all your data>.");
+  else
+  if (strcasecmp (cmd, "scrub_file") == 0 || strcasecmp (cmd, "scrub-file") == 0)
+    pod2text ("scrub-file - scrub (securely wipe) a file", " scrub-file <file>\n\nThis command writes patterns over a file to make data retrieval\nmore difficult.\n\nThe file is I<removed> after scrubbing.\n\nIt is an interface to the L<scrub(1)> program.  See that\nmanual page for more details.");
+  else
+  if (strcasecmp (cmd, "scrub_freespace") == 0 || strcasecmp (cmd, "scrub-freespace") == 0)
+    pod2text ("scrub-freespace - scrub (securely wipe) free space", " scrub-freespace <dir>\n\nThis command creates the directory C<dir> and then fills it\nwith files until the filesystem is full, and scrubs the files\nas for C<scrub_file>, and deletes them.\nThe intention is to scrub any free space on the partition\ncontaining C<dir>.\n\nIt is an interface to the L<scrub(1)> program.  See that\nmanual page for more details.");
+  else
     display_builtin_command (cmd);
 }
 
@@ -2801,6 +2813,48 @@ static int run_glob_expand (const char *cmd, int argc, char *argv[])
   return 0;
 }
 
+static int run_scrub_device (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *device;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  device = argv[0];
+  r = guestfs_scrub_device (g, device);
+  return r;
+}
+
+static int run_scrub_file (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *file;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  file = argv[0];
+  r = guestfs_scrub_file (g, file);
+  return r;
+}
+
+static int run_scrub_freespace (const char *cmd, int argc, char *argv[])
+{
+  int r;
+  const char *dir;
+  if (argc != 1) {
+    fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+    fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+    return -1;
+  }
+  dir = argv[0];
+  r = guestfs_scrub_freespace (g, dir);
+  return r;
+}
+
 int run_action (const char *cmd, int argc, char *argv[])
 {
   if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0)
@@ -3205,6 +3259,15 @@ int run_action (const char *cmd, int argc, char *argv[])
   if (strcasecmp (cmd, "glob_expand") == 0 || strcasecmp (cmd, "glob-expand") == 0)
     return run_glob_expand (cmd, argc, argv);
   else
+  if (strcasecmp (cmd, "scrub_device") == 0 || strcasecmp (cmd, "scrub-device") == 0)
+    return run_scrub_device (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "scrub_file") == 0 || strcasecmp (cmd, "scrub-file") == 0)
+    return run_scrub_file (cmd, argc, argv);
+  else
+  if (strcasecmp (cmd, "scrub_freespace") == 0 || strcasecmp (cmd, "scrub-freespace") == 0)
+    return run_scrub_freespace (cmd, argc, argv);
+  else
     {
       fprintf (stderr, "%s: unknown command\n", cmd);
       return -1;
index e8d8cea..92843e5 100644 (file)
@@ -180,6 +180,9 @@ static const char *const commands[] = {
   "sh",
   "sh-lines",
   "glob-expand",
+  "scrub-device",
+  "scrub-file",
+  "scrub-freespace",
   NULL
 };
 
index 4845990..d4f4930 100644 (file)
@@ -26,7 +26,7 @@ for whatever operations you want to perform (ie. read access if you
 just want to read the image or write access if you want to modify the
 image).
 
-This is equivalent to the qemu parameter C<-drive file=filename>.
+This is equivalent to the qemu parameter C<-drive file=filename,cache=off>.
 
 Note that this call checks for the existence of C<filename>.  This
 stops you from specifying other types of drive which are supported
@@ -1092,6 +1092,44 @@ command.
 
 Remove the single directory C<path>.
 
+=head2 scrub-device
+
+ scrub-device device
+
+This command writes patterns over C<device> to make data retrieval
+more difficult.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
+=head2 scrub-file
+
+ scrub-file file
+
+This command writes patterns over a file to make data retrieval
+more difficult.
+
+The file is I<removed> after scrubbing.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+=head2 scrub-freespace
+
+ scrub-freespace dir
+
+This command creates the directory C<dir> and then fills it
+with files until the filesystem is full, and scrubs the files
+as for C<scrub-file>, and deletes them.
+The intention is to scrub any free space on the partition
+containing C<dir>.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
 =head2 set-append | append
 
  set-append append
@@ -1520,6 +1558,8 @@ How many blocks are zeroed isn't specified (but it's I<not> enough
 to securely wipe the device).  It should be sufficient to remove
 any partition tables, filesystem superblocks and so on.
 
+See also: C<scrub-device>.
+
 =head2 zerofree
 
  zerofree device
index d35d1aa..9afa5de 100644 (file)
@@ -30,7 +30,7 @@ for whatever operations you want to perform (ie. read access if you
 just want to read the image or write access if you want to modify the
 image).
 
-This is equivalent to the qemu parameter C<-drive file=filename>.
+This is equivalent to the qemu parameter C<-drive file=filename,cache=off>.
 
 Note that this call checks for the existence of C<filename>.  This
 stops you from specifying other types of drive which are supported
@@ -1448,6 +1448,53 @@ Remove the single directory C<path>.
 
 This function returns 0 on success or -1 on error.
 
+=head2 guestfs_scrub_device
+
+ int guestfs_scrub_device (guestfs_h *handle,
+               const char *device);
+
+This command writes patterns over C<device> to make data retrieval
+more difficult.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+This function returns 0 on success or -1 on error.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
+=head2 guestfs_scrub_file
+
+ int guestfs_scrub_file (guestfs_h *handle,
+               const char *file);
+
+This command writes patterns over a file to make data retrieval
+more difficult.
+
+The file is I<removed> after scrubbing.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+This function returns 0 on success or -1 on error.
+
+=head2 guestfs_scrub_freespace
+
+ int guestfs_scrub_freespace (guestfs_h *handle,
+               const char *dir);
+
+This command creates the directory C<dir> and then fills it
+with files until the filesystem is full, and scrubs the files
+as for C<guestfs_scrub_file>, and deletes them.
+The intention is to scrub any free space on the partition
+containing C<dir>.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+This function returns 0 on success or -1 on error.
+
 =head2 guestfs_set_append
 
  int guestfs_set_append (guestfs_h *handle,
@@ -2051,6 +2098,8 @@ How many blocks are zeroed isn't specified (but it's I<not> enough
 to securely wipe the device).  It should be sufficient to remove
 any partition tables, filesystem superblocks and so on.
 
+See also: C<guestfs_scrub_device>.
+
 This function returns 0 on success or -1 on error.
 
 =head2 guestfs_zerofree
index 210274e..ddbad46 100644 (file)
@@ -83,7 +83,10 @@ module Guestfs (
   zerofree,
   pvresize,
   resize2fs,
-  e2fsck_f
+  e2fsck_f,
+  scrub_device,
+  scrub_file,
+  scrub_freespace
   ) where
 import Foreign
 import Foreign.C
@@ -853,3 +856,39 @@ e2fsck_f h device = do
       fail err
     else return ()
 
+foreign import ccall unsafe "guestfs_scrub_device" c_scrub_device
+  :: GuestfsP -> CString -> IO (CInt)
+
+scrub_device :: GuestfsH -> String -> IO ()
+scrub_device h device = do
+  r <- withCString device $ \device -> withForeignPtr h (\p -> c_scrub_device p device)
+  if (r == -1)
+    then do
+      err <- last_error h
+      fail err
+    else return ()
+
+foreign import ccall unsafe "guestfs_scrub_file" c_scrub_file
+  :: GuestfsP -> CString -> IO (CInt)
+
+scrub_file :: GuestfsH -> String -> IO ()
+scrub_file h file = do
+  r <- withCString file $ \file -> withForeignPtr h (\p -> c_scrub_file p file)
+  if (r == -1)
+    then do
+      err <- last_error h
+      fail err
+    else return ()
+
+foreign import ccall unsafe "guestfs_scrub_freespace" c_scrub_freespace
+  :: GuestfsP -> CString -> IO (CInt)
+
+scrub_freespace :: GuestfsH -> String -> IO ()
+scrub_freespace h dir = do
+  r <- withCString dir $ \dir -> withForeignPtr h (\p -> c_scrub_freespace p dir)
+  if (r == -1)
+    then do
+      err <- last_error h
+      fail err
+    else return ()
+
index cdc0f09..3668970 100644 (file)
@@ -427,7 +427,7 @@ public HashMap<String,String> test0rhashtableerr ()
    * to modify the image).
    * <p>
    * This is equivalent to the qemu parameter "-drive
-   * file=filename".
+   * file=filename,cache=off".
    * <p>
    * Note that this call checks for the existence of
    * "filename". This stops you from specifying other types
@@ -2801,6 +2801,8 @@ public HashMap<String,String> test0rhashtableerr ()
    * sufficient to remove any partition tables, filesystem
    * superblocks and so on.
    * <p>
+   * See also: "g.scrub_device".
+   * <p>
    * @throws LibGuestFSException
    */
   public void zero (String device)
@@ -3461,4 +3463,75 @@ public HashMap<String,String> test0rhashtableerr ()
   private native String[] _glob_expand (long g, String pattern)
     throws LibGuestFSException;
 
+  /**
+   * scrub (securely wipe) a device
+   * <p>
+   * This command writes patterns over "device" to make data
+   * retrieval more difficult.
+   * <p>
+   * It is an interface to the scrub(1) program. See that
+   * manual page for more details.
+   * <p>
+   * This command is dangerous. Without careful use you can
+   * easily destroy all your data.
+   * <p>
+   * @throws LibGuestFSException
+   */
+  public void scrub_device (String device)
+    throws LibGuestFSException
+  {
+    if (g == 0)
+      throw new LibGuestFSException ("scrub_device: handle is closed");
+    _scrub_device (g, device);
+  }
+  private native void _scrub_device (long g, String device)
+    throws LibGuestFSException;
+
+  /**
+   * scrub (securely wipe) a file
+   * <p>
+   * This command writes patterns over a file to make data
+   * retrieval more difficult.
+   * <p>
+   * The file is *removed* after scrubbing.
+   * <p>
+   * It is an interface to the scrub(1) program. See that
+   * manual page for more details.
+   * <p>
+   * @throws LibGuestFSException
+   */
+  public void scrub_file (String file)
+    throws LibGuestFSException
+  {
+    if (g == 0)
+      throw new LibGuestFSException ("scrub_file: handle is closed");
+    _scrub_file (g, file);
+  }
+  private native void _scrub_file (long g, String file)
+    throws LibGuestFSException;
+
+  /**
+   * scrub (securely wipe) free space
+   * <p>
+   * This command creates the directory "dir" and then fills
+   * it with files until the filesystem is full, and scrubs
+   * the files as for "g.scrub_file", and deletes them. The
+   * intention is to scrub any free space on the partition
+   * containing "dir".
+   * <p>
+   * It is an interface to the scrub(1) program. See that
+   * manual page for more details.
+   * <p>
+   * @throws LibGuestFSException
+   */
+  public void scrub_freespace (String dir)
+    throws LibGuestFSException
+  {
+    if (g == 0)
+      throw new LibGuestFSException ("scrub_freespace: handle is closed");
+    _scrub_freespace (g, dir);
+  }
+  private native void _scrub_freespace (long g, String dir)
+    throws LibGuestFSException;
+
 }
index 8a5afe9..37cd4c9 100644 (file)
@@ -4073,3 +4073,54 @@ Java_com_redhat_et_libguestfs_GuestFS__1glob_1expand
   return jr;
 }
 
+JNIEXPORT void JNICALL
+Java_com_redhat_et_libguestfs_GuestFS__1scrub_1device
+  (JNIEnv *env, jobject obj, jlong jg, jstring jdevice)
+{
+  guestfs_h *g = (guestfs_h *) (long) jg;
+  int r;
+  const char *device;
+
+  device = (*env)->GetStringUTFChars (env, jdevice, NULL);
+  r = guestfs_scrub_device (g, device);
+  (*env)->ReleaseStringUTFChars (env, jdevice, device);
+  if (r == -1) {
+    throw_exception (env, guestfs_last_error (g));
+    return ;
+  }
+}
+
+JNIEXPORT void JNICALL
+Java_com_redhat_et_libguestfs_GuestFS__1scrub_1file
+  (JNIEnv *env, jobject obj, jlong jg, jstring jfile)
+{
+  guestfs_h *g = (guestfs_h *) (long) jg;
+  int r;
+  const char *file;
+
+  file = (*env)->GetStringUTFChars (env, jfile, NULL);
+  r = guestfs_scrub_file (g, file);
+  (*env)->ReleaseStringUTFChars (env, jfile, file);
+  if (r == -1) {
+    throw_exception (env, guestfs_last_error (g));
+    return ;
+  }
+}
+
+JNIEXPORT void JNICALL
+Java_com_redhat_et_libguestfs_GuestFS__1scrub_1freespace
+  (JNIEnv *env, jobject obj, jlong jg, jstring jdir)
+{
+  guestfs_h *g = (guestfs_h *) (long) jg;
+  int r;
+  const char *dir;
+
+  dir = (*env)->GetStringUTFChars (env, jdir, NULL);
+  r = guestfs_scrub_freespace (g, dir);
+  (*env)->ReleaseStringUTFChars (env, jdir, dir);
+  if (r == -1) {
+    throw_exception (env, guestfs_last_error (g));
+    return ;
+  }
+}
+
index fb83545..f0c789b 100644 (file)
@@ -280,3 +280,6 @@ external ntfs_3g_probe : t -> bool -> string -> int = "ocaml_guestfs_ntfs_3g_pro
 external sh : t -> string -> string = "ocaml_guestfs_sh"
 external sh_lines : t -> string -> string array = "ocaml_guestfs_sh_lines"
 external glob_expand : t -> string -> string array = "ocaml_guestfs_glob_expand"
+external scrub_device : t -> string -> unit = "ocaml_guestfs_scrub_device"
+external scrub_file : t -> string -> unit = "ocaml_guestfs_scrub_file"
+external scrub_freespace : t -> string -> unit = "ocaml_guestfs_scrub_freespace"
index 345c7ec..d93cfdc 100644 (file)
@@ -619,3 +619,12 @@ val sh_lines : t -> string -> string array
 val glob_expand : t -> string -> string array
 (** expand a wildcard path *)
 
+val scrub_device : t -> string -> unit
+(** scrub (securely wipe) a device *)
+
+val scrub_file : t -> string -> unit
+(** scrub (securely wipe) a file *)
+
+val scrub_freespace : t -> string -> unit
+(** scrub (securely wipe) free space *)
+
index 1ce55f0..a8204e8 100644 (file)
@@ -4274,3 +4274,72 @@ ocaml_guestfs_glob_expand (value gv, value patternv)
   CAMLreturn (rv);
 }
 
+CAMLprim value
+ocaml_guestfs_scrub_device (value gv, value devicev)
+{
+  CAMLparam2 (gv, devicev);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("scrub_device: used handle after closing it");
+
+  const char *device = String_val (devicev);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_scrub_device (g, device);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "scrub_device");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_scrub_file (value gv, value filev)
+{
+  CAMLparam2 (gv, filev);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("scrub_file: used handle after closing it");
+
+  const char *file = String_val (filev);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_scrub_file (g, file);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "scrub_file");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_scrub_freespace (value gv, value dirv)
+{
+  CAMLparam2 (gv, dirv);
+  CAMLlocal1 (rv);
+
+  guestfs_h *g = Guestfs_val (gv);
+  if (g == NULL)
+    caml_failwith ("scrub_freespace: used handle after closing it");
+
+  const char *dir = String_val (dirv);
+  int r;
+
+  caml_enter_blocking_section ();
+  r = guestfs_scrub_freespace (g, dir);
+  caml_leave_blocking_section ();
+  if (r == -1)
+    ocaml_guestfs_raise_error (g, "scrub_freespace");
+
+  rv = Val_unit;
+  CAMLreturn (rv);
+}
+
index 5905dc5..4b05910 100644 (file)
@@ -2602,3 +2602,36 @@ PREINIT:
       }
       free (paths);
 
+void
+scrub_device (g, device)
+      guestfs_h *g;
+      char *device;
+PREINIT:
+      int r;
+ PPCODE:
+      r = guestfs_scrub_device (g, device);
+      if (r == -1)
+        croak ("scrub_device: %s", guestfs_last_error (g));
+
+void
+scrub_file (g, file)
+      guestfs_h *g;
+      char *file;
+PREINIT:
+      int r;
+ PPCODE:
+      r = guestfs_scrub_file (g, file);
+      if (r == -1)
+        croak ("scrub_file: %s", guestfs_last_error (g));
+
+void
+scrub_freespace (g, dir)
+      guestfs_h *g;
+      char *dir;
+PREINIT:
+      int r;
+ PPCODE:
+      r = guestfs_scrub_freespace (g, dir);
+      if (r == -1)
+        croak ("scrub_freespace: %s", guestfs_last_error (g));
+
index 436f218..9c5aa18 100644 (file)
@@ -115,7 +115,7 @@ for whatever operations you want to perform (ie. read access if you
 just want to read the image or write access if you want to modify the
 image).
 
-This is equivalent to the qemu parameter C<-drive file=filename>.
+This is equivalent to the qemu parameter C<-drive file=filename,cache=off>.
 
 Note that this call checks for the existence of C<filename>.  This
 stops you from specifying other types of drive which are supported
@@ -999,6 +999,38 @@ command.
 
 Remove the single directory C<path>.
 
+=item $h->scrub_device ($device);
+
+This command writes patterns over C<device> to make data retrieval
+more difficult.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+B<This command is dangerous.  Without careful use you
+can easily destroy all your data>.
+
+=item $h->scrub_file ($file);
+
+This command writes patterns over a file to make data retrieval
+more difficult.
+
+The file is I<removed> after scrubbing.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
+=item $h->scrub_freespace ($dir);
+
+This command creates the directory C<dir> and then fills it
+with files until the filesystem is full, and scrubs the files
+as for C<$h-E<gt>scrub_file>, and deletes them.
+The intention is to scrub any free space on the partition
+containing C<dir>.
+
+It is an interface to the L<scrub(1)> program.  See that
+manual page for more details.
+
 =item $h->set_append ($append);
 
 This function is used to add additional options to the
@@ -1365,6 +1397,8 @@ How many blocks are zeroed isn't specified (but it's I<not> enough
 to securely wipe the device).  It should be sufficient to remove
 any partition tables, filesystem superblocks and so on.
 
+See also: C<$h-E<gt>scrub_device>.
+
 =item $h->zerofree ($device);
 
 This runs the I<zerofree> program on C<device>.  This program
index 60e20d2..b1623fe 100644 (file)
@@ -4531,6 +4531,81 @@ py_guestfs_glob_expand (PyObject *self, PyObject *args)
   return py_r;
 }
 
+static PyObject *
+py_guestfs_scrub_device (PyObject *self, PyObject *args)
+{
+  PyObject *py_g;
+  guestfs_h *g;
+  PyObject *py_r;
+  int r;
+  const char *device;
+
+  if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_scrub_device",
+                         &py_g, &device))
+    return NULL;
+  g = get_handle (py_g);
+
+  r = guestfs_scrub_device (g, device);
+  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_scrub_file (PyObject *self, PyObject *args)
+{
+  PyObject *py_g;
+  guestfs_h *g;
+  PyObject *py_r;
+  int r;
+  const char *file;
+
+  if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_scrub_file",
+                         &py_g, &file))
+    return NULL;
+  g = get_handle (py_g);
+
+  r = guestfs_scrub_file (g, file);
+  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_scrub_freespace (PyObject *self, PyObject *args)
+{
+  PyObject *py_g;
+  guestfs_h *g;
+  PyObject *py_r;
+  int r;
+  const char *dir;
+
+  if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_scrub_freespace",
+                         &py_g, &dir))
+    return NULL;
+  g = get_handle (py_g);
+
+  r = guestfs_scrub_freespace (g, dir);
+  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 },
@@ -4699,6 +4774,9 @@ static PyMethodDef methods[] = {
   { (char *) "sh", py_guestfs_sh, METH_VARARGS, NULL },
   { (char *) "sh_lines", py_guestfs_sh_lines, METH_VARARGS, NULL },
   { (char *) "glob_expand", py_guestfs_glob_expand, METH_VARARGS, NULL },
+  { (char *) "scrub_device", py_guestfs_scrub_device, METH_VARARGS, NULL },
+  { (char *) "scrub_file", py_guestfs_scrub_file, METH_VARARGS, NULL },
+  { (char *) "scrub_freespace", py_guestfs_scrub_freespace, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
 };
 
index 3976610..0bc041e 100644 (file)
@@ -197,7 +197,7 @@ class GuestFS:
         to modify the image).
         
         This is equivalent to the qemu parameter "-drive
-        file=filename".
+        file=filename,cache=off".
         
         Note that this call checks for the existence of
         "filename". This stops you from specifying other types
@@ -1342,6 +1342,8 @@ class GuestFS:
         *not* enough to securely wipe the device). It should be
         sufficient to remove any partition tables, filesystem
         superblocks and so on.
+        
+        See also: "g.scrub_device".
         """
         return libguestfsmod.zero (self._o, device)
 
@@ -1667,3 +1669,38 @@ class GuestFS:
         """
         return libguestfsmod.glob_expand (self._o, pattern)
 
+    def scrub_device (self, device):
+        u"""This command writes patterns over "device" to make data
+        retrieval more difficult.
+        
+        It is an interface to the scrub(1) program. See that
+        manual page for more details.
+        
+        This command is dangerous. Without careful use you can
+        easily destroy all your data.
+        """
+        return libguestfsmod.scrub_device (self._o, device)
+
+    def scrub_file (self, file):
+        u"""This command writes patterns over a file to make data
+        retrieval more difficult.
+        
+        The file is *removed* after scrubbing.
+        
+        It is an interface to the scrub(1) program. See that
+        manual page for more details.
+        """
+        return libguestfsmod.scrub_file (self._o, file)
+
+    def scrub_freespace (self, dir):
+        u"""This command creates the directory "dir" and then fills
+        it with files until the filesystem is full, and scrubs
+        the files as for "g.scrub_file", and deletes them. The
+        intention is to scrub any free space on the partition
+        containing "dir".
+        
+        It is an interface to the scrub(1) program. See that
+        manual page for more details.
+        """
+        return libguestfsmod.scrub_freespace (self._o, dir)
+
index 202c3ca..ba9de13 100644 (file)
@@ -4243,6 +4243,72 @@ static VALUE ruby_guestfs_glob_expand (VALUE gv, VALUE patternv)
   return rv;
 }
 
+static VALUE ruby_guestfs_scrub_device (VALUE gv, VALUE devicev)
+{
+  guestfs_h *g;
+  Data_Get_Struct (gv, guestfs_h, g);
+  if (!g)
+    rb_raise (rb_eArgError, "%s: used handle after closing it", "scrub_device");
+
+  Check_Type (devicev, T_STRING);
+  const char *device = StringValueCStr (devicev);
+  if (!device)
+    rb_raise (rb_eTypeError, "expected string for parameter %s of %s",
+              "device", "scrub_device");
+
+  int r;
+
+  r = guestfs_scrub_device (g, device);
+  if (r == -1)
+    rb_raise (e_Error, "%s", guestfs_last_error (g));
+
+  return Qnil;
+}
+
+static VALUE ruby_guestfs_scrub_file (VALUE gv, VALUE filev)
+{
+  guestfs_h *g;
+  Data_Get_Struct (gv, guestfs_h, g);
+  if (!g)
+    rb_raise (rb_eArgError, "%s: used handle after closing it", "scrub_file");
+
+  Check_Type (filev, T_STRING);
+  const char *file = StringValueCStr (filev);
+  if (!file)
+    rb_raise (rb_eTypeError, "expected string for parameter %s of %s",
+              "file", "scrub_file");
+
+  int r;
+
+  r = guestfs_scrub_file (g, file);
+  if (r == -1)
+    rb_raise (e_Error, "%s", guestfs_last_error (g));
+
+  return Qnil;
+}
+
+static VALUE ruby_guestfs_scrub_freespace (VALUE gv, VALUE dirv)
+{
+  guestfs_h *g;
+  Data_Get_Struct (gv, guestfs_h, g);
+  if (!g)
+    rb_raise (rb_eArgError, "%s: used handle after closing it", "scrub_freespace");
+
+  Check_Type (dirv, T_STRING);
+  const char *dir = StringValueCStr (dirv);
+  if (!dir)
+    rb_raise (rb_eTypeError, "expected string for parameter %s of %s",
+              "dir", "scrub_freespace");
+
+  int r;
+
+  r = guestfs_scrub_freespace (g, dir);
+  if (r == -1)
+    rb_raise (e_Error, "%s", guestfs_last_error (g));
+
+  return Qnil;
+}
+
 /* Initialize the module. */
 void Init__guestfs ()
 {
@@ -4583,4 +4649,10 @@ void Init__guestfs ()
         ruby_guestfs_sh_lines, 1);
   rb_define_method (c_guestfs, "glob_expand",
         ruby_guestfs_glob_expand, 1);
+  rb_define_method (c_guestfs, "scrub_device",
+        ruby_guestfs_scrub_device, 1);
+  rb_define_method (c_guestfs, "scrub_file",
+        ruby_guestfs_scrub_file, 1);
+  rb_define_method (c_guestfs, "scrub_freespace",
+        ruby_guestfs_scrub_freespace, 1);
 }
index e1ec158..7402105 100644 (file)
@@ -10360,3 +10360,264 @@ char **guestfs_glob_expand (guestfs_h *g,
   return ctx.ret.paths.paths_val;
 }
 
+struct scrub_device_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;
+};
+
+static void scrub_device_reply_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  struct scrub_device_ctx *ctx = (struct scrub_device_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_scrub_device");
+    return;
+  }
+
+  ml->main_loop_quit (ml, g);
+
+  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {
+    error (g, "%s: failed to parse reply header", "guestfs_scrub_device");
+    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_scrub_device");
+      return;
+    }
+    goto done;
+  }
+ done:
+  ctx->cb_sequence = 1;
+}
+
+int guestfs_scrub_device (guestfs_h *g,
+               const char *device)
+{
+  struct guestfs_scrub_device_args args;
+  struct scrub_device_ctx ctx;
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  int serial;
+
+  if (check_state (g, "guestfs_scrub_device") == -1) return -1;
+  guestfs_set_busy (g);
+
+  memset (&ctx, 0, sizeof ctx);
+
+  args.device = (char *) device;
+  serial = guestfs__send_sync (g, GUESTFS_PROC_SCRUB_DEVICE,
+        (xdrproc_t) xdr_guestfs_scrub_device_args, (char *) &args);
+  if (serial == -1) {
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  guestfs__switch_to_receiving (g);
+  ctx.cb_sequence = 0;
+  guestfs_set_reply_callback (g, scrub_device_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_scrub_device");
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SCRUB_DEVICE, serial) == -1) {
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", ctx.err.error_message);
+    free (ctx.err.error_message);
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  guestfs_end_busy (g);
+  return 0;
+}
+
+struct scrub_file_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;
+};
+
+static void scrub_file_reply_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  struct scrub_file_ctx *ctx = (struct scrub_file_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_scrub_file");
+    return;
+  }
+
+  ml->main_loop_quit (ml, g);
+
+  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {
+    error (g, "%s: failed to parse reply header", "guestfs_scrub_file");
+    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_scrub_file");
+      return;
+    }
+    goto done;
+  }
+ done:
+  ctx->cb_sequence = 1;
+}
+
+int guestfs_scrub_file (guestfs_h *g,
+               const char *file)
+{
+  struct guestfs_scrub_file_args args;
+  struct scrub_file_ctx ctx;
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  int serial;
+
+  if (check_state (g, "guestfs_scrub_file") == -1) return -1;
+  guestfs_set_busy (g);
+
+  memset (&ctx, 0, sizeof ctx);
+
+  args.file = (char *) file;
+  serial = guestfs__send_sync (g, GUESTFS_PROC_SCRUB_FILE,
+        (xdrproc_t) xdr_guestfs_scrub_file_args, (char *) &args);
+  if (serial == -1) {
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  guestfs__switch_to_receiving (g);
+  ctx.cb_sequence = 0;
+  guestfs_set_reply_callback (g, scrub_file_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_scrub_file");
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SCRUB_FILE, serial) == -1) {
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", ctx.err.error_message);
+    free (ctx.err.error_message);
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  guestfs_end_busy (g);
+  return 0;
+}
+
+struct scrub_freespace_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;
+};
+
+static void scrub_freespace_reply_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  struct scrub_freespace_ctx *ctx = (struct scrub_freespace_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_scrub_freespace");
+    return;
+  }
+
+  ml->main_loop_quit (ml, g);
+
+  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {
+    error (g, "%s: failed to parse reply header", "guestfs_scrub_freespace");
+    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_scrub_freespace");
+      return;
+    }
+    goto done;
+  }
+ done:
+  ctx->cb_sequence = 1;
+}
+
+int guestfs_scrub_freespace (guestfs_h *g,
+               const char *dir)
+{
+  struct guestfs_scrub_freespace_args args;
+  struct scrub_freespace_ctx ctx;
+  guestfs_main_loop *ml = guestfs_get_main_loop (g);
+  int serial;
+
+  if (check_state (g, "guestfs_scrub_freespace") == -1) return -1;
+  guestfs_set_busy (g);
+
+  memset (&ctx, 0, sizeof ctx);
+
+  args.dir = (char *) dir;
+  serial = guestfs__send_sync (g, GUESTFS_PROC_SCRUB_FREESPACE,
+        (xdrproc_t) xdr_guestfs_scrub_freespace_args, (char *) &args);
+  if (serial == -1) {
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  guestfs__switch_to_receiving (g);
+  ctx.cb_sequence = 0;
+  guestfs_set_reply_callback (g, scrub_freespace_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_scrub_freespace");
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SCRUB_FREESPACE, serial) == -1) {
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {
+    error (g, "%s", ctx.err.error_message);
+    free (ctx.err.error_message);
+    guestfs_end_busy (g);
+    return -1;
+  }
+
+  guestfs_end_busy (g);
+  return 0;
+}
+
index 19996e8..0f85df7 100644 (file)
@@ -184,3 +184,6 @@ extern int guestfs_ntfs_3g_probe (guestfs_h *handle, int rw, const char *device)
 extern char *guestfs_sh (guestfs_h *handle, const char *command);
 extern char **guestfs_sh_lines (guestfs_h *handle, const char *command);
 extern char **guestfs_glob_expand (guestfs_h *handle, const char *pattern);
+extern int guestfs_scrub_device (guestfs_h *handle, const char *device);
+extern int guestfs_scrub_file (guestfs_h *handle, const char *file);
+extern int guestfs_scrub_freespace (guestfs_h *handle, const char *dir);
index b2d435e..a241aa0 100644 (file)
@@ -1933,6 +1933,36 @@ xdr_guestfs_glob_expand_ret (XDR *xdrs, guestfs_glob_expand_ret *objp)
 }
 
 bool_t
+xdr_guestfs_scrub_device_args (XDR *xdrs, guestfs_scrub_device_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->device, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_scrub_file_args (XDR *xdrs, guestfs_scrub_file_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->file, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_scrub_freespace_args (XDR *xdrs, guestfs_scrub_freespace_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->dir, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
 xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
 {
        register int32_t *buf;
index 8fdb664..dc88c70 100644 (file)
@@ -982,6 +982,21 @@ struct guestfs_glob_expand_ret {
 };
 typedef struct guestfs_glob_expand_ret guestfs_glob_expand_ret;
 
+struct guestfs_scrub_device_args {
+       char *device;
+};
+typedef struct guestfs_scrub_device_args guestfs_scrub_device_args;
+
+struct guestfs_scrub_file_args {
+       char *file;
+};
+typedef struct guestfs_scrub_file_args guestfs_scrub_file_args;
+
+struct guestfs_scrub_freespace_args {
+       char *dir;
+};
+typedef struct guestfs_scrub_freespace_args guestfs_scrub_freespace_args;
+
 enum guestfs_procedure {
        GUESTFS_PROC_MOUNT = 1,
        GUESTFS_PROC_SYNC = 2,
@@ -1096,7 +1111,10 @@ enum guestfs_procedure {
        GUESTFS_PROC_SH = 111,
        GUESTFS_PROC_SH_LINES = 112,
        GUESTFS_PROC_GLOB_EXPAND = 113,
-       GUESTFS_PROC_NR_PROCS = 113 + 1,
+       GUESTFS_PROC_SCRUB_DEVICE = 114,
+       GUESTFS_PROC_SCRUB_FILE = 115,
+       GUESTFS_PROC_SCRUB_FREESPACE = 116,
+       GUESTFS_PROC_NR_PROCS = 116 + 1,
 };
 typedef enum guestfs_procedure guestfs_procedure;
 #define GUESTFS_MESSAGE_MAX 4194304
@@ -1303,6 +1321,9 @@ extern  bool_t xdr_guestfs_sh_lines_args (XDR *, guestfs_sh_lines_args*);
 extern  bool_t xdr_guestfs_sh_lines_ret (XDR *, guestfs_sh_lines_ret*);
 extern  bool_t xdr_guestfs_glob_expand_args (XDR *, guestfs_glob_expand_args*);
 extern  bool_t xdr_guestfs_glob_expand_ret (XDR *, guestfs_glob_expand_ret*);
+extern  bool_t xdr_guestfs_scrub_device_args (XDR *, guestfs_scrub_device_args*);
+extern  bool_t xdr_guestfs_scrub_file_args (XDR *, guestfs_scrub_file_args*);
+extern  bool_t xdr_guestfs_scrub_freespace_args (XDR *, guestfs_scrub_freespace_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*);
@@ -1468,6 +1489,9 @@ extern bool_t xdr_guestfs_sh_lines_args ();
 extern bool_t xdr_guestfs_sh_lines_ret ();
 extern bool_t xdr_guestfs_glob_expand_args ();
 extern bool_t xdr_guestfs_glob_expand_ret ();
+extern bool_t xdr_guestfs_scrub_device_args ();
+extern bool_t xdr_guestfs_scrub_file_args ();
+extern bool_t xdr_guestfs_scrub_freespace_args ();
 extern bool_t xdr_guestfs_procedure ();
 extern bool_t xdr_guestfs_message_direction ();
 extern bool_t xdr_guestfs_message_status ();
index d662726..094200e 100644 (file)
@@ -756,6 +756,18 @@ struct guestfs_glob_expand_ret {
   str paths<>;
 };
 
+struct guestfs_scrub_device_args {
+  string device<>;
+};
+
+struct guestfs_scrub_file_args {
+  string file<>;
+};
+
+struct guestfs_scrub_freespace_args {
+  string dir<>;
+};
+
 enum guestfs_procedure {
   GUESTFS_PROC_MOUNT = 1,
   GUESTFS_PROC_SYNC = 2,
@@ -870,6 +882,9 @@ enum guestfs_procedure {
   GUESTFS_PROC_SH = 111,
   GUESTFS_PROC_SH_LINES = 112,
   GUESTFS_PROC_GLOB_EXPAND = 113,
+  GUESTFS_PROC_SCRUB_DEVICE = 114,
+  GUESTFS_PROC_SCRUB_FILE = 115,
+  GUESTFS_PROC_SCRUB_FREESPACE = 116,
   GUESTFS_PROC_NR_PROCS
 };