Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
sqlstorage.cc
1 #include "sqlstorage.h"
2 
3 #include <sys/stat.h>
4 #include <sys/statvfs.h>
5 #include <iostream>
6 #include <map>
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "logging/logging.h"
12 #include "sql_utils.h"
13 #include "utilities/utils.h"
14 
15 // find metadata with version set to -1 (e.g. after migration) and assign proper version to it
16 void SQLStorage::cleanMetaVersion(Uptane::RepositoryType repo, const Uptane::Role& role) {
17  SQLite3Guard db = dbConnection();
18 
19  if (!db.beginTransaction()) {
20  LOG_ERROR << "Can't start transaction: " << db.errmsg();
21  return;
22  }
23 
24  auto statement = db.prepareStatement<int, int, int>(
25  "SELECT meta FROM meta WHERE (repo=? AND meta_type=? AND version=?);", static_cast<int>(repo), role.ToInt(), -1);
26 
27  int result = statement.step();
28 
29  if (result == SQLITE_DONE) {
30  LOG_TRACE << "meta with role " << role.ToString() << " in repo " << repo.toString() << " not present in db";
31  return;
32  } else if (result != SQLITE_ROW) {
33  LOG_ERROR << "Can't get meta: " << db.errmsg();
34  return;
35  }
36  std::string meta = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
37 
38  int version = Uptane::extractVersionUntrusted(meta);
39  if (version < 0) {
40  LOG_ERROR << "Corrupted metadata";
41  return;
42  }
43 
44  // in there is already metadata with such version delete it
45  statement = db.prepareStatement<int, int, int>("DELETE FROM meta WHERE (repo=? AND meta_type=? AND version=?);",
46  static_cast<int>(repo), role.ToInt(), version);
47 
48  if (statement.step() != SQLITE_DONE) {
49  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
50  return;
51  }
52 
53  statement = db.prepareStatement<int, int, int, int>(
54  "UPDATE meta SET version = ? WHERE (repo=? AND meta_type=? AND version=?);", version, static_cast<int>(repo),
55  role.ToInt(), -1);
56 
57  if (statement.step() != SQLITE_DONE) {
58  LOG_ERROR << "Can't update metadata: " << db.errmsg();
59  return;
60  }
61 
62  db.commitTransaction();
63 }
64 
65 SQLStorage::SQLStorage(const StorageConfig& config, bool readonly)
66  : SQLStorageBase(config.sqldb_path.get(config.path), readonly, libaktualizr_schema_migrations,
67  libaktualizr_schema_rollback_migrations, libaktualizr_current_schema,
68  libaktualizr_current_schema_version),
69  INvStorage(config) {
70  try {
71  cleanMetaVersion(Uptane::RepositoryType::Director(), Uptane::Role::Root());
72  cleanMetaVersion(Uptane::RepositoryType::Image(), Uptane::Role::Root());
73  } catch (...) {
74  LOG_ERROR << "SQLite database metadata version migration failed";
75  }
76 }
77 
78 void SQLStorage::storePrimaryKeys(const std::string& public_key, const std::string& private_key) {
79  SQLite3Guard db = dbConnection();
80 
81  auto statement = db.prepareStatement<std::string>(
82  "INSERT OR REPLACE INTO primary_keys(unique_mark,public,private) VALUES (0,?,?);", public_key, private_key);
83  if (statement.step() != SQLITE_DONE) {
84  LOG_ERROR << "Can't set primary keys: " << db.errmsg();
85  return;
86  }
87 }
88 
89 bool SQLStorage::loadPrimaryKeys(std::string* public_key, std::string* private_key) {
90  return loadPrimaryPublic(public_key) && loadPrimaryPrivate(private_key);
91 }
92 
93 bool SQLStorage::loadPrimaryPublic(std::string* public_key) {
94  SQLite3Guard db = dbConnection();
95 
96  auto statement = db.prepareStatement("SELECT public FROM primary_keys LIMIT 1;");
97 
98  int result = statement.step();
99  if (result == SQLITE_DONE) {
100  LOG_TRACE << "No public key in db";
101  return false;
102  } else if (result != SQLITE_ROW) {
103  LOG_ERROR << "Can't get public key: " << db.errmsg();
104  return false;
105  }
106 
107  auto pub = statement.get_result_col_str(0);
108  if (pub == boost::none) {
109  return false;
110  }
111 
112  if (public_key != nullptr) {
113  *public_key = std::move(pub.value());
114  }
115 
116  return true;
117 }
118 
119 bool SQLStorage::loadPrimaryPrivate(std::string* private_key) {
120  SQLite3Guard db = dbConnection();
121 
122  auto statement = db.prepareStatement("SELECT private FROM primary_keys LIMIT 1;");
123 
124  int result = statement.step();
125  if (result == SQLITE_DONE) {
126  LOG_TRACE << "No private key in db";
127  return false;
128  } else if (result != SQLITE_ROW) {
129  LOG_ERROR << "Can't get private key: " << db.errmsg();
130  return false;
131  }
132 
133  auto priv = statement.get_result_col_str(0);
134  if (priv == boost::none) {
135  return false;
136  }
137 
138  if (private_key != nullptr) {
139  *private_key = std::move(priv.value());
140  }
141 
142  return true;
143 }
144 
145 void SQLStorage::clearPrimaryKeys() {
146  SQLite3Guard db = dbConnection();
147 
148  if (db.exec("DELETE FROM primary_keys;", nullptr, nullptr) != SQLITE_OK) {
149  LOG_ERROR << "Can't clear primary keys: " << db.errmsg();
150  return;
151  }
152 }
153 
154 void SQLStorage::storeTlsCreds(const std::string& ca, const std::string& cert, const std::string& pkey) {
155  storeTlsCa(ca);
156  storeTlsCert(cert);
157  storeTlsPkey(pkey);
158 }
159 
160 void SQLStorage::storeTlsCa(const std::string& ca) {
161  SQLite3Guard db = dbConnection();
162 
163  if (!db.beginTransaction()) {
164  LOG_ERROR << "Can't start transaction: " << db.errmsg();
165  return;
166  }
167 
168  auto statement = db.prepareStatement("SELECT count(*) FROM tls_creds;");
169  if (statement.step() != SQLITE_ROW) {
170  LOG_ERROR << "Can't get count of tls_creds table: " << db.errmsg();
171  return;
172  }
173 
174  const char* req;
175  if (statement.get_result_col_int(0) != 0) {
176  req = "UPDATE OR REPLACE tls_creds SET ca_cert = ?;";
177  } else {
178  req = "INSERT INTO tls_creds(ca_cert) VALUES (?);";
179  }
180 
181  statement = db.prepareStatement<SQLBlob>(req, SQLBlob(ca));
182  if (statement.step() != SQLITE_DONE) {
183  LOG_ERROR << "Can't set ca_cert: " << db.errmsg();
184  return;
185  }
186 
187  db.commitTransaction();
188 }
189 
190 void SQLStorage::storeTlsCert(const std::string& cert) {
191  SQLite3Guard db = dbConnection();
192 
193  if (!db.beginTransaction()) {
194  LOG_ERROR << "Can't start transaction: " << db.errmsg();
195  return;
196  }
197 
198  auto statement = db.prepareStatement("SELECT count(*) FROM tls_creds;");
199  if (statement.step() != SQLITE_ROW) {
200  LOG_ERROR << "Can't get count of tls_creds table: " << db.errmsg();
201  return;
202  }
203 
204  const char* req;
205  if (statement.get_result_col_int(0) != 0) {
206  req = "UPDATE OR REPLACE tls_creds SET client_cert = ?;";
207  } else {
208  req = "INSERT INTO tls_creds(client_cert) VALUES (?);";
209  }
210 
211  statement = db.prepareStatement<SQLBlob>(req, SQLBlob(cert));
212  if (statement.step() != SQLITE_DONE) {
213  LOG_ERROR << "Can't set client_cert: " << db.errmsg();
214  return;
215  }
216 
217  db.commitTransaction();
218 }
219 
220 void SQLStorage::storeTlsPkey(const std::string& pkey) {
221  SQLite3Guard db = dbConnection();
222 
223  if (!db.beginTransaction()) {
224  LOG_ERROR << "Can't start transaction: " << db.errmsg();
225  return;
226  }
227  auto statement = db.prepareStatement("SELECT count(*) FROM tls_creds;");
228  if (statement.step() != SQLITE_ROW) {
229  LOG_ERROR << "Can't get count of tls_creds table: " << db.errmsg();
230  return;
231  }
232 
233  const char* req;
234  if (statement.get_result_col_int(0) != 0) {
235  req = "UPDATE OR REPLACE tls_creds SET client_pkey = ?;";
236  } else {
237  req = "INSERT INTO tls_creds(client_pkey) VALUES (?);";
238  }
239 
240  statement = db.prepareStatement<SQLBlob>(req, SQLBlob(pkey));
241  if (statement.step() != SQLITE_DONE) {
242  LOG_ERROR << "Can't set client_pkey: " << db.errmsg();
243  return;
244  }
245 
246  db.commitTransaction();
247 }
248 
249 bool SQLStorage::loadTlsCreds(std::string* ca, std::string* cert, std::string* pkey) {
250  SQLite3Guard db = dbConnection();
251 
252  if (!db.beginTransaction()) {
253  LOG_ERROR << "Can't start transaction: " << db.errmsg();
254  return false;
255  }
256  auto statement = db.prepareStatement("SELECT ca_cert, client_cert, client_pkey FROM tls_creds LIMIT 1;");
257 
258  int result = statement.step();
259  if (result == SQLITE_DONE) {
260  LOG_TRACE << "Tls creds not present";
261  return false;
262  } else if (result != SQLITE_ROW) {
263  LOG_ERROR << "Can't get tls_creds: " << db.errmsg();
264  return false;
265  }
266 
267  std::string ca_v, cert_v, pkey_v;
268  try {
269  ca_v = statement.get_result_col_str(0).value();
270  cert_v = statement.get_result_col_str(1).value();
271  pkey_v = statement.get_result_col_str(2).value();
272  } catch (const boost::bad_optional_access&) {
273  return false;
274  }
275 
276  if (ca != nullptr) {
277  *ca = std::move(ca_v);
278  }
279  if (cert != nullptr) {
280  *cert = std::move(cert_v);
281  }
282  if (pkey != nullptr) {
283  *pkey = std::move(pkey_v);
284  }
285 
286  db.commitTransaction();
287 
288  return true;
289 }
290 
291 void SQLStorage::clearTlsCreds() {
292  SQLite3Guard db = dbConnection();
293 
294  if (db.exec("DELETE FROM tls_creds;", nullptr, nullptr) != SQLITE_OK) {
295  LOG_ERROR << "Can't clear tls_creds: " << db.errmsg();
296  return;
297  }
298 }
299 
300 bool SQLStorage::loadTlsCa(std::string* ca) {
301  SQLite3Guard db = dbConnection();
302 
303  auto statement = db.prepareStatement("SELECT ca_cert FROM tls_creds LIMIT 1;");
304 
305  int result = statement.step();
306  if (result == SQLITE_DONE) {
307  LOG_TRACE << "ca_cert not present";
308  return false;
309  } else if (result != SQLITE_ROW) {
310  LOG_ERROR << "Can't get ca_cert: " << db.errmsg();
311  return false;
312  }
313 
314  auto ca_r = statement.get_result_col_str(0);
315  if (ca_r == boost::none) {
316  return false;
317  }
318 
319  if (ca != nullptr) {
320  *ca = std::move(ca_r.value());
321  }
322 
323  return true;
324 }
325 
326 bool SQLStorage::loadTlsCert(std::string* cert) {
327  SQLite3Guard db = dbConnection();
328 
329  auto statement = db.prepareStatement("SELECT client_cert FROM tls_creds LIMIT 1;");
330 
331  int result = statement.step();
332  if (result == SQLITE_DONE) {
333  LOG_TRACE << "client_cert not present in db";
334  return false;
335  } else if (result != SQLITE_ROW) {
336  LOG_ERROR << "Can't get client_cert: " << db.errmsg();
337  return false;
338  }
339 
340  auto cert_r = statement.get_result_col_str(0);
341  if (cert_r == boost::none) {
342  return false;
343  }
344 
345  if (cert != nullptr) {
346  *cert = std::move(cert_r.value());
347  }
348 
349  return true;
350 }
351 
352 bool SQLStorage::loadTlsPkey(std::string* pkey) {
353  SQLite3Guard db = dbConnection();
354 
355  auto statement = db.prepareStatement("SELECT client_pkey FROM tls_creds LIMIT 1;");
356 
357  int result = statement.step();
358  if (result == SQLITE_DONE) {
359  LOG_TRACE << "client_pkey not present in db";
360  return false;
361  } else if (result != SQLITE_ROW) {
362  LOG_ERROR << "Can't get client_pkey: " << db.errmsg();
363  return false;
364  }
365 
366  auto pkey_r = statement.get_result_col_str(0);
367  if (pkey_r == boost::none) {
368  return false;
369  }
370 
371  if (pkey != nullptr) {
372  *pkey = std::move(pkey_r.value());
373  }
374 
375  return true;
376 }
377 
378 void SQLStorage::storeRoot(const std::string& data, Uptane::RepositoryType repo, Uptane::Version version) {
379  SQLite3Guard db = dbConnection();
380 
381  if (!db.beginTransaction()) {
382  LOG_ERROR << "Can't start transaction: " << db.errmsg();
383  return;
384  }
385 
386  auto del_statement =
387  db.prepareStatement<int, int, int>("DELETE FROM meta WHERE (repo=? AND meta_type=? AND version=?);",
388  static_cast<int>(repo), Uptane::Role::Root().ToInt(), version.version());
389 
390  if (del_statement.step() != SQLITE_DONE) {
391  LOG_ERROR << "Can't clear root metadata: " << db.errmsg();
392  return;
393  }
394 
395  auto ins_statement = db.prepareStatement<SQLBlob, int, int, int>("INSERT INTO meta VALUES (?, ?, ?, ?);",
396  SQLBlob(data), static_cast<int>(repo),
397  Uptane::Role::Root().ToInt(), version.version());
398 
399  if (ins_statement.step() != SQLITE_DONE) {
400  LOG_ERROR << "Can't add metadata: " << db.errmsg();
401  return;
402  }
403 
404  db.commitTransaction();
405 }
406 
407 void SQLStorage::storeNonRoot(const std::string& data, Uptane::RepositoryType repo, const Uptane::Role role) {
408  SQLite3Guard db = dbConnection();
409 
410  if (!db.beginTransaction()) {
411  LOG_ERROR << "Can't start transaction: " << db.errmsg();
412  return;
413  }
414 
415  auto del_statement = db.prepareStatement<int, int>("DELETE FROM meta WHERE (repo=? AND meta_type=?);",
416  static_cast<int>(repo), role.ToInt());
417 
418  if (del_statement.step() != SQLITE_DONE) {
419  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
420  return;
421  }
422 
423  auto ins_statement =
424  db.prepareStatement<SQLBlob, int, int, int>("INSERT INTO meta VALUES (?, ?, ?, ?);", SQLBlob(data),
425  static_cast<int>(repo), role.ToInt(), Uptane::Version().version());
426 
427  if (ins_statement.step() != SQLITE_DONE) {
428  LOG_ERROR << "Can't add metadata: " << db.errmsg();
429  return;
430  }
431 
432  db.commitTransaction();
433 }
434 
435 bool SQLStorage::loadRoot(std::string* data, Uptane::RepositoryType repo, Uptane::Version version) {
436  SQLite3Guard db = dbConnection();
437 
438  // version < 0 => latest metadata requested
439  if (version.version() < 0) {
440  auto statement = db.prepareStatement<int, int>(
441  "SELECT meta FROM meta WHERE (repo=? AND meta_type=?) ORDER BY version DESC LIMIT 1;", static_cast<int>(repo),
442  Uptane::Role::Root().ToInt());
443  int result = statement.step();
444 
445  if (result == SQLITE_DONE) {
446  LOG_TRACE << "Meta not present";
447  return false;
448  } else if (result != SQLITE_ROW) {
449  LOG_ERROR << "Can't get meta: " << db.errmsg();
450  return false;
451  }
452  if (data != nullptr) {
453  *data = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
454  }
455  } else {
456  auto statement =
457  db.prepareStatement<int, int, int>("SELECT meta FROM meta WHERE (repo=? AND meta_type=? AND version=?);",
458  static_cast<int>(repo), Uptane::Role::Root().ToInt(), version.version());
459 
460  int result = statement.step();
461 
462  if (result == SQLITE_DONE) {
463  LOG_TRACE << "Meta not present";
464  return false;
465  } else if (result != SQLITE_ROW) {
466  LOG_ERROR << "Can't get meta: " << db.errmsg();
467  return false;
468  }
469 
470  const auto blob = reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0));
471  if (blob == nullptr) {
472  LOG_ERROR << "Can't get meta: " << db.errmsg();
473  return false;
474  }
475 
476  if (data != nullptr) {
477  *data = std::string(blob);
478  }
479  }
480 
481  return true;
482 }
483 
484 bool SQLStorage::loadNonRoot(std::string* data, Uptane::RepositoryType repo, const Uptane::Role role) {
485  SQLite3Guard db = dbConnection();
486 
487  auto statement = db.prepareStatement<int, int>(
488  "SELECT meta FROM meta WHERE (repo=? AND meta_type=?) ORDER BY version DESC LIMIT 1;", static_cast<int>(repo),
489  role.ToInt());
490  int result = statement.step();
491 
492  if (result == SQLITE_DONE) {
493  LOG_TRACE << "Meta not present";
494  return false;
495  } else if (result != SQLITE_ROW) {
496  LOG_ERROR << "Can't get meta: " << db.errmsg();
497  return false;
498  }
499  if (data != nullptr) {
500  *data = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
501  }
502 
503  return true;
504 }
505 
506 void SQLStorage::clearNonRootMeta(Uptane::RepositoryType repo) {
507  SQLite3Guard db = dbConnection();
508 
509  auto del_statement =
510  db.prepareStatement<int>("DELETE FROM meta WHERE (repo=? AND meta_type != 0);", static_cast<int>(repo));
511 
512  if (del_statement.step() != SQLITE_DONE) {
513  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
514  }
515 }
516 
517 void SQLStorage::clearMetadata() {
518  SQLite3Guard db = dbConnection();
519 
520  if (db.exec("DELETE FROM meta;", nullptr, nullptr) != SQLITE_OK) {
521  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
522  return;
523  }
524 }
525 
526 void SQLStorage::storeDelegation(const std::string& data, const Uptane::Role role) {
527  SQLite3Guard db = dbConnection();
528 
529  if (!db.beginTransaction()) {
530  LOG_ERROR << "Can't start transaction: " << db.errmsg();
531  return;
532  }
533 
534  auto statement = db.prepareStatement<SQLBlob, std::string>("INSERT OR REPLACE INTO delegations VALUES (?, ?);",
535  SQLBlob(data), role.ToString());
536 
537  if (statement.step() != SQLITE_DONE) {
538  LOG_ERROR << "Can't add delegation metadata: " << db.errmsg();
539  return;
540  }
541 
542  db.commitTransaction();
543 }
544 
545 bool SQLStorage::loadDelegation(std::string* data, const Uptane::Role role) {
546  SQLite3Guard db = dbConnection();
547 
548  auto statement =
549  db.prepareStatement<std::string>("SELECT meta FROM delegations WHERE role_name=? LIMIT 1;", role.ToString());
550  int result = statement.step();
551 
552  if (result == SQLITE_DONE) {
553  LOG_TRACE << "Delegations metadata not present";
554  return false;
555  } else if (result != SQLITE_ROW) {
556  LOG_ERROR << "Can't get delegations metadata: " << db.errmsg();
557  return false;
558  }
559  if (data != nullptr) {
560  *data = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
561  }
562 
563  return true;
564 }
565 
566 bool SQLStorage::loadAllDelegations(std::vector<std::pair<Uptane::Role, std::string>>& data) const {
567  bool result = false;
568 
569  try {
570  SQLite3Guard db = dbConnection();
571 
572  auto statement = db.prepareStatement("SELECT meta, role_name FROM delegations;");
573  auto statement_state = statement.step();
574 
575  if (statement_state == SQLITE_DONE) {
576  LOG_TRACE << "Delegations metadata are not present";
577  return true;
578  } else if (statement_state != SQLITE_ROW) {
579  LOG_ERROR << "Can't get delegations metadata: " << db.errmsg();
580  return false;
581  }
582 
583  do {
584  data.emplace_back(Uptane::Role::Delegation(statement.get_result_col_str(1).value()),
585  statement.get_result_col_blob(0).value());
586  } while ((statement_state = statement.step()) == SQLITE_ROW);
587 
588  result = true;
589  } catch (const std::exception& exc) {
590  LOG_ERROR << "Failed to fetch records from `delegations` table: " << exc.what();
591  }
592 
593  return result;
594 }
595 
596 void SQLStorage::deleteDelegation(const Uptane::Role role) {
597  SQLite3Guard db = dbConnection();
598 
599  auto statement = db.prepareStatement<std::string>("DELETE FROM delegations WHERE role_name=?;", role.ToString());
600  statement.step();
601 }
602 
603 void SQLStorage::clearDelegations() {
604  SQLite3Guard db = dbConnection();
605 
606  if (db.exec("DELETE FROM delegations;", nullptr, nullptr) != SQLITE_OK) {
607  LOG_ERROR << "Can't clear delegations metadata: " << db.errmsg();
608  }
609 }
610 
611 void SQLStorage::storeDeviceId(const std::string& device_id) {
612  SQLite3Guard db = dbConnection();
613 
614  auto statement = db.prepareStatement<std::string>(
615  "INSERT OR REPLACE INTO device_info(unique_mark,device_id,is_registered) VALUES(0,?,0);", device_id);
616  if (statement.step() != SQLITE_DONE) {
617  LOG_ERROR << "Can't set device ID: " << db.errmsg();
618  return;
619  }
620 }
621 
622 bool SQLStorage::loadDeviceId(std::string* device_id) {
623  SQLite3Guard db = dbConnection();
624 
625  auto statement = db.prepareStatement("SELECT device_id FROM device_info LIMIT 1;");
626 
627  int result = statement.step();
628  if (result == SQLITE_DONE) {
629  LOG_TRACE << "device_id not present in db";
630  return false;
631  } else if (result != SQLITE_ROW) {
632  LOG_ERROR << "Can't get device ID: " << db.errmsg();
633  return false;
634  }
635 
636  auto did = statement.get_result_col_str(0);
637  if (did == boost::none) {
638  LOG_ERROR << "Empty device ID" << db.errmsg();
639  return false;
640  }
641 
642  if (device_id != nullptr) {
643  *device_id = std::move(did.value());
644  }
645 
646  return true;
647 }
648 
649 void SQLStorage::clearDeviceId() {
650  SQLite3Guard db = dbConnection();
651 
652  if (db.exec("DELETE FROM device_info;", nullptr, nullptr) != SQLITE_OK) {
653  LOG_ERROR << "Can't clear device ID: " << db.errmsg();
654  return;
655  }
656 }
657 
658 void SQLStorage::storeEcuRegistered() {
659  SQLite3Guard db = dbConnection();
660 
661  if (!db.beginTransaction()) {
662  LOG_ERROR << "Can't start transaction: " << db.errmsg();
663  return;
664  }
665 
666  auto statement = db.prepareStatement("SELECT count(*) FROM device_info;");
667  if (statement.step() != SQLITE_ROW) {
668  throw std::runtime_error("Could not get device_info count");
669  }
670  if (statement.get_result_col_int(0) != 1) {
671  throw std::runtime_error("Cannot set ecu registered if no device_info set");
672  }
673 
674  std::string req = "UPDATE device_info SET is_registered = 1";
675  if (db.exec(req.c_str(), nullptr, nullptr) != SQLITE_OK) {
676  LOG_ERROR << "Can't set is_registered: " << db.errmsg();
677  return;
678  }
679 
680  db.commitTransaction();
681 }
682 
683 bool SQLStorage::loadEcuRegistered() {
684  SQLite3Guard db = dbConnection();
685 
686  auto statement = db.prepareStatement("SELECT is_registered FROM device_info LIMIT 1;");
687 
688  int result = statement.step();
689  if (result == SQLITE_DONE) {
690  return false;
691  } else if (result != SQLITE_ROW) {
692  LOG_ERROR << "Can't get is_registered in device_info " << db.errmsg();
693  return false;
694  }
695 
696  return statement.get_result_col_int(0) != 0;
697 }
698 
699 void SQLStorage::clearEcuRegistered() {
700  SQLite3Guard db = dbConnection();
701 
702  // note: if the table is empty, nothing is done but that's fine
703  std::string req = "UPDATE device_info SET is_registered = 0";
704  if (db.exec(req.c_str(), nullptr, nullptr) != SQLITE_OK) {
705  LOG_ERROR << "Can't set is_registered: " << db.errmsg();
706  return;
707  }
708 }
709 
710 void SQLStorage::storeNeedReboot() {
711  SQLite3Guard db = dbConnection();
712 
713  auto statement = db.prepareStatement<int>("INSERT OR REPLACE INTO need_reboot(unique_mark,flag) VALUES(0,?);", 1);
714  if (statement.step() != SQLITE_DONE) {
715  LOG_ERROR << "Can't set need_reboot: " << db.errmsg();
716  return;
717  }
718 }
719 
720 bool SQLStorage::loadNeedReboot(bool* need_reboot) {
721  SQLite3Guard db = dbConnection();
722 
723  auto statement = db.prepareStatement("SELECT flag FROM need_reboot LIMIT 1;");
724 
725  int result = statement.step();
726  if (result == SQLITE_DONE) {
727  if (need_reboot != nullptr) {
728  *need_reboot = false;
729  }
730  return true;
731  } else if (result != SQLITE_ROW) {
732  LOG_ERROR << "Can't get need_reboot: " << db.errmsg();
733  return false;
734  }
735 
736  auto flag = static_cast<bool>(statement.get_result_col_int(0));
737  if (need_reboot != nullptr) {
738  *need_reboot = flag;
739  }
740 
741  return true;
742 }
743 
744 void SQLStorage::clearNeedReboot() {
745  SQLite3Guard db = dbConnection();
746 
747  if (db.exec("DELETE FROM need_reboot;", nullptr, nullptr) != SQLITE_OK) {
748  LOG_ERROR << "Can't clear need_reboot: " << db.errmsg();
749  return;
750  }
751 }
752 
753 void SQLStorage::storeEcuSerials(const EcuSerials& serials) {
754  if (serials.size() >= 1) {
755  SQLite3Guard db = dbConnection();
756 
757  if (!db.beginTransaction()) {
758  LOG_ERROR << "Can't start transaction: " << db.errmsg();
759  return;
760  }
761 
762  if (db.exec("DELETE FROM ecu_serials;", nullptr, nullptr) != SQLITE_OK) {
763  LOG_ERROR << "Can't clear ecu_serials: " << db.errmsg();
764  return;
765  }
766 
767  // first is the primary
768  std::string serial = serials[0].first.ToString();
769  std::string hwid = serials[0].second.ToString();
770  {
771  auto statement = db.prepareStatement<std::string, std::string>(
772  "INSERT INTO ecu_serials(id, serial,hardware_id,is_primary) VALUES (0, ?,?,1);", serial, hwid);
773  if (statement.step() != SQLITE_DONE) {
774  LOG_ERROR << "Can't set ecu_serial: " << db.errmsg();
775  return;
776  }
777 
778  // update lazily stored installed version
779  auto statement_ivupdate = db.prepareStatement<std::string>(
780  "UPDATE installed_versions SET ecu_serial = ? WHERE ecu_serial = '';", serial);
781 
782  if (statement_ivupdate.step() != SQLITE_DONE) {
783  LOG_ERROR << "Can't set ecu_serial: " << db.errmsg();
784  return;
785  }
786  }
787 
788  for (auto it = serials.cbegin() + 1; it != serials.cend(); it++) {
789  auto statement = db.prepareStatement<int64_t, std::string, std::string>(
790  "INSERT INTO ecu_serials(id,serial,hardware_id) VALUES (?,?,?);", it - serials.cbegin(), it->first.ToString(),
791  it->second.ToString());
792 
793  if (statement.step() != SQLITE_DONE) {
794  LOG_ERROR << "Can't set ecu_serial: " << db.errmsg();
795  return;
796  }
797  }
798 
799  db.commitTransaction();
800  }
801 }
802 
803 bool SQLStorage::loadEcuSerials(EcuSerials* serials) {
804  SQLite3Guard db = dbConnection();
805 
806  // order by auto-incremented primary key so that the ecu order is kept constant
807  auto statement = db.prepareStatement("SELECT serial, hardware_id FROM ecu_serials ORDER BY id;");
808  int statement_state;
809 
810  EcuSerials new_serials;
811  bool empty = true;
812  while ((statement_state = statement.step()) == SQLITE_ROW) {
813  try {
814  new_serials.emplace_back(Uptane::EcuSerial(statement.get_result_col_str(0).value()),
815  Uptane::HardwareIdentifier(statement.get_result_col_str(1).value()));
816  empty = false;
817  } catch (const boost::bad_optional_access&) {
818  return false;
819  }
820  }
821 
822  if (statement_state != SQLITE_DONE) {
823  LOG_ERROR << "Can't get ecu_serials: " << db.errmsg();
824  return false;
825  }
826 
827  if (serials != nullptr) {
828  *serials = std::move(new_serials);
829  }
830 
831  return !empty;
832 }
833 
834 void SQLStorage::clearEcuSerials() {
835  SQLite3Guard db = dbConnection();
836 
837  if (db.exec("DELETE FROM ecu_serials;", nullptr, nullptr) != SQLITE_OK) {
838  LOG_ERROR << "Can't clear ecu_serials: " << db.errmsg();
839  return;
840  }
841 }
842 
843 void SQLStorage::storeMisconfiguredEcus(const std::vector<MisconfiguredEcu>& ecus) {
844  if (ecus.size() >= 1) {
845  SQLite3Guard db = dbConnection();
846 
847  if (!db.beginTransaction()) {
848  LOG_ERROR << "Can't start transaction: " << db.errmsg();
849  return;
850  }
851 
852  if (db.exec("DELETE FROM misconfigured_ecus;", nullptr, nullptr) != SQLITE_OK) {
853  LOG_ERROR << "Can't clear misconfigured_ecus: " << db.errmsg();
854  return;
855  }
856 
857  std::vector<MisconfiguredEcu>::const_iterator it;
858  for (it = ecus.begin(); it != ecus.end(); it++) {
859  auto statement = db.prepareStatement<std::string, std::string, int>(
860  "INSERT INTO misconfigured_ecus VALUES (?,?,?);", it->serial.ToString(), it->hardware_id.ToString(),
861  static_cast<int>(it->state));
862 
863  if (statement.step() != SQLITE_DONE) {
864  LOG_ERROR << "Can't set misconfigured_ecus: " << db.errmsg();
865  return;
866  }
867  }
868 
869  db.commitTransaction();
870  }
871 }
872 
873 bool SQLStorage::loadMisconfiguredEcus(std::vector<MisconfiguredEcu>* ecus) {
874  SQLite3Guard db = dbConnection();
875 
876  auto statement = db.prepareStatement("SELECT serial, hardware_id, state FROM misconfigured_ecus;");
877  int statement_state;
878 
879  std::vector<MisconfiguredEcu> new_ecus;
880  bool empty = true;
881  while ((statement_state = statement.step()) == SQLITE_ROW) {
882  try {
883  new_ecus.emplace_back(Uptane::EcuSerial(statement.get_result_col_str(0).value()),
884  Uptane::HardwareIdentifier(statement.get_result_col_str(1).value()),
885  static_cast<EcuState>(statement.get_result_col_int(2)));
886  empty = false;
887  } catch (const boost::bad_optional_access&) {
888  return false;
889  }
890  }
891 
892  if (statement_state != SQLITE_DONE) {
893  LOG_ERROR << "Can't get misconfigured_ecus: " << db.errmsg();
894  return false;
895  }
896 
897  if (ecus != nullptr) {
898  *ecus = std::move(new_ecus);
899  }
900 
901  return !empty;
902 }
903 
904 void SQLStorage::clearMisconfiguredEcus() {
905  SQLite3Guard db = dbConnection();
906 
907  if (db.exec("DELETE FROM misconfigured_ecus;", nullptr, nullptr) != SQLITE_OK) {
908  LOG_ERROR << "Can't clear misconfigured_ecus: " << db.errmsg();
909  return;
910  }
911 }
912 
913 void SQLStorage::saveInstalledVersion(const std::string& ecu_serial, const Uptane::Target& target,
914  InstalledVersionUpdateMode update_mode) {
915  SQLite3Guard db = dbConnection();
916 
917  if (!db.beginTransaction()) {
918  LOG_ERROR << "Can't start transaction: " << db.errmsg();
919  return;
920  }
921 
922  // either adds a new entry or update the last one's status
923 
924  // empty serial: use primary
925  std::string ecu_serial_real = ecu_serial;
926  if (ecu_serial_real.empty()) {
927  auto statement = db.prepareStatement("SELECT serial FROM ecu_serials WHERE is_primary = 1;");
928  if (statement.step() == SQLITE_ROW) {
929  ecu_serial_real = statement.get_result_col_str(0).value();
930  } else {
931  LOG_WARNING << "Could not find primary ecu serial, set to lazy init mode";
932  }
933  }
934 
935  std::string hashes_encoded = Uptane::Hash::encodeVector(target.hashes());
936 
937  // get the last time this version was installed on this ecu
938  boost::optional<int64_t> old_id;
939  bool old_was_installed = false;
940  {
941  auto statement = db.prepareStatement<std::string>(
942  "SELECT id, sha256, name, was_installed FROM installed_versions WHERE ecu_serial = ? ORDER BY id DESC "
943  "LIMIT 1;",
944  ecu_serial_real);
945 
946  if (statement.step() == SQLITE_ROW) {
947  int64_t rid = statement.get_result_col_int(0);
948  std::string rsha256 = statement.get_result_col_str(1).value_or("");
949  std::string rname = statement.get_result_col_str(2).value_or("");
950  bool rwasi = statement.get_result_col_int(3) == 1;
951 
952  if (rsha256 == target.sha256Hash() && rname == target.filename()) {
953  old_id = rid;
954  old_was_installed = rwasi;
955  }
956  }
957  }
958 
959  if (update_mode == InstalledVersionUpdateMode::kCurrent) {
960  // unset 'current' and 'pending' on all versions for this ecu
961  auto statement = db.prepareStatement<std::string>(
962  "UPDATE installed_versions SET is_current = 0, is_pending = 0 WHERE ecu_serial = ?", ecu_serial_real);
963  if (statement.step() != SQLITE_DONE) {
964  LOG_ERROR << "Can't set installed_versions: " << db.errmsg();
965  return;
966  }
967  } else if (update_mode == InstalledVersionUpdateMode::kPending) {
968  // unset 'pending' on all versions for this ecu
969  auto statement = db.prepareStatement<std::string>(
970  "UPDATE installed_versions SET is_pending = 0 WHERE ecu_serial = ?", ecu_serial_real);
971  if (statement.step() != SQLITE_DONE) {
972  LOG_ERROR << "Can't set installed_versions: " << db.errmsg();
973  return;
974  }
975  }
976 
977  if (!!old_id) {
978  auto statement = db.prepareStatement<std::string, int, int, int64_t>(
979  "UPDATE installed_versions SET correlation_id = ?, is_current = ?, is_pending = ?, was_installed = ? WHERE id "
980  "= ?;",
981  target.correlation_id(), static_cast<int>(update_mode == InstalledVersionUpdateMode::kCurrent),
982  static_cast<int>(update_mode == InstalledVersionUpdateMode::kPending),
983  static_cast<int>(update_mode == InstalledVersionUpdateMode::kCurrent || old_was_installed), old_id.value());
984 
985  if (statement.step() != SQLITE_DONE) {
986  LOG_ERROR << "Can't set installed_versions: " << db.errmsg();
987  return;
988  }
989  } else {
990  std::string custom = Utils::jsonToCanonicalStr(target.custom_data());
991  auto statement = db.prepareStatement<std::string, std::string, std::string, std::string, int64_t, std::string,
992  std::string, int, int>(
993  "INSERT INTO installed_versions(ecu_serial, sha256, name, hashes, length, custom_meta, correlation_id, "
994  "is_current, is_pending, was_installed) VALUES (?,?,?,?,?,?,?,?,?,?);",
995  ecu_serial_real, target.sha256Hash(), target.filename(), hashes_encoded, static_cast<int64_t>(target.length()),
996  custom, target.correlation_id(), static_cast<int>(update_mode == InstalledVersionUpdateMode::kCurrent),
997  static_cast<int>(update_mode == InstalledVersionUpdateMode::kPending),
998  static_cast<int>(update_mode == InstalledVersionUpdateMode::kCurrent));
999 
1000  if (statement.step() != SQLITE_DONE) {
1001  LOG_ERROR << "Can't set installed_versions: " << db.errmsg();
1002  return;
1003  }
1004  }
1005 
1006  db.commitTransaction();
1007 }
1008 
1009 static void loadEcuMap(SQLite3Guard& db, std::string& ecu_serial, Uptane::EcuMap& ecu_map) {
1010  if (ecu_serial.empty()) {
1011  auto statement = db.prepareStatement("SELECT serial FROM ecu_serials WHERE is_primary = 1;");
1012  if (statement.step() == SQLITE_ROW) {
1013  ecu_serial = statement.get_result_col_str(0).value();
1014  } else {
1015  LOG_WARNING << "Could not find primary ecu serial, defaulting to empty serial: " << db.errmsg();
1016  }
1017  }
1018 
1019  {
1020  auto statement =
1021  db.prepareStatement<std::string>("SELECT hardware_id FROM ecu_serials WHERE serial = ?;", ecu_serial);
1022  if (statement.step() == SQLITE_ROW) {
1023  ecu_map.insert(
1024  {Uptane::EcuSerial(ecu_serial), Uptane::HardwareIdentifier(statement.get_result_col_str(0).value())});
1025  } else {
1026  LOG_WARNING << "Could not find hardware_id for serial " << ecu_serial << ": " << db.errmsg();
1027  }
1028  }
1029 }
1030 
1031 bool SQLStorage::loadInstallationLog(const std::string& ecu_serial, std::vector<Uptane::Target>* log,
1032  bool only_installed) {
1033  SQLite3Guard db = dbConnection();
1034 
1035  std::string ecu_serial_real = ecu_serial;
1036  Uptane::EcuMap ecu_map;
1037  loadEcuMap(db, ecu_serial_real, ecu_map);
1038 
1039  std::string query =
1040  "SELECT id, sha256, name, hashes, length, correlation_id, custom_meta FROM installed_versions WHERE "
1041  "ecu_serial = ? ORDER BY id;";
1042  if (only_installed) {
1043  query =
1044  "SELECT id, sha256, name, hashes, length, correlation_id, custom_meta FROM installed_versions WHERE "
1045  "ecu_serial = ? AND was_installed = 1 ORDER BY id;";
1046  }
1047 
1048  auto statement = db.prepareStatement<std::string>(query, ecu_serial_real);
1049  int statement_state;
1050 
1051  std::vector<Uptane::Target> new_log;
1052  std::map<int64_t, size_t> ids_map;
1053  size_t k = 0;
1054  while ((statement_state = statement.step()) == SQLITE_ROW) {
1055  try {
1056  auto id = statement.get_result_col_int(0);
1057  auto sha256 = statement.get_result_col_str(1).value();
1058  auto filename = statement.get_result_col_str(2).value();
1059  auto hashes_str = statement.get_result_col_str(3).value();
1060  auto length = statement.get_result_col_int(4);
1061  auto correlation_id = statement.get_result_col_str(5).value();
1062  auto custom_str = statement.get_result_col_str(6).value();
1063 
1064  // note: sha256 should always be present and is used to uniquely identify
1065  // a version. It should normally be part of the hash list as well.
1066  std::vector<Uptane::Hash> hashes = Uptane::Hash::decodeVector(hashes_str);
1067 
1068  auto find_sha256 = std::find_if(hashes.cbegin(), hashes.cend(),
1069  [](const Uptane::Hash& h) { return h.type() == Uptane::Hash::Type::kSha256; });
1070  if (find_sha256 == hashes.cend()) {
1071  LOG_WARNING << "No sha256 in hashes list";
1072  hashes.emplace_back(Uptane::Hash::Type::kSha256, sha256);
1073  }
1074 
1075  Uptane::Target t(filename, ecu_map, hashes, static_cast<uint64_t>(length), correlation_id);
1076  if (!custom_str.empty()) {
1077  std::istringstream css(custom_str);
1078  std::string errs;
1079  Json::Value custom;
1080  if (Json::parseFromStream(Json::CharReaderBuilder(), css, &custom, nullptr)) {
1081  t.updateCustom(custom);
1082  } else {
1083  LOG_ERROR << "Unable to parse custom data: " << errs;
1084  }
1085  }
1086  new_log.emplace_back(t);
1087 
1088  ids_map[id] = k;
1089  k++;
1090  } catch (const boost::bad_optional_access&) {
1091  LOG_ERROR << "Incompleted installed version, keeping old one";
1092  return false;
1093  }
1094  }
1095 
1096  if (statement_state != SQLITE_DONE) {
1097  LOG_ERROR << "Can't get installed_versions: " << db.errmsg();
1098  return false;
1099  }
1100 
1101  if (log == nullptr) {
1102  return true;
1103  }
1104 
1105  *log = std::move(new_log);
1106 
1107  return true;
1108 }
1109 
1110 bool SQLStorage::loadInstalledVersions(const std::string& ecu_serial, boost::optional<Uptane::Target>* current_version,
1111  boost::optional<Uptane::Target>* pending_version) {
1112  SQLite3Guard db = dbConnection();
1113 
1114  std::string ecu_serial_real = ecu_serial;
1115  Uptane::EcuMap ecu_map;
1116  loadEcuMap(db, ecu_serial_real, ecu_map);
1117 
1118  auto read_target = [&ecu_map](SQLiteStatement& statement) -> Uptane::Target {
1119  auto sha256 = statement.get_result_col_str(0).value();
1120  auto filename = statement.get_result_col_str(1).value();
1121  auto hashes_str = statement.get_result_col_str(2).value();
1122  auto length = statement.get_result_col_int(3);
1123  auto correlation_id = statement.get_result_col_str(4).value();
1124  auto custom_str = statement.get_result_col_str(5).value();
1125 
1126  // note: sha256 should always be present and is used to uniquely identify
1127  // a version. It should normally be part of the hash list as well.
1128  std::vector<Uptane::Hash> hashes = Uptane::Hash::decodeVector(hashes_str);
1129 
1130  auto find_sha256 = std::find_if(hashes.cbegin(), hashes.cend(),
1131  [](const Uptane::Hash& h) { return h.type() == Uptane::Hash::Type::kSha256; });
1132  if (find_sha256 == hashes.cend()) {
1133  LOG_WARNING << "No sha256 in hashes list";
1134  hashes.emplace_back(Uptane::Hash::Type::kSha256, sha256);
1135  }
1136  Uptane::Target t(filename, ecu_map, hashes, static_cast<uint64_t>(length), correlation_id);
1137  if (!custom_str.empty()) {
1138  std::istringstream css(custom_str);
1139  Json::Value custom;
1140  std::string errs;
1141  if (Json::parseFromStream(Json::CharReaderBuilder(), css, &custom, &errs)) {
1142  t.updateCustom(custom);
1143  } else {
1144  LOG_ERROR << "Unable to parse custom data: " << errs;
1145  }
1146  }
1147 
1148  return t;
1149  };
1150 
1151  if (current_version != nullptr) {
1152  auto statement = db.prepareStatement<std::string>(
1153  "SELECT sha256, name, hashes, length, correlation_id, custom_meta FROM installed_versions WHERE "
1154  "ecu_serial = ? AND is_current = 1 LIMIT 1;",
1155  ecu_serial_real);
1156 
1157  if (statement.step() == SQLITE_ROW) {
1158  try {
1159  *current_version = read_target(statement);
1160  } catch (const boost::bad_optional_access&) {
1161  LOG_ERROR << "Could not read current installed version";
1162  return false;
1163  }
1164  } else {
1165  LOG_TRACE << "Cannot get current installed version: " << db.errmsg();
1166  *current_version = boost::none;
1167  }
1168  }
1169 
1170  if (pending_version != nullptr) {
1171  auto statement = db.prepareStatement<std::string>(
1172  "SELECT sha256, name, hashes, length, correlation_id, custom_meta FROM installed_versions WHERE "
1173  "ecu_serial = ? AND is_pending = 1 LIMIT 1;",
1174  ecu_serial_real);
1175 
1176  if (statement.step() == SQLITE_ROW) {
1177  try {
1178  *pending_version = read_target(statement);
1179  } catch (const boost::bad_optional_access&) {
1180  LOG_ERROR << "Could not read pending installed version";
1181  return false;
1182  }
1183  } else {
1184  LOG_TRACE << "Cannot get pending installed version: " << db.errmsg();
1185  *pending_version = boost::none;
1186  }
1187  }
1188 
1189  return true;
1190 }
1191 
1192 bool SQLStorage::hasPendingInstall() {
1193  SQLite3Guard db = dbConnection();
1194 
1195  auto statement = db.prepareStatement("SELECT count(*) FROM installed_versions where is_pending = 1");
1196  if (statement.step() != SQLITE_ROW) {
1197  LOG_ERROR << "Can't get tables count: " << db.errmsg();
1198  throw std::runtime_error("Could not count pending installations");
1199  }
1200 
1201  return statement.get_result_col_int(0) > 0;
1202 }
1203 
1204 void SQLStorage::getPendingEcus(std::vector<std::pair<Uptane::EcuSerial, Uptane::Hash>>* pendingEcus) {
1205  SQLite3Guard db = dbConnection();
1206 
1207  auto statement = db.prepareStatement("SELECT ecu_serial, sha256 FROM installed_versions where is_pending = 1");
1208  int statement_result = statement.step();
1209  if (statement_result != SQLITE_DONE && statement_result != SQLITE_ROW) {
1210  throw std::runtime_error("Failed to get ECUs with a pending target installation: " + db.errmsg());
1211  }
1212 
1213  std::vector<std::pair<Uptane::EcuSerial, Uptane::Hash>> ecu_res;
1214 
1215  if (statement_result == SQLITE_DONE) {
1216  // if there are no any record in the DB
1217  return;
1218  }
1219 
1220  for (; statement_result != SQLITE_DONE; statement_result = statement.step()) {
1221  std::string ecu_serial = statement.get_result_col_str(0).value();
1222  std::string hash = statement.get_result_col_str(1).value();
1223  ecu_res.emplace_back(
1224  std::make_pair(Uptane::EcuSerial(ecu_serial), Uptane::Hash(Uptane::Hash::Type::kSha256, hash)));
1225  }
1226 
1227  if (pendingEcus != nullptr) {
1228  *pendingEcus = std::move(ecu_res);
1229  }
1230 }
1231 
1232 void SQLStorage::clearInstalledVersions() {
1233  SQLite3Guard db = dbConnection();
1234 
1235  if (db.exec("DELETE FROM installed_versions;", nullptr, nullptr) != SQLITE_OK) {
1236  LOG_ERROR << "Can't clear installed_versions: " << db.errmsg();
1237  return;
1238  }
1239 }
1240 
1241 void SQLStorage::saveEcuInstallationResult(const Uptane::EcuSerial& ecu_serial,
1243  SQLite3Guard db = dbConnection();
1244 
1245  auto statement = db.prepareStatement<std::string, int, std::string, std::string>(
1246  "INSERT OR REPLACE INTO ecu_installation_results (ecu_serial, success, result_code, description) VALUES "
1247  "(?,?,?,?);",
1248  ecu_serial.ToString(), static_cast<int>(result.success), result.result_code.toRepr(), result.description);
1249  if (statement.step() != SQLITE_DONE) {
1250  LOG_ERROR << "Can't set ecu installation result: " << db.errmsg();
1251  return;
1252  }
1253 }
1254 
1255 bool SQLStorage::loadEcuInstallationResults(
1256  std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>>* results) {
1257  SQLite3Guard db = dbConnection();
1258 
1259  std::vector<std::pair<Uptane::EcuSerial, data::InstallationResult>> ecu_res;
1260 
1261  // keep the same order as in ecu_serials (start with primary)
1262  auto statement = db.prepareStatement(
1263  "SELECT ecu_serial, success, result_code, description FROM ecu_installation_results INNER JOIN ecu_serials ON "
1264  "ecu_serials.serial = ecu_serial ORDER BY ecu_serials.id;");
1265  int statement_result = statement.step();
1266  if (statement_result != SQLITE_DONE && statement_result != SQLITE_ROW) {
1267  LOG_ERROR << "Can't get ecu_installation_results: " << db.errmsg();
1268  return false;
1269  }
1270 
1271  if (statement_result == SQLITE_DONE) {
1272  // if there are no any record in the DB
1273  return false;
1274  }
1275 
1276  for (; statement_result != SQLITE_DONE; statement_result = statement.step()) {
1277  try {
1278  std::string ecu_serial = statement.get_result_col_str(0).value();
1279  auto success = static_cast<bool>(statement.get_result_col_int(1));
1280  data::ResultCode result_code = data::ResultCode::fromRepr(statement.get_result_col_str(2).value());
1281  std::string description = statement.get_result_col_str(3).value();
1282 
1283  ecu_res.emplace_back(Uptane::EcuSerial(ecu_serial), data::InstallationResult(success, result_code, description));
1284  } catch (const boost::bad_optional_access&) {
1285  return false;
1286  }
1287  }
1288 
1289  if (results != nullptr) {
1290  *results = std::move(ecu_res);
1291  }
1292 
1293  return true;
1294 }
1295 
1296 void SQLStorage::storeDeviceInstallationResult(const data::InstallationResult& result, const std::string& raw_report,
1297  const std::string& correlation_id) {
1298  SQLite3Guard db = dbConnection();
1299 
1300  auto statement = db.prepareStatement<int, std::string, std::string, std::string, std::string>(
1301  "INSERT OR REPLACE INTO device_installation_result (unique_mark, success, result_code, description, raw_report, "
1302  "correlation_id) "
1303  "VALUES (0,?,?,?,?,?);",
1304  static_cast<int>(result.success), result.result_code.toRepr(), result.description, raw_report, correlation_id);
1305  if (statement.step() != SQLITE_DONE) {
1306  LOG_ERROR << "Can't set device installation result: " << db.errmsg();
1307  return;
1308  }
1309 }
1310 
1311 bool SQLStorage::loadDeviceInstallationResult(data::InstallationResult* result, std::string* raw_report,
1312  std::string* correlation_id) {
1313  SQLite3Guard db = dbConnection();
1314 
1315  data::InstallationResult dev_res;
1316  std::string raw_report_res;
1317  std::string corrid_res;
1318 
1319  auto statement = db.prepareStatement(
1320  "SELECT success, result_code, description, raw_report, correlation_id FROM device_installation_result;");
1321  int statement_result = statement.step();
1322  if (statement_result == SQLITE_DONE) {
1323  LOG_TRACE << "No device installation result in db";
1324  return false;
1325  } else if (statement_result != SQLITE_ROW) {
1326  LOG_ERROR << "Can't get device_installation_result: " << db.errmsg();
1327  return false;
1328  }
1329 
1330  try {
1331  auto success = static_cast<bool>(statement.get_result_col_int(0));
1332  data::ResultCode result_code = data::ResultCode::fromRepr(statement.get_result_col_str(1).value());
1333  std::string description = statement.get_result_col_str(2).value();
1334  raw_report_res = statement.get_result_col_str(3).value();
1335  corrid_res = statement.get_result_col_str(4).value();
1336 
1337  dev_res = data::InstallationResult(success, result_code, description);
1338  } catch (const boost::bad_optional_access&) {
1339  return false;
1340  }
1341 
1342  if (result != nullptr) {
1343  *result = std::move(dev_res);
1344  }
1345 
1346  if (raw_report != nullptr) {
1347  *raw_report = std::move(raw_report_res);
1348  }
1349 
1350  if (correlation_id != nullptr) {
1351  *correlation_id = std::move(corrid_res);
1352  }
1353 
1354  return true;
1355 }
1356 
1357 void SQLStorage::saveEcuReportCounter(const Uptane::EcuSerial& ecu_serial, const int64_t counter) {
1358  SQLite3Guard db = dbConnection();
1359 
1360  auto statement = db.prepareStatement<std::string, int64_t>(
1361  "INSERT OR REPLACE INTO ecu_report_counter (ecu_serial, counter) VALUES "
1362  "(?,?);",
1363  ecu_serial.ToString(), counter);
1364  if (statement.step() != SQLITE_DONE) {
1365  LOG_ERROR << "Can't set ecu counter: " << db.errmsg();
1366  return;
1367  }
1368 }
1369 
1370 bool SQLStorage::loadEcuReportCounter(std::vector<std::pair<Uptane::EcuSerial, int64_t>>* results) {
1371  SQLite3Guard db = dbConnection();
1372 
1373  std::vector<std::pair<Uptane::EcuSerial, int64_t>> ecu_cnt;
1374 
1375  // keep the same order as in ecu_serials (start with primary)
1376  auto statement = db.prepareStatement(
1377  "SELECT ecu_serial, counter FROM ecu_report_counter INNER JOIN ecu_serials ON "
1378  "ecu_serials.serial = ecu_serial ORDER BY ecu_serials.id;");
1379  int statement_result = statement.step();
1380  if (statement_result != SQLITE_DONE && statement_result != SQLITE_ROW) {
1381  LOG_ERROR << "Can't get ecu_report_counter: " << db.errmsg();
1382  return false;
1383  }
1384 
1385  if (statement_result == SQLITE_DONE) {
1386  // if there are no any record in the DB
1387  return false;
1388  }
1389 
1390  for (; statement_result != SQLITE_DONE; statement_result = statement.step()) {
1391  try {
1392  std::string ecu_serial = statement.get_result_col_str(0).value();
1393  int64_t counter = statement.get_result_col_int(1);
1394 
1395  ecu_cnt.emplace_back(Uptane::EcuSerial(ecu_serial), counter);
1396  } catch (const boost::bad_optional_access&) {
1397  return false;
1398  }
1399  }
1400 
1401  if (results != nullptr) {
1402  *results = std::move(ecu_cnt);
1403  }
1404 
1405  return true;
1406 }
1407 
1408 void SQLStorage::clearInstallationResults() {
1409  SQLite3Guard db = dbConnection();
1410  if (!db.beginTransaction()) {
1411  LOG_ERROR << "Can't start transaction: " << db.errmsg();
1412  return;
1413  }
1414 
1415  if (db.exec("DELETE FROM device_installation_result;", nullptr, nullptr) != SQLITE_OK) {
1416  LOG_ERROR << "Can't clear device_installation_result: " << db.errmsg();
1417  return;
1418  }
1419 
1420  if (db.exec("DELETE FROM ecu_installation_results;", nullptr, nullptr) != SQLITE_OK) {
1421  LOG_ERROR << "Can't clear ecu_installation_results: " << db.errmsg();
1422  return;
1423  }
1424 
1425  db.commitTransaction();
1426 }
1427 
1428 bool SQLStorage::checkAvailableDiskSpace(const uint64_t required_bytes) const {
1429  struct statvfs stvfsbuf {};
1430  const int stat_res = statvfs(dbPath().c_str(), &stvfsbuf);
1431  if (stat_res < 0) {
1432  LOG_WARNING << "Unable to read filesystem statistics: error code " << stat_res;
1433  return true;
1434  }
1435  const uint64_t available_bytes = (stvfsbuf.f_bsize * stvfsbuf.f_bavail);
1436  const uint64_t reserved_bytes = 1 << 20;
1437 
1438  if (required_bytes + reserved_bytes < available_bytes) {
1439  return true;
1440  } else {
1441  LOG_ERROR << "Insufficient disk space available to download target! Required: " << required_bytes
1442  << ", available: " << available_bytes << ", reserved: " << reserved_bytes;
1443  return false;
1444  }
1445 }
1446 
1447 boost::optional<std::pair<size_t, std::string>> SQLStorage::checkTargetFile(const Uptane::Target& target) const {
1448  SQLite3Guard db = dbConnection();
1449 
1450  auto statement = db.prepareStatement<std::string>(
1451  "SELECT real_size, sha256, sha512, filename FROM target_images WHERE targetname = ?;", target.filename());
1452 
1453  int statement_state;
1454  while ((statement_state = statement.step()) == SQLITE_ROW) {
1455  auto sha256 = statement.get_result_col_str(1);
1456  auto sha512 = statement.get_result_col_str(2);
1457  if ((*sha256).empty() && (*sha512).empty()) {
1458  // Old aktualizr didn't save checksums, this could require to redownload old images.
1459  LOG_WARNING << "Image without checksum: " << target.filename();
1460  continue;
1461  }
1462  bool sha256_match = false;
1463  bool sha512_match = false;
1464  if (!(*sha256).empty()) {
1465  if (target.MatchHash(Uptane::Hash(Uptane::Hash::Type::kSha256, *sha256))) {
1466  sha256_match = true;
1467  }
1468  }
1469 
1470  if (!(*sha512).empty()) {
1471  if (target.MatchHash(Uptane::Hash(Uptane::Hash::Type::kSha512, *sha512))) {
1472  sha512_match = true;
1473  }
1474  }
1475  if (((*sha256).empty() || sha256_match) && ((*sha512).empty() || sha512_match)) {
1476  if (boost::filesystem::exists(images_path_ / *statement.get_result_col_str(3))) {
1477  return {{static_cast<size_t>(statement.get_result_col_int(0)), *statement.get_result_col_str(3)}};
1478  } else {
1479  return boost::none;
1480  }
1481  }
1482  }
1483 
1484  if (statement_state == SQLITE_DONE) {
1485  LOG_INFO << "No file '" + target.filename() << "' with matched hash in the database";
1486  return boost::none;
1487  }
1488  assert(statement_state != SQLITE_ROW); // from the previous loop precondition
1489  LOG_ERROR << "Statement step failure: " << db.errmsg();
1490 
1491  return boost::none;
1492 }
1493 
1495  public:
1496  SQLTargetWHandle(const SQLStorage& storage, Uptane::Target target)
1497  : db_(storage.dbPath()), target_(std::move(target)) {
1498  StorageTargetWHandle::WriteError exc("could not save file " + target_.filename() + " to the filesystem");
1499 
1500  std::string sha256Hash;
1501  std::string sha512Hash;
1502  for (const auto& hash : target_.hashes()) {
1503  if (hash.type() == Uptane::Hash::Type::kSha256) {
1504  sha256Hash = hash.HashString();
1505  } else if (hash.type() == Uptane::Hash::Type::kSha512) {
1506  sha512Hash = hash.HashString();
1507  }
1508  }
1509  std::string filename = (storage.images_path_ / target_.hashes()[0].HashString()).string();
1510  auto statement = db_.prepareStatement<std::string, std::string, std::string>(
1511  "INSERT OR REPLACE INTO target_images (targetname, sha256, sha512, filename) VALUES ( ?, ?, ?, ?);",
1512  target_.filename(), sha256Hash, sha512Hash, target_.hashes()[0].HashString());
1513 
1514  if (statement.step() != SQLITE_DONE) {
1515  LOG_ERROR << "Statement step failure: " << db_.errmsg();
1516  throw exc;
1517  }
1518  boost::filesystem::create_directories(storage.images_path_);
1519  stream_.open(filename);
1520  if (!stream_.good()) {
1521  LOG_ERROR << "Could not open image for write: " << storage.images_path_ / target_.filename();
1522  throw exc;
1523  }
1524  }
1525 
1526  ~SQLTargetWHandle() override {
1527  try {
1528  SQLTargetWHandle::wcommit();
1529  } catch (std::exception& ex) {
1530  LOG_ERROR << "Failed to commit to database: " << ex.what();
1531  } catch (...) {
1532  LOG_ERROR << "Failed to commit to database: unknown error";
1533  }
1534  }
1535 
1536  size_t wfeed(const uint8_t* buf, size_t size) override {
1537  stream_.write(reinterpret_cast<const char*>(buf), static_cast<std::streamsize>(size));
1538  written_size_ += size;
1539 
1540  return size;
1541  }
1542 
1543  void wcommit() override {
1544  if (stream_) {
1545  stream_.close();
1546  auto statement =
1547  db_.prepareStatement<int64_t, std::string>("UPDATE target_images SET real_size = ? WHERE targetname = ?;",
1548  static_cast<int64_t>(written_size_), target_.filename());
1549 
1550  int err = statement.step();
1551  if (err != SQLITE_DONE) {
1552  LOG_ERROR << "Could not save size in db: " << db_.errmsg();
1553  throw StorageTargetWHandle::WriteError("could not update size of " + target_.filename() + " in sql storage");
1554  }
1555  }
1556  }
1557 
1558  void wabort() noexcept override {
1559  if (stream_) {
1560  stream_.close();
1561  }
1562  if (sqlite3_changes(db_.get()) > 0) {
1563  auto statement =
1564  db_.prepareStatement<std::string>("DELETE FROM target_images WHERE targetname=?;", target_.filename());
1565  if (statement.step() != SQLITE_DONE) {
1566  LOG_ERROR << "could not delete " << target_.filename() << " from sql storage";
1567  }
1568  }
1569  }
1570 
1571  friend class SQLTargetRHandle;
1572 
1573  private:
1574  SQLTargetWHandle(const boost::filesystem::path& db_path, Uptane::Target target,
1575  const boost::filesystem::path& image_path, const size_t& start_from = 0)
1576  : db_(db_path), target_(std::move(target)) {
1577  if (db_.get_rc() != SQLITE_OK) {
1578  LOG_ERROR << "Can't open database: " << db_.errmsg();
1579  throw StorageTargetWHandle::WriteError("could not open sql storage");
1580  }
1581  stream_.open(image_path.string(), std::ofstream::out | std::ofstream::app);
1582  if (!stream_.good()) {
1583  LOG_ERROR << "Could not open image for write: " << image_path;
1584  throw StorageTargetWHandle::WriteError("could not open file for write: " + image_path.string());
1585  }
1586 
1587  written_size_ = start_from;
1588  }
1589  SQLite3Guard db_;
1590  Uptane::Target target_;
1591  std::ofstream stream_;
1592 };
1593 
1594 std::unique_ptr<StorageTargetWHandle> SQLStorage::allocateTargetFile(bool from_director, const Uptane::Target& target) {
1595  (void)from_director;
1596  return std::unique_ptr<StorageTargetWHandle>(new SQLTargetWHandle(*this, target));
1597 }
1598 
1600  public:
1601  SQLTargetRHandle(const SQLStorage& storage, Uptane::Target target)
1602  : db_path_(storage.dbPath()), db_(db_path_), target_(std::move(target)), size_(0) {
1603  StorageTargetRHandle::ReadError exc("could not read file " + target_.filename() + " from sql storage");
1604 
1605  auto exists = storage.checkTargetFile(target_);
1606  if (!exists) {
1607  throw exc;
1608  }
1609 
1610  size_ = exists->first;
1611  partial_ = size_ < target_.length();
1612  image_path_ = storage.images_path_ / exists->second;
1613  stream_.open(image_path_.string());
1614  if (!stream_.good()) {
1615  LOG_ERROR << "Could not open image: " << storage.images_path_ / target_.filename();
1616  throw exc;
1617  }
1618  }
1619 
1620  ~SQLTargetRHandle() override { SQLTargetRHandle::rclose(); }
1621 
1622  size_t rsize() const override { return size_; }
1623 
1624  size_t rread(uint8_t* buf, size_t size) override {
1625  stream_.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(size));
1626  return static_cast<size_t>(stream_.gcount());
1627  }
1628 
1629  void rclose() noexcept override {
1630  if (stream_.is_open()) {
1631  stream_.close();
1632  }
1633  }
1634 
1635  bool isPartial() const noexcept override { return partial_; }
1636  std::unique_ptr<StorageTargetWHandle> toWriteHandle() override {
1637  return std::unique_ptr<StorageTargetWHandle>(new SQLTargetWHandle(db_path_, target_, image_path_, size_));
1638  }
1639 
1640  private:
1641  boost::filesystem::path db_path_;
1642  SQLite3Guard db_;
1643  Uptane::Target target_;
1644  size_t size_;
1645  bool partial_{false};
1646  boost::filesystem::path image_path_;
1647  std::ifstream stream_;
1648 };
1649 
1650 std::unique_ptr<StorageTargetRHandle> SQLStorage::openTargetFile(const Uptane::Target& target) {
1651  return std_::make_unique<SQLTargetRHandle>(*this, target);
1652 }
1653 
1654 std::vector<Uptane::Target> SQLStorage::getTargetFiles() {
1655  SQLite3Guard db = dbConnection();
1656 
1657  auto statement = db.prepareStatement<>("SELECT targetname, real_size, sha256, sha512 FROM target_images;");
1658 
1659  std::vector<Uptane::Target> v;
1660 
1661  int result = statement.step();
1662  while (result != SQLITE_DONE) {
1663  if (result != SQLITE_ROW) {
1664  LOG_ERROR << "Statement step failure: " << db.errmsg();
1665  throw std::runtime_error("Error getting target files");
1666  }
1667 
1668  auto tname = statement.get_result_col_str(0).value();
1669  auto tsize = statement.get_result_col_int(1);
1670  auto sha256 = statement.get_result_col_str(2).value();
1671  auto sha512 = statement.get_result_col_str(3).value();
1672 
1673  std::vector<Uptane::Hash> hashes;
1674  if (!sha256.empty()) {
1675  hashes.emplace_back(Uptane::Hash::Type::kSha256, sha256);
1676  }
1677  if (!sha512.empty()) {
1678  hashes.emplace_back(Uptane::Hash::Type::kSha512, sha512);
1679  }
1680  v.emplace_back(tname, Uptane::EcuMap{}, hashes, static_cast<size_t>(tsize));
1681 
1682  result = statement.step();
1683  }
1684 
1685  return v;
1686 }
1687 
1688 void SQLStorage::removeTargetFile(const std::string& target_name) {
1689  SQLite3Guard db = dbConnection();
1690 
1691  if (!db.beginTransaction()) {
1692  LOG_ERROR << "Can't start transaction: " << db.errmsg();
1693  return;
1694  }
1695 
1696  auto statement =
1697  db.prepareStatement<std::string>("SELECT filename FROM target_images WHERE targetname = ?;", target_name);
1698 
1699  if (statement.step() != SQLITE_ROW) {
1700  LOG_ERROR << "Statement step failure: " << db.errmsg();
1701  throw std::runtime_error("Could not find target file");
1702  }
1703 
1704  std::string filename = statement.get_result_col_str(0).value();
1705 
1706  statement = db.prepareStatement<std::string>("DELETE FROM target_images WHERE targetname=?;", target_name);
1707 
1708  if (statement.step() != SQLITE_DONE) {
1709  LOG_ERROR << "Statement step failure: " << db.errmsg();
1710  throw std::runtime_error("Could not remove target file");
1711  }
1712  try {
1713  boost::filesystem::remove(images_path_ / filename);
1714  } catch (std::exception& e) {
1715  LOG_ERROR << "Could not remove target file";
1716  throw;
1717  }
1718 
1719  db.commitTransaction();
1720 }
1721 
1722 void SQLStorage::cleanUp() { boost::filesystem::remove_all(dbPath()); }
data::ResultCode
Definition: types.h:122
data::InstallationResult
Definition: types.h:179
Uptane::Version
Metadata version numbers.
Definition: tuf.h:116
SQLBlob
Definition: sql_utils.h:17
StorageConfig
Definition: storage_config.h:15
data
General data structures.
Definition: types.cc:44
Uptane::HardwareIdentifier
Definition: tuf.h:143
StorageTargetWHandle::WriteError
Definition: invstorage.h:38
Uptane::Hash
The hash of a file or TUF metadata.
Definition: tuf.h:209
Uptane::RepositoryType
Definition: tuf.h:20
Uptane::EcuSerial
Definition: tuf.h:174
StorageTargetRHandle
Definition: invstorage.h:61
SQLTargetRHandle
Definition: sqlstorage.cc:1599
result
Results of libaktualizr API calls.
Definition: results.h:13
SQLite3Guard
Definition: sql_utils.h:136
SQLiteStatement
Definition: sql_utils.h:32
SQLTargetWHandle
Definition: sqlstorage.cc:1494
Uptane::Role
TUF Roles.
Definition: tuf.h:57
Uptane::Target
Definition: tuf.h:238
SQLStorageBase
Definition: sqlstorage_base.h:33
StorageTargetWHandle
Definition: invstorage.h:36
SQLStorage
Definition: sqlstorage.h:18
INvStorage
Definition: invstorage.h:109
StorageTargetRHandle::ReadError
Definition: invstorage.h:63