LCOV - code coverage report
Current view: top level - src - sha512sum.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.1 % 127 89
Test Date: 2026-03-01 04:31:48 Functions: 100.0 % 3 3
Branches: 67.7 % 96 65

             Branch data     Line data    Source code
       1                 :             : #include "precizer.h"
       2                 :             : #include <errno.h>
       3                 :             : 
       4                 :             : #ifdef TESTITALL_TEST_HOOKS
       5                 :             : /**
       6                 :             :  * @brief Check whether a path points to the large interruption test file.
       7                 :             :  */
       8                 :        1670 : static bool is_huge_interruption_target(const char *path)
       9                 :             : {
      10         [ -  + ]:        1670 :         if(path == NULL)
      11                 :             :         {
      12                 :           0 :                 return(false);
      13                 :             :         }
      14                 :             : 
      15                 :        1670 :         const char *needle = "hugetestfile";
      16                 :        1670 :         const size_t path_length = strlen(path);
      17                 :        1670 :         const size_t needle_length = strlen(needle);
      18                 :             : 
      19         [ -  + ]:        1670 :         if(path_length < needle_length)
      20                 :             :         {
      21                 :           0 :                 return(false);
      22                 :             :         }
      23                 :             : 
      24                 :        1670 :         return(0 == strcmp(path + (path_length - needle_length),needle));
      25                 :             : }
      26                 :             : 
      27                 :             : /**
      28                 :             :  * @brief Generate a pseudo-random stop byte in the closed range [1, file_size].
      29                 :             :  */
      30                 :           4 : static uint64_t random_stop_byte(const uint64_t file_size)
      31                 :             : {
      32         [ -  + ]:           4 :         if(file_size == 0U)
      33                 :             :         {
      34                 :           0 :                 return(0U);
      35                 :             :         }
      36                 :             : 
      37                 :           4 :         struct timespec now = {0};
      38                 :           4 :         (void)clock_gettime(CLOCK_MONOTONIC,&now);
      39                 :             : 
      40                 :           4 :         uint64_t seed = (uint64_t)now.tv_nsec;
      41                 :           4 :         seed ^= ((uint64_t)now.tv_sec << 32);
      42                 :           4 :         seed ^= (uint64_t)getpid();
      43                 :             : 
      44                 :           4 :         return((seed % file_size) + 1U);
      45                 :             : }
      46                 :             : #endif
      47                 :             : 
      48                 :             : /**
      49                 :             :  * @brief Calculate SHA512 cryptographic hash of a file, optionally resuming from offset.
      50                 :             :  *
      51                 :             :  * Reads file data starting from @p offset, updates @p mdContext, increments
      52                 :             :  * @p summary->total_hashed_bytes for each processed chunk, and accumulates
      53                 :             :  * per-call hashing elapsed time into @p summary->total_hashing_elapsed_ns.
      54                 :             :  *
      55                 :             :  * @param path File path (relative or absolute).
      56                 :             :  * @param path_size Length of @p path.
      57                 :             :  * @param file_buffer Read buffer descriptor.
      58                 :             :  * @param sha512 Output digest buffer (written after finalization).
      59                 :             :  * @param offset In/out byte offset for resume/interruption handling.
      60                 :             :  * @param summary Traversal counters updated with hashed byte count and hashing time.
      61                 :             :  * @param mdContext SHA512 context for incremental hashing.
      62                 :             :  * @param read_error Output flag set when reading fails.
      63                 :             :  * @param read_errno Output errno snapshot for read errors.
      64                 :             :  * @param wrong_file_type Output flag for non-seekable/special files.
      65                 :             :  * @return SUCCESS or FAILURE.
      66                 :             :  */
      67                 :        1672 : Return sha512sum(
      68                 :             :         const char       *path,
      69                 :             :         const size_t     path_size,
      70                 :             :         memory           *file_buffer,
      71                 :             :         unsigned char    *sha512,
      72                 :             :         sqlite3_int64    *offset,
      73                 :             :         TraversalSummary *summary,
      74                 :             :         SHA512_Context   *mdContext,
      75                 :             : #ifdef TESTITALL_TEST_HOOKS
      76                 :             :         const off_t      file_size,
      77                 :             : #endif
      78                 :             :         bool             *read_error,
      79                 :             :         int              *read_errno,
      80                 :             :         bool             *wrong_file_type)
      81                 :             : {
      82                 :             :         /* Status returned by this function through provide()
      83                 :             :            Default value assumes successful completion */
      84                 :        1672 :         Return status = SUCCESS;
      85                 :             : 
      86         [ -  + ]:        1672 :         if(file_buffer->length == 0)
      87                 :             :         {
      88                 :           0 :                 slog(ERROR,"Invalid buffer size: %ld bytes\n",file_buffer->length);
      89                 :           0 :                 provide(FAILURE);
      90                 :             :         }
      91                 :             : 
      92                 :        1672 :         char *absolute_path = NULL;
      93                 :             : 
      94                 :        1672 :         FILE *fileptr = fopen(path,"rb");
      95                 :             : 
      96         [ +  + ]:        1672 :         if(fileptr == NULL)
      97                 :             :         {
      98                 :             :                 // No read permission
      99         [ -  + ]:        1564 :                 if(errno == EACCES)
     100                 :             :                 {
     101                 :           0 :                         *read_error = true;
     102                 :             : 
     103                 :           0 :                         *read_errno = errno;
     104                 :             : 
     105                 :           0 :                         provide(status);
     106                 :             :                 }
     107                 :             : 
     108                 :        1564 :                 status = path_absolute_from_relative(&absolute_path,path,path_size);
     109                 :             : 
     110   [ +  -  -  + ]:        1564 :                 if(absolute_path == NULL || (TRIUMPH & status) == 0)
     111                 :             :                 {
     112                 :           0 :                         slog(ERROR,"Can't constructs an absolute path from the base directory %s and a relative path %s\n",config->running_dir,path);
     113                 :             : 
     114         [ #  # ]:           0 :                         if(absolute_path != NULL)
     115                 :             :                         {
     116                 :           0 :                                 free(absolute_path);
     117                 :             :                         }
     118                 :           0 :                         provide(status);
     119                 :             :                 }
     120                 :             : 
     121                 :        1564 :                 fileptr = fopen(absolute_path,"rb");
     122                 :             : 
     123         [ -  + ]:        1564 :                 if(fileptr == NULL)
     124                 :             :                 {
     125                 :             :                         // No read permission
     126         [ #  # ]:           0 :                         if(errno == EACCES)
     127                 :             :                         {
     128                 :           0 :                                 *read_error = true;
     129                 :             : 
     130                 :           0 :                                 *read_errno = errno;
     131                 :             : 
     132                 :           0 :                                 free(absolute_path);
     133                 :           0 :                                 provide(status);
     134                 :             :                         }
     135                 :             : 
     136                 :           0 :                         slog(ERROR,"Can open the file using neither relative %s nor absolute %s path with errno: %d\n",path,absolute_path,errno);
     137                 :           0 :                         free(absolute_path);
     138                 :           0 :                         provide(FAILURE);
     139                 :             :                 }
     140                 :             :         }
     141                 :             : 
     142                 :             :         // It moves the file pointer "offset" bytes from the beginning of the file
     143         [ -  + ]:        1672 :         if(fseek(fileptr,*offset,SEEK_SET) != 0)
     144                 :             :         {
     145                 :             :                 /*
     146                 :             :                  * Looks like the wrong file type.
     147                 :             :                  * Doesn't need to return FAILURE status.
     148                 :             :                  */
     149                 :           0 :                 *wrong_file_type = true;
     150                 :           0 :                 free(absolute_path);
     151                 :           0 :                 fclose(fileptr);
     152                 :           0 :                 provide(status);
     153                 :             :         }
     154                 :             : 
     155                 :        1672 :         bool loop_was_interrupted = false;
     156                 :        3344 :         bool perform_file_hashing = config->dry_run == false
     157   [ +  +  +  + ]:        1672 :                 || config->dry_run_with_checksums == true;
     158                 :             : 
     159                 :             : #ifdef TESTITALL_TEST_HOOKS
     160                 :             :         /*
     161                 :             :          * 0 means random-stop flow is disabled for this file.
     162                 :             :          * Non-zero means upper bound for random stop byte selection.
     163                 :             :          */
     164                 :        1672 :         uint64_t random_stop_limit = 0U;
     165                 :             :         /* 0 means stop byte has not been selected yet. */
     166                 :        1672 :         uint64_t random_stop_byte_value = 0U;
     167                 :             :         /* Separate state flag: do not overload stop-byte numeric value. */
     168                 :        1672 :         bool random_stop_triggered = false;
     169                 :             : #endif
     170                 :             : 
     171         [ +  + ]:        1672 :         if(*offset == 0)
     172                 :             :         {
     173         [ -  + ]:        1670 :                 if(sha512_init(mdContext) == 1)
     174                 :             :                 {
     175                 :           0 :                         slog(ERROR,"SHA512 initialization failed\n");
     176                 :           0 :                         free(absolute_path);
     177                 :           0 :                         fclose(fileptr);
     178                 :           0 :                         provide(FAILURE);
     179                 :             :                 }
     180                 :             :         }
     181                 :             : 
     182                 :             : #ifdef TESTITALL_TEST_HOOKS
     183                 :             :         /*
     184                 :             :          * Activate random interruption only for a fresh pass of hugetestfile.
     185                 :             :          * Resume path (*offset > 0 at entry) must continue from saved state
     186                 :             :          * without selecting a new random stop point.
     187                 :             :          */
     188   [ +  +  +  +  :        1672 :         if(*offset == 0 && is_huge_interruption_target(path) == true && file_size > 0)
                   +  - ]
     189                 :             :         {
     190                 :           4 :                 random_stop_limit = (uint64_t)file_size;
     191                 :             : 
     192                 :             :                 /*
     193                 :             :                  * Select interruption target before the first fread() call so
     194                 :             :                  * the first chunk can be bounded and the stop point can land
     195                 :             :                  * anywhere in [1, file_size].
     196                 :             :                  */
     197                 :           4 :                 random_stop_byte_value = random_stop_byte(random_stop_limit);
     198                 :             : 
     199                 :             :                 /* Defensive fallback: never allow a zero stop byte. */
     200         [ -  + ]:           4 :                 if(random_stop_byte_value == 0U)
     201                 :             :                 {
     202                 :           0 :                         random_stop_byte_value = 1U;
     203                 :             :                 }
     204                 :             : 
     205                 :             :                 /*
     206                 :             :                  * Keep the stop point strictly inside file data for multi-byte files.
     207                 :             :                  * If random selection lands exactly at EOF, shift it one byte left.
     208                 :             :                  * The guard keeps subtraction safe and avoids unsigned underflow.
     209                 :             :                  */
     210   [ +  -  -  + ]:           4 :                 if(random_stop_limit > 1U && random_stop_byte_value >= random_stop_limit)
     211                 :             :                 {
     212                 :           0 :                         random_stop_byte_value = random_stop_limit - 1U;
     213                 :             :                 }
     214                 :             :         }
     215                 :             : #endif
     216                 :             : 
     217         [ +  + ]:        1672 :         if(perform_file_hashing == true)
     218                 :             :         {
     219                 :        1612 :                 long long int hashing_start_ns = cur_time_monotonic_ns();
     220                 :             : 
     221                 :        1612 :                 unsigned char *buffer = rawdata(file_buffer);
     222                 :             : 
     223                 :             :                 while(true)
     224                 :        1613 :                 {
     225                 :             : #ifdef TESTITALL_TEST_HOOKS
     226                 :             :                         /*
     227                 :             :                          * Trigger point 2 exactly once when selected stop byte is reached.
     228                 :             :                          */
     229         [ +  + ]:        3225 :                         if(random_stop_limit > 0U
     230         [ +  + ]:          10 :                                 && random_stop_triggered == false
     231         [ +  - ]:           8 :                                 && random_stop_byte_value > 0U
     232         [ +  + ]:           8 :                                 && (uint64_t)(*offset) >= random_stop_byte_value)
     233                 :             :                         {
     234                 :           4 :                                 signal_wait_at_point(2U);
     235                 :           4 :                                 random_stop_triggered = true;
     236                 :             :                         }
     237                 :             : #endif
     238                 :             : 
     239                 :             :                         /* Interrupt the loop smoothly */
     240                 :             :                         /* Interrupt when Ctrl+C */
     241                 :             : #ifdef TESTITALL_TEST_HOOKS
     242                 :             :                         /*
     243                 :             :                          * Temporary guard: when random-stop mode is active for hugetestfile,
     244                 :             :                          * do not break on global_interrupt_flag until point 2 has really
     245                 :             :                          * happened. Otherwise interruption may fire too early and miss the
     246                 :             :                          * controlled "interrupt at random byte" scenario.
     247                 :             :                          */
     248                 :        3225 :                         bool delay_interrupt_for_random_stop = false;
     249                 :             : 
     250   [ +  +  +  + ]:        3225 :                         if(random_stop_limit > 0U && random_stop_triggered == false)
     251                 :             :                         {
     252         [ -  + ]:           4 :                                 if(random_stop_byte_value == 0U)
     253                 :             :                                 {
     254                 :             :                                         /* No stop byte yet: wait until at least one block is hashed. */
     255                 :           0 :                                         delay_interrupt_for_random_stop = true;
     256                 :             : 
     257         [ +  - ]:           4 :                                 } else if((uint64_t)(*offset) < random_stop_byte_value){
     258                 :             :                                         /* Stop byte is known but not reached yet: keep hashing. */
     259                 :           4 :                                         delay_interrupt_for_random_stop = true;
     260                 :             :                                 }
     261                 :             :                         }
     262                 :             : #endif
     263                 :             : 
     264         [ +  + ]:        3225 :                         if(global_interrupt_flag == true
     265                 :             : #ifdef TESTITALL_TEST_HOOKS
     266         [ +  - ]:           2 :                                 && delay_interrupt_for_random_stop == false
     267                 :             : #endif
     268                 :             :                         )
     269                 :             :                         {
     270                 :           2 :                                 loop_was_interrupted = true;
     271                 :           2 :                                 break;
     272                 :             :                         }
     273                 :             : 
     274                 :        3223 :                         size_t read_limit = file_buffer->length;
     275                 :             : 
     276                 :             : #ifdef TESTITALL_TEST_HOOKS
     277                 :             :                         /*
     278                 :             :                          * Keep the read bounded so offset can stop exactly at the selected
     279                 :             :                          * byte instead of jumping to EOF in a single large fread().
     280                 :             :                          */
     281         [ +  + ]:        3223 :                         if(random_stop_limit > 0U
     282         [ +  + ]:           8 :                                 && random_stop_triggered == false
     283         [ +  - ]:           4 :                                 && random_stop_byte_value > 0U
     284         [ +  - ]:           4 :                                 && (uint64_t)(*offset) < random_stop_byte_value)
     285                 :             :                         {
     286                 :           4 :                                 const uint64_t bytes_left_to_stop = random_stop_byte_value - (uint64_t)(*offset);
     287                 :             : 
     288         [ +  - ]:           4 :                                 if(bytes_left_to_stop < (uint64_t)read_limit)
     289                 :             :                                 {
     290                 :           4 :                                         read_limit = (size_t)bytes_left_to_stop;
     291                 :             :                                 }
     292                 :             :                         }
     293                 :             : #endif
     294                 :             : 
     295                 :        3223 :                         size_t len = fread(buffer,sizeof(unsigned char),read_limit,fileptr);
     296                 :             : 
     297         [ +  + ]:        3223 :                         if(len == 0)
     298                 :             :                         {
     299         [ +  + ]:        1610 :                                 if(ferror(fileptr))
     300                 :             :                                 {
     301                 :           1 :                                         *read_error = true;
     302                 :           1 :                                         *read_errno = errno;
     303                 :             :                                 }
     304                 :             : 
     305                 :        1610 :                                 break;
     306                 :             :                         }
     307                 :             : 
     308         [ +  - ]:        1613 :                         if(SUCCESS == status)
     309                 :             :                         {
     310         [ -  + ]:        1613 :                                 if(sha512_update(mdContext,buffer,len) == 1)
     311                 :             :                                 {
     312                 :           0 :                                         slog(ERROR,"SHA512 update failed\n");
     313                 :           0 :                                         status = FAILURE;
     314                 :           0 :                                         break;
     315                 :             :                                 }
     316                 :             : 
     317                 :        1613 :                                 *offset += (sqlite3_int64)len;
     318                 :        1613 :                                 summary->total_hashed_bytes += len;
     319                 :             :                         }
     320                 :             :                 }
     321                 :             : 
     322                 :        1612 :                 long long int hashing_stop_ns = cur_time_monotonic_ns();
     323                 :             : 
     324                 :        1612 :                 long long int hashing_elapsed_ns = hashing_stop_ns - hashing_start_ns;
     325                 :             : 
     326         [ -  + ]:        1612 :                 if(hashing_elapsed_ns < 0LL)
     327                 :             :                 {
     328                 :           0 :                         hashing_elapsed_ns = 0LL;
     329                 :             :                 }
     330                 :             : 
     331                 :        1612 :                 summary->total_hashing_elapsed_ns += hashing_elapsed_ns;
     332                 :             :         }
     333                 :             : 
     334         [ -  + ]:        1672 :         if(fclose(fileptr) != 0)
     335                 :             :         {
     336                 :           0 :                 slog(ERROR,"Error closing file %s\n",path);
     337                 :             :         }
     338                 :             : 
     339                 :        1672 :         free(absolute_path);
     340                 :             : 
     341         [ +  - ]:        1672 :         if(SUCCESS == status
     342         [ +  + ]:        1672 :                 && perform_file_hashing == true
     343         [ +  + ]:        1612 :                 && loop_was_interrupted == false)
     344                 :             :         {
     345                 :        1610 :                 *offset = 0;
     346                 :             : 
     347         [ -  + ]:        1610 :                 if(sha512_final(mdContext,sha512) == 1)
     348                 :             :                 {
     349                 :           0 :                         slog(ERROR,"SHA512 finalization failed\n");
     350                 :           0 :                         status = FAILURE;
     351                 :             :                 }
     352                 :             :         }
     353                 :             : 
     354                 :             : #if 0
     355                 :             :         for(size_t i = 0; i < SHA512_DIGEST_LENGTH; i++)
     356                 :             :         {
     357                 :             :                 printf("%02x",sha512[i]);
     358                 :             :         }
     359                 :             :         putchar('\n');
     360                 :             : #endif
     361                 :             : 
     362                 :        1672 :         provide(status);
     363                 :             : }
        

Generated by: LCOV version 2.0-1