Add helper function to insert with retry.
This breaks the code that tries to insert into a database with a
random id into a helper function, since it's now shared between two
functions.
Test: keystore2_test
Change-Id: Ib03b6082246120e6b3ff2a45b5bf8d324c40f7a7
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 75f2027..d53bd4b 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -268,27 +268,14 @@
.context(format!("Domain {:?} must be either App or SELinux.", domain));
}
}
- // Loop until we get a unique id.
- loop {
- let newid: i64 = random();
- let ret = self.conn.execute(
+ Self::insert_with_retry(|id| {
+ self.conn.execute(
"INSERT into persistent.keyentry (id, creation_date, domain, namespace, alias)
VALUES(?, datetime('now'), ?, ?, NULL);",
- params![newid, domain as i64, namespace],
- );
- match ret {
- // If the id already existed, try again.
- Err(rusqlite::Error::SqliteFailure(
- libsqlite3_sys::Error {
- code: libsqlite3_sys::ErrorCode::ConstraintViolation,
- extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
- },
- _,
- )) => (),
- Err(e) => return Err(e).context("Failed to create key entry."),
- _ => return Ok(newid),
- }
- }
+ params![id, domain as i64, namespace],
+ )
+ })
+ .context("In create_key_entry")
}
/// Inserts a new blob and associates it with the given key id. Each blob
@@ -611,26 +598,14 @@
.context("In grant: Failed to update existing grant.")?;
grant_id
} else {
- loop {
- let newid: i64 = random();
- let ret = tx.execute(
+ Self::insert_with_retry(|id| {
+ tx.execute(
"INSERT INTO perboot.grant (id, grantee, keyentryid, access_vector)
VALUES (?, ?, ?, ?);",
- params![newid, grantee_uid, key_id, i32::from(access_vector)],
- );
- match ret {
- // If the id already existed, try again.
- Err(rusqlite::Error::SqliteFailure(
- libsqlite3_sys::Error {
- code: libsqlite3_sys::ErrorCode::ConstraintViolation,
- extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
- },
- _,
- )) => (),
- Err(e) => return Err(e).context("Failed to insert grant."),
- Ok(_) => break newid,
- }
- }
+ params![id, grantee_uid, key_id, i32::from(access_vector)],
+ )
+ })
+ .context("In grant")?
};
tx.commit().context("In grant: failed to commit transaction.")?;
@@ -672,6 +647,29 @@
Ok(())
}
+ // Generates a random id and passes it to the given function, which will
+ // try to insert it into a database. If that insertion fails, retry;
+ // otherwise return the id.
+ fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> {
+ loop {
+ let newid: i64 = random();
+ match inserter(newid) {
+ // If the id already existed, try again.
+ Err(rusqlite::Error::SqliteFailure(
+ libsqlite3_sys::Error {
+ code: libsqlite3_sys::ErrorCode::ConstraintViolation,
+ extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
+ },
+ _,
+ )) => (),
+ Err(e) => {
+ return Err(e).context("In insert_with_retry: failed to insert into database.")
+ }
+ _ => return Ok(newid),
+ }
+ }
+ }
+
// Takes Rows as returned by a query call on prepared statement.
// Extracts exactly one row with the `row_extractor` and fails if more
// rows are available.