LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssarchive.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 159 0.0 %
Date: 2025-12-06 04:45:29 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             :   char  path[ PATH_MAX ];
      13             : };
      14             : typedef struct fd_ssarchive_entry fd_ssarchive_entry_t;
      15             : 
      16             : #define SORT_NAME  sort_ssarchive_entries
      17           0 : #define SORT_KEY_T fd_ssarchive_entry_t
      18           0 : #define SORT_BEFORE(a,b) ( (a).slot>(b).slot )
      19             : #include "../../../util/tmpl/fd_sort.c"
      20             : 
      21             : #define FD_SSARCHIVE_MAX_FULL_ENTRIES        (512UL)
      22             : #define FD_SSARCHIVE_MAX_INCREMENTAL_ENTRIES (512UL)
      23             : 
      24             : int
      25             : fd_ssarchive_parse_filename( char const * _name,
      26             :                              ulong *      full_slot,
      27             :                              ulong *      incremental_slot,
      28             :                              uchar        hash[ static FD_HASH_FOOTPRINT ],
      29           0 :                              int *        is_zstd ) {
      30           0 :   char name[ PATH_MAX ];
      31           0 :   fd_cstr_ncpy( name, _name, sizeof(name) );
      32             : 
      33           0 :   char * ptr = name;
      34           0 :   int is_incremental;
      35           0 :   if( !strncmp( ptr, "incremental-snapshot-", 21UL ) ) {
      36           0 :     is_incremental = 1;
      37           0 :     ptr += 21UL;
      38           0 :   } else if( !strncmp( ptr, "snapshot-", 9UL ) ) {
      39           0 :     is_incremental = 0;
      40           0 :     ptr += 9UL;
      41           0 :   } else {
      42           0 :     return -1;
      43           0 :   }
      44             : 
      45           0 :   char * next = strchr( ptr, '-' );
      46           0 :   if( FD_UNLIKELY( !next ) ) return -1;
      47           0 :   *next = '\0';
      48           0 :   char * endptr;
      49           0 :   *full_slot = strtoul( ptr, &endptr, 10 );
      50           0 :   if( FD_UNLIKELY( *endptr!='\0' || endptr==ptr || *full_slot==ULONG_MAX ) ) return -1;
      51             : 
      52           0 :   if( is_incremental ) {
      53           0 :     ptr = next + 1;
      54           0 :     next = strchr( ptr, '-' );
      55           0 :     if( FD_UNLIKELY( !next ) ) return -1;
      56           0 :     *next = '\0';
      57           0 :     *incremental_slot = strtoul( ptr, &endptr, 10 );
      58           0 :     if( FD_UNLIKELY( *endptr!='\0' || endptr==ptr || *incremental_slot==ULONG_MAX ) ) return -1;
      59           0 :   } else {
      60           0 :     *incremental_slot = ULONG_MAX;
      61           0 :   }
      62             : 
      63           0 :   ptr = next + 1;
      64           0 :   next = strchr( ptr, '.' );
      65           0 :   if( FD_UNLIKELY( !next ) ) return -1;
      66           0 :   *next = '\0';
      67           0 :   ulong sz = (ulong)(next - ptr);
      68           0 :   if( FD_UNLIKELY( sz>FD_BASE58_ENCODED_32_LEN ) ) return -1;
      69           0 :   uchar * result = fd_base58_decode_32( ptr, hash );
      70           0 :   if( FD_UNLIKELY( result!=hash ) ) return -1;
      71             : 
      72           0 :   ptr = next + 1;
      73             : 
      74           0 :   if(       FD_LIKELY( 0==strncmp( ptr, "tar.zst", 7UL ) ) ) *is_zstd = 1;
      75           0 :   else if ( FD_LIKELY( 0==strncmp( ptr, "tar",     3UL ) ) ) *is_zstd = 0;
      76           0 :   else return -1;
      77             : 
      78           0 :   return 0;
      79           0 : }
      80             : 
      81             : int
      82             : fd_ssarchive_latest_pair( char const * directory,
      83             :                           int          incremental_snapshot,
      84             :                           ulong *      full_slot,
      85             :                           ulong *      incremental_slot,
      86             :                           char         full_path[ static PATH_MAX ],
      87             :                           char         incremental_path[ static PATH_MAX ],
      88             :                           int *        full_is_zstd,
      89           0 :                           int *        incremental_is_zstd ) {
      90           0 :   *full_slot = ULONG_MAX;
      91           0 :   *incremental_slot = ULONG_MAX;
      92             : 
      93           0 :   DIR * dir = opendir( directory );
      94           0 :   if( FD_UNLIKELY( !dir ) ) {
      95           0 :     if( FD_LIKELY( errno==ENOENT ) ) return -1;
      96           0 :     FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
      97           0 :   }
      98             : 
      99           0 :   struct dirent * entry;
     100             : 
     101           0 :   errno = 0;
     102           0 :   while(( entry = readdir( dir ) )) {
     103           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
     104             : 
     105           0 :     int is_zstd;
     106           0 :     ulong entry_full_slot, entry_incremental_slot;
     107           0 :     uchar decoded_hash[ FD_HASH_FOOTPRINT ];
     108           0 :     if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash, &is_zstd ) ) ) {
     109           0 :       FD_LOG_INFO(( "unrecognized snapshot file `%s/%s` in snapshots directory", directory, entry->d_name ));
     110           0 :       continue;
     111           0 :     }
     112             : 
     113           0 :     if( FD_LIKELY( entry_incremental_slot==ULONG_MAX && (entry_full_slot>*full_slot || *full_slot==ULONG_MAX) ) ) {
     114           0 :       *full_slot = entry_full_slot;
     115           0 :       *full_is_zstd = is_zstd;
     116           0 :       if( FD_UNLIKELY( !fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) ) ) {
     117           0 :         FD_LOG_ERR(( "snapshot path too long `%s/%s`", directory, entry->d_name ));
     118           0 :       }
     119           0 :     }
     120           0 :   }
     121             : 
     122           0 :   if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     123           0 :   if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     124             : 
     125           0 :   if( FD_UNLIKELY( *full_slot==ULONG_MAX ) ) return -1;
     126           0 :   if( FD_UNLIKELY( !incremental_snapshot ) ) return 0;
     127             : 
     128           0 :   dir = opendir( directory );
     129           0 :   if( FD_UNLIKELY( !dir ) ) {
     130           0 :     if( FD_LIKELY( errno==ENOENT ) ) return 0;
     131           0 :     FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
     132           0 :   }
     133             : 
     134             :   /* FIXME: This logic may not select the newest snapshot pair.  For
     135             :      example (100, 110) and (90, 120). */
     136           0 :   errno = 0;
     137           0 :   while(( entry = readdir( dir ) )) {
     138           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
     139             : 
     140           0 :     int is_zstd;
     141           0 :     ulong entry_full_slot, entry_incremental_slot;
     142           0 :     uchar decoded_hash[ FD_HASH_FOOTPRINT ];
     143           0 :     if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash, &is_zstd ) ) ) continue;
     144             : 
     145           0 :     if( FD_UNLIKELY( entry_incremental_slot==ULONG_MAX || *full_slot!=entry_full_slot ) ) continue;
     146             : 
     147           0 :     if( FD_LIKELY( *incremental_slot==ULONG_MAX || entry_incremental_slot>*incremental_slot ) ) {
     148           0 :       *incremental_slot = entry_incremental_slot;
     149           0 :       *incremental_is_zstd = is_zstd;
     150           0 :       if( FD_UNLIKELY( !fd_cstr_printf_check( incremental_path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) ) ) {
     151           0 :         FD_LOG_ERR(( "snapshot path too long `%s/%s`", directory, entry->d_name ));
     152           0 :       }
     153           0 :     }
     154           0 :   }
     155             : 
     156           0 :   if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     157           0 :   if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     158           0 :   return 0;
     159           0 : }
     160             : 
     161             : void
     162             : fd_ssarchive_remove_old_snapshots( char const * directory,
     163             :                                    uint         max_full_snapshots_to_keep,
     164           0 :                                    uint         max_incremental_snapshots_to_keep ) {
     165           0 :   ulong full_snapshots_cnt        = 0UL;
     166           0 :   ulong incremental_snapshots_cnt = 0UL;
     167           0 :   fd_ssarchive_entry_t full_snapshots[ FD_SSARCHIVE_MAX_FULL_ENTRIES ];
     168           0 :   fd_ssarchive_entry_t incremental_snapshots[ FD_SSARCHIVE_MAX_INCREMENTAL_ENTRIES ];
     169             : 
     170           0 :   DIR * dir = opendir( directory );
     171           0 :   if( FD_UNLIKELY( !dir ) ) {
     172           0 :     if( FD_LIKELY( errno==ENOENT ) ) return;
     173           0 :     FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
     174           0 :   }
     175             : 
     176           0 :   struct dirent * entry;
     177             : 
     178           0 :   errno = 0;
     179           0 :   while(( entry = readdir( dir ) )) {
     180           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
     181             : 
     182           0 :     int is_zstd;
     183           0 :     ulong entry_full_slot, entry_incremental_slot;
     184           0 :     uchar decoded_hash[ FD_HASH_FOOTPRINT ];
     185           0 :     if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash, &is_zstd ) ) ) {
     186           0 :       FD_LOG_INFO(( "unrecognized snapshot file `%s/%s` in snapshots directory", directory, entry->d_name ));
     187           0 :       continue;
     188           0 :     }
     189             : 
     190           0 :     if( FD_LIKELY( entry_incremental_slot==ULONG_MAX ) ) {
     191           0 :       FD_TEST( entry_full_slot!=ULONG_MAX );
     192             : 
     193           0 :       if( FD_UNLIKELY( full_snapshots_cnt>=FD_SSARCHIVE_MAX_FULL_ENTRIES ) ) {
     194           0 :         continue;
     195           0 :       }
     196             : 
     197           0 :       full_snapshots[ full_snapshots_cnt ].slot = entry_full_slot;
     198           0 :       FD_TEST( fd_cstr_printf_check( full_snapshots[ full_snapshots_cnt ].path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) );
     199           0 :       full_snapshots_cnt++;
     200           0 :     } else {
     201             : 
     202           0 :       if( FD_UNLIKELY( incremental_snapshots_cnt>=FD_SSARCHIVE_MAX_INCREMENTAL_ENTRIES ) ) {
     203           0 :         continue;
     204           0 :       }
     205             : 
     206           0 :       incremental_snapshots[ incremental_snapshots_cnt ].slot = entry_incremental_slot;
     207           0 :       FD_TEST( fd_cstr_printf_check( incremental_snapshots[ incremental_snapshots_cnt ].path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) );
     208           0 :       incremental_snapshots_cnt++;
     209           0 :     }
     210           0 :   }
     211             : 
     212           0 :   if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     213           0 :   if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     214             : 
     215           0 :   if( FD_LIKELY( full_snapshots_cnt>max_full_snapshots_to_keep ) ) {
     216           0 :     sort_ssarchive_entries_inplace( full_snapshots, full_snapshots_cnt );
     217           0 :     for( ulong i=max_full_snapshots_to_keep; i<full_snapshots_cnt; i++ ) {
     218           0 :       if( FD_UNLIKELY( -1==unlink( full_snapshots[ i ].path ) ) ) {
     219           0 :         FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", full_snapshots[ i ].path, errno, fd_io_strerror( errno ) ));
     220           0 :       }
     221           0 :     }
     222           0 :   }
     223             : 
     224           0 :   if( FD_LIKELY( incremental_snapshots_cnt>max_incremental_snapshots_to_keep ) ) {
     225           0 :     sort_ssarchive_entries_inplace( incremental_snapshots, incremental_snapshots_cnt );
     226           0 :     for( ulong i=max_incremental_snapshots_to_keep; i<incremental_snapshots_cnt; i++ ) {
     227           0 :       if( FD_UNLIKELY( -1==unlink( incremental_snapshots[ i ].path ) ) ) {
     228           0 :         FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", incremental_snapshots[ i ].path, errno, fd_io_strerror( errno ) ));
     229           0 :       }
     230           0 :     }
     231           0 :   }
     232           0 : }

Generated by: LCOV version 1.14