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