From 8e6bf7cf7d13c8734a5c469b2a3f96b974b34127 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Tue, 3 Mar 2009 16:17:33 +0100 Subject: Handle partition creation - works on zeroed devices - works when partition table exists but no partition is selected - detects optical media and floppy disks and skips partition creation - volume chooser in standalone mode now lists only parent devices from toplevel removable ones --- TODO | 7 +- src/format-window-operation.c | 470 +++++++++++++++++++++++++++++++++++------- src/format-window-operation.h | 20 +- src/format-window.c | 52 +++-- src/format-window.h | 15 +- src/gdu-utils.c | 128 ++++++++---- 6 files changed, 535 insertions(+), 157 deletions(-) diff --git a/TODO b/TODO index d3a3c0a..233f2db 100644 --- a/TODO +++ b/TODO @@ -27,7 +27,7 @@ DONE, NEEDS_TESTING - monitor device/presentable events like eject, umount etc. - show custom icon if user has set it in Nautilus - is firmware needed for some devices? - different partition tables? -- what to do if device is blank (zeroed, no MBR)? +DONE - what to do if device is blank (zeroed, no MBR)? DONE - handle read only device - gdu_device_is_read_only() - test with DVD-RAM media - stay consistent with volume labels in Nautilus and gdu (requires gvfs-devicekit port) - localization (transifex? -- ask hughsie) @@ -50,4 +50,7 @@ Bugs: - libgdu should return always valid presentable names NO - should the presentable instance from the pool::presentable-{added,changed,removed} signals be unreffed? - complete CD-RW medium is not marked as readonly -- are we gonna have burning backend in DK-disks? - + - would be great to have gdu_pool_get_toplevel_presentables() + - gdu_presentable_get_toplevel() always returns valid presentable even if it's already toplevel (i.e. /dev/md0), never returns NULL + - gdu_device_drive_get_media_compatibility() returns const char* pointers - check and document properly + - BUG !! gdu_device_op_partition_create() and gdu_device_op_partition_table_create() should spawn their finish callbacks only after device is ready (and pool up-to-date) so we can start another operation right away diff --git a/src/format-window-operation.c b/src/format-window-operation.c index d27af1a..eec5c45 100644 --- a/src/format-window-operation.c +++ b/src/format-window-operation.c @@ -35,6 +35,9 @@ #include "format-window-operation.h" + +#define DEVICE_SETTLE_TIMEOUT 3000 + /* ---------------------------------------------------------------------------------------------------- */ /* TODO: this is quite dangerous, but needed */ @@ -63,6 +66,30 @@ mbr_part_is_linux_type (const char *part_type) strcasecmp (part_type, "0xfd") == 0); } + +/* Look whether the device needs to be partitioned */ +/* - generally we don't want to have partitions on optical drives and floppy disks */ +static gboolean +device_needs_partition_table (GduDevice *device) +{ + char **media_compat; + gboolean needs = TRUE; /* default to TRUE */ + + media_compat = gdu_device_drive_get_media_compatibility (device); + for (; *media_compat; media_compat++) { + g_print (" compat '%s'\n", *media_compat); + /* http://hal.freedesktop.org/docs/DeviceKit-disks/Device.html#Device:drive-media-compatibility */ + if (strstr (*media_compat, "optical") == *media_compat || + strstr (*media_compat, "floppy") == *media_compat) { + needs = FALSE; + break; + } + } +// g_strfreev (media_compat); /* so, is this const then? */ + g_print ("device_needs_partition_table = %d\n", needs); + return needs; +} + /* ---------------------------------------------------------------------------------------------------- */ static gboolean @@ -72,10 +99,45 @@ job_progress_pulse_timeout_handler (gpointer user_data) g_return_val_if_fail (data != NULL, TRUE); + g_print ("job_progress_pulse_timeout_handler\n"); + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data->priv->progress_bar)); return TRUE; } +static void +do_progress_bar_update (FormatProcessData *data, const char *label, double percentage, gboolean active) +{ + if (active) { + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (data->priv->progress_bar), label); + + if (percentage < 0) { + gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (data->priv->progress_bar), 2.0 / 50); + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data->priv->progress_bar)); + if (data->job_progress_pulse_timer_id == 0) { + g_print ("activating progress bounce...\n"); + data->job_progress_pulse_timer_id = g_timeout_add ( + 1000 / 50, + job_progress_pulse_timeout_handler, + data); + } + } else { + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->priv->progress_bar), + percentage / 100.0); + if (data->job_progress_pulse_timer_id > 0) { + g_source_remove (data->job_progress_pulse_timer_id); + data->job_progress_pulse_timer_id = 0; + } + } + } + else { + if (data->job_progress_pulse_timer_id > 0) { + g_source_remove (data->job_progress_pulse_timer_id); + data->job_progress_pulse_timer_id = 0; + } + } +} + static void presentable_job_changed (GduPresentable *presentable, gpointer user_data) { @@ -85,6 +147,8 @@ presentable_job_changed (GduPresentable *presentable, gpointer user_data) char *task_description; double percentage; + g_print ("presentable_job_changed\n"); + g_return_if_fail (data != NULL); if (data->device != NULL && gdu_device_job_in_progress (data->device)) { @@ -95,36 +159,16 @@ presentable_job_changed (GduPresentable *presentable, gpointer user_data) (task_description && strlen (task_description) > 0) ? task_description : job_description, gdu_device_job_get_cur_task (data->device) + 1, gdu_device_job_get_num_tasks (data->device)); - gtk_progress_bar_set_text (GTK_PROGRESS_BAR (data->priv->progress_bar), s); - g_free (s); percentage = gdu_device_job_get_cur_task_percentage (data->device); - if (percentage < 0) { - gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (data->priv->progress_bar), 2.0 / 50); - gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data->priv->progress_bar)); - if (data->job_progress_pulse_timer_id == 0) { - data->job_progress_pulse_timer_id = g_timeout_add ( - 1000 / 50, - job_progress_pulse_timeout_handler, - data); - } - } else { - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->priv->progress_bar), - percentage / 100.0); - if (data->job_progress_pulse_timer_id > 0) { - g_source_remove (data->job_progress_pulse_timer_id); - data->job_progress_pulse_timer_id = 0; - } - } + do_progress_bar_update (data, s, percentage, TRUE); + g_free (s); g_free (job_description); g_free (task_description); } else { - if (data->job_progress_pulse_timer_id > 0) { - g_source_remove (data->job_progress_pulse_timer_id); - data->job_progress_pulse_timer_id = 0; - } + do_progress_bar_update (data, NULL, -1, FALSE); } } @@ -137,6 +181,10 @@ static void format_auth_end_callback (PolKitGnomeAction *action, gboolean gained static void format_action_callback (GtkAction *action, gpointer user_data); static void part_modify_auth_end_callback (PolKitGnomeAction *action, gboolean gained_privilege, gpointer user_data); static void part_modify_action_callback (GtkAction *action, gpointer user_data); +static void part_table_new_auth_end_callback (PolKitGnomeAction *action, gboolean gained_privilege, gpointer user_data); +static void part_table_new_action_callback (GtkAction *action, gpointer user_data); +static void part_new_auth_end_callback (PolKitGnomeAction *action, gboolean gained_privilege, gpointer user_data); +static void part_new_action_callback (GtkAction *action, gpointer user_data); void update_ui_progress (FormatDialogPrivate *priv, @@ -155,32 +203,63 @@ update_ui_progress (FormatDialogPrivate *priv, gtk_button_set_label (GTK_BUTTON (priv->close_button), GTK_STOCK_STOP); gtk_widget_show_all (priv->progress_bar_box); if (data) { - data->job_handler_id = g_signal_connect (priv->presentable, "job-changed", G_CALLBACK (presentable_job_changed), data); - data->pk_unmount_auth_end_handler_id = g_signal_connect (priv->unmount_action, "auth-end", G_CALLBACK (unmount_auth_end_callback), data); - data->pk_unmount_action_handler_id = g_signal_connect (priv->unmount_action, "activate", G_CALLBACK (unmount_action_callback), data); - data->pk_format_auth_end_handler_id = g_signal_connect (priv->format_action, "auth-end", G_CALLBACK (format_auth_end_callback), data); - data->pk_format_action_handler_id = g_signal_connect (priv->format_action, "activate", G_CALLBACK (format_action_callback), data); - data->pk_part_modify_auth_end_handler_id = g_signal_connect (priv->part_modify_action, "auth-end", G_CALLBACK (part_modify_auth_end_callback), data); - data->pk_part_modify_action_handler_id = g_signal_connect (priv->part_modify_action, "activate", G_CALLBACK (part_modify_action_callback), data); + g_signal_connect (data->presentable, "job-changed", G_CALLBACK (presentable_job_changed), data); + + /* set up PolicyKit actions */ + data->pk_unmount_action = polkit_action_new (); + polkit_action_set_action_id (data->pk_unmount_action, "org.freedesktop.devicekit.disks.filesystem-unmount-others"); + data->unmount_action = polkit_gnome_action_new_default ("unmount", data->pk_unmount_action, NULL, NULL); + g_signal_connect (data->unmount_action, "auth-end", G_CALLBACK (unmount_auth_end_callback), data); + g_signal_connect (data->unmount_action, "activate", G_CALLBACK (unmount_action_callback), data); + + data->pk_format_action = polkit_action_new (); + polkit_action_set_action_id (data->pk_format_action, "org.freedesktop.devicekit.disks.change"); + data->format_action = polkit_gnome_action_new_default ("format", data->pk_format_action, NULL, NULL); + g_signal_connect (data->format_action, "auth-end", G_CALLBACK (format_auth_end_callback), data); + g_signal_connect (data->format_action, "activate", G_CALLBACK (format_action_callback), data); + + data->pk_part_modify_action = polkit_action_new (); + /* action_id is the same as for format, but sometimes authentication is one shot */ + polkit_action_set_action_id (data->pk_part_modify_action, "org.freedesktop.devicekit.disks.change"); + data->part_modify_action = polkit_gnome_action_new_default ("part_modify", data->pk_part_modify_action, NULL, NULL); + g_signal_connect (data->part_modify_action, "auth-end", G_CALLBACK (part_modify_auth_end_callback), data); + g_signal_connect (data->part_modify_action, "activate", G_CALLBACK (part_modify_action_callback), data); + + data->pk_part_table_new_action = polkit_action_new (); + polkit_action_set_action_id (data->pk_part_table_new_action, "org.freedesktop.devicekit.disks.change"); + data->part_table_new_action = polkit_gnome_action_new_default ("part_table_new", data->pk_part_table_new_action, NULL, NULL); + g_signal_connect (data->part_table_new_action, "auth-end", G_CALLBACK (part_table_new_auth_end_callback), data); + g_signal_connect (data->part_table_new_action, "activate", G_CALLBACK (part_table_new_action_callback), data); + + data->pk_part_new_action = polkit_action_new (); + polkit_action_set_action_id (data->pk_part_new_action, "org.freedesktop.devicekit.disks.change"); + data->part_new_action = polkit_gnome_action_new_default ("part_new", data->pk_part_new_action, NULL, NULL); + g_signal_connect (data->part_new_action, "auth-end", G_CALLBACK (part_new_auth_end_callback), data); + g_signal_connect (data->part_new_action, "activate", G_CALLBACK (part_new_action_callback), data); } } else { if (data) { - if (data->job_handler_id) - g_signal_handler_disconnect (priv->presentable, data->job_handler_id); - if (data->pk_unmount_auth_end_handler_id) - g_signal_handler_disconnect (priv->unmount_action, data->pk_unmount_auth_end_handler_id); - if (data->pk_unmount_action_handler_id) - g_signal_handler_disconnect (priv->unmount_action, data->pk_unmount_action_handler_id); - if (data->pk_format_auth_end_handler_id) - g_signal_handler_disconnect (priv->format_action, data->pk_format_auth_end_handler_id); - if (data->pk_format_action_handler_id) - g_signal_handler_disconnect (priv->format_action, data->pk_format_action_handler_id); - if (data->pk_part_modify_auth_end_handler_id) - g_signal_handler_disconnect (priv->part_modify_action, data->pk_part_modify_auth_end_handler_id); - if (data->pk_part_modify_action_handler_id) - g_signal_handler_disconnect (priv->part_modify_action, data->pk_part_modify_action_handler_id); + g_signal_handlers_disconnect_matched (data->unmount_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data); + g_signal_handlers_disconnect_matched (data->format_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data); + g_signal_handlers_disconnect_matched (data->part_modify_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data); + g_signal_handlers_disconnect_matched (data->part_table_new_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data); + g_signal_handlers_disconnect_matched (data->part_new_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data); + + /* destroy PolicyKit actions */ + polkit_action_unref (data->pk_unmount_action); + g_object_unref (data->unmount_action); + polkit_action_unref (data->pk_format_action); + g_object_unref (data->format_action); + polkit_action_unref (data->pk_part_modify_action); + g_object_unref (data->part_modify_action); + polkit_action_unref (data->pk_part_table_new_action); + g_object_unref (data->part_table_new_action); + polkit_action_unref (data->pk_part_new_action); + g_object_unref (data->part_new_action); + + g_signal_handlers_disconnect_matched (data->presentable, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data); if (data->job_progress_pulse_timer_id > 0) { g_source_remove (data->job_progress_pulse_timer_id); data->job_progress_pulse_timer_id = 0; @@ -197,6 +276,8 @@ free_format_action_data (FormatProcessData *data) { if (data) { update_ui_progress (data->priv, data, FALSE); + if (data->presentable != NULL) + g_object_unref (data->presentable); if (data->device != NULL) g_object_unref (data->device); if (data->encrypt_passphrase != NULL) @@ -211,6 +292,51 @@ free_format_action_data (FormatProcessData *data) } } +/* ---------------------------------------------------------------------------------------------------- */ + +static void +action_finished (FormatProcessData *data, char *new_device_path) +{ + g_return_if_fail (data != NULL); + GduDevice *new_device; + GduPresentable *new_presentable = NULL; + + /* we don't want to destroy objects at this point, don't pass data */ + update_ui_progress (data->priv, NULL, FALSE); + + /* change to the new device */ + if (new_device_path) { + new_device = gdu_pool_get_by_object_path (data->priv->pool, new_device_path); + if (new_device) { + g_object_unref (data->device); + data->device = new_device; + new_presentable = gdu_pool_get_volume_by_device (data->priv->pool, new_device); + if (new_presentable) { + /* switch to new presentable */ + g_print ("setting new presentable...\n"); + } + } else { + g_warning ("action_finished: cannot find device for the %s device path\n", new_device_path); + } + g_free (new_device_path); + } + + /* Force refresh of the new presentable */ + select_new_presentable (data->priv, new_presentable != NULL ? new_presentable : data->priv->presentable); + if (new_presentable) + g_object_unref (new_presentable); + + /* TODO: show encryption info somewhere? */ + if (data->encrypt_passphrase != NULL) { + /* now set the passphrase if requested */ + if (data->save_in_keyring || data->save_in_keyring_session) { + gdu_util_save_secret (data->device, + data->encrypt_passphrase, + data->save_in_keyring_session); + } + } +} + /* ---------------------------------------------------------------------------------------------------- */ static void nautilus_gdu_show_error (GtkWidget *parent_window, @@ -274,7 +400,7 @@ modify_partition_completed (GduDevice *device, if (error != NULL) { nautilus_gdu_show_error (GTK_WIDGET (data->priv->dialog), - data->priv->presentable, + data->presentable, error, _("Error modifying partition")); g_error_free (error); @@ -284,6 +410,8 @@ modify_partition_completed (GduDevice *device, /* -- don't refresh here, wait for the "changed" callback update_ui (data->priv); */ } + /* save encryption info even if operation fails */ + action_finished (data, NULL); free_format_action_data (data); } @@ -299,13 +427,13 @@ part_modify_action_callback (GtkAction *action, gpointer user_data) return; /* DK is buggy, passing a label string causes the operation to fail */ - gdu_device_op_partition_modify (data->device, data->recommended_part_type, NULL /* data->fslabel */, NULL, modify_partition_completed, data); + gdu_device_op_partition_modify (data->device, data->recommended_part_type, NULL, NULL, modify_partition_completed, data); } static void part_modify_auth_end_callback (PolKitGnomeAction *action, - gboolean gained_privilege, - gpointer user_data) + gboolean gained_privilege, + gpointer user_data) { FormatProcessData *data = user_data; @@ -337,24 +465,13 @@ format_action_completed (GduDevice *device, if (error != NULL) { nautilus_gdu_show_error (GTK_WIDGET (data->priv->dialog), - data->priv->presentable, + data->presentable, error, _("Error creating partition")); g_error_free (error); } else { - /* formatting finished */ - /* TODO: show encryption info somewhere? */ - if (data->encrypt_passphrase != NULL) { - /* now set the passphrase if requested */ - if (data->save_in_keyring || data->save_in_keyring_session) { - gdu_util_save_secret (device, - data->encrypt_passphrase, - data->save_in_keyring_session); - } - } - scheme = gdu_device_partition_get_scheme (device); // g_print ("format_action_completed: scheme = %s\n", scheme); /* TODO: extend to other partition types once this feature is working in DK */ @@ -367,13 +484,13 @@ format_action_completed (GduDevice *device, ! (mbr_part_is_linux_type (part_type) && mbr_part_is_linux_type (data->recommended_part_type))) { g_print ("changing part type to %s, device = %s\n", data->recommended_part_type, gdu_device_get_device_file (device)); - gtk_action_activate (GTK_ACTION (data->priv->part_modify_action)); + gtk_action_activate (GTK_ACTION (data->part_modify_action)); return; /* don't change the UI yet */ } } - /* -- don't refresh here, wait for the "changed" callback - update_ui (data->priv); */ + /* formatting finished */ + action_finished (data, NULL); } free_format_action_data (data); } @@ -426,10 +543,10 @@ unmount_show_busy (gpointer user_data) { FormatProcessData *data = user_data; - if (gdu_util_dialog_show_filesystem_busy (GTK_WIDGET (data->priv->dialog), data->priv->presentable)) { + if (gdu_util_dialog_show_filesystem_busy (GTK_WIDGET (data->priv->dialog), data->presentable)) { /* user managed to kill all applications; try again */ if (! data->priv->job_cancelled) { - gtk_action_activate (GTK_ACTION (data->priv->unmount_action)); + gtk_action_activate (GTK_ACTION (data->unmount_action)); } } else { @@ -460,7 +577,7 @@ unmount_action_completed (GduDevice *device, else { nautilus_gdu_show_error (GTK_WIDGET (data->priv->dialog), - data->priv->presentable, + data->presentable, error, _("Error unmounting device")); free_format_action_data (data); @@ -473,7 +590,7 @@ unmount_action_completed (GduDevice *device, g_print (" formatting...\n"); if (data->priv->job_cancelled) return; - gtk_action_activate (GTK_ACTION (data->priv->format_action)); + gtk_action_activate (GTK_ACTION (data->format_action)); } } @@ -510,6 +627,171 @@ unmount_auth_end_callback (PolKitGnomeAction *action, } } +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +part_table_new_timeout_handler (gpointer user_data) +{ + FormatProcessData *data = user_data; + + g_return_val_if_fail (data != NULL, FALSE); + g_print ("part_table_new_timeout_handler\n"); + + gtk_action_activate (GTK_ACTION (data->part_new_action)); + + return FALSE; +} + +static void +part_table_new_completed (GduDevice *device, + GError *error, + gpointer user_data) +{ + FormatProcessData *data = user_data; + + /* BUG: callback shouldn't be spawned until all changes are reflected in pool */ + g_return_if_fail (data != NULL); + + g_print ("part_table_new_completed\n"); + update_ui_controls (data->priv); + + if (error != NULL) { + nautilus_gdu_show_error (GTK_WIDGET (data->priv->dialog), + data->presentable, + error, + _("Error creating new partition table")); + free_format_action_data (data); + g_error_free (error); + } + else + { + g_print (" creating partition...\n"); + if (data->priv->job_cancelled) + return; + /* TODO: we should wait here for proper refresh */ + g_timeout_add (DEVICE_SETTLE_TIMEOUT, part_table_new_timeout_handler, data); + do_progress_bar_update (data, _("Waiting for device to settle..."), -1, TRUE); +// gtk_action_activate (GTK_ACTION (data->part_new_action)); + } +} + +static void +part_table_new_action_callback (GtkAction *action, gpointer user_data) +{ + FormatProcessData *data = user_data; + + g_return_if_fail (data != NULL); + g_print ("part_table_new_action_callback\n"); + + if (data->priv->job_cancelled) + return; + + /* default to MBR */ + data->scheme = "mbr"; + + gdu_device_op_partition_table_create (data->device, data->scheme, data->secure_erase, part_table_new_completed, data); +} + +static void +part_table_new_auth_end_callback (PolKitGnomeAction *action, + gboolean gained_privilege, + gpointer user_data) +{ + FormatProcessData *data = user_data; + + g_return_if_fail (data != NULL); + g_print ("part_table_new_auth_end_callback\n"); + + if (! gained_privilege) { + /* cancel the whole operation */ + free_format_action_data (data); + } + else { + /* positive reply should be handled by unmount_action_callback */ + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +part_new_completed (GduDevice *device, + char *created_device_object_path, + GError *error, + gpointer user_data) +{ + FormatProcessData *data = user_data; + + /* BUG: callback shouldn't be spawned until all changes are reflected in pool */ + g_return_if_fail (data != NULL); + + g_print ("part_new_completed, created_device_object_path = %s\n", error == NULL ? created_device_object_path : NULL); + + if (error != NULL) { + nautilus_gdu_show_error (GTK_WIDGET (data->priv->dialog), + data->presentable, + error, + _("Error creating new partition")); + g_error_free (error); + } + else + { + /* formatting finished */ + /* TODO: we should wait here for proper refresh */ + action_finished (data, g_strdup (created_device_object_path)); + } + free_format_action_data (data); +} + +static void +part_new_action_callback (GtkAction *action, gpointer user_data) +{ + FormatProcessData *data = user_data; + guint64 offset; + guint64 size; + char *type; + + g_return_if_fail (data != NULL); + g_print ("part_new_action_callback, device = %s\n", gdu_device_get_device_file (data->device)); + + if (data->priv->job_cancelled) + return; + + offset = gdu_presentable_get_offset (data->presentable); + size = gdu_presentable_get_size (data->presentable); + + if (! data->scheme || strlen (data->scheme) == 0) + data->scheme = gdu_device_partition_table_get_scheme (data->device); /* we should have toplevel device here */ + if (! data->scheme || strlen (data->scheme) == 0) + data->scheme = "mbr"; /* default to MBR */ + + type = gdu_util_get_default_part_type_for_scheme_and_fstype (data->scheme, data->fstype, size); + + g_print ("creating new partition, offset = %lu, size = %lu, scheme = %s, type = %s\n", offset, size, data->scheme, type); + + gdu_device_op_partition_create (data->device, offset, size, type, NULL, NULL, + data->fstype, data->fslabel, data->secure_erase, data->encrypt_passphrase, data->take_ownership, + part_new_completed, data); + g_free (type); +} + +static void +part_new_auth_end_callback (PolKitGnomeAction *action, + gboolean gained_privilege, + gpointer user_data) +{ + FormatProcessData *data = user_data; + + g_return_if_fail (data != NULL); + g_print ("part_new_auth_end_callback\n"); + + if (! gained_privilege) { + /* cancel the whole operation */ + free_format_action_data (data); + } + else { + /* positive reply should be handled by unmount_action_callback */ + } +} /* ---------------------------------------------------------------------------------------------------- */ @@ -523,26 +805,24 @@ do_format (FormatDialogPrivate *priv) char *primary; char *secondary; char *drive_name; + gboolean create_new_part_table = FALSE; + gboolean create_new_partition = FALSE; GduKnownFilesystem *kfs; priv->job_cancelled = FALSE; data = g_new0 (FormatProcessData, 1); data->priv = priv; + data->job_progress_pulse_timer_id = 0; primary = NULL; secondary = NULL; toplevel_presentable = NULL; toplevel_device = NULL; drive_name = NULL; - data->device = gdu_presentable_get_device (priv->presentable); - if (data->device == NULL) { - g_warning ("%s: device is not supposed to be NULL", __FUNCTION__); - free_format_action_data (data); - goto out; - } - toplevel_presentable = gdu_presentable_get_toplevel (priv->presentable); + data->presentable = g_object_ref (priv->presentable); + toplevel_presentable = gdu_presentable_get_toplevel (data->presentable); if (toplevel_presentable == NULL) { g_warning ("%s: no toplevel presentable", __FUNCTION__); } @@ -552,6 +832,33 @@ do_format (FormatDialogPrivate *priv) free_format_action_data (data); goto out; } + data->device = gdu_presentable_get_device (data->presentable); + + if (data->device == NULL && toplevel_device != NULL) { + /* no device, i.e. partition table exists but no partition */ + data->device = g_object_ref (toplevel_device); + create_new_part_table = FALSE; + create_new_partition = TRUE; + g_print ("Partition table exists but has no partition for the selected device.\n"); + } else + if (toplevel_device != NULL && ! gdu_device_is_partition_table (toplevel_device)) { + /* no partition table on the device, create partition table first. */ + /* also empty (zeroed) device */ + create_new_part_table = TRUE; + create_new_partition = TRUE; + g_print ("Device is known but doesn't have partition table, we need to create it first.\n"); + } else + if (toplevel_device != NULL && data->device != NULL && toplevel_device == data->device && device_needs_partition_table (data->device)) { + /* device is toplevel, check if we need new partitions */ + create_new_partition = TRUE; + g_print ("Device is known but requires partitioning, we'll create new one.\n"); + } + + if (data->device == NULL) { + g_warning ("%s: device is not supposed to be NULL", __FUNCTION__); + free_format_action_data (data); + goto out; + } drive_name = gdu_presentable_get_name (toplevel_presentable); data->fstype = gdu_util_fstype_combo_box_get_selected (priv->part_type_combo_box); @@ -628,11 +935,20 @@ do_format (FormatDialogPrivate *priv) } } - /* unmount the device before formatting */ + + if (create_new_part_table && device_needs_partition_table (data->device)) { + /* device is zeroed, create partition table first */ + gtk_action_activate (GTK_ACTION (data->part_table_new_action)); + } else + if (create_new_partition && device_needs_partition_table (data->device)) { + /* device has partition table but has no partition */ + gtk_action_activate (GTK_ACTION (data->part_new_action)); + } else if (gdu_device_is_mounted (data->device)) { - gtk_action_activate (GTK_ACTION (priv->unmount_action)); + /* unmount the device before formatting */ + gtk_action_activate (GTK_ACTION (data->unmount_action)); } else { - gtk_action_activate (GTK_ACTION (priv->format_action)); + gtk_action_activate (GTK_ACTION (data->format_action)); } out: diff --git a/src/format-window-operation.h b/src/format-window-operation.h index d5b1dac..f05b806 100644 --- a/src/format-window-operation.h +++ b/src/format-window-operation.h @@ -39,18 +39,24 @@ typedef struct { char *fslabel; char *fstype; GduDevice *device; + GduPresentable *presentable; gboolean take_ownership; char *secure_erase; const char *recommended_part_type; + const char *scheme; guint job_progress_pulse_timer_id; - gulong job_handler_id; - gulong pk_unmount_action_handler_id; - gulong pk_unmount_auth_end_handler_id; - gulong pk_format_action_handler_id; - gulong pk_format_auth_end_handler_id; - gulong pk_part_modify_action_handler_id; - gulong pk_part_modify_auth_end_handler_id; + + PolKitAction *pk_unmount_action; + PolKitGnomeAction *unmount_action; + PolKitAction *pk_format_action; + PolKitGnomeAction *format_action; + PolKitAction *pk_part_modify_action; + PolKitGnomeAction *part_modify_action; + PolKitAction *pk_part_table_new_action; + PolKitGnomeAction *part_table_new_action; + PolKitAction *pk_part_new_action; + PolKitGnomeAction *part_new_action; } FormatProcessData; diff --git a/src/format-window.c b/src/format-window.c index 48ba203..17fc595 100644 --- a/src/format-window.c +++ b/src/format-window.c @@ -36,6 +36,8 @@ +/* ---------------------------------------------------------------------------------------------------- */ +static void set_new_presentable (FormatDialogPrivate *priv, GduPresentable *presentable); /* ---------------------------------------------------------------------------------------------------- */ @@ -133,9 +135,10 @@ update_ui_controls (FormatDialogPrivate *priv) gtk_widget_show_all (priv->no_media_warning); /* controls sensitivity */ - sensitive = priv->presentable != NULL && GDU_IS_PRESENTABLE (priv->presentable) && - device != NULL && (! priv->job_running) && - (! gdu_device_is_read_only (device)) && (gdu_device_is_media_available (device)); + sensitive = priv->presentable != NULL && GDU_IS_PRESENTABLE (priv->presentable) && (! priv->job_running); + if (device) + sensitive = sensitive && ! gdu_device_is_read_only (device) && gdu_device_is_media_available (device); + gtk_widget_set_sensitive (priv->controls_box, sensitive); gtk_widget_set_sensitive (priv->partition_label, sensitive); if (priv->volume_selector) @@ -495,7 +498,6 @@ update_ui (FormatDialogPrivate *priv) /* ---------------------------------------------------------------------------------------------------- */ -static void set_new_presentable (FormatDialogPrivate *priv, GduPresentable *presentable); static void nautilus_gdu_destroy (FormatDialogPrivate *priv) @@ -510,14 +512,6 @@ nautilus_gdu_destroy (FormatDialogPrivate *priv) set_new_presentable (priv, NULL); g_signal_handlers_disconnect_matched (priv->pool, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, priv); - /* destroy PolicyKit actions */ - polkit_action_unref (priv->pk_unmount_action); - g_object_unref (priv->unmount_action); - polkit_action_unref (priv->pk_format_action); - g_object_unref (priv->format_action); - polkit_action_unref (priv->pk_part_modify_action); - g_object_unref (priv->part_modify_action); - /* destroy the dialog and internal struct */ gtk_widget_destroy (GTK_WIDGET (priv->dialog)); g_object_unref (priv->pool); @@ -553,6 +547,19 @@ presentable_changed (GduPresentable *presentable, FormatDialogPrivate *priv) update_ui (priv); } +/* we do ref presentable ourselves */ +void +select_new_presentable (FormatDialogPrivate *priv, GduPresentable *presentable) +{ + if (priv->volume_selector) { + gdu_util_presentable_combo_box_rebuild (priv->volume_selector, priv->pool); + gdu_util_presentable_combo_box_select (priv->volume_selector, presentable); + } + /* force refresh when no standalone mode */ + if (presentable != priv->presentable || ! priv->volume_selector) + set_new_presentable (priv, presentable); +} + static void set_new_presentable (FormatDialogPrivate *priv, GduPresentable *presentable) { @@ -583,8 +590,11 @@ pool_presentable_changed (GduPool *pool, GduPresentable *presentable, FormatDial { g_return_if_fail (priv != NULL); g_return_if_fail (priv->volume_selector != NULL); - g_warning ("Pool presentable changed event.\n"); - gdu_util_presentable_combo_box_rebuild (priv->volume_selector, pool); + + if (! priv->job_running) { + g_warning ("Pool presentable changed event.\n"); + gdu_util_presentable_combo_box_rebuild (priv->volume_selector, pool); + } } static void @@ -615,6 +625,7 @@ cancel_operation (FormatDialogPrivate *priv) g_return_if_fail (priv != NULL); g_return_if_fail (priv->job_running == TRUE); + /* TODO: check for valid device */ g_return_if_fail (priv->presentable != NULL); g_warning ("Cancelling...\n"); @@ -932,19 +943,6 @@ nautilus_gdu_spawn_dialog (GduPresentable *presentable, gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); - /* set up PolicyKit actions */ - priv->pk_unmount_action = polkit_action_new (); - polkit_action_set_action_id (priv->pk_unmount_action, "org.freedesktop.devicekit.disks.filesystem-unmount-others"); - priv->unmount_action = polkit_gnome_action_new_default ("unmount", priv->pk_unmount_action, NULL, NULL); - priv->pk_format_action = polkit_action_new (); - polkit_action_set_action_id (priv->pk_format_action, "org.freedesktop.devicekit.disks.change"); - priv->format_action = polkit_gnome_action_new_default ("format", priv->pk_format_action, NULL, NULL); - priv->pk_part_modify_action = polkit_action_new (); - /* action_id is the same as for format, but sometimes authentication is one shot */ - polkit_action_set_action_id (priv->pk_part_modify_action, "org.freedesktop.devicekit.disks.change"); - priv->part_modify_action = polkit_gnome_action_new_default ("part_modify", priv->pk_part_modify_action, NULL, NULL); - - // gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, FALSE); g_signal_connect (priv->dialog, "delete-event", diff --git a/src/format-window.h b/src/format-window.h index 15ada27..94445a5 100644 --- a/src/format-window.h +++ b/src/format-window.h @@ -61,19 +61,12 @@ typedef struct { gboolean job_running; gboolean job_cancelled; - - PolKitAction *pk_unmount_action; - PolKitGnomeAction *unmount_action; - PolKitAction *pk_format_action; - PolKitGnomeAction *format_action; - PolKitAction *pk_part_modify_action; - PolKitGnomeAction *part_modify_action; } FormatDialogPrivate; -/* . pass presentable=NULL and standalone_mode=TRUE to display volume selector */ -/* . we do ref presentable ourselves */ +/* pass presentable=NULL and standalone_mode=TRUE to display volume selector */ +/* we do ref presentable ourselves */ void nautilus_gdu_spawn_dialog (GduPresentable *presentable, gboolean standalone_mode); @@ -82,6 +75,10 @@ void nautilus_gdu_spawn_dialog (GduPresentable *presentable, void update_ui_controls (FormatDialogPrivate *priv); +/* we do ref presentable ourselves */ +void select_new_presentable (FormatDialogPrivate *priv, + GduPresentable *presentable); + G_END_DECLS #endif diff --git a/src/gdu-utils.c b/src/gdu-utils.c index e85eee3..0d01aef 100644 --- a/src/gdu-utils.c +++ b/src/gdu-utils.c @@ -147,6 +147,66 @@ find_presentable_from_device_path (char *device_path) /* ---------------------------------------------------------------------------------------------------- */ /* Volume selector widget */ +static void +gdu_util_presentable_combo_box_add_item (GtkListStore *store, GduPresentable *presentable) +{ + char *name = NULL; + char *display_name = NULL; + GdkPixbuf *pixbuf = NULL; + GtkTreeIter iter; + GduDevice *device = NULL; + GduPresentable *toplevel_presentable = NULL; + GduDevice *toplevel_device = NULL; + + device = gdu_presentable_get_device (presentable); + toplevel_presentable = gdu_presentable_get_toplevel (presentable); + if (toplevel_presentable) + toplevel_device = gdu_presentable_get_device (toplevel_presentable); + + /* Make display name */ + if (device) { + name = g_strdup (gdu_device_id_get_label (device)); + if (name && strlen (name) > 0) + display_name = g_strdup (name); + g_free (name); + name = NULL; + } + if (! display_name) { + name = gdu_presentable_get_name (presentable); + if (name && strlen (name) > 0) + display_name = g_strdup_printf ("(%s)", name); + g_free (name); + name = NULL; + } + if (! display_name && device) + display_name = g_strdup_printf ("(%s)", gdu_device_get_device_file (device)); + + /* Get an icon */ + pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_DIALOG); + + if (device || toplevel_device) { + gtk_list_store_append (store, &iter); + /* gtk_list_store_set() refs objects itself */ + gtk_list_store_set (store, &iter, + 0, presentable, + 1, display_name, + 2, device ? gdu_device_get_device_file (device) : gdu_device_get_device_file (toplevel_device), + 3, pixbuf, + -1); + } + + g_free (display_name); + if (device) + g_object_unref (device); + if (toplevel_presentable) + g_object_unref (toplevel_presentable); + if (toplevel_device) + g_object_unref (toplevel_device); + if (name) + g_free (name); + if (pixbuf) + g_object_unref (pixbuf); +} static GtkListStore * gdu_util_presentable_combo_box_create_store (GduPool *pool) @@ -173,55 +233,53 @@ gdu_util_presentable_combo_box_create_store (GduPool *pool) GduPresentable *toplevel_presentable; GduDevice *toplevel_device = NULL; GduDevice *device; - GdkPixbuf *pixbuf = NULL; GList *enclosed_presentables = NULL; - char *name = NULL; - char *display_name = NULL; - device = gdu_presentable_get_device (presentable); toplevel_presentable = gdu_presentable_get_toplevel (presentable); - if (device != NULL && toplevel_presentable != NULL) { + if (toplevel_presentable) toplevel_device = gdu_presentable_get_device (toplevel_presentable); - pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_DIALOG); + /* First eliminate all non-toplevel presentables */ + if (/* device != NULL && */ + toplevel_device != NULL && + toplevel_presentable == presentable && + gdu_device_is_removable (toplevel_device) + /* && ! gdu_device_is_system_internal (device) */) { /* determine parent presentables (in case of toplevel drive) */ enclosed_presentables = gdu_pool_get_enclosed_presentables (pool, presentable); - if (toplevel_device != NULL && gdu_device_is_removable (toplevel_device) && g_list_length (enclosed_presentables) == 0) { - name = g_strdup (gdu_device_id_get_label (device)); - if (name && strlen (name) > 0) - display_name = g_strdup (name); - g_free (name); - name = NULL; - if (! display_name) { - name = gdu_presentable_get_name (presentable); - if (name && strlen (name) > 0) - display_name = g_strdup_printf ("(%s)", name); - g_free (name); - name = NULL; - } - if (! display_name) - display_name = g_strdup_printf ("(%s)", gdu_device_get_device_file (device)); - - gtk_list_store_append (store, &iter); - /* gtk_list_store_set() refs objects itself */ - gtk_list_store_set (store, &iter, - 0, presentable, - 1, display_name, - 2, gdu_device_get_device_file (device), - 3, pixbuf, - -1); - g_free (display_name); - if (name) - g_free (name); + g_print ("presentable %s [%s], enclosed_presentables = %d, toplevel_presentable = %p vs. %p, gdu_device_is_partition_table = %d, gdu_device_is_optical_disc = %d, gdu_device_drive_get_media = '%s'\n", + gdu_presentable_get_name (presentable), device ? gdu_device_get_device_file (device) : "no device.", g_list_length (enclosed_presentables), toplevel_presentable, presentable, gdu_device_is_partition_table (device), gdu_device_is_optical_disc (device), gdu_device_drive_get_media (device)); +#if 0 + char **media_compat = gdu_device_drive_get_media_compatibility (device); + for (; *media_compat; media_compat++) { + g_print (" compat '%s'\n", *media_compat); + } +#endif + + + GList *enclosed_presentables_l; + for (enclosed_presentables_l = enclosed_presentables; enclosed_presentables_l != NULL; enclosed_presentables_l = enclosed_presentables_l->next) { + GduPresentable *presentable_e = enclosed_presentables_l->data; + GduDevice *device_e = gdu_presentable_get_device (presentable_e); + g_print (" +--- %s, GDU_IS_DRIVE = %d, GDU_IS_VOLUME = %d, GDU_IS_VOLUME_HOLE = %d, device = %p [%s]\n", + gdu_presentable_get_name (presentable_e), GDU_IS_DRIVE (presentable_e), GDU_IS_VOLUME (presentable_e), GDU_IS_VOLUME_HOLE (presentable_e), device_e, device_e ? gdu_device_get_device_file (device_e) : "no device."); + gdu_util_presentable_combo_box_add_item (store, presentable_e); + if (device_e) + g_object_unref (device_e); + } + +#if 0 + if (/* toplevel_device != NULL && gdu_device_is_removable (toplevel_device) && */ g_list_length (enclosed_presentables) == 0) { + gdu_util_presentable_combo_box_add_item (store, presentable); } +#endif + g_list_foreach (enclosed_presentables, (GFunc) g_object_unref, NULL); g_list_free (enclosed_presentables); } - if (pixbuf) - g_object_unref (pixbuf); if (toplevel_presentable) g_object_unref (toplevel_presentable); if (device) -- cgit