Line data Source code
1 : #include "../../burp.h"
2 : #include "../../alloc.h"
3 : #include "../../cmd.h"
4 : #include "../../cstat.h"
5 : #include "../../fsops.h"
6 : #include "../../iobuf.h"
7 : #include "../../lock.h"
8 : #include "../../log.h"
9 : #include "../../prepend.h"
10 : #include "../sdirs.h"
11 : #include "sparse_min.h"
12 :
13 : #include <uthash.h>
14 :
15 : struct cname_backup
16 : {
17 : uint8_t keep;
18 : uint8_t delete;
19 : char *id;
20 : char *cname;
21 : char *backup;
22 : uint64_t size;
23 : UT_hash_handle hh;
24 : };
25 : struct cname_backup *chash_table=NULL;
26 :
27 9 : static void cname_backup_free(struct cname_backup **cb)
28 : {
29 9 : if(!cb || !*cb)
30 : return;
31 9 : free_w(&(*cb)->cname);
32 9 : free_w(&(*cb)->backup);
33 9 : free_w(&(*cb)->id);
34 9 : free_v((void **)cb);
35 : }
36 :
37 : /*
38 : static void dump_table()
39 : {
40 : uint64_t s=0;
41 : struct cname_backup *tmp;
42 : struct cname_backup *x;
43 :
44 : HASH_ITER(hh, chash_table, x, tmp)
45 : {
46 : printf("HERE %d %d - %s %s: %" PRIu64 "\n",
47 : x->keep, x->delete,
48 : x->cname, x->backup, x->size);
49 : s+=x->size;
50 : }
51 : printf("s: %" PRIu64 "\n", s);
52 : }
53 : */
54 :
55 1 : static void free_table()
56 : {
57 : struct cname_backup *tmp;
58 : struct cname_backup *x;
59 :
60 10 : HASH_ITER(hh, chash_table, x, tmp)
61 : {
62 9 : HASH_DEL(chash_table, x);
63 9 : cname_backup_free(&x);
64 : }
65 1 : chash_table=NULL;
66 1 : }
67 :
68 : // This gets the oldest one that is not marked to keep or to delete.
69 : // Does not distinguish between different clients.
70 0 : static struct cname_backup *find_one_to_delete()
71 : {
72 0 : struct cname_backup *tmp=NULL;
73 0 : struct cname_backup *best=NULL;
74 0 : struct cname_backup *cand=NULL;
75 :
76 0 : if(!chash_table)
77 : return NULL;
78 :
79 0 : HASH_ITER(hh, chash_table, cand, tmp)
80 : {
81 0 : if(cand->delete || cand->keep)
82 0 : continue;
83 0 : if(!best)
84 : {
85 0 : best=cand;
86 0 : continue;
87 : }
88 0 : if(strcmp(cand->backup, best->backup)<0)
89 0 : best=cand;
90 : }
91 : return best;
92 : }
93 :
94 9 : static int parse_man(
95 : char *man,
96 : char **id,
97 : char **cname,
98 : char **backup
99 : )
100 : {
101 9 : int t=0;
102 9 : char *tok=NULL;
103 9 : if(!(tok=strtok(man, "/")))
104 : {
105 0 : logp("Could not tokenise %s in %s\n", man, __func__);
106 0 : return -1;
107 : }
108 54 : while((tok=strtok(NULL, "/")))
109 : {
110 45 : t++;
111 45 : if(t==2)
112 9 : *cname=tok;
113 36 : else if(t==3)
114 9 : *backup=tok;
115 : }
116 9 : if(!*cname || !*backup)
117 : {
118 0 : logp("Could not get cname/backup in %s\n", __func__);
119 0 : return -1;
120 : }
121 9 : if(!(*id=prepend_s(*cname, *backup)))
122 : return -1;
123 9 : return 0;
124 : }
125 :
126 9 : static int add_to_chash_table(
127 : char *man,
128 : int fsize
129 : ) {
130 9 : int ret=-1;
131 9 : char *id=NULL;
132 9 : char *cname=NULL;
133 9 : char *backup=NULL;
134 9 : struct cname_backup *cnb=NULL;
135 :
136 9 : if(parse_man(man, &id, &cname, &backup))
137 : goto end;
138 :
139 9 : HASH_FIND_STR(chash_table, id, cnb);
140 9 : if(cnb)
141 0 : free_w(&id);
142 : else
143 : {
144 9 : if(!(cnb=calloc_w(1, sizeof(*cnb), __func__))
145 9 : || !(cnb->cname=strdup_w(cname, __func__))
146 9 : || !(cnb->backup=strdup_w(backup, __func__)))
147 : goto end;
148 9 : cnb->size=0;
149 9 : cnb->id=id;
150 9 : id=NULL;
151 9 : HASH_ADD_KEYPTR(hh, chash_table,
152 : cnb->id, strlen(cnb->id), cnb);
153 : }
154 9 : cnb->size+=fsize;
155 9 : ret=0;
156 : end:
157 9 : if(ret)
158 0 : cname_backup_free(&cnb);
159 9 : free_w(&id);
160 9 : return ret;
161 : }
162 :
163 0 : static void keep_most_recent_of_each_client(struct cstat *clist)
164 : {
165 : struct cstat *c;
166 0 : if(!chash_table)
167 : return;
168 0 : for(c=clist; c; c=c->next)
169 : {
170 0 : struct cname_backup *tmp=NULL;
171 0 : struct cname_backup *best=NULL;
172 0 : struct cname_backup *cand=NULL;
173 :
174 0 : HASH_ITER(hh, chash_table, cand, tmp)
175 : {
176 0 : if(strcmp(cand->cname, c->name))
177 0 : continue;
178 0 : if(!best)
179 : {
180 0 : best=cand;
181 0 : continue;
182 : }
183 0 : if(strcmp(cand->backup, best->backup)>0)
184 0 : best=cand;
185 : }
186 0 : if(best)
187 0 : best->keep=1;
188 : }
189 : }
190 :
191 0 : static void mark_deletable(struct cstat *clist, uint64_t need)
192 : {
193 0 : uint64_t got=0;
194 0 : struct cname_backup *cnb=NULL;
195 :
196 0 : keep_most_recent_of_each_client(clist);
197 : while(1)
198 : {
199 0 : if((cnb=find_one_to_delete()))
200 : {
201 0 : cnb->delete=1;
202 0 : got+=cnb->size;
203 0 : if(got>=need)
204 : break;
205 : }
206 : else
207 : {
208 : // Did not get enough. Do our best anyway.
209 : break;
210 : }
211 : }
212 0 : logp(" will prune: %"PRIu64 " bytes\n", got);
213 0 : }
214 :
215 : static char *get_global_sparse_tmp(const char *global_sparse)
216 : {
217 0 : return prepend_n(global_sparse, "tmp", strlen("tmp"), ".");
218 : }
219 :
220 0 : static int do_minimise(const char *global_sparse)
221 : {
222 0 : int ret=-1;
223 : struct iobuf rbuf;
224 0 : struct fzp *fzp=NULL;
225 0 : struct fzp *fzp_tmp=NULL;
226 0 : char *id=NULL;
227 0 : char *copy=NULL;
228 0 : char *junk1=NULL;
229 0 : char *junk2=NULL;
230 0 : char *sparse_tmp=NULL;
231 0 : int delete=0;
232 :
233 0 : if(!(sparse_tmp=get_global_sparse_tmp(global_sparse)))
234 : goto end;
235 :
236 0 : memset(&rbuf, 0, sizeof(struct iobuf));
237 0 : if(!(fzp=fzp_gzopen(global_sparse, "rb")))
238 : goto end;
239 0 : if(!(fzp_tmp=fzp_gzopen(sparse_tmp, "wb")))
240 : goto end;
241 : while(1)
242 : {
243 0 : iobuf_free_content(&rbuf);
244 0 : switch(iobuf_fill_from_fzp(&rbuf, fzp))
245 : {
246 : case 1:
247 : // All OK.
248 0 : if(fzp_close(&fzp_tmp))
249 : {
250 0 : logp("error closing %s in %s\n",
251 : sparse_tmp, __func__);
252 0 : goto end;
253 : }
254 0 : if(do_rename(sparse_tmp, global_sparse))
255 : goto end;
256 0 : ret=0;
257 0 : goto end;
258 : case -1:
259 : goto end; // Error.
260 : }
261 :
262 0 : if(rbuf.cmd==CMD_MANIFEST)
263 : {
264 0 : struct cname_backup *cnb=NULL;
265 0 : if(!(copy=strdup_w(rbuf.buf, __func__)))
266 : goto end;
267 : // Do not pass in rbuf here, as we want to write
268 : // it back to the new file, and parse_man destroys it
269 : // with strtok.
270 0 : if(parse_man(copy, &id, &junk1, &junk2))
271 : goto end;
272 0 : HASH_FIND_STR(chash_table, id, cnb);
273 0 : if(cnb && cnb->delete)
274 : delete=1;
275 : else
276 : {
277 0 : delete=0;
278 0 : if(iobuf_send_msg_fzp(&rbuf, fzp_tmp))
279 : goto end;
280 : //fzp_printf(fzp_tmp, "%c%04lX%s\n", CMD_MANIFEST,
281 : // strlen(copy), copy);
282 : }
283 0 : free_w(&id);
284 0 : free_w(©);
285 : }
286 0 : else if(rbuf.cmd==CMD_FINGERPRINT)
287 : {
288 0 : if(delete)
289 0 : continue;
290 0 : if(iobuf_send_msg_fzp(&rbuf, fzp_tmp))
291 : goto end;
292 : }
293 : }
294 :
295 : end:
296 0 : fzp_close(&fzp);
297 0 : fzp_close(&fzp_tmp);
298 0 : free_w(&id);
299 0 : free_w(©);
300 0 : free_w(&sparse_tmp);
301 0 : iobuf_free_content(&rbuf);
302 0 : return ret;
303 : }
304 :
305 1 : static int load_chash_table(const char *global_sparse, uint64_t *tsize)
306 : {
307 1 : int ret=-1;
308 : struct iobuf rbuf;
309 1 : struct fzp *fzp=NULL;
310 1 : int fsize=0;
311 1 : char *man=NULL;
312 :
313 1 : memset(&rbuf, 0, sizeof(struct iobuf));
314 1 : if(!(fzp=fzp_gzopen(global_sparse, "rb")))
315 : goto end;
316 : while(1)
317 : {
318 37 : iobuf_free_content(&rbuf);
319 37 : switch(iobuf_fill_from_fzp(&rbuf, fzp))
320 : {
321 : case 1:
322 1 : if(man)
323 : {
324 1 : if(add_to_chash_table(man, fsize))
325 : goto end;
326 1 : free_w(&man);
327 : }
328 : // All OK.
329 : ret=0;
330 : goto end;
331 : case -1:
332 : goto end; // Error.
333 : }
334 :
335 36 : if(rbuf.cmd==CMD_MANIFEST)
336 : {
337 9 : if(man)
338 : {
339 8 : if(add_to_chash_table(man, fsize))
340 : goto end;
341 8 : free_w(&man);
342 : }
343 9 : man=rbuf.buf;
344 9 : rbuf.buf=NULL;
345 :
346 : // Starting a new one.
347 9 : fsize=rbuf.len+6; // 5 leading chars, plus newline.
348 9 : *tsize+=rbuf.len+6; // 5 leading chars, plus newline.
349 : }
350 27 : else if(rbuf.cmd==CMD_FINGERPRINT)
351 : {
352 : // Each fingerprint is 14 bytes.
353 27 : fsize+=14;
354 27 : *tsize+=14;
355 : }
356 : }
357 :
358 : end:
359 1 : fzp_close(&fzp);
360 1 : iobuf_free_content(&rbuf);
361 1 : free_w(&man);
362 1 : return ret;
363 : }
364 :
365 1 : int sparse_minimise(
366 : struct conf **conf,
367 : const char *global_sparse,
368 : struct lock *sparse_lock,
369 : struct cstat *clist
370 : ) {
371 1 : int ret=-1;
372 1 : uint64_t need=0;
373 1 : uint64_t tsize=0;
374 1 : uint64_t size_max=get_uint64_t(conf[OPT_SPARSE_SIZE_MAX]);
375 :
376 1 : if(sparse_lock->status!=GET_LOCK_GOT)
377 : {
378 0 : logp("Was not given a valid lock in %s()!", __func__);
379 0 : goto end;
380 : }
381 :
382 1 : if(load_chash_table(global_sparse, &tsize))
383 : goto end;
384 1 : logp("sparse_size_max: %" PRIu64 " bytes\n", size_max);
385 1 : logp(" actual size: %" PRIu64 " bytes\n", tsize);
386 1 : if(tsize<=size_max)
387 : {
388 1 : logp("Do not need to prune\n");
389 1 : ret=0;
390 1 : goto end;
391 : }
392 0 : need=tsize-size_max;
393 0 : logp(" too big by: %" PRIu64 " bytes\n", need);
394 0 : mark_deletable(clist, need);
395 0 : ret=do_minimise(global_sparse);
396 : end:
397 1 : free_table();
398 1 : return ret;
399 : }
|