LCOV - code coverage report
Current view: top level - src/server/protocol2 - sparse_min.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 88 183 48.1 %
Date: 2021-07-02 04:40:54 Functions: 6 10 60.0 %

          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(&copy);
     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(&copy);
     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             : }

Generated by: LCOV version 1.13