[gnome-disk-utility/udisks2-port] Make it possible to restore a disk image



commit 4a01a5b424d4513c66d0933d2aa1ae216bde1732
Author: David Zeuthen <davidz redhat com>
Date:   Mon Dec 5 13:35:05 2011 -0500

    Make it possible to restore a disk image
    
     http://people.freedesktop.org/~david/gdu2-disk-image-restore-1.png
     http://people.freedesktop.org/~david/gdu2-disk-image-restore-2.png
     http://people.freedesktop.org/~david/gdu2-disk-image-restore-3.png
     http://people.freedesktop.org/~david/gdu2-disk-image-restore-4.png
     http://people.freedesktop.org/~david/gdu2-disk-image-restore-5.png
     http://people.freedesktop.org/~david/gdu2-disk-image-restore-6.png
    
    As evident in the first screenshot, the popup menu is slowly getting
    out of hand. It would probably be worth-while to rework it so it looks
    like e.g. the "Epiphany Mega Menu", see
    
     https://live.gnome.org/Design/Whiteboards/Menus
    
    with one column for "Disk" and one for "Volume". Something to poke the
    designers about.
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 data/ui/Makefile.am                        |    1 +
 data/ui/palimpsest.ui                      |   38 ++-
 data/ui/restore-disk-image-dialog.ui       |  306 +++++++++++++++++
 src/palimpsest/Makefile.am                 |    1 +
 src/palimpsest/gdurestorediskimagedialog.c |  503 ++++++++++++++++++++++++++++
 src/palimpsest/gdurestorediskimagedialog.h |   36 ++
 src/palimpsest/gduwindow.c                 |   95 +++++-
 7 files changed, 953 insertions(+), 27 deletions(-)
---
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
index 544f3be..50a8fd3 100644
--- a/data/ui/Makefile.am
+++ b/data/ui/Makefile.am
@@ -18,6 +18,7 @@ ui_DATA = 				\
 	filesystem-create.ui		\
 	format-disk-dialog.ui		\
 	create-disk-image-dialog.ui	\
+	restore-disk-image-dialog.ui	\
 	$(NULL)
 
 clean-local :
diff --git a/data/ui/palimpsest.ui b/data/ui/palimpsest.ui
index 23f8241..642b5e1 100644
--- a/data/ui/palimpsest.ui
+++ b/data/ui/palimpsest.ui
@@ -67,46 +67,46 @@
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <child>
-      <object class="GtkMenuItem" id="generic-menu-item-create-disk-image">
+      <object class="GtkMenuItem" id="generic-menu-item-format-disk">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="use_action_appearance">False</property>
-        <property name="label" translatable="yes">Create Disk Image...</property>
+        <property name="label" translatable="yes">Format Whole Disk...</property>
         <property name="use_underline">True</property>
       </object>
     </child>
     <child>
-      <object class="GtkMenuItem" id="generic-menu-item-format-disk">
+      <object class="GtkMenuItem" id="generic-menu-item-create-disk-image">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="use_action_appearance">False</property>
-        <property name="label" translatable="yes">Format Whole Disk...</property>
+        <property name="label" translatable="yes">Create Disk Image...</property>
         <property name="use_underline">True</property>
       </object>
     </child>
     <child>
-      <object class="GtkMenuItem" id="generic-menu-item-view-smart">
+      <object class="GtkMenuItem" id="generic-menu-item-restore-disk-image">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="use_action_appearance">False</property>
-        <property name="label" translatable="yes">View SMART Data...</property>
+        <property name="label" translatable="yes">Restore Disk Image to Whole Disk...</property>
         <property name="use_underline">True</property>
       </object>
     </child>
     <child>
-      <object class="GtkSeparatorMenuItem" id="generic-menu-item-drive-separator">
+      <object class="GtkMenuItem" id="generic-menu-item-view-smart">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="use_action_appearance">False</property>
+        <property name="label" translatable="yes">View SMART Data...</property>
+        <property name="use_underline">True</property>
       </object>
     </child>
     <child>
-      <object class="GtkMenuItem" id="generic-menu-item-create-volume-image">
+      <object class="GtkSeparatorMenuItem" id="generic-menu-item-drive-separator">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="use_action_appearance">False</property>
-        <property name="label" translatable="yes">Create Disk Image from Volume...</property>
-        <property name="use_underline">True</property>
       </object>
     </child>
     <child>
@@ -154,6 +154,24 @@
         <property name="use_underline">True</property>
       </object>
     </child>
