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