core/collection: Handle -EIO errors coming from the filesystem

One of my disks frequently returns this error, and has wiped out my
track database on more than one occasion.  Let's handle this error by
disabling the library until the user tells us it is safe to use again.

Fixes #33: Handle music directories disappearing better
Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2016-03-21 08:33:14 -04:00
parent 2dd4f93bd0
commit 5979d1dcaf
4 changed files with 95 additions and 3 deletions

View File

@ -5,8 +5,10 @@
#include <core/idle.h>
#include <core/playlist.h>
#include <errno.h>
#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
static struct file c_file = FILE_INIT("library.q", 0);
@ -20,6 +22,29 @@ struct scan_data {
static void __scan_dir(void *);
#ifdef CONFIG_TESTING
bool test_collection_eio = false;
static inline int __g_access(const gchar *path, int mode)
{
if (test_collection_eio) {
errno = EIO;
return -1;
}
return g_access(path, F_OK);
}
#else
#define __g_access g_access
#endif /* CONFIG_TESTING */
static inline int __check_access(const gchar *path)
{
int ret = __g_access(path, F_OK);
if (ret == -1)
return -errno;
return ret;
}
static void __scan_dir_later(struct library *library, const gchar *dir)
{
struct scan_data *data = g_malloc(sizeof(struct scan_data));
@ -77,6 +102,7 @@ static void __validate_library(void *data)
struct library *library = data;
struct db_entry *dbe, *next;
struct track *track;
int access;
gchar *path;
db_for_each(dbe, next, track_db_get()) {
@ -84,12 +110,18 @@ static void __validate_library(void *data)
if (track->tr_library != library)
continue;
path = track_path(track);
if (g_file_test(path, G_FILE_TEST_EXISTS) == false) {
path = track_path(track);
access = __check_access(path);
g_free(path);
if (access == -EIO) {
if (collection_check_library(library) == -EIO)
return;
}
if (access < 0) {
queue_remove_all(&c_queue, track);
track_remove(track);
}
g_free(path);
}
}
@ -223,6 +255,8 @@ void collection_set_enabled(struct library *library, bool enabled)
if (!library || (library->li_enabled == enabled))
return;
if (enabled && (collection_check_library(library) == -EIO))
return;
library_set_enabled(library, enabled);
queue_set_flag(&c_queue, Q_ADD_FRONT);
@ -242,6 +276,16 @@ void collection_set_enabled(struct library *library, bool enabled)
queue_resort(&c_queue);
}
int collection_check_library(struct library *library)
{
int ret = __check_access(library->li_path);
if (ret == -EIO) {
g_warning("Can't access library at %s/\n", library->li_path);
collection_set_enabled(library, false);
}
return ret;
}
struct queue *collection_get_queue()
{
return &c_queue;

View File

@ -7,6 +7,7 @@
*/
#include <core/audio.h>
extern "C" {
#include <core/collection.h>
#include <core/string.h>
}
#include <gui/ocarina.h>
@ -154,6 +155,7 @@ static void parse_gst_error(GstMessage *error)
if (track) {
path = track_path(track);
g_print("Error playing file: %s\n", path);
collection_check_library(track->tr_library);
g_free(path);
}
g_print("Error: %s\n", err->message);

View File

@ -48,7 +48,15 @@ bool collection_unban(struct track *);
/* Called to enable or disable a library directory. */
void collection_set_enabled(struct library *, bool);
/* Called to check if the library path is good. */
int collection_check_library(struct library *);
/* Called to access the collection queue. */
struct queue *collection_get_queue();
#ifdef CONFIG_TESTING
extern bool test_collection_eio;
#endif /* CONFIG_TESTING */
#endif /* OCARINA_CORE_COLLECTION_H */

View File

@ -7,6 +7,7 @@
#include <core/playlist.h>
#include <core/tags/tags.h>
#include <tests/test.h>
#include <errno.h>
static void test_init()
{
@ -225,6 +226,42 @@ static void test_save_load()
test_equal(GPOINTER_TO_INT(list->data), COMPARE_GENRE);
}
static void test_eio()
{
struct queue *q = collection_get_queue();
struct library *lib = library_get(0);
test_equal(queue_size(q), 48);
test_equal(collection_check_library(lib), 0);
test_equal(lib->li_enabled, (bool)true);
test_equal(queue_size(q), 48);
test_collection_eio = true;
test_equal(collection_check_library(lib), -EIO);
test_equal(lib->li_enabled, (bool)false);
test_equal(queue_size(q), 0);
collection_set_enabled(lib, true);
test_equal(lib->li_enabled, (bool)false);
test_equal(queue_size(q), 0);
test_collection_eio = false;
collection_set_enabled(lib, true);
test_equal(lib->li_enabled, (bool)true);
test_equal(queue_size(q), 48);
test_collection_eio = true;
collection_update_all();
test_equal(idle_run_task(), (bool)true);
test_equal(lib->li_enabled, (bool)false);
test_equal(queue_size(q), 0);
test_collection_eio = false;
collection_set_enabled(lib, true);
test_equal(lib->li_enabled, (bool)true);
test_equal(queue_size(q), 48);
}
static void test_remove()
{
struct queue *q = collection_get_queue();
@ -250,5 +287,6 @@ DECLARE_UNIT_TESTS(
UNIT_TEST("Collection Ban and Unban", test_ban),
UNIT_TEST("Collection Enable and Disable", test_enable),
UNIT_TEST("Collection Save and Load", test_save_load),
UNIT_TEST("Colleciton -EIO Handling", test_eio),
UNIT_TEST("Collection Remove Path", test_remove),
);