static gboolean begin_write_state (GkmMate2Storage *self, GkmTransaction *transaction) { g_assert (GKM_IS_MATE2_STORAGE (self)); g_assert (GKM_IS_TRANSACTION (transaction)); g_return_val_if_fail (!gkm_transaction_get_failed (transaction), FALSE); /* Already in write state for this transaction? */ if (self->transaction != NULL) { g_return_val_if_fail (self->transaction == transaction, FALSE); return TRUE; } /* Lock file for the transaction */ self->read_fd = begin_lock_file (self, transaction); if (self->read_fd == -1) return FALSE; gkm_transaction_add (transaction, self, complete_write_state, NULL); self->transaction = g_object_ref (transaction); /* Open the new file */ g_assert (self->write_fd == -1); self->write_path = g_strdup_printf ("%s.XXXXXX", self->filename); self->write_fd = g_mkstemp (self->write_path); if (self->write_fd == -1) { g_message ("couldn't open new temporary store file: %s: %s", self->write_path, g_strerror (errno)); gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return FALSE; } return TRUE; }
void gkm_object_destroy (GkmObject *self, GkmTransaction *transaction) { GkmSession *session; GkmManager *manager; GkmModule *module; g_return_if_fail (GKM_IS_OBJECT (self)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); g_return_if_fail (!gkm_transaction_get_failed (transaction)); g_return_if_fail (self->pv->module); g_object_ref (self); session = gkm_session_for_session_object (self); if (session != NULL) { gkm_session_destroy_session_object (session, transaction, self); } else { manager = gkm_object_get_manager (self); module = gkm_object_get_module (self); if (manager == gkm_module_get_manager (module)) gkm_module_remove_token_object (module, transaction, self); } /* Forcefully dispose of the object once the transaction completes */ gkm_transaction_add (transaction, NULL, complete_destroy, g_object_ref (self)); g_object_unref (self); }
static gboolean complete_modification_state (GkmTransaction *transaction, GObject *object, gpointer unused) { GkmMate2Storage *self = GKM_MATE2_STORAGE (object); GkmDataResult res; if (!gkm_transaction_get_failed (transaction)) { res = gkm_mate2_file_write_fd (self->file, self->write_fd, self->login); switch(res) { case GKM_DATA_FAILURE: case GKM_DATA_UNRECOGNIZED: g_warning ("couldn't write to temporary store file: %s", self->write_path); return FALSE; case GKM_DATA_LOCKED: g_warning ("couldn't encrypt temporary store file: %s", self->write_path); return FALSE; case GKM_DATA_SUCCESS: break; default: g_assert_not_reached (); } } return TRUE; }
static void gkm_secret_item_real_set_attribute (GkmObject *base, GkmSession *session, GkmTransaction *transaction, CK_ATTRIBUTE_PTR attr) { GkmSecretItem *self = GKM_SECRET_ITEM (base); const gchar *identifier; GkmSecretData *sdata; GHashTable *fields; gchar *schema_name; GkmSecret *secret; gchar *schema; CK_RV rv; if (!self->collection) { gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); g_return_if_reached (); } /* Check that the object is not locked */ if (!gkm_secret_collection_unlocked_have (self->collection, session)) { gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN); return; } switch (attr->type) { case CKA_VALUE: sdata = gkm_secret_collection_unlocked_use (self->collection, session); g_return_if_fail (sdata); identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (self)); secret = gkm_secret_new (attr->pValue, attr->ulValueLen); gkm_secret_data_set_transacted (sdata, transaction, identifier, secret); g_object_unref (secret); g_object_unref (sdata); gkm_secret_object_begin_modified (GKM_SECRET_OBJECT (self), transaction); if (!gkm_transaction_get_failed (transaction)) gkm_transaction_add (transaction, self, complete_set_secret, NULL); return; case CKA_G_FIELDS: rv = gkm_secret_fields_parse (attr, &fields, &schema_name); if (rv != CKR_OK) { gkm_transaction_fail (transaction, rv); } else { begin_set_fields (self, transaction, fields); if (schema_name) begin_set_schema (self, transaction, schema_name); } return; case CKA_G_SCHEMA: rv = gkm_attribute_get_string (attr, &schema); if (rv != CKR_OK) gkm_transaction_fail (transaction, rv); else begin_set_schema (self, transaction, schema); return; } GKM_OBJECT_CLASS (gkm_secret_item_parent_class)->set_attribute (base, session, transaction, attr); }
void gkm_mate2_storage_destroy (GkmMate2Storage *self, GkmTransaction *transaction, GkmObject *object) { GkmDataResult res; gchar *identifier; gchar *path; g_return_if_fail (GKM_IS_MATE2_STORAGE (self)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); g_return_if_fail (!gkm_transaction_get_failed (transaction)); g_return_if_fail (object); /* Lookup the object identifier */ identifier = g_hash_table_lookup (self->object_to_identifier, object); g_return_if_fail (identifier); if (!begin_modification_state (self, transaction)) return; /* First actually delete the file */ path = g_build_filename (self->directory, identifier, NULL); gkm_transaction_remove_file (transaction, path); g_free (path); if (gkm_transaction_get_failed (transaction)) return; /* Now delete the entry from our store */ res = gkm_mate2_file_destroy_entry (self->file, identifier); switch(res) { case GKM_DATA_FAILURE: case GKM_DATA_UNRECOGNIZED: gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return; case GKM_DATA_LOCKED: gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN); return; case GKM_DATA_SUCCESS: break; default: g_assert_not_reached (); } /* Actual removal of object happened as a callback above */ g_return_if_fail (g_hash_table_lookup (self->object_to_identifier, object) == NULL); }
static gboolean complete_transient_add (GkmTransaction *transaction, GkmModule *self, GkmObject *object) { if (gkm_transaction_get_failed (transaction)) remove_transient_object (self, NULL, object); g_object_unref (object); return TRUE; }
static gboolean complete_set_secret (GkmTransaction *transaction, GObject *obj, gpointer user_data) { if (!gkm_transaction_get_failed (transaction)) { gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_VALUE); } return TRUE; }
static void begin_set_fields (GkmSecretItem *self, GkmTransaction *transaction, GHashTable *fields) { g_assert (GKM_IS_SECRET_OBJECT (self)); g_assert (!gkm_transaction_get_failed (transaction)); gkm_transaction_add (transaction, self, complete_set_fields, self->fields); self->fields = fields; }
static void begin_set_label (GkmSecretObject *self, GkmTransaction *transaction, gchar *label) { g_assert (GKM_IS_SECRET_OBJECT (self)); g_assert (!gkm_transaction_get_failed (transaction)); gkm_transaction_add (transaction, self, complete_set_label, self->pv->label); self->pv->label = label; }
static gboolean begin_new_file (GkmTransaction *self, const gchar *filename) { g_assert (GKM_IS_TRANSACTION (self)); g_assert (!gkm_transaction_get_failed (self)); g_assert (filename); gkm_transaction_add (self, NULL, complete_new_file, g_strdup (filename)); return TRUE; }
static gboolean complete_expose (GkmTransaction *transaction, GObject *obj, gpointer user_data) { GkmObject *self = GKM_OBJECT (obj); gboolean expose = GPOINTER_TO_UINT (user_data); if (gkm_transaction_get_failed (transaction)) gkm_object_expose (self, !expose); return TRUE; }
static void begin_set_schema (GkmSecretItem *self, GkmTransaction *transaction, gchar *schema) { g_assert (GKM_IS_SECRET_OBJECT (self)); g_assert (!gkm_transaction_get_failed (transaction)); if (self->schema != schema) { gkm_transaction_add (transaction, self, complete_set_schema, self->schema); self->schema = schema; } }
static gboolean complete_set_secret (GkmTransaction *transaction, GObject *obj, gpointer user_data) { GkmSecretItem *self = GKM_SECRET_ITEM (obj); if (!gkm_transaction_get_failed (transaction)) { gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_VALUE); gkm_secret_object_was_modified (GKM_SECRET_OBJECT (self)); } return TRUE; }
static gboolean complete_add_assertion (GkmTransaction *transaction, GObject *object, gpointer user_data) { GkmAssertion *assertion = GKM_ASSERTION (user_data); GkmXdgTrust *self = GKM_XDG_TRUST (object); if (gkm_transaction_get_failed (transaction)) remove_assertion_from_trust (self, assertion, NULL); g_object_unref (assertion); return TRUE; }
static gint begin_lock_file (GkmMate2Storage *self, GkmTransaction *transaction) { guint tries = 0; gint fd = -1; /* * In this function we don't actually put the object into a 'write' state, * that's the callers job if necessary. */ g_assert (GKM_IS_MATE2_STORAGE (self)); g_assert (GKM_IS_TRANSACTION (transaction)); g_return_val_if_fail (!gkm_transaction_get_failed (transaction), -1); /* File lock retry loop */ for (tries = 0; TRUE; ++tries) { if (tries > MAX_LOCK_TRIES) { g_message ("couldn't write to store file: %s: file is locked", self->filename); gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return -1; } fd = open (self->filename, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) { g_message ("couldn't open store file: %s: %s", self->filename, g_strerror (errno)); gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return -1; } if (flock (fd, LOCK_EX | LOCK_NB) < 0) { if (errno != EWOULDBLOCK) { g_message ("couldn't lock store file: %s: %s", self->filename, g_strerror (errno)); close (fd); gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return -1; } close (fd); fd = -1; g_usleep (200000); continue; } /* Successfully opened file */; gkm_transaction_add (transaction, self, complete_lock_file, GINT_TO_POINTER (fd)); return fd; } g_assert_not_reached (); }
static void gkm_mate2_storage_real_write_value (GkmStore *base, GkmTransaction *transaction, GkmObject *object, CK_ATTRIBUTE_PTR attr) { GkmMate2Storage *self = GKM_MATE2_STORAGE (base); const gchar *identifier; GkmDataResult res; CK_RV rv; g_return_if_fail (GKM_IS_MATE2_STORAGE (self)); g_return_if_fail (GKM_IS_OBJECT (object)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); g_return_if_fail (!gkm_transaction_get_failed (transaction)); g_return_if_fail (attr); identifier = g_hash_table_lookup (self->object_to_identifier, object); if (!identifier) { gkm_transaction_fail (transaction, CKR_ATTRIBUTE_READ_ONLY); return; } if (self->last_mtime == 0) { rv = gkm_mate2_storage_refresh (self); if (rv != CKR_OK) { gkm_transaction_fail (transaction, rv); return; } } res = gkm_mate2_file_write_value (self->file, identifier, attr->type, attr->pValue, attr->ulValueLen); switch (res) { case GKM_DATA_FAILURE: rv = CKR_FUNCTION_FAILED; break; case GKM_DATA_UNRECOGNIZED: rv = CKR_ATTRIBUTE_READ_ONLY; break; case GKM_DATA_LOCKED: rv = CKR_USER_NOT_LOGGED_IN; break; case GKM_DATA_SUCCESS: rv = CKR_OK; break; default: g_assert_not_reached (); } if (rv != CKR_OK) gkm_transaction_fail (transaction, rv); }
void gkm_object_set_attribute (GkmObject *self, GkmSession *session, GkmTransaction *transaction, CK_ATTRIBUTE_PTR attr) { g_return_if_fail (GKM_IS_OBJECT (self)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); g_return_if_fail (!gkm_transaction_get_failed (transaction)); g_return_if_fail (attr); g_assert (GKM_OBJECT_GET_CLASS (self)->set_attribute); /* Check that the value will actually change */ if (!gkm_object_match (self, session, attr)) GKM_OBJECT_GET_CLASS (self)->set_attribute (self, session, transaction, attr); }
void gkm_object_create_attributes (GkmObject *self, GkmSession *session, GkmTransaction *transaction, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs) { g_return_if_fail (GKM_IS_OBJECT (self)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); g_return_if_fail (!gkm_transaction_get_failed (transaction)); g_return_if_fail (GKM_IS_SESSION (session)); g_return_if_fail (attrs); g_assert (GKM_OBJECT_GET_CLASS (self)->create_attributes); /* Check that the value will actually change */ GKM_OBJECT_GET_CLASS (self)->create_attributes (self, session, transaction, attrs, n_attrs); }
void gkm_object_expose_full (GkmObject *self, GkmTransaction *transaction, gboolean expose) { if (!expose && !self) return; g_return_if_fail (GKM_IS_OBJECT (self)); g_return_if_fail (!transaction || !gkm_transaction_get_failed (transaction)); if (self->pv->exposed != expose) { if (transaction) gkm_transaction_add (transaction, self, complete_expose, GUINT_TO_POINTER (expose)); gkm_object_expose (self, expose); } }
static gboolean complete_new_file (GkmTransaction *self, GObject *unused, gpointer user_data) { gchar *path = user_data; gboolean ret = TRUE; if (gkm_transaction_get_failed (self)) { if (g_unlink (path) < 0) { g_warning ("couldn't delete aborted file, data may be lost: %s: %s", path, g_strerror (errno)); ret = FALSE; } } g_free (path); return ret; }
static gboolean complete_set_schema (GkmTransaction *transaction, GObject *obj, gpointer user_data) { GkmSecretItem *self = GKM_SECRET_ITEM (obj); gchar *old_schema = user_data; if (gkm_transaction_get_failed (transaction)) { g_free (self->schema); self->schema = old_schema; } else { gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_G_SCHEMA); g_object_notify (G_OBJECT (obj), "schema"); g_free (old_schema); } return TRUE; }
static gboolean complete_set_label (GkmTransaction *transaction, GObject *obj, gpointer user_data) { GkmSecretObject *self = GKM_SECRET_OBJECT (obj); gchar *old_label = user_data; if (gkm_transaction_get_failed (transaction)) { g_free (self->pv->label); self->pv->label = old_label; } else { gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_LABEL); g_object_notify (G_OBJECT (obj), "label"); gkm_secret_object_was_modified (self); g_free (old_label); } return TRUE; }
static gboolean complete_set_fields (GkmTransaction *transaction, GObject *obj, gpointer user_data) { GkmSecretItem *self = GKM_SECRET_ITEM (obj); GHashTable *old_fields = user_data; if (gkm_transaction_get_failed (transaction)) { if (self->fields) g_hash_table_unref (self->fields); self->fields = old_fields; } else { gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_G_FIELDS); g_object_notify (G_OBJECT (obj), "fields"); if (old_fields) g_hash_table_unref (old_fields); } return TRUE; }
static void relock_each_object (GkmMate2File *file, const gchar *identifier, gpointer data) { RelockArgs *args = data; gchar *path; guint section; g_assert (GKM_IS_MATE2_STORAGE (args->self)); if (gkm_transaction_get_failed (args->transaction)) return; if (!gkm_mate2_file_lookup_entry (file, identifier, §ion)) g_return_if_reached (); /* Only operate on private files */ if (section != GKM_MATE2_FILE_SECTION_PRIVATE) return; path = g_build_filename (args->self->directory, identifier, NULL); relock_object (args->self, args->transaction, path, identifier, args->old_login, args->new_login); g_free (path); }
static gboolean complete_link_temporary (GkmTransaction *self, GObject *unused, gpointer user_data) { gchar *path = user_data; gboolean ret = TRUE; gchar *original; gchar *ext; /* When failed, rename temporary back */ if (gkm_transaction_get_failed (self)) { /* Figure out the original file name */ original = g_strdup (path); ext = strrchr (original, '.'); g_return_val_if_fail (ext, FALSE); *ext = '\0'; /* Now rename us back */ if (g_rename (path, original) == -1) { g_warning ("couldn't restore original file, data may be lost: %s: %s", original, g_strerror (errno)); ret = FALSE; } g_free (original); /* When succeeded, remove temporary */ } else { if (g_unlink (path) == -1) { g_warning ("couldn't delete temporary backup file: %s: %s", path, g_strerror (errno)); ret = TRUE; /* Not actually that bad of a situation */ } } g_free (path); return ret; }
static gboolean complete_write_state (GkmTransaction *transaction, GObject *object, gpointer unused) { GkmMate2Storage *self = GKM_MATE2_STORAGE (object); gboolean ret = TRUE; struct stat sb; g_return_val_if_fail (GKM_IS_MATE2_STORAGE (object), FALSE); g_return_val_if_fail (GKM_IS_TRANSACTION (transaction), FALSE); g_return_val_if_fail (self->transaction == transaction, FALSE); /* Transaction succeeded, overwrite the old with the new */ if (!gkm_transaction_get_failed (transaction)) { if (g_rename (self->write_path, self->filename) == -1) { g_warning ("couldn't rename temporary store file: %s", self->write_path); ret = FALSE; } else { if (fstat (self->write_fd, &sb) >= 0) self->last_mtime = sb.st_mtime; } } /* read_fd is closed by complete_lock_file */ if (self->write_fd != -1) close (self->write_fd); self->write_fd = -1; g_free (self->write_path); self->write_path = NULL; g_object_unref (self->transaction); self->transaction = NULL; return ret; }
void gkm_mate2_storage_create (GkmMate2Storage *self, GkmTransaction *transaction, GkmObject *object) { gboolean is_private; GkmDataResult res; gchar *identifier; gpointer data; gsize n_data; gchar *path; g_return_if_fail (GKM_IS_MATE2_STORAGE (self)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); g_return_if_fail (!gkm_transaction_get_failed (transaction)); g_return_if_fail (GKM_IS_OBJECT (object)); /* Make sure we haven't already stored it */ identifier = g_hash_table_lookup (self->object_to_identifier, object); g_return_if_fail (identifier == NULL); /* Double check that the object is in fact serializable */ if (!GKM_IS_SERIALIZABLE (object)) { g_warning ("can't store object of type '%s' on token", G_OBJECT_TYPE_NAME (object)); gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); g_return_if_reached (); } /* Figure out whether this is a private object */ if (!gkm_object_get_attribute_boolean (object, NULL, CKA_PRIVATE, &is_private)) is_private = FALSE; /* Can't serialize private if we're not unlocked */ if (is_private && !self->login) { gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN); return; } /* Hook ourselves into the transaction */ if (!begin_modification_state (self, transaction)) return; /* Create an identifier guaranteed unique by this transaction */ identifier = identifier_for_object (object); if (gkm_mate2_file_unique_entry (self->file, &identifier) != GKM_DATA_SUCCESS) { gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); g_return_if_reached (); } /* We don't want to get signals about this item being added */ g_signal_handlers_block_by_func (self->file, data_file_entry_added, self); g_signal_handlers_block_by_func (self->file, data_file_entry_changed, self); res = gkm_mate2_file_create_entry (self->file, identifier, is_private ? GKM_MATE2_FILE_SECTION_PRIVATE : GKM_MATE2_FILE_SECTION_PUBLIC); g_signal_handlers_unblock_by_func (self->file, data_file_entry_added, self); g_signal_handlers_unblock_by_func (self->file, data_file_entry_changed, self); switch(res) { case GKM_DATA_FAILURE: case GKM_DATA_UNRECOGNIZED: g_free (identifier); gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return; case GKM_DATA_LOCKED: g_free (identifier); gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN); return; case GKM_DATA_SUCCESS: break; default: g_assert_not_reached (); } /* Serialize the object in question */ if (!gkm_serializable_save (GKM_SERIALIZABLE (object), is_private ? self->login : NULL, &data, &n_data)) { gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); g_return_if_reached (); } path = g_build_filename (self->directory, identifier, NULL); gkm_transaction_write_file (transaction, path, data, n_data); /* Make sure we write in the object hash */ if (!gkm_transaction_get_failed (transaction)) store_object_hash (self, transaction, identifier, data, n_data); /* Now we decide to own the object */ if (!gkm_transaction_get_failed (transaction)) take_object_ownership (self, identifier, object); g_free (identifier); g_free (path); g_free (data); }
void gkm_mate2_storage_relock (GkmMate2Storage *self, GkmTransaction *transaction, GkmSecret *old_login, GkmSecret *new_login) { GkmMate2File *file; GkmDataResult res; RelockArgs args; g_return_if_fail (GKM_IS_MATE2_STORAGE (self)); g_return_if_fail (GKM_IS_TRANSACTION (transaction)); /* Reload the file with the old password and start transaction */ if (!begin_write_state (self, transaction)) return; file = gkm_mate2_file_new (); /* Read in from the old file */ res = gkm_mate2_file_read_fd (file, self->read_fd, old_login); switch(res) { case GKM_DATA_FAILURE: case GKM_DATA_UNRECOGNIZED: gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return; case GKM_DATA_LOCKED: gkm_transaction_fail (transaction, CKR_PIN_INCORRECT); return; case GKM_DATA_SUCCESS: break; default: g_assert_not_reached (); } /* Write out to new path as new file */ res = gkm_mate2_file_write_fd (file, self->write_fd, new_login); switch(res) { case GKM_DATA_FAILURE: case GKM_DATA_UNRECOGNIZED: gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); return; case GKM_DATA_LOCKED: gkm_transaction_fail (transaction, CKR_PIN_INCORRECT); return; case GKM_DATA_SUCCESS: break; default: g_assert_not_reached (); } /* Now go through all objects in the file, and load and reencode them */ args.transaction = transaction; args.old_login = old_login; args.new_login = new_login; gkm_mate2_file_foreach_entry (file, relock_each_object, &args); if (!gkm_transaction_get_failed (transaction) && self->login) { if (new_login) g_object_ref (new_login); g_object_unref (self->login); self->login = new_login; g_object_notify (G_OBJECT (self), "login"); } g_object_unref (file); }
static void relock_object (GkmMate2Storage *self, GkmTransaction *transaction, const gchar *path, const gchar *identifier, GkmSecret *old_login, GkmSecret *new_login) { GError *error = NULL; GkmObject *object; gpointer data; gsize n_data; GType type; g_assert (GKM_IS_MATE2_STORAGE (self)); g_assert (GKM_IS_TRANSACTION (transaction)); g_assert (identifier); g_assert (path); g_assert (!gkm_transaction_get_failed (transaction)); /* Figure out the type of object */ type = type_from_identifier (identifier); if (type == 0) { g_warning ("don't know how to relock file in user store: %s", identifier); gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); return; } /* Create a dummy object for this identifier */ object = g_object_new (type, "unique", identifier, "module", self->module, NULL); if (!GKM_IS_SERIALIZABLE (object)) { g_warning ("cannot relock unserializable object for file in user store: %s", identifier); gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); return; } /* Read in the data for the object */ if (!g_file_get_contents (path, (gchar**)&data, &n_data, &error)) { g_message ("couldn't load file in user store in order to relock: %s: %s", identifier, egg_error_message (error)); g_clear_error (&error); g_object_unref (object); gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); return; } /* Make sure the data matches the hash */ if (!check_object_hash (self, identifier, data, n_data)) { g_message ("file in data store doesn't match hash: %s", identifier); gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); return; } /* Load it into our temporary object */ if (!gkm_serializable_load (GKM_SERIALIZABLE (object), old_login, data, n_data)) { g_message ("unrecognized or invalid user store file: %s", identifier); gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED); g_free (data); g_object_unref (object); return; } g_free (data); data = NULL; /* Read it out of our temporary object */ if (!gkm_serializable_save (GKM_SERIALIZABLE (object), new_login, &data, &n_data)) { g_warning ("unable to serialize data with new login: %s", identifier); gkm_transaction_fail (transaction, CKR_GENERAL_ERROR); g_object_unref (object); g_free (data); return; } g_object_unref (object); /* And write it back out to the file */ gkm_transaction_write_file (transaction, path, data, n_data); /* Create and save the hash here */ if (!gkm_transaction_get_failed (transaction)) store_object_hash (self, transaction, identifier, data, n_data); g_free (data); }
static gboolean begin_link_temporary_if_exists (GkmTransaction *self, const gchar *filename, gboolean *exists) { guint i = 0; g_assert (GKM_IS_TRANSACTION (self)); g_assert (!gkm_transaction_get_failed (self)); g_assert (filename); g_assert (exists); for (i = 0; i < MAX_TRIES; ++i) { struct stat sb; unsigned int nlink; int stat_failed = 0; *exists = TRUE; /* Try to link to random temporary file names. We try * to use a hardlink to create a copy but if that * fails (i.e. not supported by the FS), we copy the * entire file. The result should be the same except * that the file times will change if we need to * rollback the transaction. */ if (stat (filename, &sb)) { stat_failed = 1; } else { gchar *result; result = g_strdup_printf ("%s.temp-%d", filename, g_random_int_range (0, G_MAXINT)); nlink = (unsigned int)sb.st_nlink; /* The result code of link(2) is not reliable. * Unless it fails with EEXIST we stat the * file to check for success. Note that there * is a race here: If another process adds a * link to the source file between link and * stat, the check on the increased link count * will fail. Fortunately the case for * hardlinks are not working solves it. */ if (link (filename, result) && errno == EEXIST) { /* This is probably a valid error. * Let us try another temporary file. */ } else if (stat (filename, &sb)) { stat_failed = 1; } else { if ((sb.st_nlink == nlink + 1) || !copy_to_temp_file (result, filename)) { /* Either the link worked or * the copy succeeded. */ gkm_transaction_add (self, NULL, complete_link_temporary, result); return TRUE; } } g_free (result); } if (stat_failed && (errno == ENOENT || errno == ENOTDIR)) { /* The original file does not exist */ *exists = FALSE; return TRUE; } /* If exists, try again, otherwise fail */ if (errno != EEXIST) { g_warning ("couldn't create temporary file for: %s: %s", filename, g_strerror (errno)); gkm_transaction_fail (self, CKR_DEVICE_ERROR); return FALSE; } } g_assert_not_reached (); }