diff --git a/boltd/bolt-manager.c b/boltd/bolt-manager.c index ea4a605f2711977f84338039d4ed552ab7062edb..6fc88e79142f56ef7f967f57d93d192f52a6bff8 100644 --- a/boltd/bolt-manager.c +++ b/boltd/bolt-manager.c @@ -53,6 +53,11 @@ static gboolean bolt_manager_initialize (GInitable *initable, GCancellable *cancellable, GError **error); +static gboolean bolt_manager_store_init (BoltManager *mgr, + GError **error); + +static void bolt_manager_store_upgrade (BoltManager *mgr); + /* internal manager functions */ static void manager_sd_notify_status (BoltManager *mgr); @@ -352,7 +357,6 @@ static void bolt_manager_init (BoltManager *mgr) { mgr->devices = g_ptr_array_new_with_free_func (g_object_unref); - mgr->store = bolt_store_new (bolt_get_store_path ()); mgr->probing_roots = g_ptr_array_new_with_free_func (g_free); mgr->probing_tsettle = PROBING_SETTLE_TIME_MS; /* milliseconds */ @@ -362,14 +366,6 @@ bolt_manager_init (BoltManager *mgr) /* default configuration */ mgr->policy = BOLT_POLICY_AUTO; mgr->authmode = BOLT_AUTH_ENABLED; - - g_signal_connect_object (mgr->store, "device-added", - G_CALLBACK (handle_store_device_added), - mgr, 0); - - g_signal_connect_object (mgr->store, "device-removed", - G_CALLBACK (handle_store_device_removed), - mgr, 0); } static void @@ -487,6 +483,11 @@ bolt_manager_initialize (GInitable *initable, mgr = BOLT_MANAGER (initable); + /* store setup */ + ok = bolt_manager_store_init (mgr, error); + if (!ok) + return FALSE; + /* load dynamic user configuration */ manager_load_user_config (mgr); @@ -574,11 +575,64 @@ bolt_manager_initialize (GInitable *initable, udev_enumerate_unref (enumerate); + /* upgrade the store, if needed */ + bolt_manager_store_upgrade (mgr); + manager_sd_notify_status (mgr); return TRUE; } +static gboolean +bolt_manager_store_init (BoltManager *mgr, GError **error) +{ + bolt_info (LOG_TOPIC ("manager"), "initializing store"); + + mgr->store = bolt_store_new (bolt_get_store_path (), error); + if (mgr->store == NULL) + return FALSE; + + g_signal_connect_object (mgr->store, "device-added", + G_CALLBACK (handle_store_device_added), + mgr, 0); + + g_signal_connect_object (mgr->store, "device-removed", + G_CALLBACK (handle_store_device_removed), + mgr, 0); + + return TRUE; +} + +static void +bolt_manager_store_upgrade (BoltManager *mgr) +{ + g_autoptr(GError) err = NULL; + BoltStore *store = mgr->store; + guint ver; + gboolean ok; + + ver = bolt_store_get_version (store); + + if (ver == BOLT_STORE_VERSION) + { + bolt_debug (LOG_TOPIC ("store"), "store is up to date"); + return; + } + + bolt_info (LOG_TOPIC ("store"), "attempting upgrade from '%d'", + ver); + + ok = bolt_store_upgrade (store, NULL, &err); + if (!ok) + { + bolt_warn_err (err, LOG_TOPIC ("store"), "upgrade failed"); + return; + } + + bolt_info (LOG_TOPIC ("store"), "upgraded to version '%d'", + ver); +} + /* internal functions */ static void manager_sd_notify_status (BoltManager *mgr) diff --git a/boltd/bolt-store.c b/boltd/bolt-store.c index 0e857832a9420fea0e3b0f47f8283f804c552ccd..f771dcad623ab5138f2185d43260684190cdc5c3 100644 --- a/boltd/bolt-store.c +++ b/boltd/bolt-store.c @@ -34,6 +34,16 @@ /* ************************************ */ /* BoltStore */ +static void bolt_store_initable_iface_init (GInitableIface *iface); + + +static gboolean bolt_store_initialize (GInitable *initable, + GCancellable *cancellable, + GError **error); + +static gboolean bolt_store_init_store (DIR *root, + GError **error); + struct _BoltStore { GObject object; @@ -43,6 +53,8 @@ struct _BoltStore GFile *devices; GFile *keys; GFile *times; + + guint version; }; @@ -50,6 +62,7 @@ enum { PROP_STORE_0, PROP_ROOT, + PROP_VERSION, PROP_STORE_LAST }; @@ -66,9 +79,11 @@ enum { static guint signals[SIGNAL_LAST] = {0}; -G_DEFINE_TYPE (BoltStore, - bolt_store, - G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_CODE (BoltStore, + bolt_store, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + bolt_store_initable_iface_init)); static void @@ -104,6 +119,10 @@ bolt_store_get_property (GObject *object, g_value_set_object (value, store->root); break; + case PROP_VERSION: + g_value_set_uint (value, store->version); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -165,6 +184,13 @@ bolt_store_class_init (BoltStoreClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME); + store_props[PROP_VERSION] = + g_param_spec_uint ("version", + NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, PROP_STORE_LAST, store_props); @@ -190,6 +216,44 @@ bolt_store_class_init (BoltStoreClass *klass) 1, G_TYPE_STRING); } +static void +bolt_store_initable_iface_init (GInitableIface *iface) +{ + iface->init = bolt_store_initialize; +} + +static gboolean +bolt_store_initialize (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GError) err = NULL; + g_autoptr(DIR) root = NULL; + g_autofree char *path = NULL; + BoltStore *store = BOLT_STORE (initable); + gboolean ok; + + path = g_file_get_path (store->root); + root = bolt_opendir (path, error); + + if (root == NULL) + return FALSE; + + ok = bolt_store_init_store (root, error); + if (!ok) + return FALSE; + + ok = bolt_read_uint_at (dirfd (root), + "version", + &store->version, + &err); + + if (!ok && !bolt_err_notfound (err)) + return bolt_error_propagate (error, &err); + + return TRUE; +} + /* internal methods */ #define DOMAIN_GROUP "domain" #define DEVICE_GROUP "device" @@ -197,22 +261,61 @@ bolt_store_class_init (BoltStoreClass *klass) #define CFG_FILE "boltd.conf" +static gboolean +bolt_store_init_store (DIR *root, + GError **error) +{ + gboolean empty; + gboolean ok; + + /* initialize an empty store with the basic layout, + * which currently is just a 'version' field, since + * all other directories are created on-demand */ + + ok = bolt_dir_is_empty (root, &empty, error); + if (!ok) + return FALSE; + + bolt_debug (LOG_TOPIC ("store"), "needs init: %s", + bolt_yesno (empty)); + + if (!empty) + return TRUE; + + bolt_info (LOG_TOPIC ("store"), "initializing"); + + /* store is empty, create the 'version' file */ + ok = bolt_write_uint_at (dirfd (root), + "version", + BOLT_STORE_VERSION, + error); + return ok; +} + /* public methods */ BoltStore * -bolt_store_new (const char *path) +bolt_store_new (const char *path, GError **error) { g_autoptr(GFile) root = NULL; BoltStore *store; root = g_file_new_for_path (path); - store = g_object_new (BOLT_TYPE_STORE, - "root", root, - NULL); - + store = g_initable_new (BOLT_TYPE_STORE, + NULL, error, + "root", root, + NULL); return store; } +guint +bolt_store_get_version (BoltStore *store) +{ + g_return_val_if_fail (BOLT_IS_STORE (store), 0); + + return store->version; +} + GKeyFile * bolt_store_config_load (BoltStore *store, GError **error) @@ -1166,3 +1269,56 @@ bolt_store_has_journal (BoltStore *store, return g_file_query_exists (journal, NULL); } + +gboolean +bolt_store_upgrade (BoltStore *store, + gboolean *upgrade, + GError **error) +{ + g_autoptr(DIR) root = NULL; + g_autofree char *path = NULL; + gboolean need_upgrade; + gboolean ok; + + g_return_val_if_fail (BOLT_IS_STORE (store), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + need_upgrade = store->version != BOLT_STORE_VERSION; + + if (upgrade) + *upgrade = need_upgrade; + + if (!need_upgrade) + return TRUE; + + path = g_file_get_path (store->root); + root = bolt_opendir (path, error); + + if (!root) + return FALSE; + + ok = bolt_write_int_at (dirfd (root), + ".version-upgrade", + BOLT_STORE_VERSION, + error); + if (!ok) + return FALSE; + + ok = bolt_renameat (dirfd (root), + ".version-upgrade", + dirfd (root), + "version", + error); + + if (!ok) + { + bolt_unlink_at (dirfd (root), ".version-upgrade", 0, NULL); + return FALSE; + } + + store->version = BOLT_STORE_VERSION; + g_object_notify_by_pspec (G_OBJECT (store), + store_props[PROP_VERSION]); + + return ok; +} diff --git a/boltd/bolt-store.h b/boltd/bolt-store.h index e9c6504d8f4a851cd257d10cae1beb3d270b8258..1cb6d4618804aa48097f04750785620c01d77821 100644 --- a/boltd/bolt-store.h +++ b/boltd/bolt-store.h @@ -30,11 +30,16 @@ G_BEGIN_DECLS +#define BOLT_STORE_VERSION 1 + /* BoltStore - database for devices, keys */ #define BOLT_TYPE_STORE bolt_store_get_type () G_DECLARE_FINAL_TYPE (BoltStore, bolt_store, BOLT, STORE, GObject); -BoltStore * bolt_store_new (const char *path); +BoltStore * bolt_store_new (const char *path, + GError **error); + +guint bolt_store_get_version (BoltStore *store); GKeyFile * bolt_store_config_load (BoltStore *store, GError **error); @@ -139,4 +144,9 @@ gboolean bolt_store_has_journal (BoltStore *store, const char *type, const char *name); +/* store upgrades */ +gboolean bolt_store_upgrade (BoltStore *store, + gboolean *upgrade, + GError **error); + G_END_DECLS diff --git a/common/bolt-io.c b/common/bolt-io.c index 18d9f6f930f7590585bc69b42f035ee2f26a5281..063dd57d52c3b42bd9f171374d18c7d01fba2c72 100644 --- a/common/bolt-io.c +++ b/common/bolt-io.c @@ -51,6 +51,11 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (FILE, fclose); +/* standard open flags for overwriting a file, i.e. close on + * exec (3), open in write only mode, truncate before writing, + * and create it if it does not yet exist */ +#define BOLT_O_OVERWRITE (O_CLOEXEC | O_WRONLY | O_TRUNC | O_CREAT) + int bolt_open (const char *path, int flags, int mode, GError **error) { @@ -318,6 +323,34 @@ bolt_closedir (DIR *d, } +gboolean +bolt_mkdirat (int dirfd, + const char *name, + mode_t mode, + GError **error) +{ + int r = 0; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + r = mkdirat (dirfd, name, mode); + + if (r < 0) + { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + "failed to create directory '%s': %s", + name, + g_strerror (errno)); + return FALSE; + } + + return TRUE; +} + + gboolean bolt_rmdir (const char *name, GError **error) @@ -396,6 +429,35 @@ bolt_unlink_at (int dirfd, return TRUE; } +gboolean +bolt_write_file_at (int dirfd, + const char *name, + const char *data, + gssize len, + GError **error) +{ + int fd; + gboolean ok; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + fd = bolt_openat (dirfd, name, BOLT_O_OVERWRITE, 0666, error); + + if (fd < 0) + return FALSE; + + ok = bolt_write_all (fd, data, len, error); + + if (!ok) + (void) close (fd); + else + ok = bolt_close (fd, error); + + return ok; +} + char * bolt_read_value_at (int dirfd, const char *name, @@ -499,6 +561,25 @@ retry: return n > 0; } +gboolean +bolt_write_int_at (int dirfd, + const char *name, + gint val, + GError **error) +{ + char buf[256] = {0, }; + gsize n; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + n = g_snprintf (buf, sizeof (buf), "%d", val); + + g_assert (n < sizeof (buf)); + + return bolt_write_file_at (dirfd, name, buf, n, error); +} + gboolean bolt_read_int_at (int dirfd, const char *name, @@ -518,6 +599,44 @@ bolt_read_int_at (int dirfd, return bolt_str_parse_as_int (str, val, error); } +gboolean +bolt_write_uint_at (int dirfd, + const char *name, + guint val, + GError **error) +{ + char buf[256] = {0, }; + gsize n; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + n = g_snprintf (buf, sizeof (buf), "%u", val); + + g_assert (n < sizeof (buf)); + + return bolt_write_file_at (dirfd, name, buf, n, error); +} + +gboolean +bolt_read_uint_at (int dirfd, + const char *name, + guint *val, + GError **error) +{ + g_autofree char *str = NULL; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + str = bolt_read_value_at (dirfd, name, error); + + if (str == NULL) + return FALSE; + + return bolt_str_parse_as_uint (str, val, error); +} + gboolean bolt_verify_uid (int dirfd, const char *want, @@ -563,26 +682,11 @@ bolt_file_write_all (const char *fn, gssize n, GError **error) { - int fd = -1; - gboolean ok; - g_return_val_if_fail (fn != NULL, FALSE); g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - fd = bolt_open (fn, O_CLOEXEC | O_WRONLY | O_TRUNC | O_CREAT, 0666, error); - - if (fd < 0) - return FALSE; - - ok = bolt_write_all (fd, data, n, error); - - if (ok) - ok = bolt_close (fd, error); - else - (void) close (fd); - - return ok; + return bolt_write_file_at (AT_FDCWD, fn, data, n, error); } gboolean @@ -791,6 +895,34 @@ bolt_rename (const char *from, return FALSE; } +gboolean +bolt_renameat (int from_dir, + const char *from, + int to_dir, + const char *to, + GError **error) +{ + int code; + int r; + + g_return_val_if_fail (from != NULL, FALSE); + g_return_val_if_fail (to != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + r = renameat (from_dir, from, to_dir, to); + + if (r == 0) + return TRUE; + + code = errno; + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (code), + "could not rename '%s' to '%s': %s", + from, to, g_strerror (code)); + + return FALSE; +} + #if !HAVE_FN_COPY_FILE_RANGE static loff_t copy_file_range (int fd_in, @@ -848,6 +980,50 @@ bolt_copy_bytes (int fd_from, return len == 0; } +gboolean +bolt_dir_is_empty (DIR *dir, + gboolean *empty, + GError **error) +{ + struct dirent *de = NULL; + long n; + + g_return_val_if_fail (dir != NULL, FALSE); + g_return_val_if_fail (empty != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + *empty = TRUE; + + n = telldir (dir); + if (n == -1) + { + int code = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (code), + "telldir(3) failed: %s", + g_strerror (code)); + + return FALSE; + } + + rewinddir (dir); + + while ((de = readdir (dir)) != NULL) + { + + if (!g_strcmp0 (de->d_name, ".") || + !g_strcmp0 (de->d_name, "..")) + continue; + + *empty = FALSE; + break; + } + + seekdir (dir, n); + + return TRUE; +} /* auto cleanup helpers */ void diff --git a/common/bolt-io.h b/common/bolt-io.h index 669997b1e8cf411093bd6ce77e1686b4099ff964..d8565820f3cfe4014246dfb70c5f016a414fdd91 100644 --- a/common/bolt-io.h +++ b/common/bolt-io.h @@ -70,6 +70,11 @@ DIR * bolt_opendir_at (int dirfd, gboolean bolt_closedir (DIR *d, GError **error); +gboolean bolt_mkdirat (int dirfd, + const char *name, + mode_t mode, + GError **error); + gboolean bolt_rmdir (const char *name, GError **error); @@ -87,6 +92,12 @@ gboolean bolt_unlink_at (int dirfd, int flag, GError **error); +gboolean bolt_write_file_at (int dirfd, + const char *name, + const char *data, + gssize len, + GError **error); + char * bolt_read_value_at (int dirfd, const char *name, GError **error); @@ -96,11 +107,26 @@ gboolean bolt_write_char_at (int dirfd, char value, GError **error); +gboolean bolt_write_int_at (int dirfd, + const char *name, + gint val, + GError **error); + gboolean bolt_read_int_at (int dirfd, const char *name, gint *val, GError **error); +gboolean bolt_write_uint_at (int dirfd, + const char *name, + guint val, + GError **error); + +gboolean bolt_read_uint_at (int dirfd, + const char *name, + guint *val, + GError **error); + gboolean bolt_verify_uid (int dirfd, const char *uid, GError **error); @@ -141,11 +167,21 @@ gboolean bolt_rename (const char *from, const char *to, GError **error); +gboolean bolt_renameat (int from_dir, + const char *from, + int to_dir, + const char *to, + GError **error); + gboolean bolt_copy_bytes (int fd_from, int fd_to, size_t len, GError **error); +gboolean bolt_dir_is_empty (DIR *dir, + gboolean *empty, + GError **error); + /* auto cleanup for I/O handles */ void bolt_cleanup_close_intpr (int *fd); #define bolt_autoclose bolt_cleanup (bolt_cleanup_close_intpr) diff --git a/tests/test-common.c b/tests/test-common.c index a6d0f080237d4878688072826356e8b49322c387..2134c4921e8c728fe11700ba963514c879187414 100644 --- a/tests/test-common.c +++ b/tests/test-common.c @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,57 @@ typedef struct int dummy; } TestDummy; +/* test tables for string to number conversions */ +struct +{ + const char *str; + gint val; + gboolean error; +} str_to_int_table[] = { + {"0", 0, FALSE}, + {"1", 1, FALSE}, + {"-1", -1, FALSE}, +#if __SIZEOF_INT__ == 4 + {"2147483647", 2147483647, FALSE}, /* MAX_INT */ + {"-2147483648", -2147483648, FALSE}, /* MIN_INT */ + {"2147483648", 0, TRUE}, /* MAX_INT + 1 */ + {"-2147483649", 0, TRUE}, /* MIN_INT - 1 */ +#elif __SIZEOF_INT__ == 8 + {"9223372036854775807", 9223372036854775807, FALSE}, /* MAX_INT */ + {"-9223372036854775808", -9223372036854775808, FALSE}, /* MIN_INT */ + {"9223372036854775808", 0, TRUE}, /* MAX_INT + 1 */ + {"-9223372036854775809", 0, TRUE}, /* MIN_INT - 1 */ +#else +#warning __SIZEOF_INT__ not handled +#endif + {"notanint", 0, TRUE}, + {"9223372036854775808", 0, TRUE}, /* overflow */ + {"-9223372036854775809", 0, TRUE}, /* underflow */ +}; + +struct +{ + const char *str; + guint val; + gboolean error; +} str_to_uint_table[] = { + {"0", 0, FALSE}, + {"1", 1, FALSE}, + {"-1", 0, TRUE}, /* negative */ +#if __SIZEOF_INT__ == 4 + {"4294967295", 4294967295, FALSE}, /* MAX_UINT */ + {"4294967296", 0, TRUE}, /* MAX_UINT + 1 */ +#elif __SIZEOF_INT__ == 8 + {"18446744073709551615", 18446744073709551615, FALSE}, /* MAX_INT */ + {"18446744073709551616", 0, TRUE}, /* MAX_INT + 1 */ +#else +#warning __SIZEOF_INT__ not handled +#endif + {"notanint", 0, TRUE}, + {"18446744073709551617", 0, TRUE}, /* overflow */ +}; + + #define TEST_DBUS_GRESOURCE_PATH "/bolt/tests/exported/example.bolt.xml" #define TEST_DBUS_INTERFACE "org.gnome.bolt.Example" @@ -672,6 +724,7 @@ test_io_errors (TestIO *tt, gconstpointer user_data) struct stat st; char buffer[256] = {0, }; gboolean ok; + unsigned int uiv; int fd = -1; int iv; int r; @@ -828,6 +881,17 @@ test_io_errors (TestIO *tt, gconstpointer user_data) g_assert_false (ok); g_clear_pointer (&err, g_error_free); + /* read_uint_at */ + ok = bolt_read_uint_at (dirfd (root), "NONEXISTENT", &uiv, &err); + g_assert_error (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_false (ok); + g_clear_pointer (&err, g_error_free); + + ok = bolt_read_uint_at (dirfd (root), "readonly", &uiv, &err); + g_assert_error (err, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_false (ok); + g_clear_pointer (&err, g_error_free); + /* pipe error checking */ fd = bolt_mkfifo (tt->path, 0600, &err); g_assert_error (err, G_IO_ERROR, G_IO_ERROR_EXISTS); @@ -878,6 +942,34 @@ test_io_errors (TestIO *tt, gconstpointer user_data) g_clear_pointer (&err, g_error_free); } +static void +test_io_mkdirat (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(DIR) d = NULL; + struct stat st; + gboolean ok; + + d = bolt_opendir (tt->path, &error); + + g_assert_no_error (error); + g_assert_nonnull (d); + + ok = bolt_mkdirat (dirfd (d), "directory", 0666, &error); + g_assert_no_error (error); + g_assert_true (ok); + + ok = bolt_fstatat (dirfd (d), "directory", &st, 0, &error); + g_assert_no_error (error); + g_assert_true (ok); + + g_assert_true (S_ISDIR (st.st_mode)); + + ok = bolt_mkdirat (dirfd (d), "directory", 0666, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_assert_false (ok); +} + static void test_io_verify (TestIO *tt, gconstpointer user_data) { @@ -932,6 +1024,179 @@ test_io_verify (TestIO *tt, gconstpointer user_data) unlinkat (dirfd (d), "unique_id", 0); } +static void +test_io_write_file_at (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(DIR) dir = NULL; + g_autofree char *path = NULL; + g_autofree char *data = NULL; + static const char *ref = "The world is everything that is the case."; + gboolean ok; + gsize len; + + dir = bolt_opendir (tt->path, &error); + g_assert_no_error (error); + g_assert_nonnull (dir); + + ok = bolt_write_file_at (dirfd (dir), "test.txt", ref, -1, &error); + + g_assert_no_error (error); + g_assert_true (ok); + + path = g_build_filename (tt->path, "test.txt", NULL); + ok = g_file_get_contents (path, &data, &len, &error); + g_assert_no_error (error); + g_assert_true (ok); + g_assert_cmpuint (strlen (ref), ==, len); + g_assert_cmpstr (ref, ==, data); + + g_clear_pointer (&data, g_free); + ok = bolt_file_write_all (path, ref, 5, &error); + + g_assert_no_error (error); + g_assert_true (ok); + + ok = g_file_get_contents (path, &data, &len, &error); + g_assert_no_error (error); + g_assert_true (ok); + g_assert_cmpuint (len, ==, 5); + g_assert_true (strncmp (data, ref, 5) == 0); +} + +static void +test_io_write_int_at (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(DIR) dir = NULL; + gboolean ok; + int tests[] = {0, 1, 42, G_MAXINT}; + + dir = bolt_opendir (tt->path, &error); + g_assert_no_error (error); + g_assert_nonnull (dir); + + for (gsize i = 0; i < G_N_ELEMENTS (tests); i++) + { + g_autoptr(GError) err = NULL; + int ref = tests[i]; + int val; + + ok = bolt_write_int_at (dirfd (dir), "int.txt", ref, &err); + g_assert_true (ok); + + ok = bolt_read_int_at (dirfd (dir), "int.txt", &val, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_cmpint (val, ==, ref); + } +} + +static void +test_io_read_int_at (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(DIR) dir = NULL; + gboolean ok; + + dir = bolt_opendir (tt->path, &error); + g_assert_no_error (error); + g_assert_nonnull (dir); + + for (gsize i = 0; i < G_N_ELEMENTS (str_to_int_table); i++) + { + g_autoptr(GError) err = NULL; + const char *txt = str_to_int_table[i].str; + gboolean expect_error = str_to_int_table[i].error; + gint val = str_to_int_table[i].val; + gint v; + + ok = bolt_write_file_at (dirfd (dir), "int.txt", txt, -1, &err); + g_assert_true (ok); + + ok = bolt_read_int_at (dirfd (dir), "int.txt", &v, &err); + if (expect_error) + { + g_assert_nonnull (err); + g_assert_false (ok); + } + else + { + g_assert_no_error (err); + g_assert_cmpint (val, ==, v); + g_assert_true (ok); + } + } +} + +static void +test_io_write_uint_at (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(DIR) dir = NULL; + gboolean ok; + unsigned int tests[] = {42, 0, 1, G_MAXUINT}; + + dir = bolt_opendir (tt->path, &error); + g_assert_no_error (error); + g_assert_nonnull (dir); + + for (gsize i = 0; i < G_N_ELEMENTS (tests); i++) + { + g_autoptr(GError) err = NULL; + unsigned int ref = tests[i]; + unsigned int val; + + ok = bolt_write_uint_at (dirfd (dir), "uint.txt", ref, &err); + g_assert_true (ok); + + ok = bolt_read_uint_at (dirfd (dir), "uint.txt", &val, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_cmpint (val, ==, ref); + } +} + +static void +test_io_read_uint_at (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(DIR) dir = NULL; + gboolean ok; + + dir = bolt_opendir (tt->path, &error); + g_assert_no_error (error); + g_assert_nonnull (dir); + + for (gsize i = 0; i < G_N_ELEMENTS (str_to_uint_table); i++) + { + g_autoptr(GError) err = NULL; + const char *txt = str_to_uint_table[i].str; + gboolean expect_error = str_to_uint_table[i].error; + guint val = str_to_uint_table[i].val; + guint v; + + if (g_test_verbose ()) + g_test_message ("bolt_read_uint: '%s'", txt); + + ok = bolt_write_file_at (dirfd (dir), "uint.txt", txt, -1, &err); + g_assert_true (ok); + + ok = bolt_read_uint_at (dirfd (dir), "uint.txt", &v, &err); + if (expect_error) + { + g_assert_nonnull (err); + g_assert_false (ok); + } + else + { + g_assert_no_error (err); + g_assert_cmpuint (val, ==, v); + g_assert_true (ok); + } + } +} + static void test_io_file_write_all (TestIO *tt, gconstpointer user_data) { @@ -967,6 +1232,67 @@ test_io_file_write_all (TestIO *tt, gconstpointer user_data) g_assert_true (strncmp (data, ref, 5) == 0); } +static void +test_io_renameat (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) err = NULL; + g_autoptr(DIR) root = NULL; + g_autoptr(DIR) subdir = NULL; + struct stat st; + gboolean ok; + + root = bolt_opendir (tt->path, &err); + g_assert_no_error (err); + g_assert_nonnull (root); + + ok = bolt_write_file_at (dirfd (root), "a", "a", -1, &err); + g_assert_no_error (err); + g_assert_true (ok); + + ok = bolt_mkdirat (dirfd (root), "subdir", 0777, &err); + g_assert_no_error (err); + g_assert_true (ok); + + subdir = bolt_opendir_at (dirfd (root), "subdir", O_RDONLY, &err); + g_assert_no_error (err); + g_assert_nonnull (subdir); + + ok = bolt_renameat (dirfd (root), "a", + dirfd (subdir), "b", + &err); + + g_assert_no_error (err); + g_assert_true (ok); + + ok = bolt_fstatat (dirfd (subdir), "b", &st, 0, &err); + g_assert_no_error (err); + g_assert_true (ok); + + g_assert_true (S_ISREG (st.st_mode)); + + ok = bolt_renameat (dirfd (subdir), "b", + dirfd (subdir), "c", + &err); + + g_assert_no_error (err); + g_assert_true (ok); + + memset (&st, 0, sizeof (st)); + ok = bolt_fstatat (dirfd (subdir), "c", &st, 0, &err); + g_assert_no_error (err); + g_assert_true (ok); + + g_assert_true (S_ISREG (st.st_mode)); + + /* error reporting: file not found */ + ok = bolt_renameat (dirfd (subdir), "b", + dirfd (subdir), "c", + &err); + + g_assert_error (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_false (ok); +} + static void test_io_copy_bytes (TestIO *tt, gconstpointer user_data) { @@ -1056,6 +1382,72 @@ test_io_copy_bytes (TestIO *tt, gconstpointer user_data) chksum); } +static void +test_io_dir_is_empty (TestIO *tt, gconstpointer user_data) +{ + g_autoptr(GError) err = NULL; + g_autoptr(DIR) root = NULL; + struct dirent *de = NULL; + gboolean empty; + gboolean ok; + long pos; + + root = bolt_opendir (tt->path, &err); + g_assert_no_error (err); + g_assert_nonnull (root); + + empty = FALSE; + ok = bolt_dir_is_empty (root, &empty, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_true (empty); + + ok = bolt_write_file_at (dirfd (root), "a", "a", -1, &err); + g_assert_no_error (err); + g_assert_true (ok); + + empty = TRUE; + ok = bolt_dir_is_empty (root, &empty, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_false (empty); + + /* check that we don't mess with the dir pointer offset, + * by creating a second file, iterating exactly once + * and then making sure we are at the same position + * after the call to bolt_dir_is_empty */ + + ok = bolt_write_file_at (dirfd (root), "b", "b", -1, &err); + g_assert_no_error (err); + g_assert_true (ok); + + empty = TRUE; + ok = bolt_dir_is_empty (root, &empty, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_false (empty); + + while ((de = readdir (root)) != NULL) + { + + if (!g_strcmp0 (de->d_name, ".") || + !g_strcmp0 (de->d_name, "..")) + continue; + + break; + } + + pos = telldir (root); + + empty = TRUE; + ok = bolt_dir_is_empty (root, &empty, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_false (empty); + + g_assert_cmpint (telldir (root), ==, pos); +} + static void test_autoclose (TestIO *tt, gconstpointer user_data) { @@ -1327,43 +1719,19 @@ test_str_erase (TestRng *tt, gconstpointer user_data) static void test_str_parse_int (TestRng *tt, gconstpointer user_data) { - struct - { - const char *str; - gint val; - gboolean error; - } table[] = { - {"0", 0, FALSE}, - {"1", 1, FALSE}, - {"-1", -1, FALSE}, -#if __SIZEOF_INT__ == 4 - {"2147483647", 2147483647, FALSE}, /* MAX_INT */ - {"-2147483648", -2147483648, FALSE}, /* MIN_INT */ - {"2147483648", 0, TRUE}, /* MAX_INT + 1 */ - {"-2147483649", 0, TRUE}, /* MIN_INT - 1 */ -#elif __SIZEOF_INT__ == 8 - {"9223372036854775807", 9223372036854775807, FALSE}, /* MAX_INT */ - {"-9223372036854775808", -9223372036854775808, FALSE}, /* MIN_INT */ - {"9223372036854775808", 0, TRUE}, /* MAX_INT + 1 */ - {"-9223372036854775809", 0, TRUE}, /* MIN_INT - 1 */ -#else - #warning __SIZEOF_INT__ not handled -#endif - {"notanint", 0, TRUE}, - {"9223372036854775808", 0, TRUE}, /* overflow */ - {"-9223372036854775809", 0, TRUE}, /* underflow */ - }; - - for (gsize i = 0; i < G_N_ELEMENTS (table); i++) + for (gsize i = 0; i < G_N_ELEMENTS (str_to_int_table); i++) { g_autoptr(GError) error = NULL; + const char *txt = str_to_int_table[i].str; + gboolean expect_error = str_to_int_table[i].error; + gint val = str_to_int_table[i].val; gboolean ok; gint v; errno = 0; - ok = bolt_str_parse_as_int (table[i].str, &v, &error); + ok = bolt_str_parse_as_int (txt, &v, &error); - if (table[i].error) + if (expect_error) { int err = errno; @@ -1373,7 +1741,7 @@ test_str_parse_int (TestRng *tt, gconstpointer user_data) } else { - g_assert_cmpint (table[i].val, ==, v); + g_assert_cmpint (val, ==, v); g_assert_true (ok); } } @@ -1382,42 +1750,23 @@ test_str_parse_int (TestRng *tt, gconstpointer user_data) static void test_str_parse_uint (TestRng *tt, gconstpointer user_data) { - struct - { - const char *str; - guint val; - gboolean error; - } table[] = { - {"0", 0, FALSE}, - {"1", 1, FALSE}, - {"-1", 0, TRUE}, /* negative */ -#if __SIZEOF_INT__ == 4 - {"4294967295", 4294967295, FALSE}, /* MAX_UINT */ - {"4294967296", 0, TRUE}, /* MAX_UINT + 1 */ -#elif __SIZEOF_INT__ == 8 - {"18446744073709551615", 18446744073709551615, FALSE}, /* MAX_INT */ - {"18446744073709551616", 0, TRUE}, /* MAX_INT + 1 */ -#else - #warning __SIZEOF_INT__ not handled -#endif - {"notanint", 0, TRUE}, - {"18446744073709551617", 0, TRUE}, /* overflow */ - }; - - for (gsize i = 0; i < G_N_ELEMENTS (table); i++) + for (gsize i = 0; i < G_N_ELEMENTS (str_to_uint_table); i++) { g_autoptr(GError) error = NULL; + const char *txt = str_to_uint_table[i].str; + gboolean expect_error = str_to_uint_table[i].error; + guint val = str_to_uint_table[i].val; gboolean ok; guint v; errno = 0; - ok = bolt_str_parse_as_uint (table[i].str, &v, &error); + ok = bolt_str_parse_as_uint (txt, &v, &error); if (g_test_verbose ()) - g_test_message ("parsing '%s', expecting: %s", table[i].str, - (table[i].error ? "error" : "success")); + g_test_message ("parsing '%s', expecting: %s", txt, + (expect_error ? "error" : "success")); - if (table[i].error) + if (expect_error) { int err = errno; @@ -1428,7 +1777,7 @@ test_str_parse_uint (TestRng *tt, gconstpointer user_data) } else { - g_assert_cmpuint (table[i].val, ==, v); + g_assert_cmpuint (val, ==, v); g_assert_true (ok); } } @@ -2112,6 +2461,13 @@ main (int argc, char **argv) test_io_errors, test_io_tear_down); + g_test_add ("/common/io/mkdirat", + TestIO, + NULL, + test_io_setup, + test_io_mkdirat, + test_io_tear_down); + g_test_add ("/common/io/verify", TestIO, NULL, @@ -2119,6 +2475,41 @@ main (int argc, char **argv) test_io_verify, test_io_tear_down); + g_test_add ("/common/io/write_file_at", + TestIO, + NULL, + test_io_setup, + test_io_write_file_at, + test_io_tear_down); + + g_test_add ("/common/io/write_int_at", + TestIO, + NULL, + test_io_setup, + test_io_write_int_at, + test_io_tear_down); + + g_test_add ("/common/io/read_int_at", + TestIO, + NULL, + test_io_setup, + test_io_read_int_at, + test_io_tear_down); + + g_test_add ("/common/io/write_uint_at", + TestIO, + NULL, + test_io_setup, + test_io_write_uint_at, + test_io_tear_down); + + g_test_add ("/common/io/read_uint_at", + TestIO, + NULL, + test_io_setup, + test_io_read_uint_at, + test_io_tear_down); + g_test_add ("/common/io/file_write_all", TestIO, NULL, @@ -2126,6 +2517,13 @@ main (int argc, char **argv) test_io_file_write_all, test_io_tear_down); + g_test_add ("/common/io/renameat", + TestIO, + NULL, + test_io_setup, + test_io_renameat, + test_io_tear_down); + g_test_add ("/common/io/copy_bytes", TestIO, NULL, @@ -2133,6 +2531,13 @@ main (int argc, char **argv) test_io_copy_bytes, test_io_tear_down); + g_test_add ("/common/io/dir_is_empty", + TestIO, + NULL, + test_io_setup, + test_io_dir_is_empty, + test_io_tear_down); + g_test_add ("/common/io/autoclose", TestIO, NULL, diff --git a/tests/test-store.c b/tests/test-store.c index 655d1d2389b24e97500f8b89a578b1e11bdd39da..93ce1ea29efaf37cc66d3a41c9c8786b66e1174b 100644 --- a/tests/test-store.c +++ b/tests/test-store.c @@ -66,11 +66,11 @@ test_store_setup (TestStore *tt, gconstpointer user_data) } - tt->store = bolt_store_new (tt->path); + tt->store = bolt_store_new (tt->path, &error); if (tt->store == NULL) { - g_critical ("Could not create store at %s", - tt->path); + g_critical ("Could not create store at %s: %s", + tt->path, error->message); return; } @@ -105,12 +105,19 @@ test_store_basic (TestStore *tt, gconstpointer user_data) g_autofree char *path = NULL; char uid[] = "fbc83890-e9bf-45e5-a777-b3728490989c"; BoltKeyState keystate; + guint version; gboolean ok; g_object_get (tt->store, "root", &root, NULL); path = g_file_get_path (root); g_assert_cmpstr (tt->path, ==, path); + g_object_get (tt->store, "version", &version, NULL); + g_assert_cmpuint (version, ==, BOLT_STORE_VERSION); + + version = bolt_store_get_version (tt->store); + g_assert_cmpuint (version, ==, BOLT_STORE_VERSION); + dev = g_object_new (BOLT_TYPE_DEVICE, "uid", uid, "name", "Laptop", @@ -820,6 +827,79 @@ test_store_journal (TestStore *tt, gconstpointer user_data) } +static void +test_store_upgrade (TestStore *tt, gconstpointer user_data) +{ + g_autoptr(BoltDevice) dev = NULL; + g_autoptr(GError) err = NULL; + g_autoptr(DIR) root = NULL; + char uid[] = "fbc83890-e9bf-45e5-a777-b3728490989c"; + guint version; + gboolean up; + gboolean ok; + + /* simulate a version 0 store, i.e. has some entries, + * but no 'version' file */ + + root = bolt_opendir (tt->path, &err); + g_assert_no_error (err); + g_assert_nonnull (root); + + version = bolt_store_get_version (tt->store); + g_assert_cmpuint (version, ==, BOLT_STORE_VERSION); + + dev = g_object_new (BOLT_TYPE_DEVICE, + "uid", uid, + "name", "Laptop", + "vendor", "GNOME.org", + "status", BOLT_STATUS_DISCONNECTED, + NULL); + + ok = bolt_store_put_device (tt->store, dev, BOLT_POLICY_AUTO, NULL, &err); + g_assert_no_error (err); + g_assert_true (ok); + + /* close the store, delete 'version' */ + g_clear_object (&tt->store); + + ok = bolt_unlink_at (dirfd (root), "version", 0, &err); + g_assert_no_error (err); + g_assert_true (ok); + + /* re-create the store object */ + tt->store = bolt_store_new (tt->path, &err); + g_assert_no_error (err); + g_assert_nonnull (tt->store); + + version = bolt_store_get_version (tt->store); + g_assert_cmpuint (version, ==, 0); + + /* no upgrade the store */ + ok = bolt_store_upgrade (tt->store, &up, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_true (up); + + /* assert the upgrade changed the version */ + version = bolt_store_get_version (tt->store); + g_assert_cmpuint (version, ==, BOLT_STORE_VERSION); + + /* upgrade again, check it did not do anything */ + ok = bolt_store_upgrade (tt->store, &up, &err); + g_assert_no_error (err); + g_assert_true (ok); + g_assert_false (up); + + version = bolt_store_get_version (tt->store); + g_assert_cmpuint (version, ==, BOLT_STORE_VERSION); + + /* ensure 'upgrade' argument is optional */ + ok = bolt_store_upgrade (tt->store, NULL, &err); + g_assert_no_error (err); + g_assert_true (ok); + +} + int main (int argc, char **argv) { @@ -886,5 +966,12 @@ main (int argc, char **argv) test_store_journal, test_store_tear_down); + g_test_add ("/daemon/store/upgrade", + TestStore, + NULL, + test_store_setup, + test_store_upgrade, + test_store_tear_down); + return g_test_run (); } diff --git a/tests/test-sysfs.c b/tests/test-sysfs.c index fc071a2789a1cd23f24e33f681dcacf850ab1c29..9b9b3ed5a202572ce9bd53eeefc35a54580e312c 100644 --- a/tests/test-sysfs.c +++ b/tests/test-sysfs.c @@ -1104,7 +1104,10 @@ test_bootacl_update_offline (TestBootacl *tt, gconstpointer user) guint k; dir = bolt_tmp_dir_make ("bolt.sysfs.XXXXXX", NULL); - store = bolt_store_new (dir); + store = bolt_store_new (dir, &err); + + g_assert_no_error (err); + g_assert_nonnull (store); ok = bolt_store_put_domain (store, dom, &err); g_assert_no_error (err);