Branch data Line data Source code
1 : : #include "sute.h"
2 : :
3 : : /**
4 : : * @brief Open SQLite database from TMPDIR by relative filename
5 : : *
6 : : * @param[in] db_filename Database filename relative to TMPDIR
7 : : * @param[in] open_flags Flags passed to sqlite3_open_v2
8 : : * @param[out] db_out Opened database handle
9 : : *
10 : : * @return Return status code:
11 : : * - SUCCESS: Database opened successfully
12 : : * - FAILURE: Validation, path construction, or open failed
13 : : */
14 : 340 : Return open_db_from_tmpdir(
15 : : const char *db_filename,
16 : : const int open_flags,
17 : : sqlite3 **db_out)
18 : : {
19 : : /* Status returned by this function through provide()
20 : : Default value assumes successful completion */
21 : 340 : Return status = SUCCESS;
22 : :
23 : 340 : m_create(char,db_path,MEMORY_STRING);
24 : :
25 [ + - - + ]: 340 : if(db_filename == NULL || db_out == NULL)
26 : : {
27 : 0 : status = FAILURE;
28 : : }
29 : :
30 [ + - ]: 340 : if(SUCCESS == status)
31 : : {
32 : 340 : status = construct_path(db_filename,db_path);
33 : : }
34 : :
35 [ + - ]: 340 : if(SUCCESS == status)
36 : : {
37 : 340 : *db_out = NULL;
38 : :
39 [ - + ]: 340 : if(SQLITE_OK != sqlite3_open_v2(m_text(db_path),db_out,open_flags,NULL))
40 : : {
41 : 0 : status = FAILURE;
42 : :
43 : 0 : (void)sqlite3_close(*db_out);
44 : 0 : *db_out = NULL;
45 : : }
46 : : }
47 : :
48 : 340 : m_del(db_path);
49 : :
50 : 340 : return(status);
51 : : }
52 : :
53 : : /**
54 : : * @brief Verify that DB relative_path set matches expected list exactly
55 : : *
56 : : * @param[in] db_filename DB file name relative to TMPDIR
57 : : * @param[in] expected_paths Sorted expected relative_path values
58 : : * @param[in] expected_count Number of expected paths
59 : : *
60 : : * @return Return status code:
61 : : * - SUCCESS: DB rows match expected paths exactly
62 : : * - FAILURE: Mismatch or DB access error
63 : : */
64 : 10 : Return db_paths_match(
65 : : const char *db_filename,
66 : : const char *const *expected_paths,
67 : : const int expected_count)
68 : : {
69 : : /* Status returned by this function through provide()
70 : : Default value assumes successful completion */
71 : 10 : Return status = SUCCESS;
72 : 10 : sqlite3 *db = NULL;
73 : 10 : sqlite3_stmt *stmt = NULL;
74 : 10 : const char *sql = "SELECT relative_path FROM files ORDER BY relative_path ASC;";
75 : 10 : m_create(char,db_path,MEMORY_STRING);
76 : :
77 [ + - + - : 10 : if(SUCCESS == status && (db_filename == NULL || expected_paths == NULL || expected_count < 0))
+ - - + ]
78 : : {
79 : 0 : status = FAILURE;
80 : : }
81 : :
82 [ + - ]: 10 : if(SUCCESS == status)
83 : : {
84 : 10 : status = construct_path(db_filename,db_path);
85 : : }
86 : :
87 [ + - - + ]: 10 : if(SUCCESS == status && SQLITE_OK != sqlite3_open_v2(m_text(db_path),&db,SQLITE_OPEN_READONLY,NULL))
88 : : {
89 : 0 : status = FAILURE;
90 : : }
91 : :
92 [ + - - + ]: 10 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
93 : : {
94 : 0 : status = FAILURE;
95 : : }
96 : :
97 : 10 : int index = 0;
98 : :
99 [ + - ]: 10 : if(SUCCESS == status)
100 : : {
101 : 10 : int rc = sqlite3_step(stmt);
102 : :
103 [ + + ]: 68 : while(rc == SQLITE_ROW)
104 : : {
105 [ - + ]: 58 : if(index >= expected_count)
106 : : {
107 : 0 : status = FAILURE;
108 : 0 : break;
109 : : }
110 : :
111 : 58 : const unsigned char *db_path_text = sqlite3_column_text(stmt,0);
112 : :
113 [ + - - + ]: 58 : if(db_path_text == NULL || strcmp((const char *)db_path_text,expected_paths[index]) != 0)
114 : : {
115 : 0 : status = FAILURE;
116 : 0 : break;
117 : : }
118 : :
119 : 58 : index++;
120 : 58 : rc = sqlite3_step(stmt);
121 : : }
122 : :
123 [ + - - + ]: 10 : if(SUCCESS == status && rc != SQLITE_DONE)
124 : : {
125 : 0 : status = FAILURE;
126 : : }
127 : : }
128 : :
129 [ + - - + ]: 10 : if(SUCCESS == status && index != expected_count)
130 : : {
131 : 0 : status = FAILURE;
132 : : }
133 : :
134 : 10 : (void)sqlite3_finalize(stmt);
135 : :
136 : 10 : (void)sqlite3_close(db);
137 : :
138 : 10 : m_del(db_path);
139 : :
140 : 10 : return(status);
141 : : }
142 : :
143 : : /**
144 : : * @brief Check whether one files-table row exists by relative path
145 : : *
146 : : * @param[in] db_filename Database filename relative to TMPDIR
147 : : * @param[in] relative_path Relative path key in files table
148 : : * @param[out] exists_out Output existence flag
149 : : *
150 : : * @return Return status code:
151 : : * - SUCCESS: Query completed and output was filled
152 : : * - FAILURE: Validation, DB access, bind, or query execution failed
153 : : */
154 : 72 : Return db_relative_path_exists(
155 : : const char *db_filename,
156 : : const char *relative_path,
157 : : bool *exists_out)
158 : : {
159 : : /* Status returned by this function through provide()
160 : : Default value assumes successful completion */
161 : 72 : Return status = SUCCESS;
162 : 72 : sqlite3 *db = NULL;
163 : 72 : sqlite3_stmt *stmt = NULL;
164 : 72 : const char *sql = "SELECT 1 FROM files WHERE relative_path = ?1 LIMIT 1;";
165 : :
166 [ + - + - : 72 : if(db_filename == NULL || relative_path == NULL || exists_out == NULL)
- + ]
167 : : {
168 : 0 : status = FAILURE;
169 : : }
170 : :
171 [ + - ]: 72 : if(SUCCESS == status)
172 : : {
173 : 72 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
174 : : }
175 : :
176 [ + - - + ]: 72 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
177 : : {
178 : 0 : status = FAILURE;
179 : : }
180 : :
181 [ + - - + ]: 72 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
182 : : {
183 : 0 : status = FAILURE;
184 : : }
185 : :
186 [ + - ]: 72 : if(SUCCESS == status)
187 : : {
188 : 72 : const int rc = sqlite3_step(stmt);
189 : :
190 [ + + ]: 72 : if(rc == SQLITE_ROW)
191 : : {
192 : 66 : *exists_out = true;
193 : :
194 [ - + ]: 66 : if(SQLITE_DONE != sqlite3_step(stmt))
195 : : {
196 : 0 : status = FAILURE;
197 : : }
198 : :
199 [ + - ]: 6 : } else if(rc == SQLITE_DONE){
200 : 6 : *exists_out = false;
201 : :
202 : : } else {
203 : 0 : status = FAILURE;
204 : : }
205 : : }
206 : :
207 : 72 : (void)sqlite3_finalize(stmt);
208 : :
209 : 72 : (void)sqlite3_close(db);
210 : :
211 : 72 : return(status);
212 : : }
213 : :
214 : : /**
215 : : * @brief Read files.ID for one row selected by relative path
216 : : *
217 : : * @param[in] db_filename Database filename relative to TMPDIR
218 : : * @param[in] relative_path Relative path key in files table
219 : : * @param[out] id_out Output primary key value
220 : : *
221 : : * @return Return status code:
222 : : * - SUCCESS: Exactly one row was found and ID was read
223 : : * - FAILURE: Validation, DB access, missing row, or duplicate row failed
224 : : */
225 : 8 : Return db_read_file_id(
226 : : const char *db_filename,
227 : : const char *relative_path,
228 : : sqlite3_int64 *id_out)
229 : : {
230 : : /* Status returned by this function through provide()
231 : : Default value assumes successful completion */
232 : 8 : Return status = SUCCESS;
233 : 8 : sqlite3 *db = NULL;
234 : 8 : sqlite3_stmt *stmt = NULL;
235 : 8 : const char *sql = "SELECT ID FROM files WHERE relative_path = ?1;";
236 : :
237 [ + - + - : 8 : if(db_filename == NULL || relative_path == NULL || id_out == NULL)
- + ]
238 : : {
239 : 0 : status = FAILURE;
240 : : }
241 : :
242 [ + - ]: 8 : if(SUCCESS == status)
243 : : {
244 : 8 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
245 : : }
246 : :
247 [ + - - + ]: 8 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
248 : : {
249 : 0 : status = FAILURE;
250 : : }
251 : :
252 [ + - - + ]: 8 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
253 : : {
254 : 0 : status = FAILURE;
255 : : }
256 : :
257 [ + - ]: 8 : if(SUCCESS == status)
258 : : {
259 : 8 : int rc = sqlite3_step(stmt);
260 : :
261 [ + - ]: 8 : if(rc == SQLITE_ROW)
262 : : {
263 : 8 : *id_out = sqlite3_column_int64(stmt,0);
264 : 8 : rc = sqlite3_step(stmt);
265 : :
266 [ - + ]: 8 : if(rc != SQLITE_DONE)
267 : : {
268 : 0 : status = FAILURE;
269 : : }
270 : :
271 : : } else {
272 : 0 : status = FAILURE;
273 : : }
274 : : }
275 : :
276 : 8 : (void)sqlite3_finalize(stmt);
277 : :
278 : 8 : (void)sqlite3_close(db);
279 : :
280 : 8 : return(status);
281 : : }
282 : :
283 : : /**
284 : : * @brief Read first row ID from files table
285 : : *
286 : : * @param[in] db_filename Database filename relative to TMPDIR
287 : : * @param[out] row_id_out Output row ID
288 : : *
289 : : * @return Return status code:
290 : : * - SUCCESS: First row ID was read
291 : : * - FAILURE: Validation, DB access, or query execution failed
292 : : */
293 : 6 : Return db_read_first_row_id(
294 : : const char *db_filename,
295 : : sqlite3_int64 *row_id_out)
296 : : {
297 : : /* Status returned by this function through provide()
298 : : Default value assumes successful completion */
299 : 6 : Return status = SUCCESS;
300 : 6 : sqlite3 *db = NULL;
301 : 6 : sqlite3_stmt *stmt = NULL;
302 : 6 : const char *sql = "SELECT ID FROM files ORDER BY ID ASC LIMIT 1;";
303 : :
304 [ + - - + ]: 6 : if(db_filename == NULL || row_id_out == NULL)
305 : : {
306 : 0 : status = FAILURE;
307 : : }
308 : :
309 [ + - ]: 6 : if(SUCCESS == status)
310 : : {
311 : 6 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
312 : : }
313 : :
314 [ + - - + ]: 6 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
315 : : {
316 : 0 : status = FAILURE;
317 : : }
318 : :
319 [ + - ]: 6 : if(SUCCESS == status)
320 : : {
321 : 6 : int rc = sqlite3_step(stmt);
322 : :
323 [ + - ]: 6 : if(SQLITE_ROW == rc)
324 : : {
325 : 6 : *row_id_out = sqlite3_column_int64(stmt,0);
326 : 6 : rc = sqlite3_step(stmt);
327 : :
328 [ - + ]: 6 : if(SQLITE_DONE != rc)
329 : : {
330 : 0 : status = FAILURE;
331 : : }
332 : : } else {
333 : 0 : status = FAILURE;
334 : : }
335 : : }
336 : :
337 : 6 : (void)sqlite3_finalize(stmt);
338 : :
339 : 6 : (void)sqlite3_close(db);
340 : :
341 : 6 : return(status);
342 : : }
343 : :
344 : : /**
345 : : * @brief Overwrite stat blob for a specific files row ID
346 : : *
347 : : * @param[in] db_filename Database filename relative to TMPDIR
348 : : * @param[in] row_id Row ID in files table
349 : : * @param[in] blob New blob bytes
350 : : * @param[in] blob_size Size of blob in bytes
351 : : *
352 : : * @return Return status code:
353 : : * - SUCCESS: Blob was updated
354 : : * - FAILURE: Validation, DB access, bind, step, or change check failed
355 : : */
356 : 4 : Return db_overwrite_stat_blob_by_row_id(
357 : : const char *db_filename,
358 : : const sqlite3_int64 row_id,
359 : : const void *blob,
360 : : const int blob_size)
361 : : {
362 : : /* Status returned by this function through provide()
363 : : Default value assumes successful completion */
364 : 4 : Return status = SUCCESS;
365 : 4 : sqlite3 *db = NULL;
366 : 4 : sqlite3_stmt *stmt = NULL;
367 : 4 : const char *sql = "UPDATE files SET stat = ?1 WHERE ID = ?2;";
368 : :
369 [ + - + - : 4 : if(db_filename == NULL || blob == NULL || blob_size < 0)
- + ]
370 : : {
371 : 0 : status = FAILURE;
372 : : }
373 : :
374 [ + - ]: 4 : if(SUCCESS == status)
375 : : {
376 : 4 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READWRITE,&db);
377 : : }
378 : :
379 [ + - - + ]: 4 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
380 : : {
381 : 0 : status = FAILURE;
382 : : }
383 : :
384 [ + - - + ]: 4 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_blob(stmt,1,blob,blob_size,SQLITE_TRANSIENT))
385 : : {
386 : 0 : status = FAILURE;
387 : : }
388 : :
389 [ + - - + ]: 4 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_int64(stmt,2,row_id))
390 : : {
391 : 0 : status = FAILURE;
392 : : }
393 : :
394 [ + - - + ]: 4 : if(SUCCESS == status && SQLITE_DONE != sqlite3_step(stmt))
395 : : {
396 : 0 : status = FAILURE;
397 : : }
398 : :
399 [ + - - + ]: 4 : if(SUCCESS == status && sqlite3_changes(db) < 1)
400 : : {
401 : 0 : status = FAILURE;
402 : : }
403 : :
404 : 4 : (void)sqlite3_finalize(stmt);
405 : :
406 : 4 : (void)sqlite3_close(db);
407 : :
408 : 4 : return(status);
409 : : }
410 : :
411 : : /**
412 : : * @brief Corrupt stat blob for first files row with one-byte payload
413 : : *
414 : : * @param[in] db_filename Database filename relative to TMPDIR
415 : : * @param[out] row_id_out Optional output for affected row ID
416 : : *
417 : : * @return Return status code:
418 : : * - SUCCESS: First row stat blob was corrupted
419 : : * - FAILURE: Row lookup or blob overwrite failed
420 : : */
421 : 4 : Return db_corrupt_first_row_stat_blob(
422 : : const char *db_filename,
423 : : sqlite3_int64 *row_id_out)
424 : : {
425 : : /* Status returned by this function through provide()
426 : : Default value assumes successful completion */
427 : 4 : Return status = SUCCESS;
428 : 4 : sqlite3_int64 row_id = 0;
429 : 4 : const unsigned char corrupt_blob[] = {0xA5};
430 : :
431 [ + - ]: 4 : if(SUCCESS == status)
432 : : {
433 : 4 : status = db_read_first_row_id(db_filename,&row_id);
434 : : }
435 : :
436 [ + - ]: 4 : if(SUCCESS == status)
437 : : {
438 : 4 : status = db_overwrite_stat_blob_by_row_id(db_filename,row_id,corrupt_blob,(int)sizeof(corrupt_blob));
439 : : }
440 : :
441 [ + - + - ]: 4 : if(SUCCESS == status && row_id_out != NULL)
442 : : {
443 : 4 : *row_id_out = row_id;
444 : : }
445 : :
446 : 4 : return(status);
447 : : }
448 : :
449 : : /**
450 : : * @brief Read number of rows from files table
451 : : *
452 : : * @param[in] db_filename Database filename relative to TMPDIR
453 : : * @param[out] count_out Output row count
454 : : *
455 : : * @return Return status code:
456 : : * - SUCCESS: Count value was read
457 : : * - FAILURE: Validation, DB access, or query execution failed
458 : : */
459 : 16 : Return db_read_files_count(
460 : : const char *db_filename,
461 : : int *count_out)
462 : : {
463 : : /* Status returned by this function through provide()
464 : : Default value assumes successful completion */
465 : 16 : Return status = SUCCESS;
466 : 16 : sqlite3 *db = NULL;
467 : 16 : sqlite3_stmt *stmt = NULL;
468 : 16 : const char *sql = "SELECT COUNT(*) FROM files;";
469 : :
470 [ + - - + ]: 16 : if(db_filename == NULL || count_out == NULL)
471 : : {
472 : 0 : status = FAILURE;
473 : : }
474 : :
475 [ + - ]: 16 : if(SUCCESS == status)
476 : : {
477 : 16 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
478 : : }
479 : :
480 [ + - - + ]: 16 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
481 : : {
482 : 0 : status = FAILURE;
483 : : }
484 : :
485 [ + - ]: 16 : if(SUCCESS == status)
486 : : {
487 : 16 : int rc = sqlite3_step(stmt);
488 : :
489 [ + - ]: 16 : if(SQLITE_ROW == rc)
490 : : {
491 : 16 : *count_out = sqlite3_column_int(stmt,0);
492 : 16 : rc = sqlite3_step(stmt);
493 : :
494 [ - + ]: 16 : if(SQLITE_DONE != rc)
495 : : {
496 : 0 : status = FAILURE;
497 : : }
498 : : } else {
499 : 0 : status = FAILURE;
500 : : }
501 : : }
502 : :
503 : 16 : (void)sqlite3_finalize(stmt);
504 : :
505 : 16 : (void)sqlite3_close(db);
506 : :
507 : 16 : return(status);
508 : : }
509 : :
510 : : /**
511 : : * @brief Read number of files rows whose stat blob has exact size
512 : : *
513 : : * @param[in] db_filename Database filename relative to TMPDIR
514 : : * @param[in] blob_size Expected stat blob size
515 : : * @param[out] count_out Output row count
516 : : *
517 : : * @return Return status code:
518 : : * - SUCCESS: Count value was read
519 : : * - FAILURE: Validation, DB access, bind, or query execution failed
520 : : */
521 : 4 : Return db_read_files_count_with_blob_size(
522 : : const char *db_filename,
523 : : const int blob_size,
524 : : int *count_out)
525 : : {
526 : : /* Status returned by this function through provide()
527 : : Default value assumes successful completion */
528 : 4 : Return status = SUCCESS;
529 : 4 : sqlite3 *db = NULL;
530 : 4 : sqlite3_stmt *stmt = NULL;
531 : 4 : const char *sql = "SELECT COUNT(*) FROM files WHERE length(stat) = ?1;";
532 : :
533 [ + - + - : 4 : if(db_filename == NULL || blob_size < 0 || count_out == NULL)
- + ]
534 : : {
535 : 0 : status = FAILURE;
536 : : }
537 : :
538 [ + - ]: 4 : if(SUCCESS == status)
539 : : {
540 : 4 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
541 : : }
542 : :
543 [ + - - + ]: 4 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
544 : : {
545 : 0 : status = FAILURE;
546 : : }
547 : :
548 [ + - - + ]: 4 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_int(stmt,1,blob_size))
549 : : {
550 : 0 : status = FAILURE;
551 : : }
552 : :
553 [ + - ]: 4 : if(SUCCESS == status)
554 : : {
555 : 4 : int rc = sqlite3_step(stmt);
556 : :
557 [ + - ]: 4 : if(SQLITE_ROW == rc)
558 : : {
559 : 4 : *count_out = sqlite3_column_int(stmt,0);
560 : 4 : rc = sqlite3_step(stmt);
561 : :
562 [ - + ]: 4 : if(SQLITE_DONE != rc)
563 : : {
564 : 0 : status = FAILURE;
565 : : }
566 : : } else {
567 : 0 : status = FAILURE;
568 : : }
569 : : }
570 : :
571 : 4 : (void)sqlite3_finalize(stmt);
572 : :
573 : 4 : (void)sqlite3_close(db);
574 : :
575 : 4 : return(status);
576 : : }
577 : :
578 : : /**
579 : : * @brief Read db_version value from metadata table
580 : : *
581 : : * @param[in] db_filename Database filename relative to TMPDIR
582 : : * @param[out] db_version_out Output database version
583 : : *
584 : : * @return Return status code:
585 : : * - SUCCESS: Version value was read
586 : : * - FAILURE: Validation, DB access, or query execution failed
587 : : */
588 : 10 : Return read_db_version_from_metadata(
589 : : const char *db_filename,
590 : : int *db_version_out)
591 : : {
592 : : /* Status returned by this function through provide()
593 : : Default value assumes successful completion */
594 : 10 : Return status = SUCCESS;
595 : 10 : sqlite3 *db = NULL;
596 : 10 : sqlite3_stmt *stmt = NULL;
597 : 10 : const char *sql = "SELECT db_version FROM metadata LIMIT 1;";
598 : :
599 [ + - - + ]: 10 : if(db_filename == NULL || db_version_out == NULL)
600 : : {
601 : 0 : status = FAILURE;
602 : : }
603 : :
604 [ + - ]: 10 : if(SUCCESS == status)
605 : : {
606 : 10 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
607 : : }
608 : :
609 [ + - - + ]: 10 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
610 : : {
611 : 0 : status = FAILURE;
612 : : }
613 : :
614 [ + - ]: 10 : if(SUCCESS == status)
615 : : {
616 : 10 : int rc = sqlite3_step(stmt);
617 : :
618 [ + - ]: 10 : if(SQLITE_ROW == rc)
619 : : {
620 : 10 : *db_version_out = sqlite3_column_int(stmt,0);
621 : 10 : rc = sqlite3_step(stmt);
622 : :
623 [ - + ]: 10 : if(SQLITE_DONE != rc)
624 : : {
625 : 0 : status = FAILURE;
626 : : }
627 : : } else {
628 : 0 : status = FAILURE;
629 : : }
630 : : }
631 : :
632 : 10 : (void)sqlite3_finalize(stmt);
633 : :
634 : 10 : (void)sqlite3_close(db);
635 : :
636 : 10 : return(status);
637 : : }
638 : :
639 : : /**
640 : : * @brief Update db_version value in metadata table
641 : : *
642 : : * @param[in] db_filename Database filename relative to TMPDIR
643 : : * @param[in] db_version Version value to store
644 : : *
645 : : * @return Return status code:
646 : : * - SUCCESS: Version value was updated
647 : : * - FAILURE: Validation, DB access, bind, step, or change check failed
648 : : */
649 : 2 : Return set_db_version_in_metadata(
650 : : const char *db_filename,
651 : : const int db_version)
652 : : {
653 : : /* Status returned by this function through provide()
654 : : Default value assumes successful completion */
655 : 2 : Return status = SUCCESS;
656 : 2 : sqlite3 *db = NULL;
657 : 2 : sqlite3_stmt *stmt = NULL;
658 : 2 : const char *sql = "UPDATE metadata SET db_version = ?1;";
659 : :
660 [ + - - + ]: 2 : if(db_filename == NULL || db_version < 0)
661 : : {
662 : 0 : status = FAILURE;
663 : : }
664 : :
665 [ + - ]: 2 : if(SUCCESS == status)
666 : : {
667 : 2 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READWRITE,&db);
668 : : }
669 : :
670 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
671 : : {
672 : 0 : status = FAILURE;
673 : : }
674 : :
675 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_int(stmt,1,db_version))
676 : : {
677 : 0 : status = FAILURE;
678 : : }
679 : :
680 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_DONE != sqlite3_step(stmt))
681 : : {
682 : 0 : status = FAILURE;
683 : : }
684 : :
685 [ + - - + ]: 2 : if(SUCCESS == status && sqlite3_changes(db) < 1)
686 : : {
687 : 0 : status = FAILURE;
688 : : }
689 : :
690 : 2 : (void)sqlite3_finalize(stmt);
691 : :
692 : 2 : (void)sqlite3_close(db);
693 : :
694 : 2 : return(status);
695 : : }
696 : :
697 : : /**
698 : : * @brief Read raw stat blob for a specific files row ID
699 : : *
700 : : * @param[in] db_filename Database filename relative to TMPDIR
701 : : * @param[in] row_id Row ID in files table
702 : : * @param[out] blob_out Optional output buffer for blob bytes
703 : : * @param[in] blob_out_size Size of blob_out buffer in bytes
704 : : * @param[out] blob_size_out Output blob size from DB
705 : : *
706 : : * @return Return status code:
707 : : * - SUCCESS: Blob size and optional bytes were read
708 : : * - FAILURE: Validation, DB access, bind, or row parsing failed
709 : : */
710 : 8 : Return db_read_stat_blob_by_row_id(
711 : : const char *db_filename,
712 : : const sqlite3_int64 row_id,
713 : : unsigned char *blob_out,
714 : : const size_t blob_out_size,
715 : : int *blob_size_out)
716 : : {
717 : : /* Status returned by this function through provide()
718 : : Default value assumes successful completion */
719 : 8 : Return status = SUCCESS;
720 : 8 : sqlite3 *db = NULL;
721 : 8 : sqlite3_stmt *stmt = NULL;
722 : 8 : const char *sql = "SELECT stat FROM files WHERE ID = ?1;";
723 : :
724 [ + - - + ]: 8 : if(db_filename == NULL || blob_size_out == NULL)
725 : : {
726 : 0 : status = FAILURE;
727 : : }
728 : :
729 [ + - ]: 8 : if(SUCCESS == status)
730 : : {
731 : 8 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
732 : : }
733 : :
734 [ + - - + ]: 8 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
735 : : {
736 : 0 : status = FAILURE;
737 : : }
738 : :
739 [ + - - + ]: 8 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_int64(stmt,1,row_id))
740 : : {
741 : 0 : status = FAILURE;
742 : : }
743 : :
744 [ + - ]: 8 : if(SUCCESS == status)
745 : : {
746 : 8 : int rc = sqlite3_step(stmt);
747 : :
748 [ + - ]: 8 : if(SQLITE_ROW == rc)
749 : : {
750 : 8 : const void *blob = sqlite3_column_blob(stmt,0);
751 : 8 : const int blob_size = sqlite3_column_bytes(stmt,0);
752 : :
753 [ - + ]: 8 : if(blob_size < 0)
754 : : {
755 : 0 : status = FAILURE;
756 : : }
757 : :
758 [ + - + - : 8 : if(SUCCESS == status && blob_size > 0 && blob == NULL)
- + ]
759 : : {
760 : 0 : status = FAILURE;
761 : : }
762 : :
763 [ + - + - ]: 8 : if(SUCCESS == status && blob_out != NULL)
764 : : {
765 [ - + ]: 8 : if((size_t)blob_size > blob_out_size)
766 : : {
767 : 0 : status = FAILURE;
768 [ + - ]: 8 : } else if(blob_size > 0){
769 : 8 : memcpy(blob_out,blob,(size_t)blob_size);
770 : : }
771 : : }
772 : :
773 [ + - ]: 8 : if(SUCCESS == status)
774 : : {
775 : 8 : *blob_size_out = blob_size;
776 : : }
777 : :
778 : 8 : rc = sqlite3_step(stmt);
779 : :
780 [ + - - + ]: 8 : if(SUCCESS == status && SQLITE_DONE != rc)
781 : : {
782 : 0 : status = FAILURE;
783 : : }
784 : : } else {
785 : 0 : status = FAILURE;
786 : : }
787 : : }
788 : :
789 : 8 : (void)sqlite3_finalize(stmt);
790 : :
791 : 8 : (void)sqlite3_close(db);
792 : :
793 : 8 : return(status);
794 : : }
795 : :
796 : : /**
797 : : * @brief Read CmpctStat struct from files row by ID
798 : : *
799 : : * @param[in] db_filename Database filename relative to TMPDIR
800 : : * @param[in] row_id Row ID in files table
801 : : * @param[out] stat_out Output compact stat structure
802 : : *
803 : : * @return Return status code:
804 : : * - SUCCESS: CmpctStat value was read
805 : : * - FAILURE: Validation, blob read, size check, or conversion failed
806 : : */
807 : 4 : Return db_read_cmpctstat_by_row_id(
808 : : const char *db_filename,
809 : : const sqlite3_int64 row_id,
810 : : CmpctStat *stat_out)
811 : : {
812 : : /* Status returned by this function through provide()
813 : : Default value assumes successful completion */
814 : 4 : Return status = SUCCESS;
815 : : unsigned char raw[sizeof(CmpctStat)];
816 : 4 : int blob_size = 0;
817 : :
818 [ - + ]: 4 : if(stat_out == NULL)
819 : : {
820 : 0 : status = FAILURE;
821 : : }
822 : :
823 [ + - ]: 4 : if(SUCCESS == status)
824 : : {
825 : 4 : status = db_read_stat_blob_by_row_id(db_filename,row_id,raw,sizeof(raw),&blob_size);
826 : : }
827 : :
828 [ + - - + ]: 4 : if(SUCCESS == status && blob_size != (int)sizeof(CmpctStat))
829 : : {
830 : 0 : status = FAILURE;
831 : : }
832 : :
833 [ + - ]: 4 : if(SUCCESS == status)
834 : : {
835 : 4 : memcpy(stat_out,raw,sizeof(CmpctStat));
836 : : }
837 : :
838 : 4 : return(status);
839 : : }
840 : :
841 : : /**
842 : : * @brief Read CmpctStat blob for one file by relative path
843 : : *
844 : : * @param[in] db_filename Database filename relative to TMPDIR
845 : : * @param[in] relative_path Relative path key in files table
846 : : * @param[out] stat_out Output compact stat structure
847 : : *
848 : : * @return Return status code:
849 : : * - SUCCESS: CmpctStat value was read
850 : : * - FAILURE: Validation, DB access, blob size check, or row parsing failed
851 : : */
852 : 96 : Return db_read_cmpctstat_by_relative_path(
853 : : const char *db_filename,
854 : : const char *relative_path,
855 : : CmpctStat *stat_out)
856 : : {
857 : : /* Status returned by this function through provide()
858 : : Default value assumes successful completion */
859 : 96 : Return status = SUCCESS;
860 : 96 : sqlite3 *db = NULL;
861 : 96 : sqlite3_stmt *stmt = NULL;
862 : 96 : const char *sql = "SELECT stat FROM files WHERE relative_path = ?1;";
863 : :
864 [ + - + - : 96 : if(db_filename == NULL || relative_path == NULL || stat_out == NULL)
- + ]
865 : : {
866 : 0 : status = FAILURE;
867 : : }
868 : :
869 [ + - ]: 96 : if(SUCCESS == status)
870 : : {
871 : 96 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
872 : : }
873 : :
874 [ + - - + ]: 96 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
875 : : {
876 : 0 : status = FAILURE;
877 : : }
878 : :
879 [ + - - + ]: 96 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
880 : : {
881 : 0 : status = FAILURE;
882 : : }
883 : :
884 [ + - ]: 96 : if(SUCCESS == status)
885 : : {
886 : 96 : int rc = sqlite3_step(stmt);
887 : :
888 [ + - ]: 96 : if(rc == SQLITE_ROW)
889 : : {
890 : 96 : const void *blob = sqlite3_column_blob(stmt,0);
891 : 96 : const int bytes = sqlite3_column_bytes(stmt,0);
892 : :
893 [ + - - + ]: 96 : if(blob == NULL || bytes != (int)sizeof(CmpctStat))
894 : : {
895 : 0 : status = FAILURE;
896 : : } else {
897 : 96 : memcpy(stat_out,blob,sizeof(CmpctStat));
898 : : }
899 : :
900 : 96 : rc = sqlite3_step(stmt);
901 : :
902 [ + - - + ]: 96 : if(SUCCESS == status && rc != SQLITE_DONE)
903 : : {
904 : 0 : status = FAILURE;
905 : : }
906 : : } else {
907 : 0 : status = FAILURE;
908 : : }
909 : : }
910 : :
911 : 96 : (void)sqlite3_finalize(stmt);
912 : :
913 : 96 : (void)sqlite3_close(db);
914 : :
915 : 96 : return(status);
916 : : }
917 : :
918 : : /**
919 : : * @brief Check whether DB compact-stat timestamps match current file stat timestamps
920 : : *
921 : : * @param[in] db_stat Compact stat loaded from the database
922 : : * @param[in] file_stat Current filesystem stat structure
923 : : * @return `true` when both ctime and mtime fields match exactly, otherwise `false`
924 : : */
925 : 34 : bool cmpctstat_matches_stat_timestamps(
926 : : const CmpctStat *db_stat,
927 : : const struct stat *file_stat)
928 : : {
929 [ + - - + ]: 34 : if(db_stat == NULL || file_stat == NULL)
930 : : {
931 : 0 : return(false);
932 : : }
933 : :
934 : 68 : return(db_stat->mtim_tv_sec == file_stat->st_mtim.tv_sec &&
935 [ + + ]: 34 : db_stat->mtim_tv_nsec == file_stat->st_mtim.tv_nsec &&
936 [ + - + - ]: 94 : db_stat->ctim_tv_sec == file_stat->st_ctim.tv_sec &&
937 [ + - ]: 26 : db_stat->ctim_tv_nsec == file_stat->st_ctim.tv_nsec);
938 : : }
939 : :
940 : : /**
941 : : * @brief Read final offset and SHA512 checksum for one file from files table
942 : : *
943 : : * @param[in] db_filename Database filename relative to TMPDIR
944 : : * @param[in] relative_path Relative path key in files table
945 : : * @param[out] offset_out Output offset value, 0 when SQL value is NULL
946 : : * @param[out] sha512_out Output SHA512 bytes with SHA512_DIGEST_LENGTH size
947 : : *
948 : : * @return Return status code:
949 : : * - SUCCESS: Row was found and outputs were filled
950 : : * - FAILURE: Validation, DB access, missing row, or SHA512 blob size mismatch
951 : : */
952 : 108 : Return read_final_sha512_from_db(
953 : : const char *db_filename,
954 : : const char *relative_path,
955 : : sqlite3_int64 *offset_out,
956 : : unsigned char *sha512_out)
957 : : {
958 : : /* Status returned by this function through provide()
959 : : Default value assumes successful completion */
960 : 108 : Return status = SUCCESS;
961 : 108 : sqlite3 *db = NULL;
962 : 108 : sqlite3_stmt *stmt = NULL;
963 : 108 : const char *sql = "SELECT offset, sha512 FROM files WHERE relative_path = ?1;";
964 : :
965 [ + - ]: 108 : if(db_filename == NULL
966 [ + - ]: 108 : || relative_path == NULL
967 [ + - ]: 108 : || offset_out == NULL
968 [ - + ]: 108 : || sha512_out == NULL)
969 : : {
970 : 0 : status = FAILURE;
971 : : }
972 : :
973 [ + - ]: 108 : if(SUCCESS == status)
974 : : {
975 : 108 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READONLY,&db);
976 : : }
977 : :
978 [ + - - + ]: 108 : if((SUCCESS == status) && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
979 : : {
980 : 0 : status = FAILURE;
981 : : }
982 : :
983 [ + - - + ]: 108 : if((SUCCESS == status) && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
984 : : {
985 : 0 : status = FAILURE;
986 : : }
987 : :
988 [ + - ]: 108 : if(SUCCESS == status)
989 : : {
990 : 108 : const int step_rc = sqlite3_step(stmt);
991 : :
992 [ - + ]: 108 : if(step_rc != SQLITE_ROW)
993 : : {
994 : 0 : status = FAILURE;
995 : : }
996 : : }
997 : :
998 [ + - ]: 108 : if(SUCCESS == status)
999 : : {
1000 [ + - ]: 108 : if(sqlite3_column_type(stmt,0) == SQLITE_NULL)
1001 : : {
1002 : 108 : *offset_out = 0;
1003 : : } else {
1004 : 0 : *offset_out = sqlite3_column_int64(stmt,0);
1005 : : }
1006 : :
1007 : 108 : const void *sha512_blob = sqlite3_column_blob(stmt,1);
1008 : 108 : const int sha512_bytes = sqlite3_column_bytes(stmt,1);
1009 : :
1010 [ + - - + ]: 108 : if(sha512_blob == NULL || sha512_bytes != SHA512_DIGEST_LENGTH)
1011 : : {
1012 : 0 : status = FAILURE;
1013 : : } else {
1014 : 108 : memcpy(sha512_out,sha512_blob,(size_t)SHA512_DIGEST_LENGTH);
1015 : : }
1016 : :
1017 : 108 : const int done_rc = sqlite3_step(stmt);
1018 : :
1019 [ + - - + ]: 108 : if((SUCCESS == status) && done_rc != SQLITE_DONE)
1020 : : {
1021 : 0 : status = FAILURE;
1022 : : }
1023 : : }
1024 : :
1025 : 108 : (void)sqlite3_finalize(stmt);
1026 : :
1027 : 108 : (void)sqlite3_close(db);
1028 : :
1029 : 108 : return(status);
1030 : : }
1031 : :
1032 : : /**
1033 : : * @brief Verify that a final DB checksum matches the file on disk
1034 : : *
1035 : : * Reads the application's stored SHA512 digest, requires the final offset state
1036 : : * to be clear, computes the same file with the Monocypher oracle, and compares
1037 : : * the raw digest bytes
1038 : : *
1039 : : * @param[in] db_filename Database filename relative to TMPDIR
1040 : : * @param[in] relative_path Relative path key in files table
1041 : : * @param[in] file_path Absolute path to the file being checked
1042 : : *
1043 : : * @return Return status code:
1044 : : * - SUCCESS: Stored final checksum matches the file
1045 : : * - FAILURE: Validation, DB read, non-final offset, hash, or compare failed
1046 : : */
1047 : 4 : Return db_final_sha512_matches_file(
1048 : : const char *db_filename,
1049 : : const char *relative_path,
1050 : : const char *file_path)
1051 : : {
1052 : : /* Status returned by this function through provide()
1053 : : Default value assumes successful completion */
1054 : 4 : Return status = SUCCESS;
1055 : 4 : sqlite3_int64 final_offset = -1;
1056 : 4 : unsigned char db_sha512[SHA512_DIGEST_LENGTH] = {0};
1057 : 4 : unsigned char expected_sha512[SHA512_DIGEST_LENGTH] = {0};
1058 : :
1059 [ + - + - : 4 : if(db_filename == NULL || relative_path == NULL || file_path == NULL)
- + ]
1060 : : {
1061 : 0 : status = FAILURE;
1062 : : }
1063 : :
1064 [ + - ]: 4 : if(SUCCESS == status)
1065 : : {
1066 : 4 : status = read_final_sha512_from_db(db_filename,relative_path,&final_offset,db_sha512);
1067 : : }
1068 : :
1069 [ + - - + ]: 4 : if(SUCCESS == status && final_offset != 0)
1070 : : {
1071 : 0 : status = FAILURE;
1072 : : }
1073 : :
1074 [ + - ]: 4 : if(SUCCESS == status)
1075 : : {
1076 : 4 : status = compute_file_sha512_monocypher(file_path,expected_sha512);
1077 : : }
1078 : :
1079 [ + - - + ]: 4 : if(SUCCESS == status && memcmp(db_sha512,expected_sha512,(size_t)SHA512_DIGEST_LENGTH) != 0)
1080 : : {
1081 : 0 : status = FAILURE;
1082 : : }
1083 : :
1084 : 4 : return(status);
1085 : : }
1086 : :
1087 : : /**
1088 : : * @brief Set files.sha512 to NULL for one row in the database
1089 : : *
1090 : : * @param[in] db_filename Database filename relative to TMPDIR
1091 : : * @param[in] relative_path Relative path key in files table
1092 : : *
1093 : : * @return Return status code:
1094 : : * - SUCCESS: SHA512 value was set to NULL for at least one row
1095 : : * - FAILURE: Validation, DB access, bind, step, or change check failed
1096 : : */
1097 : 2 : Return db_set_sha512_to_null(
1098 : : const char *db_filename,
1099 : : const char *relative_path)
1100 : : {
1101 : : /* Status returned by this function through provide()
1102 : : Default value assumes successful completion */
1103 : 2 : Return status = SUCCESS;
1104 : 2 : sqlite3 *db = NULL;
1105 : 2 : sqlite3_stmt *stmt = NULL;
1106 : 2 : const char *sql = "UPDATE files SET sha512 = NULL WHERE relative_path = ?1;";
1107 : :
1108 [ + - - + ]: 2 : if(db_filename == NULL || relative_path == NULL)
1109 : : {
1110 : 0 : status = FAILURE;
1111 : : }
1112 : :
1113 [ + - ]: 2 : if(SUCCESS == status)
1114 : : {
1115 : 2 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READWRITE,&db);
1116 : : }
1117 : :
1118 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
1119 : : {
1120 : 0 : status = FAILURE;
1121 : : }
1122 : :
1123 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
1124 : : {
1125 : 0 : status = FAILURE;
1126 : : }
1127 : :
1128 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_DONE != sqlite3_step(stmt))
1129 : : {
1130 : 0 : status = FAILURE;
1131 : : }
1132 : :
1133 [ + - - + ]: 2 : if(SUCCESS == status && sqlite3_changes(db) < 1)
1134 : : {
1135 : 0 : status = FAILURE;
1136 : : }
1137 : :
1138 : 2 : (void)sqlite3_finalize(stmt);
1139 : :
1140 : 2 : (void)sqlite3_close(db);
1141 : :
1142 : 2 : return(status);
1143 : : }
1144 : :
1145 : : /**
1146 : : * @brief Corrupt stored SHA512 bytes for one files-table row
1147 : : *
1148 : : * The row is selected by relative_path and the SHA512 blob is modified in a
1149 : : * deterministic way while keeping the SQL value non-NULL. This is useful for
1150 : : * tests that need a database checksum mismatch without changing fixture files
1151 : : *
1152 : : * @param[in] db_filename Database filename relative to TMPDIR
1153 : : * @param[in] relative_path Relative path key in files table
1154 : : *
1155 : : * @return Return status code:
1156 : : * - SUCCESS: SHA512 value was updated for at least one row
1157 : : * - FAILURE: Validation, DB access, bind, step, or change check failed
1158 : : */
1159 : 2 : Return db_tamper_sha512(
1160 : : const char *db_filename,
1161 : : const char *relative_path)
1162 : : {
1163 : : /* Status returned by this function through provide()
1164 : : Default value assumes successful completion */
1165 : 2 : Return status = SUCCESS;
1166 : 2 : sqlite3 *db = NULL;
1167 : 2 : sqlite3_stmt *stmt = NULL;
1168 : 2 : const char *sql = "UPDATE files SET sha512 = (substr(sha512,1,2) || X'BEEF' || substr(sha512,5)) "
1169 : : "WHERE relative_path = ?1;";
1170 : :
1171 [ + - - + ]: 2 : if(db_filename == NULL || relative_path == NULL)
1172 : : {
1173 : 0 : status = FAILURE;
1174 : : }
1175 : :
1176 [ + - ]: 2 : if(SUCCESS == status)
1177 : : {
1178 : 2 : status = open_db_from_tmpdir(db_filename,SQLITE_OPEN_READWRITE,&db);
1179 : : }
1180 : :
1181 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
1182 : : {
1183 : 0 : status = FAILURE;
1184 : : }
1185 : :
1186 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
1187 : : {
1188 : 0 : status = FAILURE;
1189 : : }
1190 : :
1191 [ + - - + ]: 2 : if(SUCCESS == status && SQLITE_DONE != sqlite3_step(stmt))
1192 : : {
1193 : 0 : status = FAILURE;
1194 : : }
1195 : :
1196 [ + - - + ]: 2 : if(SUCCESS == status && sqlite3_changes(db) < 1)
1197 : : {
1198 : 0 : status = FAILURE;
1199 : : }
1200 : :
1201 : 2 : (void)sqlite3_finalize(stmt);
1202 : :
1203 : 2 : (void)sqlite3_close(db);
1204 : :
1205 : 2 : return(status);
1206 : : }
1207 : :
1208 : : /**
1209 : : * @brief Read intermediate offset and mdContext size for one file from DB
1210 : : *
1211 : : * @param[in] db_filename DB file name relative to TMPDIR
1212 : : * @param[in] relative_path File path in DB relative_path column
1213 : : * @param[out] offset_out Output offset value from files table
1214 : : * @param[out] md_context_bytes_out Output byte size of mdContext column
1215 : : *
1216 : : * @return Return status code:
1217 : : * - SUCCESS: Row was found and outputs were filled
1218 : : * - FAILURE: Validation, DB access, or row parsing failed
1219 : : */
1220 : 14 : Return read_resume_state_from_db(
1221 : : const char *db_filename,
1222 : : const char *relative_path,
1223 : : sqlite3_int64 *offset_out,
1224 : : int *md_context_bytes_out)
1225 : : {
1226 : : /* Status returned by this function through provide()
1227 : : Default value assumes successful completion */
1228 : 14 : Return status = SUCCESS;
1229 : 14 : sqlite3 *db = NULL;
1230 : 14 : sqlite3_stmt *stmt = NULL;
1231 : 14 : const char *sql = "SELECT offset, mdContext FROM files WHERE relative_path = ?1;";
1232 : 14 : m_create(char,db_path,MEMORY_STRING);
1233 : :
1234 [ + - + - ]: 14 : if(SUCCESS == status && (db_filename == NULL
1235 [ + - ]: 14 : || relative_path == NULL
1236 [ + - ]: 14 : || offset_out == NULL
1237 [ - + ]: 14 : || md_context_bytes_out == NULL))
1238 : : {
1239 : 0 : status = FAILURE;
1240 : : }
1241 : :
1242 [ + - ]: 14 : if(SUCCESS == status)
1243 : : {
1244 : 14 : status = construct_path(db_filename,db_path);
1245 : : }
1246 : :
1247 [ + - - + ]: 14 : if(SUCCESS == status && SQLITE_OK != sqlite3_open_v2(m_text(db_path),&db,SQLITE_OPEN_READONLY,NULL))
1248 : : {
1249 : 0 : status = FAILURE;
1250 : : }
1251 : :
1252 [ + - - + ]: 14 : if(SUCCESS == status && SQLITE_OK != sqlite3_prepare_v2(db,sql,-1,&stmt,NULL))
1253 : : {
1254 : 0 : status = FAILURE;
1255 : : }
1256 : :
1257 [ + - - + ]: 14 : if(SUCCESS == status && SQLITE_OK != sqlite3_bind_text(stmt,1,relative_path,(int)strlen(relative_path),SQLITE_TRANSIENT))
1258 : : {
1259 : 0 : status = FAILURE;
1260 : : }
1261 : :
1262 [ + - ]: 14 : if(SUCCESS == status)
1263 : : {
1264 : 14 : const int step_rc = sqlite3_step(stmt);
1265 : :
1266 [ - + ]: 14 : if(step_rc != SQLITE_ROW)
1267 : : {
1268 : 0 : status = FAILURE;
1269 : : }
1270 : : }
1271 : :
1272 [ + - ]: 14 : if(SUCCESS == status)
1273 : : {
1274 [ + + ]: 14 : if(sqlite3_column_type(stmt,0) == SQLITE_NULL)
1275 : : {
1276 : 7 : *offset_out = 0;
1277 : : } else {
1278 : 7 : *offset_out = sqlite3_column_int64(stmt,0);
1279 : : }
1280 : :
1281 : 14 : *md_context_bytes_out = sqlite3_column_bytes(stmt,1);
1282 : :
1283 : 14 : const int done_rc = sqlite3_step(stmt);
1284 : :
1285 [ - + ]: 14 : if(done_rc != SQLITE_DONE)
1286 : : {
1287 : 0 : status = FAILURE;
1288 : : }
1289 : : }
1290 : :
1291 : 14 : (void)sqlite3_finalize(stmt);
1292 : :
1293 : 14 : (void)sqlite3_close(db);
1294 : :
1295 : 14 : m_del(db_path);
1296 : :
1297 : 14 : return(status);
1298 : : }
1299 : :
1300 : : /**
1301 : : * @brief Verify that a files-table row has no partial resume state
1302 : : *
1303 : : * A completed hash must have a cleared offset and an empty mdContext blob.
1304 : : * This helper intentionally treats any non-empty value as a failure because it
1305 : : * would make a future update look resumable
1306 : : *
1307 : : * @param[in] db_filename Database filename relative to TMPDIR
1308 : : * @param[in] relative_path Relative path key in files table
1309 : : *
1310 : : * @return Return status code:
1311 : : * - SUCCESS: Offset and mdContext are both clear
1312 : : * - FAILURE: Validation, DB read, non-zero offset, or non-empty context failed
1313 : : */
1314 : 7 : Return db_resume_state_is_empty(
1315 : : const char *db_filename,
1316 : : const char *relative_path)
1317 : : {
1318 : : /* Status returned by this function through provide()
1319 : : Default value assumes successful completion */
1320 : 7 : Return status = SUCCESS;
1321 : 7 : sqlite3_int64 offset = -1;
1322 : 7 : int md_context_bytes = -1;
1323 : :
1324 [ + - - + ]: 7 : if(db_filename == NULL || relative_path == NULL)
1325 : : {
1326 : 0 : status = FAILURE;
1327 : : }
1328 : :
1329 [ + - ]: 7 : if(SUCCESS == status)
1330 : : {
1331 : 7 : status = read_resume_state_from_db(db_filename,relative_path,&offset,&md_context_bytes);
1332 : : }
1333 : :
1334 [ + - - + ]: 7 : if(SUCCESS == status && offset != 0)
1335 : : {
1336 : 0 : status = FAILURE;
1337 : : }
1338 : :
1339 [ + - - + ]: 7 : if(SUCCESS == status && md_context_bytes != 0)
1340 : : {
1341 : 0 : status = FAILURE;
1342 : : }
1343 : :
1344 : 7 : return(status);
1345 : : }
|