+    <child>
+      <object class="GtkMenuItem" id="generic-menu-item-create-volume-image">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+        <property name="label" translatable="yes">Create Disk Image from Volume...</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuItem" id="generic-menu-item-restore-volume-image">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="use_action_appearance">False</property>
+        <property name="label" translatable="yes">Restore Disk Image to Volume...</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
   </object>
   <object class="GtkWindow" id="palimpsest-window">
     <property name="can_focus">False</property>
diff --git a/data/ui/restore-disk-image-dialog.ui b/data/ui/restore-disk-image-dialog.ui
new file mode 100644
index 0000000..e1fb547
--- /dev/null
+++ b/data/ui/restore-disk-image-dialog.ui
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkDialog" id="restore-disk-image-dialog">
+    <property name="width_request">500</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkLabel" id="label2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;big&gt;Restore Disk Image&lt;/big&gt;</property>
+            <property name="use_markup">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="start_copying_button">
+                <property name="label" translatable="yes">_Start Copying...</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkNotebook" id="notebook">
+            <property name="can_focus">False</property>
+            <property name="margin_left">12</property>
+            <property name="show_tabs">False</property>
+            <property name="show_border">False</property>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkBox" id="infobar-vbox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkInfoBar" id="error_infobar">
+                        <property name="message_type">error</property>
+                        <property name="no_show_all">True</property>
+                        <child internal-child="content_area">
+                          <object class="GtkBox" id="vbox3">
+                            <property name="can_focus">False</property>
+                            <property name="orientation">horizontal</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkImage" id="image1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">1.0</property>
+                                <property name="stock">gtk-dialog-error</property>
+                                <property name="icon-size">1</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="error_label">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Error: Disk image is too big.</property>
+                                <property name="use_markup">True</property>
+                                <property name="xalign">0.0</property>
+                                <property name="selectable">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkInfoBar" id="warning_infobar">
+                        <property name="message_type">warning</property>
+                        <property name="no_show_all">True</property>
+                        <child internal-child="content_area">
+                          <object class="GtkBox" id="vbox4">
+                            <property name="can_focus">False</property>
+                            <property name="orientation">horizontal</property>
+                            <property name="spacing">6</property>
+                            <child>
+                          <object class="GtkImage" id="image2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">1.0</property>
+                            <property name="stock">gtk-dialog-warning</property>
+                            <property name="icon-size">1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="warning_label">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Warning: Disk image is too small.</property>
+                                <property name="use_markup">True</property>
+                                <property name="xalign">0.0</property>
+                                <property name="selectable">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid" id="grid1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="row_spacing">12</property>
+                    <property name="column_spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="label6">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">_Image to restore</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">source_file_fcbutton</property>
+                        <attributes>
+                          <attribute name="foreground" value="#555555555555"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkFileChooserButton" id="source_file_fcbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <property name="local_only">False</property>
+                        <property name="title" translatable="yes">Select Disk Image to restore</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+            <child>
+              <object class="GtkBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkLabel" id="copying_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Copying data to device...</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkProgressBar" id="copying_progressbar">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">button1</action-widget>
+      <action-widget response="-5">start_copying_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/src/palimpsest/Makefile.am b/src/palimpsest/Makefile.am
index 43dcc5e..f834b5b 100644
--- a/src/palimpsest/Makefile.am
+++ b/src/palimpsest/Makefile.am
@@ -42,6 +42,7 @@ palimpsest_SOURCES = 							\
 	gducreatefilesystemwidget.h	gducreatefilesystemwidget.c	\
 	gduformatdiskdialog.h		gduformatdiskdialog.c		\
 	gducreatediskimagedialog.h	gducreatediskimagedialog.c	\
+	gdurestorediskimagedialog.h	gdurestorediskimagedialog.c	\
 	$(enum_built_sources)						\
 	$(NULL)
 
