LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssarchive.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 98 120 81.7 %
Date: 2026-08-13 04:56:22 Functions: 2 2 100.0 %

          Line data    Source code
       1             : #include "fd_ssarchive.h"
       2             : 
       3             : #include "../../../util/log/fd_log.h"
       4             : 
       5             : #include <errno.h>
       6             : #include <dirent.h>
       7             : #include <stdlib.h>
       8             : #include <unistd.h>
       9             : 
      10             : struct fd_ssarchive_entry {
      11             :   ulong slot;
      12             :   ulong base_slot;
      13             :   int   is_zstd;
      14             :   char  path[ PATH_MAX ];
      15             :   uchar hash[ FD_HASH_FOOTPRINT ];
      16             : };
      17             : typedef struct fd_ssarchive_entry fd_ssarchive_entry_t;
      18             : 
      19             : #define SORT_NAME  sort_ssarchive_entries
      20       10560 : #define SORT_KEY_T fd_ssarchive_entry_t
      21       14679 : #define SORT_BEFORE(a,b) ( (a).slot>(b).slot )
      22             : #include "../../../util/tmpl/fd_sort.c"
      23             : 
      24             : static fd_ssarchive_entry_t *
      25             : ssarchive_entry_insert( fd_ssarchive_entry_t * entries,
      26             :                         ulong *                cnt,
      27        1620 :                         ulong                  slot ) {
      28        1620 :   if( FD_LIKELY( *cnt<FD_SSARCHIVE_MAX_ENTRIES ) ) return &entries[ (*cnt)++ ];
      29             : 
      30          51 :   ulong oldest = 0UL;
      31       26112 :   for( ulong i=1UL; i<*cnt; i++ ) {
      32       26061 :     if( entries[ i ].slot<entries[ oldest ].slot ) oldest = i;
      33       26061 :   }
      34          51 :   if( FD_UNLIKELY( slot<=entries[ oldest ].slot ) ) return NULL;
      35          51 :   return &entries[ oldest ];
      36          51 : }
      37             : 
      38             : int
      39             : fd_ssarchive_latest_pair( char const * directory,
      40             :                           int          incremental_snapshot,
      41             :                           ulong *      full_slot,
      42             :                           ulong *      incremental_slot,
      43             :                           char         full_path[ static PATH_MAX ],
      44             :                           char         incremental_path[ static PATH_MAX ],
      45             :                           int *        full_is_zstd,
      46             :                           int *        incremental_is_zstd,
      47             :                           uchar        full_hash[ static FD_HASH_FOOTPRINT ],
      48          12 :                           uchar        incremental_hash[ static FD_HASH_FOOTPRINT ] ) {
      49          12 :   *full_slot = ULONG_MAX;
      50          12 :   *incremental_slot = ULONG_MAX;
      51             : 
      52          12 :   DIR * dir = opendir( directory );
      53          12 :   if( FD_UNLIKELY( !dir ) ) {
      54           0 :     if( FD_LIKELY( errno==ENOENT ) ) return -1;
      55           0 :     FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
      56           0 :   }
      57             : 
      58          12 :   fd_ssarchive_entry_t full_snapshots[ FD_SSARCHIVE_MAX_ENTRIES ];
      59          12 :   fd_ssarchive_entry_t incremental_snapshots[ FD_SSARCHIVE_MAX_ENTRIES ];
      60          12 :   ulong full_snapshots_cnt        = 0UL;
      61          12 :   ulong incremental_snapshots_cnt = 0UL;
      62             : 
      63          12 :   struct dirent * entry;
      64        1656 :   for(;;) {
      65        1656 :     errno = 0;
      66        1656 :     entry = readdir( dir );
      67        1656 :     if( FD_UNLIKELY( !entry ) ) break;
      68        1644 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
      69             : 
      70        1620 :     int is_zstd;
      71        1620 :     ulong entry_full_slot, entry_incremental_slot;
      72        1620 :     uchar decoded_hash[ FD_HASH_FOOTPRINT ];
      73        1620 :     if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash, &is_zstd ) ) ) {
      74           0 :       FD_LOG_INFO(( "unrecognized snapshot file `%s/%s` in snapshots directory", directory, entry->d_name ));
      75           0 :       continue;
      76           0 :     }
      77             : 
      78        1620 :     fd_ssarchive_entry_t * dst;
      79        1620 :     ulong                  dst_slot;
      80        1620 :     ulong                  dst_base_slot;
      81        1620 :     if( FD_LIKELY( entry_incremental_slot==ULONG_MAX ) ) {
      82        1605 :       dst_slot      = entry_full_slot;
      83        1605 :       dst_base_slot = ULONG_MAX;
      84        1605 :       dst           = ssarchive_entry_insert( full_snapshots, &full_snapshots_cnt, dst_slot );
      85        1605 :     } else {
      86          15 :       dst_slot      = entry_incremental_slot;
      87          15 :       dst_base_slot = entry_full_slot;
      88          15 :       dst           = ssarchive_entry_insert( incremental_snapshots, &incremental_snapshots_cnt, dst_slot );
      89          15 :     }
      90        1620 :     if( FD_UNLIKELY( !dst ) ) {
      91           0 :       FD_LOG_INFO(( "more than %lu snapshots of one kind in `%s`, ignoring `%s`",
      92           0 :                     FD_SSARCHIVE_MAX_ENTRIES, directory, entry->d_name ));
      93           0 :       continue;
      94           0 :     }
      95             : 
      96        1620 :     dst->slot      = dst_slot;
      97        1620 :     dst->base_slot = dst_base_slot;
      98        1620 :     dst->is_zstd   = is_zstd;
      99        1620 :     if( FD_UNLIKELY( !fd_cstr_printf_check( dst->path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) ) ) {
     100           0 :       FD_LOG_ERR(( "snapshot path too long `%s/%s`", directory, entry->d_name ));
     101           0 :     }
     102        1620 :     fd_memcpy( dst->hash, decoded_hash, FD_HASH_FOOTPRINT );
     103        1620 :   }
     104             : 
     105          12 :   if( FD_UNLIKELY( errno ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     106          12 :   if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     107             : 
     108          12 :   if( FD_LIKELY( incremental_snapshot ) ) {
     109           6 :     if( FD_UNLIKELY( incremental_snapshots_cnt==0UL && full_snapshots_cnt==0UL ) ) return -1;
     110           6 :     if( FD_UNLIKELY( full_snapshots_cnt==0UL ) )                                   return -1;
     111             : 
     112           6 :     sort_ssarchive_entries_inplace( incremental_snapshots, incremental_snapshots_cnt );
     113           6 :     sort_ssarchive_entries_inplace( full_snapshots, full_snapshots_cnt );
     114             : 
     115           6 :     if( FD_UNLIKELY( incremental_snapshots_cnt==0UL ) ) {
     116           0 :       FD_LOG_INFO(("no incremental snapshots found in `%s`, falling back to latest full snapshot", directory ));
     117           0 :       *full_slot           = full_snapshots[ 0UL ].slot;
     118           0 :       *full_is_zstd        = full_snapshots[ 0UL ].is_zstd;
     119           0 :       *incremental_slot    = ULONG_MAX;
     120           0 :       *incremental_is_zstd = 0;
     121           0 :       FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ 0UL ].path ) );
     122           0 :       fd_memcpy( full_hash, full_snapshots[ 0UL ].hash, FD_HASH_FOOTPRINT );
     123           0 :       memset( incremental_hash, 0, FD_HASH_FOOTPRINT );
     124           0 :       return 0;
     125           0 :     }
     126             : 
     127           9 :     for( ulong i=0UL; i<incremental_snapshots_cnt; i++ ) {
     128           6 :       ulong base_slot = incremental_snapshots[ i ].base_slot;
     129          12 :       for( ulong j=0; j<full_snapshots_cnt; j++ ) {
     130          12 :         if( FD_LIKELY( full_snapshots[ j ].slot==base_slot ) ) {
     131           3 :           *full_slot           = base_slot;
     132           3 :           *incremental_slot    = incremental_snapshots[ i ].slot;
     133           3 :           *full_is_zstd        = full_snapshots[ j ].is_zstd;
     134           3 :           *incremental_is_zstd = incremental_snapshots[ i ].is_zstd;
     135           3 :           FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ j ].path ) );
     136           3 :           FD_TEST( fd_cstr_printf_check( incremental_path, PATH_MAX, NULL, "%s", incremental_snapshots[ i ].path ) );
     137           3 :           fd_memcpy( full_hash, full_snapshots[ j ].hash, FD_HASH_FOOTPRINT );
     138           3 :           fd_memcpy( incremental_hash, incremental_snapshots[ i ].hash, FD_HASH_FOOTPRINT );
     139           3 :           return 0;
     140           9 :         } else if( FD_LIKELY( full_snapshots[ j ].slot<base_slot ) ) {
     141             :             /* full snapshots are sorted in descending order, so if we reach a
     142             :                full snapshot with slot smaller than the incremental snapshot's
     143             :                base slot, we can stop searching.  */
     144           3 :             break;
     145           3 :         }
     146          12 :       }
     147           6 :     }
     148             : 
     149             :     /* if we reach here, it means all incrementals are dangling (they
     150             :        don't build off any full snapshot). fallback to a full
     151             :        snapshot in that case. */
     152           3 :     *full_slot           = full_snapshots[ 0UL ].slot;
     153           3 :     *full_is_zstd        = full_snapshots[ 0UL ].is_zstd;
     154           3 :     *incremental_slot    = ULONG_MAX;
     155           3 :     *incremental_is_zstd = 0;
     156           3 :     FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ 0UL ].path ) );
     157           3 :     incremental_path[ 0UL ] = '\0';
     158           3 :     fd_memcpy( full_hash, full_snapshots[ 0UL ].hash, FD_HASH_FOOTPRINT );
     159           3 :     memset( incremental_hash, 0, FD_HASH_FOOTPRINT );
     160           3 :     return 0;
     161             : 
     162           6 :   } else {
     163           6 :     if( FD_UNLIKELY( full_snapshots_cnt==0UL ) ) return -1;
     164             : 
     165           6 :     sort_ssarchive_entries_inplace( full_snapshots, full_snapshots_cnt );
     166             : 
     167           6 :     *full_slot           = full_snapshots[ 0UL ].slot;
     168           6 :     *full_is_zstd        = full_snapshots[ 0UL ].is_zstd;
     169           6 :     *incremental_slot    = ULONG_MAX;
     170           6 :     *incremental_is_zstd = 0;
     171           6 :     FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ 0UL ].path ) );
     172           6 :     incremental_path[ 0UL ] = '\0';
     173           6 :     fd_memcpy( full_hash, full_snapshots[ 0UL ].hash, FD_HASH_FOOTPRINT );
     174           6 :     memset( incremental_hash, 0, FD_HASH_FOOTPRINT );
     175           6 :     return 0;
     176           6 :   }
     177          12 : }

Generated by: LCOV version 1.14