LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssarchive.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 167 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 3 0.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           0 : #define SORT_KEY_T fd_ssarchive_entry_t
      21           0 : #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           0 :                         ulong                  slot ) {
      28           0 :   if( FD_LIKELY( *cnt<FD_SSARCHIVE_MAX_ENTRIES ) ) return &entries[ (*cnt)++ ];
      29             : 
      30           0 :   ulong oldest = 0UL;
      31           0 :   for( ulong i=1UL; i<*cnt; i++ ) {
      32           0 :     if( entries[ i ].slot<entries[ oldest ].slot ) oldest = i;
      33           0 :   }
      34           0 :   if( FD_UNLIKELY( slot<=entries[ oldest ].slot ) ) return NULL;
      35           0 :   return &entries[ oldest ];
      36           0 : }
      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           0 :                           uchar        incremental_hash[ static FD_HASH_FOOTPRINT ] ) {
      49           0 :   *full_slot = ULONG_MAX;
      50           0 :   *incremental_slot = ULONG_MAX;
      51             : 
      52           0 :   DIR * dir = opendir( directory );
      53           0 :   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           0 :   fd_ssarchive_entry_t full_snapshots[ FD_SSARCHIVE_MAX_ENTRIES ];
      59           0 :   fd_ssarchive_entry_t incremental_snapshots[ FD_SSARCHIVE_MAX_ENTRIES ];
      60           0 :   ulong full_snapshots_cnt        = 0UL;
      61           0 :   ulong incremental_snapshots_cnt = 0UL;
      62             : 
      63           0 :   struct dirent * entry;
      64           0 :   for(;;) {
      65           0 :     errno = 0;
      66           0 :     entry = readdir( dir );
      67           0 :     if( FD_UNLIKELY( !entry ) ) break;
      68           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
      69             : 
      70           0 :     int is_zstd;
      71           0 :     ulong entry_full_slot, entry_incremental_slot;
      72           0 :     uchar decoded_hash[ FD_HASH_FOOTPRINT ];
      73           0 :     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           0 :     fd_ssarchive_entry_t * dst;
      79           0 :     ulong                  dst_slot;
      80           0 :     ulong                  dst_base_slot;
      81           0 :     if( FD_LIKELY( entry_incremental_slot==ULONG_MAX ) ) {
      82           0 :       dst_slot      = entry_full_slot;
      83           0 :       dst_base_slot = ULONG_MAX;
      84           0 :       dst           = ssarchive_entry_insert( full_snapshots, &full_snapshots_cnt, dst_slot );
      85           0 :     } else {
      86           0 :       dst_slot      = entry_incremental_slot;
      87           0 :       dst_base_slot = entry_full_slot;
      88           0 :       dst           = ssarchive_entry_insert( incremental_snapshots, &incremental_snapshots_cnt, dst_slot );
      89           0 :     }
      90           0 :     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           0 :     dst->slot      = dst_slot;
      97           0 :     dst->base_slot = dst_base_slot;
      98           0 :     dst->is_zstd   = is_zstd;
      99           0 :     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           0 :     fd_memcpy( dst->hash, decoded_hash, FD_HASH_FOOTPRINT );
     103           0 :   }
     104             : 
     105           0 :   if( FD_UNLIKELY( errno ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     106           0 :   if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     107             : 
     108           0 :   if( FD_LIKELY( incremental_snapshot ) ) {
     109           0 :     if( FD_UNLIKELY( incremental_snapshots_cnt==0UL && full_snapshots_cnt==0UL ) ) return -1;
     110           0 :     if( FD_UNLIKELY( full_snapshots_cnt==0UL ) )                                   return -1;
     111             : 
     112           0 :     sort_ssarchive_entries_inplace( incremental_snapshots, incremental_snapshots_cnt );
     113           0 :     sort_ssarchive_entries_inplace( full_snapshots, full_snapshots_cnt );
     114             : 
     115           0 :     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 :       incremental_path[ 0UL ] = '\0';
     123           0 :       fd_memcpy( full_hash, full_snapshots[ 0UL ].hash, FD_HASH_FOOTPRINT );
     124           0 :       memset( incremental_hash, 0, FD_HASH_FOOTPRINT );
     125           0 :       return 0;
     126           0 :     }
     127             : 
     128           0 :     for( ulong i=0UL; i<incremental_snapshots_cnt; i++ ) {
     129           0 :       ulong base_slot = incremental_snapshots[ i ].base_slot;
     130           0 :       for( ulong j=0; j<full_snapshots_cnt; j++ ) {
     131           0 :         if( FD_LIKELY( full_snapshots[ j ].slot==base_slot ) ) {
     132           0 :           *full_slot           = base_slot;
     133           0 :           *incremental_slot    = incremental_snapshots[ i ].slot;
     134           0 :           *full_is_zstd        = full_snapshots[ j ].is_zstd;
     135           0 :           *incremental_is_zstd = incremental_snapshots[ i ].is_zstd;
     136           0 :           FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ j ].path ) );
     137           0 :           FD_TEST( fd_cstr_printf_check( incremental_path, PATH_MAX, NULL, "%s", incremental_snapshots[ i ].path ) );
     138           0 :           fd_memcpy( full_hash, full_snapshots[ j ].hash, FD_HASH_FOOTPRINT );
     139           0 :           fd_memcpy( incremental_hash, incremental_snapshots[ i ].hash, FD_HASH_FOOTPRINT );
     140           0 :           return 0;
     141           0 :         } else if( FD_LIKELY( full_snapshots[ j ].slot<base_slot ) ) {
     142             :             /* full snapshots are sorted in descending order, so if we reach a
     143             :                full snapshot with slot smaller than the incremental snapshot's
     144             :                base slot, we can stop searching.  */
     145           0 :             break;
     146           0 :         }
     147           0 :       }
     148           0 :     }
     149             : 
     150             :     /* if we reach here, it means all incrementals are dangling (they
     151             :        don't build off any full snapshot). fallback to a full
     152             :        snapshot in that case. */
     153           0 :     *full_slot           = full_snapshots[ 0UL ].slot;
     154           0 :     *full_is_zstd        = full_snapshots[ 0UL ].is_zstd;
     155           0 :     *incremental_slot    = ULONG_MAX;
     156           0 :     *incremental_is_zstd = 0;
     157           0 :     FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ 0UL ].path ) );
     158           0 :     incremental_path[ 0UL ] = '\0';
     159           0 :     fd_memcpy( full_hash, full_snapshots[ 0UL ].hash, FD_HASH_FOOTPRINT );
     160           0 :     memset( incremental_hash, 0, FD_HASH_FOOTPRINT );
     161           0 :     return 0;
     162             : 
     163           0 :   } else {
     164           0 :     if( FD_UNLIKELY( full_snapshots_cnt==0UL ) ) return -1;
     165             : 
     166           0 :     sort_ssarchive_entries_inplace( full_snapshots, full_snapshots_cnt );
     167             : 
     168           0 :     *full_slot           = full_snapshots[ 0UL ].slot;
     169           0 :     *full_is_zstd        = full_snapshots[ 0UL ].is_zstd;
     170           0 :     *incremental_slot    = ULONG_MAX;
     171           0 :     *incremental_is_zstd = 0;
     172           0 :     FD_TEST( fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s", full_snapshots[ 0UL ].path ) );
     173           0 :     incremental_path[ 0UL ] = '\0';
     174           0 :     fd_memcpy( full_hash, full_snapshots[ 0UL ].hash, FD_HASH_FOOTPRINT );
     175           0 :     memset( incremental_hash, 0, FD_HASH_FOOTPRINT );
     176           0 :     return 0;
     177           0 :   }
     178           0 : }
     179             : 
     180             : int
     181             : fd_ssarchive_latest_best( char const * directory,
     182             :                           int          incremental_snapshot,
     183             :                           ulong        target_slot,
     184             :                           ulong *      full_slot,
     185             :                           ulong *      incremental_slot,
     186             :                           char         full_path[ static PATH_MAX ],
     187             :                           char         incremental_path[ static PATH_MAX ],
     188             :                           int *        full_is_zstd,
     189             :                           int *        incremental_is_zstd,
     190             :                           uchar        full_hash[ static FD_HASH_FOOTPRINT ],
     191           0 :                           uchar        incremental_hash[ static FD_HASH_FOOTPRINT ] ) {
     192             : 
     193           0 :   int found = 0==fd_ssarchive_latest_pair( directory,
     194           0 :                                            0, /* full */
     195           0 :                                            full_slot,
     196           0 :                                            incremental_slot,
     197           0 :                                            full_path,
     198           0 :                                            incremental_path,
     199           0 :                                            full_is_zstd,
     200           0 :                                            incremental_is_zstd,
     201           0 :                                            full_hash,
     202           0 :                                            incremental_hash );
     203             : 
     204             :   /* An incremental needs a full, so the pair search would fail too. */
     205             : 
     206           0 :   if( FD_UNLIKELY( !found ) ) return -1;
     207             : 
     208             :   /* Consider the pair when incrementals are enabled, or when the full
     209             :      alone falls short of the target. */
     210             : 
     211           0 :   if( FD_UNLIKELY( !incremental_snapshot && !( target_slot && *full_slot<target_slot ) ) ) return 0;
     212             : 
     213           0 :   ulong pair_full_slot = ULONG_MAX;
     214           0 :   ulong pair_incr_slot = ULONG_MAX;
     215           0 :   int   pair_full_zstd = 0;
     216           0 :   int   pair_incr_zstd = 0;
     217           0 :   char  pair_full_path[ PATH_MAX ] = {0};
     218           0 :   char  pair_incr_path[ PATH_MAX ] = {0};
     219           0 :   uchar pair_full_hash[ FD_HASH_FOOTPRINT ] = {0};
     220           0 :   uchar pair_incr_hash[ FD_HASH_FOOTPRINT ] = {0};
     221             : 
     222           0 :   if( FD_UNLIKELY( 0!=fd_ssarchive_latest_pair( directory,
     223           0 :                                                 1, /* incremental */
     224           0 :                                                 &pair_full_slot,
     225           0 :                                                 &pair_incr_slot,
     226           0 :                                                 pair_full_path,
     227           0 :                                                 pair_incr_path,
     228           0 :                                                 &pair_full_zstd,
     229           0 :                                                 &pair_incr_zstd,
     230           0 :                                                 pair_full_hash,
     231           0 :                                                 pair_incr_hash ) ) ) {
     232           0 :     return 0;
     233           0 :   }
     234             : 
     235           0 :   ulong pair_slot = fd_ulong_if( pair_incr_slot!=ULONG_MAX, pair_incr_slot, pair_full_slot );
     236           0 :   if( FD_LIKELY( pair_slot>*full_slot ) ) {
     237           0 :     *full_slot           = pair_full_slot;
     238           0 :     *incremental_slot    = pair_incr_slot;
     239           0 :     *full_is_zstd        = pair_full_zstd;
     240           0 :     *incremental_is_zstd = pair_incr_zstd;
     241           0 :     fd_cstr_ncpy( full_path,        pair_full_path, PATH_MAX );
     242           0 :     fd_cstr_ncpy( incremental_path, pair_incr_path, PATH_MAX );
     243           0 :     fd_memcpy( full_hash,        pair_full_hash, FD_HASH_FOOTPRINT );
     244           0 :     fd_memcpy( incremental_hash, pair_incr_hash, FD_HASH_FOOTPRINT );
     245           0 :   }
     246             : 
     247           0 :   return 0;
     248           0 : }

Generated by: LCOV version 1.14