diff --git a/src/palimpsest/gdurestorediskimagedialog.c b/src/palimpsest/gdurestorediskimagedialog.c
new file mode 100644
index 0000000..8ccef48
--- /dev/null
+++ b/src/palimpsest/gdurestorediskimagedialog.c
@@ -0,0 +1,503 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gio/gunixfdlist.h>
+#include <gio/gunixoutputstream.h>
+
+#include "gduapplication.h"
+#include "gduwindow.h"
+#include "gdurestorediskimagedialog.h"
+#include "gduvolumegrid.h"
+#include "gduutils.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#define BUFFER_SIZE (1024*1024)
+
+typedef struct
+{
+  volatile gint ref_count;
+
+  GduWindow *window;
+  UDisksObject *object;
+  UDisksBlock *block;
+  UDisksDrive *drive;
+
+  GtkBuilder *builder;
+  GtkWidget *dialog;
+
+  GtkWidget *notebook;
+  GtkWidget *start_copying_button;
+  GtkWidget *source_file_fcbutton;
+  GtkWidget *warning_infobar;
+  GtkWidget *warning_label;
+  GtkWidget *error_infobar;
+  GtkWidget *error_label;
+
+  GtkWidget *copying_progressbar;
+  GtkWidget *copying_label;
+
+  GCancellable *cancellable;
+  GOutputStream *block_stream;
+  GFileInputStream *input_file_stream;
+  guint64 file_size;
+
+  guchar *buffer;
+  guint64 total_bytes_read;
+  guint64 buffer_bytes_written;
+  guint64 buffer_bytes_to_write;
+} RestoreDiskImageData;
+
+static RestoreDiskImageData *
+restore_disk_image_data_ref (RestoreDiskImageData *data)
+{
+  g_atomic_int_inc (&data->ref_count);
+  return data;
+}
+
+static void
+restore_disk_image_data_unref (RestoreDiskImageData *data)
+{
+  if (g_atomic_int_dec_and_test (&data->ref_count))
+    {
+      if (data->dialog != NULL)
+        {
+          gtk_widget_hide (data->dialog);
+          gtk_widget_destroy (data->dialog);
+        }
+      g_clear_object (&data->cancellable);
+      g_clear_object (&data->input_file_stream);
+      g_clear_object (&data->block_stream);
+      g_object_unref (data->window);
+      g_object_unref (data->object);
+      g_object_unref (data->block);
+      g_clear_object (&data->drive);
+      if (data->builder != NULL)
+        g_object_unref (data->builder);
+      g_free (data->buffer);
+      g_free (data);
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+restore_disk_image_data_complete (RestoreDiskImageData *data)
+{
+  g_cancellable_cancel (data->cancellable);
+  gtk_dialog_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_CANCEL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+restore_disk_image_update (RestoreDiskImageData *data)
+{
+  gboolean can_proceed = FALSE;
+  gchar *restore_warning = NULL;
+  gchar *restore_error = NULL;
+  GFile *restore_file = NULL;
+
+  /* Check if we have a file */
+  restore_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->source_file_fcbutton));
+  if (restore_file != NULL)
+    {
+      GFileInfo *info;
+      guint64 size;
+      gchar *s;
+      info = g_file_query_info (restore_file,
+                                G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                G_FILE_QUERY_INFO_NONE,
+                                NULL,
+                                NULL);
+      size = g_file_info_get_size (info);
+      g_object_unref (info);
+
+      if (size < udisks_block_get_size (data->block))
+        {
+          /* Only complain if slack is bigger than 1MB */
+          if (udisks_block_get_size (data->block) - size > 1000L*1000L)
+            {
+              s = udisks_client_get_size_for_display (gdu_window_get_client (data->window),
+                                                      udisks_block_get_size (data->block) - size, FALSE, FALSE);
+              restore_warning = g_strdup_printf (_("The selected image is %s bytes smaller than the device"), s);
+              g_free (s);
+            }
+          can_proceed = TRUE;
+        }
+      else if (size > udisks_block_get_size (data->block))
+        {
+          s = udisks_client_get_size_for_display (gdu_window_get_client (data->window),
+                                                  size - udisks_block_get_size (data->block), FALSE, FALSE);
+          restore_error = g_strdup_printf (_("The selected image is %s bytes bigger than the device"), s);
+          g_free (s);
+        }
+      else
+        {
+          /* all good */
+          can_proceed = TRUE;
+        }
+    }
+
+  if (restore_warning != NULL)
+    {
+      gtk_label_set_text (GTK_LABEL (data->warning_label), restore_warning);
+      gtk_widget_show (data->warning_infobar);
+    }
+  else
+    {
+      gtk_widget_hide (data->warning_infobar);
+    }
+  if (restore_error != NULL)
+    {
+      gtk_label_set_text (GTK_LABEL (data->error_label), restore_error);
+      gtk_widget_show (data->error_infobar);
+    }
+  else
+    {
+      gtk_widget_hide (data->error_infobar);
+    }
+
+  g_free (restore_warning);
+  g_free (restore_error);
+  g_clear_object (&restore_file);
+
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, can_proceed);
+}
+
+static void
+on_file_set (GtkFileChooserButton   *button,
+             gpointer                user_data)
+{
+  RestoreDiskImageData *data = user_data;
+  restore_disk_image_update (data);
+}
+
+static void
+on_notify (GObject    *object,
+           GParamSpec *pspec,
+           gpointer    user_data)
+{
+  RestoreDiskImageData *data = user_data;
+  restore_disk_image_update (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+restore_disk_image_populate (RestoreDiskImageData *data)
+{
+  gdu_utils_configure_file_chooser_for_disk_images (GTK_FILE_CHOOSER (data->source_file_fcbutton));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void copy_more (RestoreDiskImageData *data);
+
+static void write_more (RestoreDiskImageData *data);
+
+static void
+write_cb (GOutputStream  *output_stream,
+          GAsyncResult  *res,
+          gpointer       user_data)
+{
+  RestoreDiskImageData *data = user_data;
+  GError *error;
+  gssize bytes_written;
+
+  error = NULL;
+  bytes_written = g_output_stream_write_finish (output_stream, res, &error);
+  if (error != NULL)
+    {
+      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
+        gdu_window_show_error (data->window,
+                               _("Error writing to backup image"), error);
+      g_error_free (error);
+      restore_disk_image_data_complete (data);
+      goto out;
+    }
+
+  data->buffer_bytes_written += bytes_written;
+  data->buffer_bytes_to_write -= bytes_written;
+
+  /* update progress bar */
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->copying_progressbar),
+                                 ((gdouble) data->total_bytes_read) / ((gdouble) data->file_size));
+
+  /* TODO: we should do fancy stuff like printing the estimated speed and time remaining */
+
+  write_more (data);
+
+ out:
+  restore_disk_image_data_unref (data);
+}
+
+static void
+write_more (RestoreDiskImageData *data)
+{
+  if (data->buffer_bytes_to_write == 0)
+    {
+      copy_more (data);
+    }
+  else
+    {
+      g_debug ("Writing %d bytes", (gint) data->buffer_bytes_to_write);
+      g_output_stream_write_async (data->block_stream,
+                                   data->buffer + data->buffer_bytes_written,
+                                   data->buffer_bytes_to_write,
+                                   G_PRIORITY_DEFAULT,
+                                   data->cancellable,
+                                   (GAsyncReadyCallback) write_cb,
+                                   restore_disk_image_data_ref (data));
+    }
+}
+
+
+static void
+read_cb (GInputStream  *input_stream,
+         GAsyncResult  *res,
+         gpointer       user_data)
+{
+  RestoreDiskImageData *data = user_data;
+  GError *error;
+  gssize bytes_read;
+
+  error = NULL;
+  bytes_read = g_input_stream_read_finish (input_stream, res, &error);
+  if (error != NULL)
+    {
+      gchar *s;
+      s = g_strdup_printf (_("Error reading from offset %" G_GUINT64_FORMAT " of file"),
+                           (guint64) data->total_bytes_read);
+      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
+        gdu_window_show_error (data->window, s, error);
+      g_free (s);
+      g_error_free (error);
+      restore_disk_image_data_complete (data);
+      goto out;
+    }
+
+  data->total_bytes_read += bytes_read;
+
+  data->buffer_bytes_written = 0;
+  data->buffer_bytes_to_write = bytes_read;
+  write_more (data);
+
+ out:
+  restore_disk_image_data_unref (data);
+}
+
+static void
+copy_more (RestoreDiskImageData *data)
+{
+  guint64 bytes_to_read;
+
+  bytes_to_read = data->file_size - data->total_bytes_read;
+  if (bytes_to_read == 0)
+    {
+      restore_disk_image_data_complete (data);
+      goto out;
+    }
+  if (bytes_to_read > BUFFER_SIZE)
+    bytes_to_read = BUFFER_SIZE;
+
+  g_input_stream_read_async (G_INPUT_STREAM (data->input_file_stream),
+                             data->buffer,
+                             bytes_to_read,
+                             G_PRIORITY_DEFAULT,
+                             data->cancellable,
+                             (GAsyncReadyCallback) read_cb,
+                             restore_disk_image_data_ref (data));
+ out:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+open_cb (UDisksBlock  *block,
+         GAsyncResult *res,
+         gpointer      user_data)
+{
+  RestoreDiskImageData *data = user_data;
+  GError *error;
+  GUnixFDList *fd_list = NULL;
+  GVariant *fd_index = NULL;
+  int fd;
+
+  error = NULL;
+  if (!udisks_block_call_open_for_restore_finish (block,
+                                                  &fd_index,
+                                                  &fd_list,
+                                                  res,
+                                                  &error))
+    {
+      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
+        gdu_window_show_error (data->window, _("Error opening device"), error);
+      g_error_free (error);
+      restore_disk_image_data_complete (data);
+      goto out;
+    }
+
+  fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_index), NULL);
+  data->block_stream = g_unix_output_stream_new (fd, TRUE);
+
+  /* Alright, time to start copying! */
+  data->cancellable = g_cancellable_new ();
+  data->buffer = g_new0 (guchar, BUFFER_SIZE);
+  copy_more (data);
+
+ out:
+  if (fd_index != NULL)
+    g_variant_unref (fd_index);
+  g_clear_object (&fd_list);
+}
+
+static gboolean
+start_copying (RestoreDiskImageData *data)
+{
+  GFile *file = NULL;
+  gboolean ret = FALSE;
+  GFileInfo *info;
+  GError *error;
+
+  error = NULL;
+  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->source_file_fcbutton));
+  data->input_file_stream = g_file_read (file,
+                                         NULL,
+                                         &error);
+  if (data->input_file_stream == NULL)
+    {
+      if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED))
+        gdu_window_show_error (data->window, _("Error opening file for reading"), error);
+      g_error_free (error);
+      restore_disk_image_data_complete (data);
+      goto out;
+    }
+
+  error = NULL;
+  info = g_file_query_info (file,
+                            G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                            G_FILE_QUERY_INFO_NONE,
+                            NULL,
+                            &error);
+  if (info == NULL)
+    {
+      gdu_window_show_error (data->window, _("Error determing size of file"), error);
+      g_error_free (error);
+      goto out;
+    }
+  data->file_size = g_file_info_get_size (info);
+  g_object_unref (info);
+
+  udisks_block_call_open_for_restore (data->block,
+                                      g_variant_new ("a{sv}", NULL), /* options */
+                                      NULL, /* fd_list */
+                                      NULL, /* cancellable */
+                                      (GAsyncReadyCallback) open_cb,
+                                      data);
+
+  ret = TRUE;
+
+ out:
+  g_clear_object (&file);
+  return ret;
+}
+
+void
+gdu_restore_disk_image_dialog_show (GduWindow    *window,
+                                   UDisksObject *object)
+{
+  RestoreDiskImageData *data;
+  gint response;
+  gchar *s;
+
+  data = g_new0 (RestoreDiskImageData, 1);
+  data->ref_count = 1;
+  data->window = g_object_ref (window);
+  data->object = g_object_ref (object);
+  data->block = udisks_object_get_block (object);
+  g_assert (data->block != NULL);
+  data->drive = udisks_client_get_drive_for_block (gdu_window_get_client (window), data->block);
+
+  data->dialog = gdu_application_new_widget (gdu_window_get_application (window),
+                                             "restore-disk-image-dialog.ui",
+                                             "restore-disk-image-dialog",
+                                             &data->builder);
+  data->notebook = GTK_WIDGET (gtk_builder_get_object (data->builder, "notebook"));
+  data->start_copying_button = GTK_WIDGET (gtk_builder_get_object (data->builder, "start_copying_button"));
+  data->source_file_fcbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, "source_file_fcbutton"));
+  g_signal_connect (data->source_file_fcbutton, "file-set",
+                    G_CALLBACK (on_file_set), data);
+  data->copying_progressbar = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progressbar"));
+  data->copying_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_label"));
+  data->warning_infobar = GTK_WIDGET (gtk_builder_get_object (data->builder, "warning_infobar"));
+  data->warning_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "warning_label"));
+  data->error_infobar = GTK_WIDGET (gtk_builder_get_object (data->builder, "error_infobar"));
+  data->error_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "error_label"));
+
+  restore_disk_image_populate (data);
+  restore_disk_image_update (data);
+
+  /* unfortunately, GtkFileChooserButton:file-set is not emitted when the user
+   * unselects a file but we can work around that.. (TODO: file bug against gtk+)
+   */
+  g_signal_connect (data->source_file_fcbutton, "notify",
+                    G_CALLBACK (on_notify), data);
+
+  /* Make sure we attach to parent */
+  gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (window));
+  gtk_dialog_set_default_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK);
+  gtk_widget_show_all (data->dialog);
+
+  response = gtk_dialog_run (GTK_DIALOG (data->dialog));
+
+  if (response != GTK_RESPONSE_OK)
+    goto out;
+
+  if (!gdu_window_show_confirmation (window,
+                                     _("Are you sure you want to write the disk image to the device?"),
+                                     _("All existing data will be lost"),
+                                     _("_Restore")))
+    goto out;
+
+  s = g_strdup_printf (_("Copying data to device <i>%s</i>..."),
+                       udisks_block_get_preferred_device (data->block));
+  gtk_label_set_markup (GTK_LABEL (data->copying_label), s);
+  g_free (s);
+
+  /* Advance to the progress page */
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (data->notebook), 1);
+  gtk_widget_hide (data->start_copying_button);
+
+  if (!start_copying (data))
+    goto out;
+
+  gtk_dialog_run (GTK_DIALOG (data->dialog));
+  restore_disk_image_data_complete (data);
+
+ out:
+  restore_disk_image_data_unref (data);
+}
diff --git a/src/palimpsest/gdurestorediskimagedialog.h b/src/palimpsest/gdurestorediskimagedialog.h
new file mode 100644
index 0000000..c8bd936
--- /dev/null
+++ b/src/palimpsest/gdurestorediskimagedialog.h
@@ -0,0 +1,36 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GDU_RESTORE_DISK_IMAGE_DIALOG_H__
+#define __GDU_RESTORE_DISK_IMAGE_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include "gdutypes.h"
+
+G_BEGIN_DECLS
+
+void     gdu_restore_disk_image_dialog_show (GduWindow    *window,
+                                             UDisksObject *object);
+
+G_END_DECLS
+
+#endif /* __GDU_RESTORE_DISK_IMAGE_DIALOG_H__ */
diff --git a/src/palimpsest/gduwindow.c b/src/palimpsest/gduwindow.c
index be06576..cc2bac2 100644
--- a/src/palimpsest/gduwindow.c
+++ b/src/palimpsest/gduwindow.c
@@ -47,6 +47,7 @@
 #include "gducreatepartitiondialog.h"
 #include "gduformatdiskdialog.h"
 #include "gducreatediskimagedialog.h"
