Beispiel #1
0
static void
update_mounts (GUnixVolumeMonitor *monitor)
{
  GList *new_mounts;
  GList *removed, *added;
  GList *l;
  GUnixMount *mount;
  GUnixVolume *volume;
  const char *mount_path;
  
  new_mounts = g_unix_mounts_get (NULL);
  
  new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
  
  diff_sorted_lists (monitor->last_mounts,
		     new_mounts, (GCompareFunc) g_unix_mount_compare,
		     &added, &removed);
  
  for (l = removed; l != NULL; l = l->next)
    {
      GUnixMountEntry *mount_entry = l->data;
      
      mount = find_mount_by_mountpath (monitor, g_unix_mount_get_mount_path (mount_entry));
      if (mount)
	{
	  _g_unix_mount_unmounted (mount);
	  monitor->mounts = g_list_remove (monitor->mounts, mount);
	  g_signal_emit_by_name (monitor, "mount-removed", mount);
	  g_signal_emit_by_name (mount, "unmounted");
	  g_object_unref (mount);
	}
    }
  
  for (l = added; l != NULL; l = l->next)
    {
      GUnixMountEntry *mount_entry = l->data;

      mount_path = g_unix_mount_get_mount_path (mount_entry);
      
      volume = _g_unix_volume_monitor_lookup_volume_for_mount_path (monitor, mount_path);
      mount = _g_unix_mount_new (G_VOLUME_MONITOR (monitor), mount_entry, volume);
      if (mount)
	{
	  monitor->mounts = g_list_prepend (monitor->mounts, mount);
	  g_signal_emit_by_name (monitor, "mount-added", mount);
	}
    }
  
  g_list_free (added);
  g_list_free (removed);
  g_list_foreach (monitor->last_mounts,
		  (GFunc)g_unix_mount_free, NULL);
  g_list_free (monitor->last_mounts);
  monitor->last_mounts = new_mounts;
}
gboolean
gsd_is_removable_mount (GUnixMountEntry *mount)
{
        const char *mount_path;
        char *path;

        mount_path = g_unix_mount_get_mount_path (mount);
        if (mount_path == NULL)
                return FALSE;

        path = g_strdup_printf ("/run/media/%s", g_get_user_name ());
        if (g_str_has_prefix (mount_path, path)) {
                g_free (path);
                return TRUE;
        }
        g_free (path);
        return FALSE;
}
static gboolean
ldsm_is_hash_item_not_in_mounts (gpointer key,
                                 gpointer value,
                                 gpointer user_data)
{
        GList *l;

        for (l = (GList *) user_data; l != NULL; l = l->next) {
                GUnixMountEntry *mount = l->data;
                const char *path;

                path = g_unix_mount_get_mount_path (mount);

                if (strcmp (path, key) == 0)
                        return FALSE;
        }

        return TRUE;
}
static gboolean
ldsm_mount_has_trash (LdsmMountInfo *mount)
{
        const gchar *user_data_dir;
        gchar *user_data_attr_id_fs;
        gchar *path_attr_id_fs;
        gboolean mount_uses_user_trash = FALSE;
        gchar *trash_files_dir;
        gboolean has_trash = FALSE;
        GDir *dir;
        const gchar *path;

        user_data_dir = g_get_user_data_dir ();
        user_data_attr_id_fs = ldsm_get_fs_id_for_path (user_data_dir);

        path = g_unix_mount_get_mount_path (mount->mount);
        path_attr_id_fs = ldsm_get_fs_id_for_path (path);

        if (g_strcmp0 (user_data_attr_id_fs, path_attr_id_fs) == 0) {
                /* The volume that is low on space is on the same volume as our home
                 * directory. This means the trash is at $XDG_DATA_HOME/Trash,
                 * not at the root of the volume which is full.
                 */
                mount_uses_user_trash = TRUE;
        }

        g_free (user_data_attr_id_fs);
        g_free (path_attr_id_fs);

        /* I can't think of a better way to find out if a volume has any trash. Any suggestions? */
        if (mount_uses_user_trash) {
                trash_files_dir = g_build_filename (g_get_user_data_dir (), "Trash", "files", NULL);
        } else {
                gchar *uid;

                uid = g_strdup_printf ("%d", getuid ());
                trash_files_dir = g_build_filename (path, ".Trash", uid, "files", NULL);
                if (!g_file_test (trash_files_dir, G_FILE_TEST_IS_DIR)) {
                        gchar *trash_dir;

                        g_free (trash_files_dir);
                        trash_dir = g_strdup_printf (".Trash-%s", uid);
                        trash_files_dir = g_build_filename (path, trash_dir, "files", NULL);
                        g_free (trash_dir);
                        if (!g_file_test (trash_files_dir, G_FILE_TEST_IS_DIR)) {
                                g_free (trash_files_dir);
                                g_free (uid);
                                return has_trash;
                        }
                }
                g_free (uid);
        }

        dir = g_dir_open (trash_files_dir, 0, NULL);
        if (dir) {
                if (g_dir_read_name (dir))
                        has_trash = TRUE;
                g_dir_close (dir);
        }

        g_free (trash_files_dir);

        return has_trash;
}
static void
ldsm_maybe_warn_mounts (GList *mounts,
                        gboolean multiple_volumes,
                        gboolean other_usable_volumes)
{
        GList *l;
        gboolean done = FALSE;

        for (l = mounts; l != NULL; l = l->next) {
                LdsmMountInfo *mount_info = l->data;
                LdsmMountInfo *previous_mount_info;
                gdouble free_space;
                gdouble previous_free_space;
                time_t curr_time;
                const gchar *path;
                gboolean show_notify;

                if (done) {
                        /* Don't show any more dialogs if the user took action with the last one. The user action
                         * might free up space on multiple volumes, making the next dialog redundant.
                         */
                        ldsm_free_mount_info (mount_info);
                        continue;
                }

                path = g_unix_mount_get_mount_path (mount_info->mount);

                previous_mount_info = g_hash_table_lookup (ldsm_notified_hash, path);
                if (previous_mount_info != NULL)
                        previous_free_space = (gdouble) previous_mount_info->buf.f_bavail / (gdouble) previous_mount_info->buf.f_blocks;

                free_space = (gdouble) mount_info->buf.f_bavail / (gdouble) mount_info->buf.f_blocks;

                if (previous_mount_info == NULL) {
                        /* We haven't notified for this mount yet */
                        show_notify = TRUE;
                        mount_info->notify_time = time (NULL);
                        g_hash_table_replace (ldsm_notified_hash, g_strdup (path), mount_info);
                } else if ((previous_free_space - free_space) > free_percent_notify_again) {
                        /* We've notified for this mount before and free space has decreased sufficiently since last time to notify again */
                        curr_time = time (NULL);
                        if (difftime (curr_time, previous_mount_info->notify_time) > (gdouble)(min_notify_period * 60)) {
                                show_notify = TRUE;
                                mount_info->notify_time = curr_time;
                        } else {
                                /* It's too soon to show the dialog again. However, we still replace the LdsmMountInfo
                                 * struct in the hash table, but give it the notfiy time from the previous dialog.
                                 * This will stop the notification from reappearing unnecessarily as soon as the timeout expires.
                                 */
                                show_notify = FALSE;
                                mount_info->notify_time = previous_mount_info->notify_time;
                        }
                        g_hash_table_replace (ldsm_notified_hash, g_strdup (path), mount_info);
                } else {
                        /* We've notified for this mount before, but the free space hasn't decreased sufficiently to notify again */
                        ldsm_free_mount_info (mount_info);
                        show_notify = FALSE;
                }

                if (show_notify) {
                        if (ldsm_notify_for_mount (mount_info, multiple_volumes, other_usable_volumes))
                                done = TRUE;
                }
        }
}
static gboolean
ldsm_check_all_mounts (gpointer data)
{
        GList *mounts;
        GList *l;
        GList *check_mounts = NULL;
        GList *full_mounts = NULL;
        guint number_of_mounts;
        guint number_of_full_mounts;
        gboolean multiple_volumes = FALSE;
        gboolean other_usable_volumes = FALSE;

        /* We iterate through the static mounts in /etc/fstab first, seeing if
         * they're mounted by checking if the GUnixMountPoint has a corresponding GUnixMountEntry.
         * Iterating through the static mounts means we automatically ignore dynamically mounted media.
         */
        mounts = g_unix_mount_points_get (time_read);

        for (l = mounts; l != NULL; l = l->next) {
                GUnixMountPoint *mount_point = l->data;
                GUnixMountEntry *mount;
                LdsmMountInfo *mount_info;
                const gchar *path;

                path = g_unix_mount_point_get_mount_path (mount_point);
                mount = g_unix_mount_at (path, time_read);
                g_unix_mount_point_free (mount_point);
                if (mount == NULL) {
                        /* The GUnixMountPoint is not mounted */
                        continue;
                }

                mount_info = g_new0 (LdsmMountInfo, 1);
                mount_info->mount = mount;

                path = g_unix_mount_get_mount_path (mount);

                if (g_unix_mount_is_readonly (mount)) {
                        ldsm_free_mount_info (mount_info);
                        continue;
                }

                if (ldsm_mount_should_ignore (mount)) {
                        ldsm_free_mount_info (mount_info);
                        continue;
                }

                if (statvfs (path, &mount_info->buf) != 0) {
                        ldsm_free_mount_info (mount_info);
                        continue;
                }

                if (ldsm_mount_is_virtual (mount_info)) {
                        ldsm_free_mount_info (mount_info);
                        continue;
                }

                check_mounts = g_list_prepend (check_mounts, mount_info);
        }
        g_list_free (mounts);

        number_of_mounts = g_list_length (check_mounts);
        if (number_of_mounts > 1)
                multiple_volumes = TRUE;

        for (l = check_mounts; l != NULL; l = l->next) {
                LdsmMountInfo *mount_info = l->data;

                if (!ldsm_mount_has_space (mount_info)) {
                        full_mounts = g_list_prepend (full_mounts, mount_info);
                } else {
                        g_hash_table_remove (ldsm_notified_hash, g_unix_mount_get_mount_path (mount_info->mount));
                        ldsm_free_mount_info (mount_info);
                }
        }

        number_of_full_mounts = g_list_length (full_mounts);
        if (number_of_mounts > number_of_full_mounts)
                other_usable_volumes = TRUE;

        ldsm_maybe_warn_mounts (full_mounts, multiple_volumes,
                                other_usable_volumes);

        g_list_free (check_mounts);
        g_list_free (full_mounts);

        return TRUE;
}
static gboolean
ldsm_mount_should_ignore (GUnixMountEntry *mount)
{
        const gchar *fs, *device, *path;
        
        path = g_unix_mount_get_mount_path (mount);
        if (ldsm_mount_is_user_ignore (path))
                return TRUE;
        
        /* This is borrowed from GLib and used as a way to determine
         * which mounts we should ignore by default. GLib doesn't
         * expose this in a way that allows it to be used for this
         * purpose
         */

        /* We also ignore network filesystems */

        const gchar *ignore_fs[] = {
                "adfs",
                "afs",
                "auto",
                "autofs",
                "autofs4",
                "cifs",
                "cxfs",
                "devfs",
                "devpts",
                "ecryptfs",
                "fdescfs",
                "gfs",
                "gfs2",
                "kernfs",
                "linprocfs",
                "linsysfs",
                "lustre",
                "lustre_lite",
                "ncpfs",
                "nfs",
                "nfs4",
                "nfsd",
                "ocfs2",
                "proc",
                "procfs",
                "ptyfs",
                "rpc_pipefs",
                "selinuxfs",
                "smbfs",
                "sysfs",
                "tmpfs",
                "usbfs",
                "zfs",
                NULL
        };
        const gchar *ignore_devices[] = {
                "none",
                "sunrpc",
                "devpts",
                "nfsd",
                "/dev/loop",
                "/dev/vn",
                NULL
        };
        
        fs = g_unix_mount_get_fs_type (mount);
        device = g_unix_mount_get_device_path (mount);
        
        if (is_in (fs, ignore_fs))
                return TRUE;
  
        if (is_in (device, ignore_devices))
                return TRUE;

        return FALSE;
}
static gboolean
ldsm_notify_for_mount (LdsmMountInfo *mount,
                       gboolean       multiple_volumes,
                       gboolean       other_usable_volumes)
{
        gchar  *name, *program;
        gint64 free_space;
        gint response;
        gboolean has_trash;
        gboolean has_disk_analyzer;
        gboolean retval = TRUE;
        gchar *path;

        /* Don't show a dialog if one is already displayed */
        if (dialog)
                return retval;

        name = g_unix_mount_guess_name (mount->mount);
        free_space = (gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail;
        has_trash = ldsm_mount_has_trash (mount);
        path = g_strdup (g_unix_mount_get_mount_path (mount->mount));

        program = g_find_program_in_path (DISK_SPACE_ANALYZER);
        has_disk_analyzer = (program != NULL);
        g_free (program);

        dialog = msd_ldsm_dialog_new (other_usable_volumes,
                                      multiple_volumes,
                                      has_disk_analyzer,
                                      has_trash,
                                      free_space,
                                      name,
                                      path);

        g_free (name);

        g_object_ref (G_OBJECT (dialog));
        response = gtk_dialog_run (GTK_DIALOG (dialog));

	gtk_widget_destroy (GTK_WIDGET (dialog));
        dialog = NULL;

        switch (response) {
        case GTK_RESPONSE_CANCEL:
                retval = FALSE;
                break;
        case MSD_LDSM_DIALOG_RESPONSE_ANALYZE:
                retval = FALSE;
                ldsm_analyze_path (path);
                break;
        case MSD_LDSM_DIALOG_RESPONSE_EMPTY_TRASH:
                retval = TRUE;
                msd_ldsm_trash_empty ();
                break;
        case GTK_RESPONSE_NONE:
        case GTK_RESPONSE_DELETE_EVENT:
                retval = TRUE;
                break;
        default:
                g_assert_not_reached ();
        }

        g_free (path);

        return retval;
}
static gboolean
ldsm_notify_for_mount (LdsmMountInfo *mount,
                       gboolean       multiple_volumes,
                       gboolean       other_usable_volumes)
{
        gchar  *name, *program;
        gint64 free_space;
        gint response;
        gboolean has_trash;
        gboolean has_disk_analyzer;
        gboolean retval = TRUE;
        gchar *path;

        /* Don't show a notice if one is already displayed */
        if (dialog != NULL || notification != NULL)
                return retval;

        name = g_unix_mount_guess_name (mount->mount);
        free_space = (gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail;
        has_trash = ldsm_mount_has_trash (mount);
        path = g_strdup (g_unix_mount_get_mount_path (mount->mount));

        program = g_find_program_in_path (DISK_SPACE_ANALYZER);
        has_disk_analyzer = (program != NULL);
        g_free (program);

        if (server_has_actions ()) {
                char *free_space_str;
                char *summary;
                char *body;

                free_space_str = g_format_size (free_space);

                if (multiple_volumes) {
                        summary = g_strdup_printf (_("Low Disk Space on \"%s\""), name);
                        if (has_trash) {
                                body = g_strdup_printf (_("The volume \"%s\" has only %s disk space remaining.  You may free up some space by emptying the trash."),
                                                        name,
                                                        free_space_str);
                        } else {
                                body = g_strdup_printf (_("The volume \"%s\" has only %s disk space remaining."),
                                                        name,
                                                        free_space_str);
                        }
                } else {
                        summary = g_strdup (_("Low Disk Space"));
                        if (has_trash) {
                                body = g_strdup_printf (_("This computer has only %s disk space remaining.  You may free up some space by emptying the trash."),
                                                        free_space_str);
                        } else {
                                body = g_strdup_printf (_("This computer has only %s disk space remaining."),
                                                        free_space_str);
                        }
                }
                g_free (free_space_str);

                notification = notify_notification_new (summary, body, "drive-harddisk-symbolic");
                g_free (summary);
                g_free (body);

                g_signal_connect (notification,
                                  "closed",
                                  G_CALLBACK (on_notification_closed),
                                  NULL);

                notify_notification_set_app_name (notification, _("Disk space"));
                notify_notification_set_hint (notification, "transient", g_variant_new_boolean (TRUE));
                notify_notification_set_urgency (notification, NOTIFY_URGENCY_CRITICAL);
                notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
                if (has_disk_analyzer) {
                        notify_notification_add_action (notification,
                                                        "examine",
                                                        _("Examine"),
                                                        (NotifyActionCallback) examine_callback,
                                                        g_strdup (path),
                                                        g_free);
                }
                if (has_trash) {
                        notify_notification_add_action (notification,
                                                        "empty-trash",
                                                        _("Empty Trash"),
                                                        (NotifyActionCallback) empty_trash_callback,
                                                        NULL,
                                                        NULL);
                }
                notify_notification_add_action (notification,
                                                "ignore",
                                                _("Ignore"),
                                                (NotifyActionCallback) ignore_callback,
                                                NULL,
                                                NULL);
                notify_notification_set_category (notification, "device");

                if (!notify_notification_show (notification, NULL)) {
                        g_warning ("failed to send disk space notification\n");
                }

        } else {
                dialog = csd_ldsm_dialog_new (other_usable_volumes,
                                              multiple_volumes,
                                              has_disk_analyzer,
                                              has_trash,
                                              free_space,
                                              name,
                                              path);

                g_object_ref (G_OBJECT (dialog));
                response = gtk_dialog_run (GTK_DIALOG (dialog));

                gtk_widget_destroy (GTK_WIDGET (dialog));
                dialog = NULL;

                switch (response) {
                case GTK_RESPONSE_CANCEL:
                        retval = FALSE;
                        break;
                case CSD_LDSM_DIALOG_RESPONSE_ANALYZE:
                        retval = FALSE;
                        ldsm_analyze_path (path);
                        break;
                case CSD_LDSM_DIALOG_RESPONSE_EMPTY_TRASH:
                        retval = TRUE;
                        csd_ldsm_show_empty_trash ();
                        break;
                case GTK_RESPONSE_NONE:
                case GTK_RESPONSE_DELETE_EVENT:
                        retval = TRUE;
                        break;
                default:
                        g_assert_not_reached ();
                }
        }

        g_free (name);
        g_free (path);

        return retval;
}
Beispiel #10
0
static VALUE
unixmount_get_mount_path(VALUE self)
{
        return CSTR2RVAL(g_unix_mount_get_mount_path(_SELF(self)));
}
Beispiel #11
0
static RmMountEntries *rm_mount_list_open(RmMountTable *table) {
    RmMountEntries *self = g_slice_new(RmMountEntries);

    self->mnt_entries = g_unix_mounts_get(NULL);
    self->entries = NULL;
    self->current = NULL;

    for(GList *iter = self->mnt_entries; iter; iter = iter->next) {
        RmMountEntry *wrap_entry = g_slice_new(RmMountEntry);
        GUnixMountEntry *entry = iter->data;

        wrap_entry->fsname = g_strdup(g_unix_mount_get_device_path(entry));
        wrap_entry->dir = g_strdup(g_unix_mount_get_mount_path(entry));
        wrap_entry->type = g_strdup(g_unix_mount_get_fs_type(entry));

        self->entries = g_list_prepend(self->entries, wrap_entry);
    }

    RmMountEntry *wrap_entry = NULL;
    while((wrap_entry = rm_mount_list_next(self))) {
        /* bindfs mounts mirror directory trees.
        * This cannot be detected properly by rmlint since
        * files in it have the same inode as their unmirrored file, but
        * a different dev_t.
        *
        * Also ignore kernel filesystems.
        *
        * So better go and ignore it.
        */
        static struct RmEvilFs {
            /* fsname as show by `mount` */
            const char *name;

            /* Wether to warn about the exclusion on this */
            bool unusual;
        } evilfs_types[] = {{"bindfs", 1},
                            {"nullfs", 1},
                            /* Ignore the usual linux file system spam */
                            {"proc", 0},
                            {"cgroup", 0},
                            {"configfs", 0},
                            {"sys", 0},
                            {"devtmpfs", 0},
                            {"debugfs", 0},
                            {NULL, 0}};

        /* btrfs and ocfs2 filesystems support reflinks for deduplication */
        static const char *reflinkfs_types[] = {"btrfs", "ocfs2", NULL};

        const struct RmEvilFs *evilfs_found = NULL;
        for(int i = 0; evilfs_types[i].name && !evilfs_found; ++i) {
            if(strcmp(evilfs_types[i].name, wrap_entry->type) == 0) {
                evilfs_found = &evilfs_types[i];
            }
        }

        const char *reflinkfs_found = NULL;
        for(int i = 0; reflinkfs_types[i] && !reflinkfs_found; ++i) {
            if(strcmp(reflinkfs_types[i], wrap_entry->type) == 0) {
                reflinkfs_found = reflinkfs_types[i];
                break;
            }
        }

        if(evilfs_found != NULL) {
            RmStat dir_stat;
            rm_sys_stat(wrap_entry->dir, &dir_stat);
            g_hash_table_insert(table->evilfs_table,
                                GUINT_TO_POINTER(dir_stat.st_dev),
                                GUINT_TO_POINTER(1));

            GLogLevelFlags log_level = G_LOG_LEVEL_DEBUG;

            if(evilfs_found->unusual) {
                log_level = G_LOG_LEVEL_WARNING;
                rm_log_warning_prefix();
            } else {
                rm_log_debug_prefix();
            }

            g_log("rmlint", log_level,
                  _("`%s` mount detected at %s (#%u); Ignoring all files in it.\n"),
                  evilfs_found->name, wrap_entry->dir, (unsigned)dir_stat.st_dev);
        }

        rm_log_debug_line("Filesystem %s: %s", wrap_entry->dir,
                          (reflinkfs_found) ? "reflink" : "normal");

        if(reflinkfs_found != NULL) {
            RmStat dir_stat;
            rm_sys_stat(wrap_entry->dir, &dir_stat);
            g_hash_table_insert(table->reflinkfs_table,
                                GUINT_TO_POINTER(dir_stat.st_dev),
                                (gpointer)reflinkfs_found);
        }
    }

    return self;
}