/** * @file gnome-cmd-con-device.cc * @copyright (C) 2001-2006 Marcus Bjurman\n * @copyright (C) 2007-2012 Piotr Eljasiak\n * @copyright (C) 2013-2023 Uwe Scholz\n * * @copyright This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * @copyright This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * @copyright You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "gnome-cmd-includes.h" #include "gnome-cmd-data.h" #include "gnome-cmd-con-device.h" #include "gnome-cmd-plain-path.h" #include "imageloader.h" #include "utils.h" #include "gnome-cmd-main-win.h" using namespace std; struct GnomeCmdConDevicePrivate { gchar *alias {nullptr}; gchar *device_fn {nullptr}; // The device identifier (either a linux device string or a uuid) gchar *mountp {nullptr}; gchar *icon_path {nullptr}; gboolean autovolume; GMount *gMount; GVolume *gVolume; }; static GnomeCmdConClass *parent_class = nullptr; static gboolean is_mounted (GnomeCmdConDevice *dev_con) { FILE *fd = fopen ("/etc/mtab", "r"); if (!fd) return FALSE; gchar tmp[512]; gboolean ret = FALSE; gchar *s; while ((s=fgets (tmp, sizeof(tmp), fd))!=nullptr) { char **v = g_strsplit (s, " ", 3); if (v[1]) { gchar *dir = g_strcompress (v[1]); if (strcmp (dir, dev_con->priv->mountp) == 0) ret = TRUE; g_free (dir); } g_strfreev (v); if (ret) break; } fclose (fd); return ret; } static void do_legacy_mount(GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON_DEVICE (con)); GnomeCmdConDevice *dev_con = GNOME_CMD_CON_DEVICE (con); gint ret, estatus; if (!is_mounted (dev_con)) { gchar *cmd = nullptr; gchar *emsg = nullptr; if (strlen(dev_con->priv->device_fn) == 0) return; DEBUG ('m', "mounting %s\n", dev_con->priv->device_fn); if (dev_con->priv->device_fn[0] == G_DIR_SEPARATOR) { cmd = g_strdup_printf ("mount %s", dev_con->priv->device_fn); } else { cmd = g_strdup_printf ("mount -L %s", dev_con->priv->device_fn); } DEBUG ('m', "Mount command: %s\n", cmd); ret = system (cmd); estatus = WEXITSTATUS (ret); g_free (cmd); DEBUG ('m', "mount returned %d and had the exitstatus %d\n", ret, estatus); if (ret == -1) emsg = g_strdup (_("Failed to execute the mount command")); else switch (estatus) { case 0: emsg = nullptr; break; case 1: emsg = g_strdup (_("Mount failed: permission denied")); break; case 32: emsg = g_strdup (_("Mount failed: no medium found")); break; default: emsg = g_strdup_printf (_("Mount failed: mount exited with exitstatus %d"), estatus); break; } if (emsg != nullptr) { con->open_result = GnomeCmdCon::OPEN_FAILED; con->state = GnomeCmdCon::STATE_CLOSED; con->open_failed_msg = emsg; return; } } else DEBUG('m', "The device was already mounted\n"); } static void set_con_mount_succeed(GnomeCmdCon *con) { con->state = GnomeCmdCon::STATE_OPEN; con->open_result = GnomeCmdCon::OPEN_OK; } static void set_con_mount_failed(GnomeCmdCon *con) { if (con->base_gFileInfo) g_object_unref (con->base_gFileInfo); con->base_gFileInfo = nullptr; con->open_result = GnomeCmdCon::OPEN_FAILED; con->state = GnomeCmdCon::STATE_CLOSED; con->open_failed_msg = con->open_failed_error->message; } static void mount_finish_callback(GObject *gVol, GAsyncResult *result, gpointer user_data) { GnomeCmdCon *con = GNOME_CMD_CON (user_data); GnomeCmdConDevice *dev_con = GNOME_CMD_CON_DEVICE (con); auto gVolume = G_VOLUME(gVol); GError *error = nullptr; if(!g_volume_mount_finish(gVolume, result, &error)) { g_critical("Unable to mount the volume, error: %s", error->message); con->open_failed_error = g_error_copy(error); set_con_mount_failed(con); g_error_free(error); return; } dev_con->priv->gMount = g_volume_get_mount (gVolume); if (!con->base_path) { set_con_base_path_for_gmount(con, dev_con->priv->gMount); } if (con->base_path) { gnome_cmd_con_set_root_path(con, con->base_path->get_path()); } set_con_base_gfileinfo(con); set_con_mount_succeed(con); } static void do_legacy_mount_thread_func(GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON_DEVICE (con)); GError *error = nullptr; if (!con->base_path) { con->base_path = new GnomeCmdPlainPath(GNOME_CMD_CON_DEVICE(con)->priv->mountp); } do_legacy_mount(con); if (con->open_result == GnomeCmdCon::OPEN_FAILED) { con->open_failed_error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to mount %s", GNOME_CMD_CON_DEVICE (con)->priv->device_fn); set_con_mount_failed(con); return; } auto gFile = gnome_cmd_con_create_gfile(con, con->base_path); con->base_gFileInfo = g_file_query_info(gFile, "*", G_FILE_QUERY_INFO_NONE, nullptr, &error); g_object_unref(gFile); if (error) { con->open_failed_error = g_error_copy(error); set_con_mount_failed(con); g_warning("Unable to mount the volume via legacy mount, error: %s", error->message); g_error_free(error); return; } set_con_mount_succeed(con); return; } static void do_mount (GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON_DEVICE (con)); GnomeCmdConDevice *dev_con = GNOME_CMD_CON_DEVICE (con); // This is a legacy-mount: If mount point is given, we mount the device with system calls ('mount') if (dev_con->priv->mountp) { auto gThread = g_thread_new (nullptr, (GThreadFunc) do_legacy_mount_thread_func, con); g_thread_unref(gThread); } // Check if the volume is already mounted: if (dev_con->priv->gVolume) { auto gMount = g_volume_get_mount (dev_con->priv->gVolume); if (gMount) { dev_con->priv->gMount = gMount; set_con_base_path_for_gmount(con, dev_con->priv->gMount); set_con_base_gfileinfo(con); set_con_mount_succeed(con); return; } auto gMountOperation = gtk_mount_operation_new ((GtkWindow*) main_win); g_volume_mount (dev_con->priv->gVolume, G_MOUNT_MOUNT_NONE, gMountOperation, nullptr, mount_finish_callback, con); } } static void dev_open (GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON_DEVICE (con)); DEBUG ('m', "Mounting device\n"); con->state = GnomeCmdCon::STATE_OPENING; con->open_result = GnomeCmdCon::OPEN_IN_PROGRESS; do_mount(con); } static void show_message_dialog_volume_unmounted() { DEBUG('m', "unmount_callback: succeeded\n"); GtkWidget *msgbox; msgbox = gtk_message_dialog_new (*main_win, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, _("Volume successfully unmounted")); gtk_dialog_run (GTK_DIALOG (msgbox)); gtk_widget_destroy (msgbox); } static void unmount_callback(GObject *gMnt, GAsyncResult *result, gpointer user_data) { GnomeCmdCon *con = GNOME_CMD_CON (user_data); auto gMount = G_MOUNT(gMnt); GError *error = nullptr; GtkWidget *msgbox; if (!g_mount_unmount_with_operation_finish (gMount, result, &error)) { DEBUG('m', "unmount_callback: failed %s\n", error->message); msgbox = gtk_message_dialog_new (*main_win, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Cannot unmount the volume:\n%s\nError code: %d"), error->message, error->code); gtk_dialog_run (GTK_DIALOG (msgbox)); gtk_widget_destroy (msgbox); g_error_free(error); return; } con->state = GnomeCmdCon::STATE_CLOSED; show_message_dialog_volume_unmounted(); } static gboolean dev_close (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON_DEVICE (con), FALSE); gint ret = 0; GnomeCmdConDevice *dev_con = GNOME_CMD_CON_DEVICE (con); gnome_cmd_con_set_default_dir (con, nullptr); if (chdir (g_get_home_dir ()) == -1) { DEBUG ('m', "Could not go back to home directory before unmounting\n"); } if (dev_con->priv->autovolume) { if (!g_mount_can_unmount(dev_con->priv->gMount)) return ret == 0; auto gMountName = g_mount_get_name(dev_con->priv->gMount); DEBUG ('m', "umounting GIO mount \"%s\"\n", gMountName); g_free(gMountName); g_mount_unmount_with_operation (dev_con->priv->gMount, G_MOUNT_UNMOUNT_NONE, nullptr, nullptr, unmount_callback, con); } else { // Legacy unmount if(dev_con->priv->mountp) { DEBUG ('m', "umounting %s\n", dev_con->priv->mountp); gchar *cmd = g_strdup_printf ("umount %s", dev_con->priv->mountp); ret = system (cmd); DEBUG ('m', "umount returned %d\n", ret); g_free (cmd); if (ret == 0) { con->state = GnomeCmdCon::STATE_CLOSED; show_message_dialog_volume_unmounted(); } } } return ret == 0; } static void dev_cancel_open (GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON_DEVICE (con)); } static gboolean dev_open_is_needed (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON_DEVICE (con), FALSE); return TRUE; } static GFile *dev_create_gfile (GnomeCmdCon *con, GnomeCmdPath *gnomeCmdPath) { g_return_val_if_fail (GNOME_CMD_IS_CON_DEVICE (con), nullptr); GFile *newGFile = nullptr; GnomeCmdConDevice *dev_con = GNOME_CMD_CON_DEVICE (con); if (dev_con->priv->gMount != nullptr) { if (gnomeCmdPath) { auto gMountGFile = g_mount_get_default_location (dev_con->priv->gMount); newGFile = g_file_resolve_relative_path(gMountGFile, gnomeCmdPath->get_path()); g_object_unref(gMountGFile); } else { newGFile = g_mount_get_default_location (dev_con->priv->gMount); } } else { if (gnomeCmdPath != nullptr) { newGFile = g_file_new_for_path(gnomeCmdPath->get_path()); } } return newGFile; } static GnomeCmdPath *dev_create_path (GnomeCmdCon *con, const gchar *path_str) { g_return_val_if_fail (GNOME_CMD_IS_CON_DEVICE (con), nullptr); g_return_val_if_fail (path_str != nullptr, nullptr); return new GnomeCmdPlainPath(path_str); } /******************************* * Gtk class implementation *******************************/ static void destroy (GtkObject *object) { GnomeCmdConDevice *con = GNOME_CMD_CON_DEVICE (object); if (con->priv->gMount) { g_object_unref(con->priv->gMount); con->priv->gMount = nullptr; } if (con->priv->gVolume) { g_object_unref(con->priv->gVolume); con->priv->gVolume = nullptr; } g_free (con->priv); if (GTK_OBJECT_CLASS (parent_class)->destroy) (*GTK_OBJECT_CLASS (parent_class)->destroy) (object); } static void class_init (GnomeCmdConDeviceClass *klass) { GtkObjectClass *object_class; GnomeCmdConClass *con_class; object_class = GTK_OBJECT_CLASS (klass); con_class = GNOME_CMD_CON_CLASS (klass); parent_class = static_cast (gtk_type_class (GNOME_CMD_TYPE_CON)); object_class->destroy = destroy; con_class->open = dev_open; con_class->close = dev_close; con_class->cancel_open = dev_cancel_open; con_class->open_is_needed = dev_open_is_needed; con_class->create_gfile = dev_create_gfile; con_class->create_path = dev_create_path; } static void init (GnomeCmdConDevice *dev_con) { GnomeCmdCon *con = GNOME_CMD_CON (dev_con); dev_con->priv = g_new0 (GnomeCmdConDevicePrivate, 1); con->method = CON_FILE; con->should_remember_dir = TRUE; con->needs_open_visprog = FALSE; con->needs_list_visprog = FALSE; con->can_show_free_space = TRUE; con->is_local = TRUE; con->is_closeable = TRUE; con->go_pixmap = nullptr; con->open_pixmap = nullptr; con->close_pixmap = nullptr; } /*********************************** * Public functions ***********************************/ GtkType gnome_cmd_con_device_get_type () { static GtkType type = 0; if (type == 0) { GtkTypeInfo info = { (gchar *) "GnomeCmdConDevice", sizeof (GnomeCmdConDevice), sizeof (GnomeCmdConDeviceClass), (GtkClassInitFunc) class_init, (GtkObjectInitFunc) init, /* reserved_1 */ nullptr, /* reserved_2 */ nullptr, (GtkClassInitFunc) nullptr }; type = gtk_type_unique (GNOME_CMD_TYPE_CON, &info); } return type; } GnomeCmdConDevice *gnome_cmd_con_device_new (const gchar *alias, const gchar *device_fn, const gchar *mountp, const gchar *icon_path) { auto dev = static_cast (g_object_new (GNOME_CMD_TYPE_CON_DEVICE, nullptr)); GnomeCmdCon *con = GNOME_CMD_CON (dev); gnome_cmd_con_device_set_device_fn (dev, device_fn); gnome_cmd_con_device_set_mountp (dev, mountp); gnome_cmd_con_device_set_icon_path (dev, icon_path); gnome_cmd_con_device_set_autovol(dev, FALSE); gnome_cmd_con_device_set_gmount(dev, nullptr); gnome_cmd_con_device_set_gvolume(dev, nullptr); gnome_cmd_con_device_set_alias (dev, alias); if (mountp) gnome_cmd_con_set_root_path (con, mountp); con->open_msg = g_strdup_printf (_("Mounting %s"), alias); return dev; } void gnome_cmd_con_device_set_alias (GnomeCmdConDevice *dev, const gchar *alias) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); g_return_if_fail (alias != nullptr); g_free (dev->priv->alias); dev->priv->alias = g_strdup (alias); GNOME_CMD_CON (dev)->alias = g_strdup (alias); if (dev->priv->mountp) GNOME_CMD_CON (dev)->go_text = g_strdup_printf (_("Go to: %s (%s)"), alias, dev->priv->mountp); else GNOME_CMD_CON (dev)->go_text = g_strdup_printf (_("Go to: %s"), alias); GNOME_CMD_CON (dev)->open_text = g_strdup_printf (_("Mount: %s"), alias); GNOME_CMD_CON (dev)->close_text = g_strdup_printf (_("Unmount: %s"), alias); } void gnome_cmd_con_device_set_device_fn (GnomeCmdConDevice *dev, const gchar *device_fn) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); g_free (dev->priv->device_fn); dev->priv->device_fn = g_strdup (device_fn ? device_fn : ""); } void gnome_cmd_con_device_set_mountp (GnomeCmdConDevice *dev, const gchar *mountp) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); if (!mountp) return; g_free (dev->priv->mountp); dev->priv->mountp = g_strdup (mountp); } void gnome_cmd_con_device_set_icon_path (GnomeCmdConDevice *dev, const gchar *icon_path) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); g_free (dev->priv->icon_path); GnomeCmdCon *con = GNOME_CMD_CON (dev); gnome_cmd_pixmap_free (con->go_pixmap); gnome_cmd_pixmap_free (con->open_pixmap); gnome_cmd_pixmap_free (con->close_pixmap); con->go_pixmap = nullptr; con->open_pixmap = nullptr; con->close_pixmap = nullptr; dev->priv->icon_path = g_strdup (icon_path); if (icon_path) { guint dev_icon_size = gnome_cmd_data.dev_icon_size; con->go_pixmap = gnome_cmd_pixmap_new_from_file (icon_path, dev_icon_size, dev_icon_size); con->open_pixmap = gnome_cmd_pixmap_new_from_file (icon_path, dev_icon_size, dev_icon_size); if (con->open_pixmap) { GdkPixbuf *overlay = gdk_pixbuf_copy (con->open_pixmap->pixbuf); if (overlay) { GdkPixbuf *umount = IMAGE_get_pixbuf (PIXMAP_OVERLAY_UMOUNT); #if defined (__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" #endif if (umount) { gdk_pixbuf_copy_area (umount, 0, 0, MIN (gdk_pixbuf_get_width (umount), dev_icon_size), MIN (gdk_pixbuf_get_height (umount), dev_icon_size), overlay, 0, 0); con->close_pixmap = gnome_cmd_pixmap_new_from_pixbuf (overlay); } #if defined (__GNUC__) #pragma GCC diagnostic pop #endif g_object_unref (overlay); } } } } void gnome_cmd_con_device_set_autovol (GnomeCmdConDevice *dev, const gboolean autovol) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); dev->priv->autovolume = autovol; } void gnome_cmd_con_device_set_gmount (GnomeCmdConDevice *dev, GMount *gMount) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); if (dev->priv->gMount) { g_object_unref (dev->priv->gMount); dev->priv->gMount = nullptr; } dev->priv->gMount = gMount; if (dev->priv->gMount) g_object_ref(dev->priv->gMount); } void gnome_cmd_con_device_set_gvolume (GnomeCmdConDevice *dev, GVolume *gVolume) { g_return_if_fail (dev != nullptr); g_return_if_fail (dev->priv != nullptr); if (dev->priv->gVolume) { g_object_unref (dev->priv->gVolume); dev->priv->gVolume = nullptr; } dev->priv->gVolume = gVolume; if (dev->priv->gVolume) g_object_ref(dev->priv->gVolume); } const gchar *gnome_cmd_con_device_get_alias (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, nullptr); g_return_val_if_fail (dev->priv != nullptr, nullptr); return dev->priv->alias; } const gchar *gnome_cmd_con_device_get_device_fn (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, nullptr); g_return_val_if_fail (dev->priv != nullptr, nullptr); return dev->priv->device_fn != nullptr ? dev->priv->device_fn : ""; } const gchar *gnome_cmd_con_device_get_mountp_string (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, nullptr); g_return_val_if_fail (dev->priv != nullptr, nullptr); return dev->priv->mountp; } const gchar *gnome_cmd_con_device_get_icon_path (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, nullptr); g_return_val_if_fail (dev->priv != nullptr, nullptr); return dev->priv->icon_path; } gboolean gnome_cmd_con_device_get_autovol (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, FALSE); g_return_val_if_fail (dev->priv != nullptr, FALSE); return dev->priv->autovolume; } GMount *gnome_cmd_con_device_get_gmount (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, nullptr); g_return_val_if_fail (dev->priv != nullptr, nullptr); return dev->priv->gMount; } GVolume *gnome_cmd_con_device_get_gvolume (GnomeCmdConDevice *dev) { g_return_val_if_fail (dev != nullptr, nullptr); g_return_val_if_fail (dev->priv != nullptr, nullptr); return dev->priv->gVolume; }