+#include "gdurestorediskimagedialog.h"
 
 /* Keep in sync with tabs in palimpsest.ui file */
 typedef enum
@@ -100,9 +101,11 @@ struct _GduWindow
   GtkWidget *devtab_toolbar_deactivate_swap_button;
 
   GtkWidget *generic_menu;
-  GtkWidget *generic_menu_item_create_disk_image;
   GtkWidget *generic_menu_item_view_smart;
   GtkWidget *generic_menu_item_format_disk;
+  GtkWidget *generic_menu_item_create_disk_image;
+  GtkWidget *generic_menu_item_restore_disk_image;
+  /* -- */
   GtkWidget *generic_menu_item_drive_separator;
   GtkWidget *generic_menu_item_configure_fstab;
   GtkWidget *generic_menu_item_configure_crypttab;
@@ -110,6 +113,7 @@ struct _GduWindow
   GtkWidget *generic_menu_item_edit_partition;
   GtkWidget *generic_menu_item_format_volume;
   GtkWidget *generic_menu_item_create_volume_image;
+  GtkWidget *generic_menu_item_restore_volume_image;
 
   GHashTable *label_connections;
 };
@@ -147,6 +151,7 @@ static const struct {
 
   {G_STRUCT_OFFSET (GduWindow, generic_menu), "generic-menu"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_create_disk_image), "generic-menu-item-create-disk-image"},
