core/file: Build in minimum version checks

I don't think it makes sense that callers of file_open() are expected to
check the file version after opening.  This should be something handled
by the file code so we can print a consistent error message.

Implements issue #5: Better file versioning
Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2016-03-30 11:25:14 -04:00
parent fe6f31b1e5
commit 9e0f017e61
19 changed files with 57 additions and 33 deletions

View File

@ -7,7 +7,7 @@
#include <core/idle.h>
#include <core/tempq.h>
static struct file audio_file = FILE_INIT("cur_track", 0);
static struct file audio_file = FILE_INIT("cur_track", 0, 0);
static struct track *audio_track = NULL;
static GstElement *audio_player = NULL;
static struct audio_ops *audio_ops = NULL;

View File

@ -10,7 +10,7 @@
#include <unistd.h>
static struct file c_file = FILE_INIT("library.q", 0);
static struct file c_file = FILE_INIT("library.q", 0, 0);
static struct queue c_queue;
struct scan_data {

View File

@ -78,7 +78,7 @@ void db_init(struct database *db, const char *filepath, bool autosave,
db->db_autosave = autosave;
db->db_entries = g_ptr_array_new();
db->db_keys = g_hash_table_new(g_str_hash, g_str_equal);
file_init(&db->db_file, filepath, 0);
file_init(&db->db_file, filepath, 0, 0);
}
void db_deinit(struct database *db)

View File

@ -61,11 +61,13 @@ static void __file_rename_tmp(struct file *file)
}
void file_init(struct file *file, const gchar *name, unsigned int version)
void file_init(struct file *file, const gchar *name,
unsigned int version, unsigned int min)
{
file->f_mode = OPEN_READ;
file->f_version = version;
file->f_prev = 0;
file->f_min = 0;
file->f_file = NULL;
file->f_name = name;
}
@ -117,7 +119,14 @@ static bool __file_open_read(struct file *file)
return false;
file->f_mode = OPEN_READ;
return file_readf(file, "%u\n", &file->f_prev) == 1;
if (file_readf(file, "%u\n", &file->f_prev) != 1)
return false;
if (file->f_prev < file->f_min) {
REPORT_ERROR(file->f_name, "File too old to be upgraded.");
file_close(file);
return false;
}
return true;
}
static bool __file_open_write(struct file *file)

View File

@ -8,7 +8,7 @@
#include <core/tempq.h>
#include <glib.h>
static struct file tempq_file = FILE_INIT("deck", 1);
static struct file tempq_file = FILE_INIT("deck", 1, 1);
static struct queue_ops *tempq_ops = NULL;
static GSList *tempq_list;
static struct file tempq_file;
@ -63,13 +63,10 @@ static void __tempq_init_idle(void *data)
if (!file_open(&tempq_file, OPEN_READ))
return;
if (file_version(&tempq_file) < 1)
goto out;
file_readf(&tempq_file, "%u", &num);
for (i = 0; i < num; i++)
__tempq_read_queue();
out:
file_close(&tempq_file);
}

View File

@ -5,7 +5,7 @@
#include <gui/settings.h>
static GHashTable *gui_settings = NULL;
static struct file gui_settings_file = FILE_INIT("settings", 0);
static struct file gui_settings_file = FILE_INIT("settings", 0, 0);
static void __settings_save_item(gpointer key, gpointer value, gpointer data)

View File

