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 : }
|