+  {G_STRUCT_OFFSET (GduWindow, generic_menu_item_restore_disk_image), "generic-menu-item-restore-disk-image"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_view_smart), "generic-menu-item-view-smart"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_format_disk), "generic-menu-item-format-disk"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_drive_separator), "generic-menu-item-drive-separator"},
@@ -156,6 +161,7 @@ static const struct {
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_edit_partition), "generic-menu-item-edit-partition"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_format_volume), "generic-menu-item-format-volume"},
   {G_STRUCT_OFFSET (GduWindow, generic_menu_item_create_volume_image), "generic-menu-item-create-volume-image"},
+  {G_STRUCT_OFFSET (GduWindow, generic_menu_item_restore_volume_image), "generic-menu-item-restore-volume-image"},
   {0, NULL}
 };
 
@@ -185,15 +191,17 @@ typedef enum
   SHOW_FLAGS_ENCRYPTED_UNLOCK_BUTTON = (1<<8),
   SHOW_FLAGS_ENCRYPTED_LOCK_BUTTON   = (1<<9),
 
-  SHOW_FLAGS_POPUP_MENU_CREATE_DISK_IMAGE    = (1<<20),
-  SHOW_FLAGS_POPUP_MENU_VIEW_SMART           = (1<<21),
-  SHOW_FLAGS_POPUP_MENU_FORMAT_DISK          = (1<<22),
-  SHOW_FLAGS_POPUP_MENU_CONFIGURE_FSTAB      = (1<<23),
-  SHOW_FLAGS_POPUP_MENU_CONFIGURE_CRYPTTAB   = (1<<24),
-  SHOW_FLAGS_POPUP_MENU_EDIT_LABEL           = (1<<25),
-  SHOW_FLAGS_POPUP_MENU_EDIT_PARTITION       = (1<<26),
-  SHOW_FLAGS_POPUP_MENU_FORMAT_VOLUME        = (1<<27),
-  SHOW_FLAGS_POPUP_MENU_CREATE_VOLUME_IMAGE  = (1<<28),
+  SHOW_FLAGS_POPUP_MENU_CREATE_DISK_IMAGE     = (1<<20),
+  SHOW_FLAGS_POPUP_MENU_RESTORE_DISK_IMAGE    = (1<<21),
+  SHOW_FLAGS_POPUP_MENU_VIEW_SMART            = (1<<22),
+  SHOW_FLAGS_POPUP_MENU_FORMAT_DISK           = (1<<23),
+  SHOW_FLAGS_POPUP_MENU_CONFIGURE_FSTAB       = (1<<24),
+  SHOW_FLAGS_POPUP_MENU_CONFIGURE_CRYPTTAB    = (1<<25),
+  SHOW_FLAGS_POPUP_MENU_EDIT_LABEL            = (1<<26),
+  SHOW_FLAGS_POPUP_MENU_EDIT_PARTITION        = (1<<27),
+  SHOW_FLAGS_POPUP_MENU_FORMAT_VOLUME         = (1<<28),
+  SHOW_FLAGS_POPUP_MENU_CREATE_VOLUME_IMAGE   = (1<<29),
+  SHOW_FLAGS_POPUP_MENU_RESTORE_VOLUME_IMAGE  = (1<<30),
 } ShowFlags;
 
 static void setup_device_page (GduWindow *window, UDisksObject *object);