@ -81,7 +81,7 @@ struct database {
{ \
.db_size = 0, \
.db_autosave = autosave, \
.db_file = FILE_INIT(fname, 0), \
.db_file = FILE_INIT(fname, 0, 0), \
.db_entries = g_ptr_array_new(), \
.db_keys = g_hash_table_new(g_str_hash, g_str_equal), \
.db_ops = ops, \

View File

@ -37,21 +37,24 @@ struct file {
enum open_mode f_mode; /* The file's current open mode. */
unsigned int f_version; /* The file's current data version. */
unsigned int f_prev; /* The file's on-disk data version. */
unsigned int f_min; /* The file's minimum data version. */
FILE *f_file; /* The file's IO stream. */
const gchar *f_name; /* The file's basename. */
};
#define FILE_INIT(fname, version) \
{ \
.f_mode = OPEN_READ, \
.f_version = version, \
.f_prev = 0, .f_file = NULL, \
.f_name = fname, \
#define FILE_INIT(fname, version, min) \
{ \
.f_mode = OPEN_READ, \
.f_version = version, \
.f_prev = 0, \
.f_min = min, \
.f_file = NULL, \
.f_name = fname, \
}
/* Initialize a new file object. */
void file_init(struct file *, const gchar *, unsigned int);
void file_init(struct file *, const gchar *, unsigned int, unsigned int);
/*
* Returns the full path of the file or an empty string if filename is not set.

View File

@ -83,7 +83,7 @@ static void test_db_entry()
test_equal(ent->ie_val, 1);
test_str_equal(int_ops.dbe_key(&ent->ie_dbe), "1");
file_init(&f, "test_db_entry", 0);
file_init(&f, "test_db_entry", 0, 0);
file_open(&f, OPEN_WRITE);
int_ops.dbe_write(&f, &ent->ie_dbe);
file_close(&f);

View File

@ -26,7 +26,7 @@ static void test_entry()
i++;
} test_loop_passed();
file_init(&f, "index_entry", 0);
file_init(&f, "index_entry", 0, 0);
file_open(&f, OPEN_WRITE);
file_writef(&f, "Zelda\n0 \n");
index_ops->dbe_write(&f, &ie->ie_dbe);

View File

@ -7,7 +7,7 @@
void test_set()
{
struct file f = FILE_INIT("set", 0);
struct file f = FILE_INIT("set", 0, 0);
struct set set = SET_INIT();
unsigned int i, N = 10;
struct set_iter it;

View File

@ -17,7 +17,7 @@ void test_date()
.d_month = 0,
.d_day = 0,
};
struct file f = FILE_INIT("date", 0);
struct file f = FILE_INIT("date", 0, 0);
gchar *str;
date_today(NULL);

View File

@ -33,20 +33,20 @@ static void test_invalid_file(struct file *file)
static void test_null()
{
struct file fnull;
file_init(&fnull, NULL, 0);
file_init(&fnull, NULL, 0, 0);
test_invalid_file(&fnull);
}
static void test_empty()
{
struct file fempty;
file_init(&fempty, "", 0);
file_init(&fempty, "", 0, 0);
test_invalid_file(&fempty);
}
static void test_file()
{
struct file file = FILE_INIT("file.txt", 0);
struct file file = FILE_INIT("file.txt", 0, 0);
gchar *basepath, *filepath, *realpath;
basepath = g_strjoin("/", g_get_user_data_dir(), OCARINA_NAME, NULL);
@ -88,8 +88,8 @@ static void test_file()
static void test_io()
{
struct file fout = FILE_INIT("file.txt", 1);
struct file fin = FILE_INIT("file.txt", 0);
struct file fout = FILE_INIT("file.txt", 1, 1);
struct file fin = FILE_INIT("file.txt", 0, 0);
char *res = NULL;
unsigned int i;
@ -129,9 +129,24 @@ static void test_io()
test_equal(file_version(&fin), 0);
}
static void test_versioning()
{
struct file fout = FILE_INIT("file.txt", 0, 0);
struct file fin = FILE_INIT("file.txt", 2, 1);
test_equal(file_open(&fout, OPEN_WRITE), (bool)true);
file_writef(&fout, "abcdefghijklmnopqrstuvwxyz");
file_close(&fout);
test_equal(file_exists(&fout), (bool)true);
test_equal(file_open(&fin, OPEN_READ), (bool)false);
test_equal((void *)fin.f_file, NULL);
}
DECLARE_UNIT_TESTS(
UNIT_TEST("File (Path = NULL)", test_null),
UNIT_TEST("File (Path = \"\")", test_empty),
UNIT_TEST("File (Path = \"file.txt\")", test_file),
UNIT_TEST("File I/O", test_io),
UNIT_TEST("File Versioning", test_versioning),
);

View File

@ -31,7 +31,7 @@ static void test_album()
album = ALBUM(album_ops->dbe_alloc("1998/Hyrule Symphony"));
test_verify_hyrule(album);
file_init(&f, "album_tag", 0);
file_init(&f, "album_tag", 0, 0);
file_open(&f, OPEN_WRITE);
file_writef(&f, "0 \n");
album_ops->dbe_write(&f, &album->al_dbe);

View File

@ -30,7 +30,7 @@ static void test_artist()
artist = ARTIST(artist_ops->dbe_alloc("Koji Kondo"));
test_verify_koji(artist);
file_init(&f, "artist_tag", 0);
file_init(&f, "artist_tag", 0, 0);
file_open(&f, OPEN_WRITE);
file_writef(&f, "1 \n2 ");
artist_ops->dbe_write(&f, &artist->ar_dbe);

View File

@ -30,7 +30,7 @@ static void test_genre()
genre = GENRE(genre_ops->dbe_alloc("Video Game Music"));
test_verify_vg(genre);
file_init(&f, "genre_tag", 0);
file_init(&f, "genre_tag", 0, 0);
file_open(&f, OPEN_WRITE);
file_writef(&f, "1 \n1 ");
genre_ops->dbe_write(&f, &genre->ge_dbe);

View File

@ -36,7 +36,7 @@ static void test_library()
link->li_size = 21;
zelda->li_size = 42;
file_init(&f, "library_tag", 0);
file_init(&f, "library_tag", 0, 0);
file_open(&f, OPEN_WRITE);
library_ops->dbe_write(&f, &link->li_dbe);
file_writef(&f, "\n");

View File

@ -74,7 +74,7 @@ static void test_track()
setlocale(LC_TIME, "C");
filter_init();
tags_init();
file_init(&f, "track_tag", 0);
file_init(&f, "track_tag", 0, 0);
while (idle_run_task()) {}
date = string_tm2str(now);

View File

@ -7,7 +7,7 @@
static void test_settings()
{
struct file f = FILE_INIT("settings", 0);
struct file f = FILE_INIT("settings", 0, 0);
test_equal((void *)test_get_gui_settings(), NULL);
gui_settings_set("test.value1", 42);