LCOV - code coverage report
Current view: top level - src - cache_hash.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 277 366 75.7 %
Date: 1970-01-01 00:00:01 Functions: 24 25 96.0 %
Branches: 87 154 56.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2018 Andrew Kelley
       3                 :            :  *
       4                 :            :  * This file is part of zig, which is MIT licensed.
       5                 :            :  * See http://opensource.org/licenses/MIT
       6                 :            :  */
       7                 :            : 
       8                 :            : #include "userland.h"
       9                 :            : #include "cache_hash.hpp"
      10                 :            : #include "all_types.hpp"
      11                 :            : #include "buffer.hpp"
      12                 :            : #include "os.hpp"
      13                 :            : 
      14                 :            : #include <stdio.h>
      15                 :            : 
      16                 :        171 : void cache_init(CacheHash *ch, Buf *manifest_dir) {
      17                 :        171 :     int rc = blake2b_init(&ch->blake, 48);
      18                 :        171 :     assert(rc == 0);
      19                 :        171 :     ch->files = {};
      20                 :        171 :     ch->manifest_dir = manifest_dir;
      21                 :        171 :     ch->manifest_file_path = nullptr;
      22                 :        171 :     ch->manifest_dirty = false;
      23                 :        171 :     ch->force_check_manifest = false;
      24                 :        171 :     ch->b64_digest = BUF_INIT;
      25                 :        171 : }
      26                 :            : 
      27                 :       3568 : void cache_str(CacheHash *ch, const char *ptr) {
      28                 :       3568 :     assert(ch->manifest_file_path == nullptr);
      29                 :       3568 :     assert(ptr != nullptr);
      30                 :            :     // + 1 to include the null byte
      31                 :       3568 :     blake2b_update(&ch->blake, ptr, strlen(ptr) + 1);
      32                 :       3568 : }
      33                 :            : 
      34                 :       1446 : void cache_int(CacheHash *ch, int x) {
      35                 :       1446 :     assert(ch->manifest_file_path == nullptr);
      36                 :            :     // + 1 to include the null byte
      37                 :            :     uint8_t buf[sizeof(int) + 1];
      38                 :       1446 :     memcpy(buf, &x, sizeof(int));
      39                 :       1446 :     buf[sizeof(int)] = 0;
      40                 :       1446 :     blake2b_update(&ch->blake, buf, sizeof(int) + 1);
      41                 :       1446 : }
      42                 :            : 
      43                 :        228 : void cache_usize(CacheHash *ch, size_t x) {
      44                 :        228 :     assert(ch->manifest_file_path == nullptr);
      45                 :            :     // + 1 to include the null byte
      46                 :            :     uint8_t buf[sizeof(size_t) + 1];
      47                 :        228 :     memcpy(buf, &x, sizeof(size_t));
      48                 :        228 :     buf[sizeof(size_t)] = 0;
      49                 :        228 :     blake2b_update(&ch->blake, buf, sizeof(size_t) + 1);
      50                 :        228 : }
      51                 :            : 
      52                 :       1406 : void cache_bool(CacheHash *ch, bool x) {
      53                 :       1406 :     assert(ch->manifest_file_path == nullptr);
      54                 :       1406 :     blake2b_update(&ch->blake, &x, 1);
      55                 :       1406 : }
      56                 :            : 
      57                 :        833 : void cache_buf(CacheHash *ch, Buf *buf) {
      58                 :        833 :     assert(ch->manifest_file_path == nullptr);
      59                 :        833 :     assert(buf != nullptr);
      60                 :            :     // + 1 to include the null byte
      61                 :        833 :     blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1);
      62                 :        833 : }
      63                 :            : 
      64                 :        336 : void cache_buf_opt(CacheHash *ch, Buf *buf) {
      65                 :        336 :     assert(ch->manifest_file_path == nullptr);
      66         [ +  + ]:        336 :     if (buf == nullptr) {
      67                 :        268 :         cache_str(ch, "");
      68                 :        268 :         cache_str(ch, "");
      69                 :            :     } else {
      70                 :         68 :         cache_buf(ch, buf);
      71                 :            :     }
      72                 :        336 : }
      73                 :            : 
      74                 :         76 : void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) {
      75                 :         76 :     assert(ch->manifest_file_path == nullptr);
      76         [ +  + ]:         90 :     for (size_t i = 0; i < len; i += 1) {
      77                 :         14 :         LinkLib *lib = ptr[i];
      78         [ +  + ]:         14 :         if (lib->provided_explicitly) {
      79                 :          6 :             cache_buf(ch, lib->name);
      80                 :            :         }
      81                 :            :     }
      82                 :         76 :     cache_str(ch, "");
      83                 :         76 : }
      84                 :            : 
      85                 :        282 : void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) {
      86                 :        282 :     assert(ch->manifest_file_path == nullptr);
      87         [ +  + ]:        346 :     for (size_t i = 0; i < len; i += 1) {
      88                 :         64 :         Buf *buf = ptr[i];
      89                 :         64 :         cache_buf(ch, buf);
      90                 :            :     }
      91                 :        282 :     cache_str(ch, "");
      92                 :        282 : }
      93                 :            : 
      94                 :         76 : void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len) {
      95                 :         76 :     assert(ch->manifest_file_path == nullptr);
      96                 :            : 
      97         [ +  + ]:        164 :     for (size_t i = 0; i < len; i += 1) {
      98                 :         88 :         Buf *buf = ptr[i];
      99                 :         88 :         cache_file(ch, buf);
     100                 :            :     }
     101                 :         76 :     cache_str(ch, "");
     102                 :         76 : }
     103                 :            : 
     104                 :        304 : void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len) {
     105                 :        304 :     assert(ch->manifest_file_path == nullptr);
     106                 :            : 
     107         [ -  + ]:        304 :     for (size_t i = 0; i < len; i += 1) {
     108                 :          0 :         const char *s = ptr[i];
     109                 :          0 :         cache_str(ch, s);
     110                 :            :     }
     111                 :        304 :     cache_str(ch, "");
     112                 :        304 : }
     113                 :            : 
     114                 :        260 : void cache_file(CacheHash *ch, Buf *file_path) {
     115                 :        260 :     assert(ch->manifest_file_path == nullptr);
     116                 :        260 :     assert(file_path != nullptr);
     117                 :        260 :     Buf *resolved_path = buf_alloc();
     118                 :        260 :     *resolved_path = os_path_resolve(&file_path, 1);
     119                 :        260 :     CacheHashFile *chf = ch->files.add_one();
     120                 :        260 :     chf->path = resolved_path;
     121                 :        260 :     cache_buf(ch, resolved_path);
     122                 :        260 : }
     123                 :            : 
     124                 :          0 : void cache_file_opt(CacheHash *ch, Buf *file_path) {
     125                 :          0 :     assert(ch->manifest_file_path == nullptr);
     126         [ #  # ]:          0 :     if (file_path == nullptr) {
     127                 :          0 :         cache_str(ch, "");
     128                 :          0 :         cache_str(ch, "");
     129                 :            :     } else {
     130                 :          0 :         cache_file(ch, file_path);
     131                 :            :     }
     132                 :          0 : }
     133                 :            : 
     134                 :            : // Ported from std/base64.zig
     135                 :            : static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
     136                 :       3294 : static void base64_encode(Slice<uint8_t> dest, Slice<uint8_t> source) {
     137                 :       3294 :     size_t dest_len = ((source.len + 2) / 3) * 4;
     138                 :       3294 :     assert(dest.len == dest_len);
     139                 :            : 
     140                 :       3294 :     size_t i = 0;
     141                 :       3294 :     size_t out_index = 0;
     142         [ +  + ]:      55998 :     for (; i + 2 < source.len; i += 3) {
     143                 :      52704 :         dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f];
     144                 :      52704 :         out_index += 1;
     145                 :            : 
     146                 :      52704 :         dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)];
     147                 :      52704 :         out_index += 1;
     148                 :            : 
     149                 :      52704 :         dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)];
     150                 :      52704 :         out_index += 1;
     151                 :            : 
     152                 :      52704 :         dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f];
     153                 :      52704 :         out_index += 1;
     154                 :            :     }
     155                 :            : 
     156                 :            :     // Assert that we never need pad characters.
     157                 :       3294 :     assert(i == source.len);
     158                 :       3294 : }
     159                 :            : 
     160                 :            : // Ported from std/base64.zig
     161                 :       2502 : static Error base64_decode(Slice<uint8_t> dest, Slice<uint8_t> source) {
     162         [ -  + ]:       2502 :     if (source.len % 4 != 0)
     163                 :          0 :         return ErrorInvalidFormat;
     164         [ -  + ]:       2502 :     if (dest.len != (source.len / 4) * 3)
     165                 :          0 :         return ErrorInvalidFormat;
     166                 :            : 
     167                 :            :     // In Zig this is comptime computed. In C++ it's not worth it to do that.
     168                 :            :     uint8_t char_to_index[256];
     169                 :       2502 :     bool char_in_alphabet[256] = {0};
     170         [ +  + ]:     162630 :     for (size_t i = 0; i < 64; i += 1) {
     171                 :     160128 :         uint8_t c = base64_fs_alphabet[i];
     172                 :     160128 :         assert(!char_in_alphabet[c]);
     173                 :     160128 :         char_in_alphabet[c] = true;
     174                 :     160128 :         char_to_index[c] = i;
     175                 :            :     }
     176                 :            : 
     177                 :       2502 :     size_t src_cursor = 0;
     178                 :       2502 :     size_t dest_cursor = 0;
     179                 :            : 
     180         [ +  + ]:      42534 :     for (;src_cursor < source.len; src_cursor += 4) {
     181         [ -  + ]:      40032 :         if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat;
     182         [ -  + ]:      40032 :         if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat;
     183         [ -  + ]:      40032 :         if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat;
     184         [ -  + ]:      40032 :         if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat;
     185                 :      40032 :         dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4);
     186                 :      40032 :         dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2);
     187                 :      40032 :         dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]);
     188                 :      40032 :         dest_cursor += 3;
     189                 :            :     }
     190                 :            : 
     191                 :       2502 :     assert(src_cursor == source.len);
     192                 :       2502 :     assert(dest_cursor == dest.len);
     193                 :       2502 :     return ErrorNone;
     194                 :            : }
     195                 :            : 
     196                 :       2952 : static Error hash_file(uint8_t *digest, OsFile handle, Buf *contents) {
     197                 :            :     Error err;
     198                 :            : 
     199         [ +  + ]:       2952 :     if (contents) {
     200                 :       1763 :         buf_resize(contents, 0);
     201                 :            :     }
     202                 :            : 
     203                 :            :     blake2b_state blake;
     204                 :       2952 :     int rc = blake2b_init(&blake, 48);
     205                 :       2952 :     assert(rc == 0);
     206                 :            : 
     207                 :            :     for (;;) {
     208                 :            :         uint8_t buf[4096];
     209                 :      65089 :         size_t amt = 4096;
     210         [ -  + ]:      65089 :         if ((err = os_file_read(handle, buf, &amt)))
     211                 :       2952 :             return err;
     212         [ +  + ]:      65089 :         if (amt == 0) {
     213                 :       2952 :             rc = blake2b_final(&blake, digest, 48);
     214                 :       2952 :             assert(rc == 0);
     215                 :       2952 :             return ErrorNone;
     216                 :            :         }
     217                 :      62137 :         blake2b_update(&blake, buf, amt);
     218         [ +  + ]:      62137 :         if (contents) {
     219                 :       4619 :             buf_append_mem(contents, (char*)buf, amt);
     220                 :            :         }
     221                 :      62137 :     }
     222                 :            : }
     223                 :            : 
     224                 :            : // If the wall clock time, rounded to the same precision as the
     225                 :            : // mtime, is equal to the mtime, then we cannot rely on this mtime
     226                 :            : // yet. We will instead save an mtime value that indicates the hash
     227                 :            : // must be unconditionally computed.
     228                 :       2952 : static bool is_problematic_timestamp(const OsTimeStamp *fs_clock) {
     229                 :       2952 :     OsTimeStamp wall_clock = os_timestamp_calendar();
     230                 :            :     // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock.
     231         [ +  + ]:       2952 :     if (fs_clock->nsec == 0) {
     232                 :          8 :         wall_clock.nsec = 0;
     233         [ -  + ]:          8 :         if (fs_clock->sec == 0) {
     234                 :          0 :             wall_clock.sec = 0;
     235                 :            :         } else {
     236                 :          8 :             wall_clock.sec &= (-1ull) << ctzll(fs_clock->sec);
     237                 :            :         }
     238                 :            :     } else {
     239                 :       2944 :         wall_clock.nsec &= (-1ull) << ctzll(fs_clock->nsec);
     240                 :            :     }
     241 [ +  + ][ -  + ]:       2952 :     return wall_clock.nsec == fs_clock->nsec && wall_clock.sec == fs_clock->sec;
     242                 :            : }
     243                 :            : 
     244                 :       2952 : static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents) {
     245                 :            :     Error err;
     246                 :            : 
     247                 :       2952 :     assert(chf->path != nullptr);
     248                 :            : 
     249                 :            :     OsFile this_file;
     250         [ -  + ]:       2952 :     if ((err = os_file_open_r(chf->path, &this_file, &chf->attr)))
     251                 :          0 :         return err;
     252                 :            : 
     253         [ -  + ]:       2952 :     if (is_problematic_timestamp(&chf->attr.mtime)) {
     254                 :          0 :         chf->attr.mtime.sec = 0;
     255                 :          0 :         chf->attr.mtime.nsec = 0;
     256                 :          0 :         chf->attr.inode = 0;
     257                 :            :     }
     258                 :            : 
     259         [ -  + ]:       2952 :     if ((err = hash_file(chf->bin_digest, this_file, contents))) {
     260                 :          0 :         os_file_close(&this_file);
     261                 :          0 :         return err;
     262                 :            :     }
     263                 :       2952 :     os_file_close(&this_file);
     264                 :            : 
     265                 :       2952 :     blake2b_update(&ch->blake, chf->bin_digest, 48);
     266                 :            : 
     267                 :       2952 :     return ErrorNone;
     268                 :            : }
     269                 :            : 
     270                 :        171 : Error cache_hit(CacheHash *ch, Buf *out_digest) {
     271                 :            :     Error err;
     272                 :            : 
     273                 :            :     uint8_t bin_digest[48];
     274                 :        171 :     int rc = blake2b_final(&ch->blake, bin_digest, 48);
     275                 :        171 :     assert(rc == 0);
     276                 :            : 
     277                 :        171 :     buf_resize(&ch->b64_digest, 64);
     278                 :        171 :     base64_encode(buf_to_slice(&ch->b64_digest), {bin_digest, 48});
     279                 :            : 
     280 [ +  - ][ +  + ]:        171 :     if (ch->files.length == 0 && !ch->force_check_manifest) {
     281                 :         23 :         buf_resize(out_digest, 64);
     282                 :         23 :         base64_encode(buf_to_slice(out_digest), {bin_digest, 48});
     283                 :         23 :         return ErrorNone;
     284                 :            :     }
     285                 :            : 
     286                 :        148 :     rc = blake2b_init(&ch->blake, 48);
     287                 :        148 :     assert(rc == 0);
     288                 :        148 :     blake2b_update(&ch->blake, bin_digest, 48);
     289                 :            : 
     290                 :        148 :     ch->manifest_file_path = buf_alloc();
     291                 :        148 :     os_path_join(ch->manifest_dir, &ch->b64_digest, ch->manifest_file_path);
     292                 :            : 
     293                 :        148 :     buf_append_str(ch->manifest_file_path, ".txt");
     294                 :            : 
     295         [ -  + ]:        148 :     if ((err = os_make_path(ch->manifest_dir)))
     296                 :          0 :         return err;
     297                 :            : 
     298         [ -  + ]:        148 :     if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file)))
     299                 :          0 :         return err;
     300                 :            : 
     301                 :        148 :     Buf line_buf = BUF_INIT;
     302                 :        148 :     buf_resize(&line_buf, 512);
     303         [ -  + ]:        148 :     if ((err = os_file_read_all(ch->manifest_file, &line_buf))) {
     304                 :          0 :         os_file_close(&ch->manifest_file);
     305                 :          0 :         return err;
     306                 :            :     }
     307                 :            : 
     308                 :        148 :     size_t input_file_count = ch->files.length;
     309                 :        148 :     bool any_file_changed = false;
     310                 :        148 :     Error return_code = ErrorNone;
     311                 :        148 :     size_t file_i = 0;
     312                 :        148 :     SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n"));
     313                 :       2502 :     for (;; file_i += 1) {
     314                 :       2650 :         Optional<Slice<uint8_t>> opt_line = SplitIterator_next(&line_it);
     315                 :            : 
     316                 :            :         CacheHashFile *chf;
     317         [ +  + ]:       2650 :         if (file_i < input_file_count) {
     318                 :        197 :             chf = &ch->files.at(file_i);
     319         [ -  + ]:       2453 :         } else if (any_file_changed) {
     320                 :            :             // cache miss.
     321                 :            :             // keep the manifest file open with the rw lock
     322                 :            :             // reset the hash
     323                 :          0 :             rc = blake2b_init(&ch->blake, 48);
     324                 :          0 :             assert(rc == 0);
     325                 :          0 :             blake2b_update(&ch->blake, bin_digest, 48);
     326                 :          0 :             ch->files.resize(input_file_count);
     327                 :            :             // bring the hash up to the input file hashes
     328         [ #  # ]:          0 :             for (file_i = 0; file_i < input_file_count; file_i += 1) {
     329                 :          0 :                 blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48);
     330                 :            :             }
     331                 :            :             // caller can notice that out_digest is unmodified.
     332                 :          0 :             return return_code;
     333         [ +  + ]:       2453 :         } else if (!opt_line.is_some) {
     334                 :         74 :             break;
     335                 :            :         } else {
     336                 :       2379 :             chf = ch->files.add_one();
     337                 :       2379 :             chf->path = nullptr;
     338                 :            :         }
     339                 :            : 
     340         [ +  + ]:       2576 :         if (!opt_line.is_some)
     341                 :         74 :             break;
     342                 :            : 
     343                 :       2502 :         SplitIterator it = memSplit(opt_line.value, str(" "));
     344                 :            : 
     345                 :       2502 :         Optional<Slice<uint8_t>> opt_inode = SplitIterator_next(&it);
     346         [ -  + ]:       2502 :         if (!opt_inode.is_some) {
     347                 :          0 :             return_code = ErrorInvalidFormat;
     348                 :          0 :             break;
     349                 :            :         }
     350                 :       2502 :         chf->attr.inode = strtoull((const char *)opt_inode.value.ptr, nullptr, 10);
     351                 :            : 
     352                 :       2502 :         Optional<Slice<uint8_t>> opt_mtime_sec = SplitIterator_next(&it);
     353         [ -  + ]:       2502 :         if (!opt_mtime_sec.is_some) {
     354                 :          0 :             return_code = ErrorInvalidFormat;
     355                 :          0 :             break;
     356                 :            :         }
     357                 :       2502 :         chf->attr.mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10);
     358                 :            : 
     359                 :       2502 :         Optional<Slice<uint8_t>> opt_mtime_nsec = SplitIterator_next(&it);
     360         [ -  + ]:       2502 :         if (!opt_mtime_nsec.is_some) {
     361                 :          0 :             return_code = ErrorInvalidFormat;
     362                 :          0 :             break;
     363                 :            :         }
     364                 :       2502 :         chf->attr.mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10);
     365                 :            : 
     366                 :       2502 :         Optional<Slice<uint8_t>> opt_digest = SplitIterator_next(&it);
     367         [ -  + ]:       2502 :         if (!opt_digest.is_some) {
     368                 :          0 :             return_code = ErrorInvalidFormat;
     369                 :          0 :             break;
     370                 :            :         }
     371         [ -  + ]:       2502 :         if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) {
     372                 :          0 :             return_code = ErrorInvalidFormat;
     373                 :          0 :             break;
     374                 :            :         }
     375                 :            : 
     376                 :       2502 :         Slice<uint8_t> file_path = SplitIterator_rest(&it);
     377         [ -  + ]:       2502 :         if (file_path.len == 0) {
     378                 :          0 :             return_code = ErrorInvalidFormat;
     379                 :          0 :             break;
     380                 :            :         }
     381                 :       2502 :         Buf *this_path = buf_create_from_slice(file_path);
     382 [ -  + ][ -  + ]:       2502 :         if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) {
                 [ +  + ]
     383                 :          0 :             return_code = ErrorInvalidFormat;
     384                 :          0 :             break;
     385                 :            :         }
     386                 :       2502 :         chf->path = this_path;
     387                 :            : 
     388                 :            :         // if the mtime matches we can trust the digest
     389                 :            :         OsFile this_file;
     390                 :            :         OsFileAttr actual_attr;
     391         [ -  + ]:       2502 :         if ((err = os_file_open_r(chf->path, &this_file, &actual_attr))) {
     392                 :          0 :             fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err));
     393                 :          0 :             os_file_close(&ch->manifest_file);
     394                 :          0 :             return ErrorCacheUnavailable;
     395                 :            :         }
     396 [ +  - ][ +  - ]:       2502 :         if (chf->attr.mtime.sec == actual_attr.mtime.sec &&
     397         [ +  - ]:       2502 :             chf->attr.mtime.nsec == actual_attr.mtime.nsec &&
     398                 :       2502 :             chf->attr.inode == actual_attr.inode)
     399                 :            :         {
     400                 :       2502 :             os_file_close(&this_file);
     401                 :            :         } else {
     402                 :            :             // we have to recompute the digest.
     403                 :            :             // later we'll rewrite the manifest with the new mtime/digest values
     404                 :          0 :             ch->manifest_dirty = true;
     405                 :          0 :             chf->attr = actual_attr;
     406                 :            : 
     407         [ #  # ]:          0 :             if (is_problematic_timestamp(&actual_attr.mtime)) {
     408                 :          0 :                 chf->attr.mtime.sec = 0;
     409                 :          0 :                 chf->attr.mtime.nsec = 0;
     410                 :          0 :                 chf->attr.inode = 0;
     411                 :            :             }
     412                 :            : 
     413                 :            :             uint8_t actual_digest[48];
     414         [ #  # ]:          0 :             if ((err = hash_file(actual_digest, this_file, nullptr))) {
     415                 :          0 :                 os_file_close(&this_file);
     416                 :          0 :                 os_file_close(&ch->manifest_file);
     417                 :          0 :                 return err;
     418                 :            :             }
     419                 :          0 :             os_file_close(&this_file);
     420         [ #  # ]:          0 :             if (memcmp(chf->bin_digest, actual_digest, 48) != 0) {
     421                 :          0 :                 memcpy(chf->bin_digest, actual_digest, 48);
     422                 :            :                 // keep going until we have the input file digests
     423                 :          0 :                 any_file_changed = true;
     424                 :            :             }
     425                 :            :         }
     426         [ +  - ]:       2502 :         if (!any_file_changed) {
     427                 :       2502 :             blake2b_update(&ch->blake, chf->bin_digest, 48);
     428                 :            :         }
     429                 :       2502 :     }
     430 [ +  + ][ +  - ]:        148 :     if (file_i < input_file_count || file_i == 0 || return_code != ErrorNone) {
                 [ -  + ]
     431                 :            :         // manifest file is empty or missing entries, so this is a cache miss
     432                 :         74 :         ch->manifest_dirty = true;
     433         [ +  + ]:        211 :         for (; file_i < input_file_count; file_i += 1) {
     434                 :        137 :             CacheHashFile *chf = &ch->files.at(file_i);
     435         [ -  + ]:        137 :             if ((err = populate_file_hash(ch, chf, nullptr))) {
     436                 :          0 :                 fprintf(stderr, "Unable to hash %s: %s\n", buf_ptr(chf->path), err_str(err));
     437                 :          0 :                 os_file_close(&ch->manifest_file);
     438                 :          0 :                 return ErrorCacheUnavailable;
     439                 :            :             }
     440                 :            :         }
     441 [ -  + ][ #  # ]:         74 :         if (return_code != ErrorNone && return_code != ErrorInvalidFormat) {
     442                 :          0 :             os_file_close(&ch->manifest_file);
     443                 :            :         }
     444                 :         74 :         return return_code;
     445                 :            :     }
     446                 :            :     // Cache Hit
     447                 :        171 :     return cache_final(ch, out_digest);
     448                 :            : }
     449                 :            : 
     450                 :       2815 : Error cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents) {
     451                 :            :     Error err;
     452                 :            : 
     453                 :       2815 :     assert(ch->manifest_file_path != nullptr);
     454                 :       2815 :     CacheHashFile *chf = ch->files.add_one();
     455                 :       2815 :     chf->path = resolved_path;
     456         [ -  + ]:       2815 :     if ((err = populate_file_hash(ch, chf, contents))) {
     457                 :          0 :         os_file_close(&ch->manifest_file);
     458                 :          0 :         return err;
     459                 :            :     }
     460                 :            : 
     461                 :       2815 :     return ErrorNone;
     462                 :            : }
     463                 :            : 
     464                 :       1052 : Error cache_add_file(CacheHash *ch, Buf *path) {
     465                 :       1052 :     Buf *resolved_path = buf_alloc();
     466                 :       1052 :     *resolved_path = os_path_resolve(&path, 1);
     467                 :       1052 :     return cache_add_file_fetch(ch, resolved_path, nullptr);
     468                 :            : }
     469                 :            : 
     470                 :         35 : Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) {
     471                 :            :     Error err;
     472                 :         35 :     Buf *contents = buf_alloc();
     473         [ -  + ]:         35 :     if ((err = os_fetch_file_path(dep_file_path, contents))) {
     474         [ #  # ]:          0 :         if (err == ErrorFileNotFound)
     475                 :          0 :             return err;
     476         [ #  # ]:          0 :         if (verbose) {
     477                 :          0 :             fprintf(stderr, "%s: unable to read .d file: %s\n", err_str(err), buf_ptr(dep_file_path));
     478                 :            :         }
     479                 :          0 :         return ErrorReadingDepFile;
     480                 :            :     }
     481                 :         35 :     auto it = stage2_DepTokenizer_init(buf_ptr(contents), buf_len(contents));
     482                 :            :     // skip first token: target
     483                 :            :     {
     484                 :         35 :         auto result = stage2_DepTokenizer_next(&it);
     485   [ -  -  +  - ]:         35 :         switch (result.type_id) {
     486                 :          0 :             case stage2_DepNextResult::error:
     487         [ #  # ]:          0 :                 if (verbose) {
     488                 :          0 :                     fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path));
     489                 :            :                 }
     490                 :          0 :                 err = ErrorInvalidDepFile;
     491                 :          0 :                 goto finish;
     492                 :          0 :             case stage2_DepNextResult::null:
     493                 :          0 :                 err = ErrorNone;
     494                 :          0 :                 goto finish;
     495                 :         35 :             case stage2_DepNextResult::target:
     496                 :            :             case stage2_DepNextResult::prereq:
     497                 :         35 :                 err = ErrorNone;
     498                 :         35 :                 break;
     499                 :            :         }
     500                 :            :     }
     501                 :            :     // Process 0+ preqreqs.
     502                 :            :     // clang is invoked in single-source mode so we never get more targets.
     503                 :            :     for (;;) {
     504                 :       1079 :         auto result = stage2_DepTokenizer_next(&it);
     505   [ -  +  +  - ]:       1079 :         switch (result.type_id) {
     506                 :          0 :             case stage2_DepNextResult::error:
     507         [ #  # ]:          0 :                 if (verbose) {
     508                 :          0 :                     fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path));
     509                 :            :                 }
     510                 :          0 :                 err = ErrorInvalidDepFile;
     511                 :         35 :                 goto finish;
     512                 :         35 :             case stage2_DepNextResult::null:
     513                 :            :             case stage2_DepNextResult::target:
     514                 :         35 :                 err = ErrorNone;
     515                 :         35 :                 goto finish;
     516                 :       1044 :             case stage2_DepNextResult::prereq:
     517                 :       1044 :                 break;
     518                 :            :         }
     519                 :       1044 :         auto textbuf = buf_alloc();
     520                 :       1044 :         buf_init_from_str(textbuf, result.textz);
     521         [ -  + ]:       1044 :         if ((err = cache_add_file(ch, textbuf))) {
     522         [ #  # ]:          0 :             if (verbose) {
     523                 :          0 :                 fprintf(stderr, "unable to add %s to cache: %s\n", result.textz, err_str(err));
     524                 :          0 :                 fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path));
     525                 :            :             }
     526                 :          0 :             goto finish;
     527                 :            :         }
     528                 :       1044 :     }
     529                 :            : 
     530                 :         35 :     finish:
     531                 :         35 :     stage2_DepTokenizer_deinit(&it);
     532                 :         35 :     return err;
     533                 :            : }
     534                 :            : 
     535                 :         74 : static Error write_manifest_file(CacheHash *ch) {
     536                 :            :     Error err;
     537                 :         74 :     Buf contents = BUF_INIT;
     538                 :         74 :     buf_resize(&contents, 0);
     539                 :            :     uint8_t encoded_digest[65];
     540                 :         74 :     encoded_digest[64] = 0;
     541         [ +  + ]:       3026 :     for (size_t i = 0; i < ch->files.length; i += 1) {
     542                 :       2952 :         CacheHashFile *chf = &ch->files.at(i);
     543                 :       2952 :         base64_encode({encoded_digest, 64}, {chf->bin_digest, 48});
     544                 :       2952 :         buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n",
     545                 :            :             chf->attr.inode, chf->attr.mtime.sec, chf->attr.mtime.nsec, encoded_digest, buf_ptr(chf->path));
     546                 :            :     }
     547         [ -  + ]:         74 :     if ((err = os_file_overwrite(ch->manifest_file, &contents)))
     548                 :          0 :         return err;
     549                 :            : 
     550                 :         74 :     return ErrorNone;
     551                 :            : }
     552                 :            : 
     553                 :        148 : Error cache_final(CacheHash *ch, Buf *out_digest) {
     554                 :        148 :     assert(ch->manifest_file_path != nullptr);
     555                 :            : 
     556                 :            :     // We don't close the manifest file yet, because we want to
     557                 :            :     // keep it locked until the API user is done using it.
     558                 :            :     // We also don't write out the manifest yet, because until
     559                 :            :     // cache_release is called we still might be working on creating
     560                 :            :     // the artifacts to cache.
     561                 :            : 
     562                 :            :     uint8_t bin_digest[48];
     563                 :        148 :     int rc = blake2b_final(&ch->blake, bin_digest, 48);
     564                 :        148 :     assert(rc == 0);
     565                 :        148 :     buf_resize(out_digest, 64);
     566                 :        148 :     base64_encode(buf_to_slice(out_digest), {bin_digest, 48});
     567                 :            : 
     568                 :        148 :     return ErrorNone;
     569                 :            : }
     570                 :            : 
     571                 :        148 : void cache_release(CacheHash *ch) {
     572                 :        148 :     assert(ch->manifest_file_path != nullptr);
     573                 :            : 
     574                 :            :     Error err;
     575                 :            : 
     576         [ +  + ]:        148 :     if (ch->manifest_dirty) {
     577         [ -  + ]:         74 :         if ((err = write_manifest_file(ch))) {
     578                 :          0 :             fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n",
     579                 :            :                     buf_ptr(ch->manifest_file_path), err_str(err));
     580                 :            :         }
     581                 :            :     }
     582                 :            : 
     583                 :        148 :     os_file_close(&ch->manifest_file);
     584                 :        148 : }
     585                 :            : 

Generated by: LCOV version 1.14