@@ -228,10 +236,14 @@ static void on_generic_menu_item_format_volume (GtkMenuItem *menu_item,
                                                 gpointer   user_data);
 static void on_generic_menu_item_format_disk (GtkMenuItem *menu_item,
                                               gpointer   user_data);
-static void on_generic_menu_item_create_volume_image (GtkMenuItem *menu_item,
-                                                      gpointer   user_data);
 static void on_generic_menu_item_create_disk_image (GtkMenuItem *menu_item,
                                                     gpointer   user_data);
+static void on_generic_menu_item_restore_disk_image (GtkMenuItem *menu_item,
+                                                     gpointer   user_data);
+static void on_generic_menu_item_create_volume_image (GtkMenuItem *menu_item,
+                                                      gpointer   user_data);
+static void on_generic_menu_item_restore_volume_image (GtkMenuItem *menu_item,
+                                                       gpointer   user_data);
 
 G_DEFINE_TYPE (GduWindow, gdu_window, GTK_TYPE_WINDOW);
 
@@ -337,10 +349,11 @@ update_for_show_flags (GduWindow *window,
 
   /* Hide Drive menu items unless it's actually a drive */
   is_drive = (udisks_object_peek_drive (window->current_object) != NULL);
-  gtk_widget_set_visible (GTK_WIDGET (window->generic_menu_item_create_disk_image), is_drive);
   gtk_widget_set_visible (GTK_WIDGET (window->generic_menu_item_view_smart), is_drive);
   gtk_widget_set_visible (GTK_WIDGET (window->generic_menu_item_format_disk), is_drive);
   gtk_widget_set_visible (GTK_WIDGET (window->generic_menu_item_drive_separator), is_drive);
+  gtk_widget_set_visible (GTK_WIDGET (window->generic_menu_item_create_disk_image), is_drive);
+  gtk_widget_set_visible (GTK_WIDGET (window->generic_menu_item_restore_disk_image), is_drive);
 
   /* except, if partitionable (example of partitionable non-drive
    * device: /dev/loop0), then show the separator and the FORMAT_DISK
@@ -356,12 +369,14 @@ update_for_show_flags (GduWindow *window,
         }
     }
 
-  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_create_disk_image),
-                            show_flags & SHOW_FLAGS_POPUP_MENU_CREATE_DISK_IMAGE);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_format_disk),
                             show_flags & SHOW_FLAGS_POPUP_MENU_FORMAT_DISK);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_view_smart),
                             show_flags & SHOW_FLAGS_POPUP_MENU_VIEW_SMART);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_create_disk_image),
+                            show_flags & SHOW_FLAGS_POPUP_MENU_CREATE_DISK_IMAGE);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_restore_disk_image),
+                            show_flags & SHOW_FLAGS_POPUP_MENU_RESTORE_DISK_IMAGE);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_configure_fstab),
                             show_flags & SHOW_FLAGS_POPUP_MENU_CONFIGURE_FSTAB);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_configure_crypttab),
@@ -374,6 +389,8 @@ update_for_show_flags (GduWindow *window,
                             show_flags & SHOW_FLAGS_POPUP_MENU_FORMAT_VOLUME);
   gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_create_volume_image),
                             show_flags & SHOW_FLAGS_POPUP_MENU_CREATE_VOLUME_IMAGE);
+  gtk_widget_set_sensitive (GTK_WIDGET (window->generic_menu_item_restore_volume_image),
+                            show_flags & SHOW_FLAGS_POPUP_MENU_RESTORE_VOLUME_IMAGE);
   /* TODO: don't show the button bringing up the popup menu if it has no items */
 }
 
