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 * _name,
26 : ulong * full_slot,
27 : ulong * incremental_slot,
28 0 : uchar hash[ static FD_HASH_FOOTPRINT ] ) {
29 0 : char name[ PATH_MAX ] = {0};
30 0 : strncpy( name, _name, PATH_MAX-1 );
31 :
32 0 : char * ptr = name;
33 0 : int is_incremental;
34 0 : if( !strncmp( ptr, "incremental-snapshot-", 21UL ) ) {
35 0 : is_incremental = 1;
36 0 : ptr += 21UL;
37 0 : } else if( !strncmp( ptr, "snapshot-", 9UL ) ) {
38 0 : is_incremental = 0;
39 0 : ptr += 9UL;
40 0 : } else {
41 0 : return -1;
42 0 : }
43 :
44 0 : char * next = strchr( ptr, '-' );
45 0 : if( FD_UNLIKELY( !next ) ) return -1;
46 :
47 0 : *next = '\0';
48 0 : char * endptr;
49 0 : ulong slot = strtoul( ptr, &endptr, 10 );
50 0 : if( FD_UNLIKELY( *endptr!='\0' || slot==ULONG_MAX ) ) return -1;
51 :
52 0 : *full_slot = slot;
53 :
54 0 : if( is_incremental ) {
55 0 : ptr = next + 1;
56 0 : next = strchr( ptr, '-' );
57 0 : if( FD_UNLIKELY( !next ) ) return -1;
58 :
59 0 : *next = '\0';
60 0 : slot = strtoul( ptr, &endptr, 10 );
61 0 : if( FD_UNLIKELY( *endptr!='\0' || slot==ULONG_MAX ) ) return -1;
62 :
63 0 : *incremental_slot = slot;
64 0 : } else {
65 0 : *incremental_slot = ULONG_MAX;
66 0 : }
67 :
68 0 : ptr = next + 1;
69 0 : next = strchr( ptr, '.' );
70 0 : if( FD_UNLIKELY( !next ) ) return -1;
71 :
72 0 : ulong sz = (ulong)(next - ptr);
73 :
74 0 : if( FD_UNLIKELY( sz>FD_BASE58_ENCODED_32_LEN ) ) return -1;
75 :
76 0 : char encoded_hash[ FD_BASE58_ENCODED_32_SZ ];
77 0 : fd_memcpy( encoded_hash, ptr, sz );
78 0 : encoded_hash[ sz ] = '\0';
79 0 : uchar * result = fd_base58_decode_32( encoded_hash, hash );
80 :
81 0 : if( FD_UNLIKELY( !result ) ) return -1;
82 :
83 0 : if( FD_UNLIKELY( strncmp( next, ".tar.zst", 8UL ) ) ) return -1;
84 0 : return 0;
85 0 : }
86 :
87 : int
88 : fd_ssarchive_latest_pair( char const * directory,
89 : int incremental_snapshot,
90 : ulong * full_slot,
91 : ulong * incremental_slot,
92 : char full_path[ static PATH_MAX ],
93 0 : char incremental_path[ static PATH_MAX ] ) {
94 0 : *full_slot = ULONG_MAX;
95 0 : *incremental_slot = ULONG_MAX;
96 :
97 0 : DIR * dir = opendir( directory );
98 0 : if( FD_UNLIKELY( !dir ) ) {
99 0 : if( FD_LIKELY( errno==ENOENT ) ) return -1;
100 0 : FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
101 0 : }
102 :
103 0 : struct dirent * entry;
104 :
105 0 : errno = 0;
106 0 : while(( entry = readdir( dir ) )) {
107 0 : if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
108 :
109 0 : ulong entry_full_slot, entry_incremental_slot;
110 0 : uchar decoded_hash[ FD_HASH_FOOTPRINT ];
111 0 : if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash ) ) ) {
112 0 : FD_LOG_INFO(( "unrecognized snapshot file `%s/%s` in snapshots directory", directory, entry->d_name ));
113 0 : continue;
114 0 : }
115 :
116 0 : if( FD_LIKELY( entry_incremental_slot==ULONG_MAX && (entry_full_slot>*full_slot || *full_slot==ULONG_MAX) ) ) {
117 0 : *full_slot = entry_full_slot;
118 0 : if( FD_UNLIKELY( !fd_cstr_printf_check( full_path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) ) ) {
119 0 : FD_LOG_ERR(( "snapshot path too long `%s/%s`", directory, entry->d_name ));
120 0 : }
121 0 : }
122 0 : }
123 :
124 0 : if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
125 0 : if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
126 :
127 0 : if( FD_UNLIKELY( *full_slot==ULONG_MAX ) ) return -1;
128 0 : if( FD_UNLIKELY( !incremental_snapshot ) ) return 0;
129 :
130 0 : dir = opendir( directory );
131 0 : if( FD_UNLIKELY( !dir ) ) {
132 0 : if( FD_LIKELY( errno==ENOENT ) ) return 0;
133 0 : FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
134 0 : }
135 :
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 : ulong entry_full_slot, entry_incremental_slot;
141 0 : uchar decoded_hash[ FD_HASH_FOOTPRINT ];
142 0 : if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash ) ) ) continue;
143 :
144 0 : if( FD_UNLIKELY( entry_incremental_slot==ULONG_MAX || *full_slot!=entry_full_slot ) ) continue;
145 :
146 0 : if( FD_LIKELY( *incremental_slot==ULONG_MAX || entry_incremental_slot>*incremental_slot ) ) {
147 0 : *incremental_slot = entry_incremental_slot;
148 0 : if( FD_UNLIKELY( !fd_cstr_printf_check( incremental_path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) ) ) {
149 0 : FD_LOG_ERR(( "snapshot path too long `%s/%s`", directory, entry->d_name ));
150 0 : }
151 0 : }
152 0 : }
153 :
154 0 : if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
155 0 : if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
156 0 : return 0;
157 0 : }
158 :
159 : void
160 : fd_ssarchive_remove_old_snapshots( char const * directory,
161 : uint max_full_snapshots_to_keep,
162 0 : uint max_incremental_snapshots_to_keep ) {
163 0 : ulong full_snapshots_cnt = 0UL;
164 0 : ulong incremental_snapshots_cnt = 0UL;
165 0 : fd_ssarchive_entry_t full_snapshots[ FD_SSARCHIVE_MAX_FULL_ENTRIES ];
166 0 : fd_ssarchive_entry_t incremental_snapshots[ FD_SSARCHIVE_MAX_INCREMENTAL_ENTRIES ];
167 :
168 0 : DIR * dir = opendir( directory );
169 0 : if( FD_UNLIKELY( !dir ) ) {
170 0 : if( FD_LIKELY( errno==ENOENT ) ) return;
171 0 : FD_LOG_ERR(( "opendir() failed `%s` (%i-%s)", directory, errno, fd_io_strerror( errno ) ));
172 0 : }
173 :
174 0 : struct dirent * entry;
175 :
176 0 : errno = 0;
177 0 : while(( entry = readdir( dir ) )) {
178 0 : if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
179 :
180 0 : ulong entry_full_slot, entry_incremental_slot;
181 0 : uchar decoded_hash[ FD_HASH_FOOTPRINT ];
182 0 : if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash ) ) ) {
183 0 : FD_LOG_INFO(( "unrecognized snapshot file `%s/%s` in snapshots directory", directory, entry->d_name ));
184 0 : continue;
185 0 : }
186 :
187 0 : if( FD_LIKELY( entry_incremental_slot==ULONG_MAX ) ) {
188 0 : FD_TEST( entry_full_slot!=ULONG_MAX );
189 :
190 0 : if( FD_UNLIKELY( full_snapshots_cnt>=FD_SSARCHIVE_MAX_FULL_ENTRIES ) ) {
191 0 : continue;
192 0 : }
193 :
194 0 : full_snapshots[ full_snapshots_cnt ].slot = entry_full_slot;
195 0 : FD_TEST( fd_cstr_printf_check( full_snapshots[ full_snapshots_cnt ].path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) );
196 0 : full_snapshots_cnt++;
197 0 : } else {
198 :
199 0 : if( FD_UNLIKELY( incremental_snapshots_cnt>=FD_SSARCHIVE_MAX_INCREMENTAL_ENTRIES ) ) {
200 0 : continue;
201 0 : }
202 :
203 0 : incremental_snapshots[ incremental_snapshots_cnt ].slot = entry_incremental_slot;
204 0 : FD_TEST( fd_cstr_printf_check( incremental_snapshots[ incremental_snapshots_cnt ].path, PATH_MAX, NULL, "%s/%s", directory, entry->d_name ) );
205 0 : incremental_snapshots_cnt++;
206 0 : }
207 0 : }
208 :
209 0 : if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
210 0 : if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
211 :
212 0 : if( FD_LIKELY( full_snapshots_cnt>max_full_snapshots_to_keep ) ) {
213 0 : sort_ssarchive_entries_inplace( full_snapshots, full_snapshots_cnt );
214 0 : for( ulong i=max_full_snapshots_to_keep; i<full_snapshots_cnt; i++ ) {
215 0 : if( FD_UNLIKELY( -1==unlink( full_snapshots[ i ].path ) ) ) {
216 0 : FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", full_snapshots[ i ].path, errno, fd_io_strerror( errno ) ));
217 0 : }
218 0 : }
219 0 : }
220 :
221 0 : if( FD_LIKELY( incremental_snapshots_cnt>max_incremental_snapshots_to_keep ) ) {
222 0 : sort_ssarchive_entries_inplace( incremental_snapshots, incremental_snapshots_cnt );
223 0 : for( ulong i=max_incremental_snapshots_to_keep; i<incremental_snapshots_cnt; i++ ) {
224 0 : if( FD_UNLIKELY( -1==unlink( incremental_snapshots[ i ].path ) ) ) {
225 0 : FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", incremental_snapshots[ i ].path, errno, fd_io_strerror( errno ) ));
226 0 : }
227 0 : }
228 0 : }
229 0 : }
|