@@ -1035,10 +1052,18 @@ gdu_window_constructed (GObject *object)
                     "activate",
                     G_CALLBACK (on_generic_menu_item_create_disk_image),
                     window);
+  g_signal_connect (window->generic_menu_item_restore_disk_image,
+                    "activate",
+                    G_CALLBACK (on_generic_menu_item_restore_disk_image),
+                    window);
   g_signal_connect (window->generic_menu_item_create_volume_image,
                     "activate",
                     G_CALLBACK (on_generic_menu_item_create_volume_image),
                     window);
+  g_signal_connect (window->generic_menu_item_restore_volume_image,
+                    "activate",
+                    G_CALLBACK (on_generic_menu_item_restore_volume_image),
+                    window);
 
   g_idle_add (on_constructed_in_idle, g_object_ref (window));
 }
@@ -1836,9 +1861,14 @@ update_device_page_for_block (GduWindow          *window,
     {
       *show_flags |= SHOW_FLAGS_POPUP_MENU_CREATE_VOLUME_IMAGE;
       if (udisks_block_get_hint_partitionable (block))
-        *show_flags |= SHOW_FLAGS_POPUP_MENU_CREATE_DISK_IMAGE;
+        {
+          *show_flags |= SHOW_FLAGS_POPUP_MENU_CREATE_DISK_IMAGE;
+          if (!read_only)
+            *show_flags |= SHOW_FLAGS_POPUP_MENU_RESTORE_DISK_IMAGE;
+        }
       if (!read_only)
         {
+          *show_flags |= SHOW_FLAGS_POPUP_MENU_RESTORE_VOLUME_IMAGE;
           if (udisks_block_get_hint_partitionable (block))
             *show_flags |= SHOW_FLAGS_POPUP_MENU_FORMAT_DISK;
           *show_flags |= SHOW_FLAGS_POPUP_MENU_FORMAT_VOLUME;
@@ -2090,7 +2120,10 @@ update_device_page_for_free_space (GduWindow          *window,
   read_only = udisks_block_get_read_only (block);
 
   if (!read_only)
-    *show_flags |= SHOW_FLAGS_POPUP_MENU_FORMAT_DISK;
+    {
+      *show_flags |= SHOW_FLAGS_POPUP_MENU_FORMAT_DISK;
+      *show_flags |= SHOW_FLAGS_POPUP_MENU_RESTORE_DISK_IMAGE;
+    }
 
   table = udisks_object_peek_partition_table (object);
   if (table != NULL)
@@ -2348,6 +2381,20 @@ on_generic_menu_item_create_disk_image (GtkMenuItem *menu_item,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+on_generic_menu_item_restore_disk_image (GtkMenuItem *menu_item,
+                                         gpointer   user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  UDisksObject *object;
+
+  object = gdu_volume_grid_get_block_object (GDU_VOLUME_GRID (window->volume_grid));
+  g_assert (object != NULL);
+  gdu_restore_disk_image_dialog_show (window, object);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 on_generic_menu_item_create_volume_image (GtkMenuItem *menu_item,
                                           gpointer   user_data)
 {
@@ -2362,6 +2409,20 @@ on_generic_menu_item_create_volume_image (GtkMenuItem *menu_item,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+on_generic_menu_item_restore_volume_image (GtkMenuItem *menu_item,
+                                           gpointer   user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  UDisksObject *object;
+
+  object = gdu_volume_grid_get_selected_device (GDU_VOLUME_GRID (window->volume_grid));
+  g_assert (object != NULL);
+  gdu_restore_disk_image_dialog_show (window, object);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 on_generic_menu_item_format_disk (GtkMenuItem *menu_item,
                                   gpointer   user_data)
 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]