LCOV - code coverage report
Current view: top level - src/server - rubble.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 9 206 4.4 %
Date: 2019-06-30 22:47:48 Functions: 2 14 14.3 %

          Line data    Source code
       1             : #include "../burp.h"
       2             : #include "../alloc.h"
       3             : #include "../asfd.h"
       4             : #include "../async.h"
       5             : #include "../fsops.h"
       6             : #include "../fzp.h"
       7             : #include "../handy.h"
       8             : #include "../lock.h"
       9             : #include "../log.h"
      10             : #include "../prepend.h"
      11             : #include "compress.h"
      12             : #include "protocol1/backup_phase4.h"
      13             : #include "protocol1/zlibio.h"
      14             : #include "protocol2/backup_phase4.h"
      15             : #include "rubble.h"
      16             : #include "sdirs.h"
      17             : #include "timestamp.h"
      18             : 
      19             : static char *get_resume_path(const char *path)
      20             : {
      21           0 :         return prepend_s(path, "resumed");
      22             : }
      23             : 
      24           0 : static int append_to_resume_file(const char *path)
      25             : {
      26           0 :         int ret=-1;
      27           0 :         char tstmp[48]="";
      28           0 :         char *resume_path=NULL;
      29             : 
      30           0 :         if(timestamp_get_new(/*index*/0,
      31             :                 tstmp, sizeof(tstmp),
      32             :                 /*bufforfile*/NULL, /*bs*/0,
      33             :                 /*format*/NULL))
      34             :                         goto end;
      35           0 :         if(!(resume_path=get_resume_path(path)))
      36             :                 goto end;
      37           0 :         ret=timestamp_write(resume_path, tstmp);
      38             : end:
      39           0 :         free_w(&resume_path);
      40           0 :         return ret;
      41             : }
      42             : 
      43           0 : static int resume_count(const char *path)
      44             : {
      45           0 :         int count=-1;
      46           0 :         char buf[256]="";
      47           0 :         struct fzp *fzp=NULL;
      48           0 :         char *resume_path=NULL;
      49             : 
      50           0 :         if(!(resume_path=get_resume_path(path)))
      51             :                 goto end;
      52           0 :         if(!(fzp=fzp_open(resume_path, "rb")))
      53             :                 goto end;
      54             :         count=0;
      55           0 :         while(fzp_gets(fzp, buf, sizeof(buf)))
      56           0 :                 count++;
      57             : end:
      58           0 :         fzp_close(&fzp);
      59           0 :         free_w(&resume_path);
      60           0 :         return count;
      61             : }
      62             : 
      63           0 : static int incexc_matches(const char *fullrealwork, const char *incexc)
      64             : {
      65           0 :         int ret=0;
      66           0 :         int got=0;
      67           0 :         struct fzp *fzp=NULL;
      68           0 :         char buf[4096]="";
      69           0 :         const char *inc=NULL;
      70           0 :         char *old_incexc_path=NULL;
      71           0 :         if(!(old_incexc_path=prepend_s(fullrealwork, "incexc")))
      72             :                 return -1;
      73           0 :         if(!(fzp=fzp_open(old_incexc_path, "rb")))
      74             :         {
      75             :                 // Assume that no incexc file could be found because the client
      76             :                 // was on an old version. Assume resume is OK and return 1.
      77             :                 ret=1;
      78             :                 goto end;
      79             :         }
      80             :         inc=incexc;
      81           0 :         while((got=fzp_read(fzp, buf, sizeof(buf)))>0)
      82             :         {
      83           0 :                 if(strlen(inc)<(size_t)got) break;
      84           0 :                 if(strncmp(buf, inc, got)) break;
      85           0 :                 inc+=got;
      86             :         }
      87           0 :         if(inc && strlen(inc)) ret=0;
      88           0 :         else ret=1;
      89             : end:
      90           0 :         fzp_close(&fzp);
      91           0 :         free_w(&old_incexc_path);
      92           0 :         return ret;
      93             : }
      94             : 
      95           0 : static int working_delete(struct async *as, struct sdirs *sdirs)
      96             : {
      97             :         // Try to remove it and start again.
      98           0 :         logp("deleting old working directory\n");
      99           0 :         if(recursive_delete(sdirs->rworking))
     100             :         {
     101           0 :                 log_and_send(as->asfd,
     102             :                         "Old working directory is in the way.\n");
     103             :                 return -1;
     104             :         }
     105             :         // Get rid of the symlink.
     106           0 :         unlink(sdirs->working);
     107             :         return 0;
     108             : }
     109             : 
     110           0 : static int working_resume(struct async *as, struct sdirs *sdirs,
     111             :         const char *incexc, int *resume, struct conf **cconfs)
     112             : {
     113           0 :         if(get_string(cconfs[OPT_SUPER_CLIENT]))
     114             :         {
     115             :                 // This client is not the original client, resuming might cause
     116             :                 // all sorts of trouble.
     117           0 :                 log_and_send(as->asfd, "Found interrupted backup - not resuming because the connected client is not the original");
     118           0 :                 return -1;
     119             :         }
     120             : 
     121           0 :         logp("Found interrupted backup.\n");
     122             : 
     123             :         // Check that the current incexc configuration is the same
     124             :         // as before.
     125           0 :         switch(incexc_matches(sdirs->rworking, incexc))
     126             :         {
     127             :                 case 1:
     128             :                         // Attempt to resume on the next backup.
     129           0 :                         logp("Will resume on the next backup request.\n");
     130           0 :                         *resume=1;
     131           0 :                         return 0;
     132             :                 case 0:
     133           0 :                         logp("Includes/excludes changed since last backup.\n");
     134           0 :                         logp("Will delete instead of resuming.\n");
     135           0 :                         return working_delete(as, sdirs);
     136             :                 case -1:
     137             :                 default:
     138             :                         return -1;
     139             :         }
     140             : }
     141             : 
     142           0 : static int get_fullrealwork(struct sdirs *sdirs)
     143             : {
     144             :         struct stat statp;
     145             : 
     146           0 :         if(sdirs_get_real_working_from_symlink(sdirs))
     147             :                 return -1;
     148             : 
     149           0 :         if(lstat(sdirs->rworking, &statp))
     150             :         {
     151           0 :                 logp("removing dangling working symlink %s -> %s\n",
     152             :                         sdirs->working, sdirs->rworking);
     153           0 :                 unlink(sdirs->working);
     154           0 :                 free_w(&sdirs->rworking);
     155             :         }
     156             :         return 0;
     157             : }
     158             : 
     159           0 : static int do_unlink(const char *path)
     160             : {
     161           0 :         if(unlink(path))
     162             :         {
     163           0 :                 logp("Could not unlink '%s': %s", path, strerror(errno));
     164           0 :                 return -1;
     165             :         }
     166             :         return 0;
     167             : }
     168             : 
     169           0 : static int fix_log_finishing(struct sdirs *sdirs, struct conf **cconfs)
     170             : {
     171           0 :         int ret=-1;
     172           0 :         char *path_log=NULL;
     173           0 :         char *path_log_gz=NULL;
     174           0 :         char *path_log_tmp=NULL;
     175             : 
     176           0 :         if(!(path_log=prepend_s(sdirs->finishing, "log"))
     177           0 :           || !(path_log_gz=prepend_s(sdirs->finishing, "log.gz"))
     178           0 :           || !(path_log_tmp=prepend_s(sdirs->finishing, "log.tmp")))
     179             :                 goto end;
     180             : 
     181           0 :         if(is_reg_lstat(path_log)==1)
     182             :         {
     183           0 :                 if(is_reg_lstat(path_log_gz)==1)
     184             :                 {
     185             :                         // If this has happened, either file should be good to
     186             :                         // use. Delete the compressed one, as we will keep
     187             :                         // logging to the uncompressed one.
     188           0 :                         do_unlink(path_log_gz);
     189             :                         goto end;
     190             :                 }
     191             :                 else
     192             :                 {
     193             :                         // Everything is OK.
     194             :                         ret=0;
     195             :                 }
     196             :         }
     197             :         else
     198             :         {
     199           0 :                 if(is_reg_lstat(path_log_gz)==1)
     200             :                 {
     201             :                         // Need to inflate so that we can log to it again,
     202             :                         // and compress it later.
     203           0 :                         if(zlib_inflate(/*asfd*/NULL, path_log_gz,
     204             :                                 path_log_tmp, get_cntr(cconfs)))
     205             :                                         goto end;
     206           0 :                         if(do_rename(path_log_tmp, path_log))
     207             :                                 goto end;
     208           0 :                         do_unlink(path_log_gz);
     209             :                         goto end;
     210             :                 }
     211             :                 else
     212             :                 {
     213           0 :                         logp("Neither %s nor %s exist. That is odd!",
     214             :                                 path_log, path_log_gz);
     215           0 :                         ret=0;
     216             :                 }
     217             :         }
     218             : end:
     219           0 :         if(!ret)
     220             :         {
     221             :                 // Should be OK to re-open the log file now.
     222           0 :                 if(log_fzp_set(path_log, cconfs))
     223           0 :                         ret=-1;
     224             :         }
     225             : 
     226           0 :         free_w(&path_log);
     227           0 :         free_w(&path_log_gz);
     228           0 :         free_w(&path_log_tmp);
     229           0 :         return ret;
     230             : }
     231             : 
     232           0 : static int recover_finishing(struct async *as,
     233             :         struct sdirs *sdirs, struct conf **cconfs)
     234             : {
     235             :         int r;
     236           0 :         char msg[128]="";
     237           0 :         struct asfd *asfd=as->asfd;
     238             : 
     239           0 :         if(fix_log_finishing(sdirs, cconfs))
     240             :                 return -1;
     241             : 
     242           0 :         logp("Found finishing symlink - attempting to complete prior backup!\n");
     243             : 
     244           0 :         if(append_to_resume_file(sdirs->finishing))
     245             :                 return -1;
     246             : 
     247           0 :         snprintf(msg, sizeof(msg),
     248             :                 "Now finalising previous backup of client. "
     249             :                 "Please try again later.");
     250           0 :         asfd->write_str(asfd, CMD_ERROR, msg);
     251             : 
     252             :         // Need to check whether the log has been compressed. If it hasn't,
     253             :         // we need to inflate it again.
     254             : 
     255             :         // Do not need the client connected any more.
     256             :         // Disconnect.
     257           0 :         logp("Disconnect from client.\n");
     258           0 :         as->asfd_remove(as, asfd);
     259           0 :         asfd_close(asfd);
     260             : 
     261           0 :         switch(get_protocol(cconfs))
     262             :         {
     263             :                 case PROTO_1:
     264           0 :                         r=backup_phase4_server_protocol1(sdirs, cconfs);
     265           0 :                         break;
     266             :                 case PROTO_2:
     267             :                 default:
     268           0 :                         r=backup_phase4_server_protocol2(sdirs, cconfs);
     269           0 :                         break;
     270             :         }
     271           0 :         if(r)
     272             :         {
     273           0 :                 logp("Problem with prior backup. Please check the client log on the server.");
     274           0 :                 return -1;
     275             :         }
     276             : 
     277           0 :         logp("Prior backup completed OK\n");
     278           0 :         log_fzp_set(NULL, cconfs);
     279           0 :         compress_filename(sdirs->finishing,
     280             :                 "log", "log.gz", get_int(cconfs[OPT_COMPRESSION]));
     281             : 
     282             :         // backup_stats?!
     283             : 
     284             : 
     285             :         // Move the symlink to indicate that we are now in the end
     286             :         // phase.
     287             :         // FIX THIS: Check whether the rename race condition is recoverable
     288             :         // here.
     289           0 :         if(do_rename(sdirs->finishing, sdirs->current)) return -1;
     290           0 :         return 0;
     291             : }
     292             : 
     293           0 : static void log_recovery_method(struct sdirs *sdirs,
     294             :         enum recovery_method recovery_method)
     295             : {
     296           0 :         logp("found old working directory: %s\n", sdirs->rworking);
     297           0 :         logp("working_dir_recovery_method: %s\n",
     298             :                 recovery_method_to_str(recovery_method));
     299           0 : }
     300             : 
     301           0 : static int recover_working(struct async *as,
     302             :         struct sdirs *sdirs, const char *incexc,
     303             :         int *resume, struct conf **cconfs)
     304             : {
     305           0 :         int ret=-1;
     306           0 :         char msg[256]="";
     307           0 :         char *logpath=NULL;
     308             :         struct stat statp;
     309           0 :         char *phase1datatmp=NULL;
     310           0 :         int resume_attempts=0;
     311           0 :         int max_resume_attempts=get_int(cconfs[OPT_MAX_RESUME_ATTEMPTS]);
     312           0 :         enum recovery_method recovery_method=get_e_recovery_method(
     313             :                 cconfs[OPT_WORKING_DIR_RECOVERY_METHOD]);
     314             : 
     315             :         // The working directory has not finished being populated.
     316             :         // Check what to do.
     317           0 :         if(get_fullrealwork(sdirs)) goto end;
     318           0 :         if(!sdirs->rworking) goto end;
     319             : 
     320           0 :         log_recovery_method(sdirs, recovery_method);
     321             : 
     322           0 :         if(!(phase1datatmp=get_tmp_filename(sdirs->phase1data)))
     323             :                 goto end;
     324             :         // If there is still a phase1 tmp file...
     325           0 :         if(!lstat(phase1datatmp, &statp)
     326           0 :           ||
     327             :                 // ...or phase1 has not even got underway yet...
     328           0 :                 (lstat(phase1datatmp, &statp)
     329           0 :                   && lstat(sdirs->phase1data, &statp)
     330           0 :                   && lstat(sdirs->changed, &statp)
     331           0 :                   && lstat(sdirs->unchanged, &statp)))
     332             :         {
     333             :                 // ...phase 1 did not complete - delete everything.
     334           0 :                 logp("Phase 1 has not completed.\n");
     335           0 :                 recovery_method=RECOVERY_METHOD_DELETE;
     336             :         }
     337             :         else
     338             :         {
     339           0 :                 append_to_resume_file(sdirs->working);
     340             : 
     341           0 :                 if(max_resume_attempts>0)
     342             :                 {
     343           0 :                         logp("max_resume_attempts: %d\n", max_resume_attempts);
     344           0 :                         if((resume_attempts=resume_count(sdirs->working))<0)
     345             :                                 goto end;
     346           0 :                         if(resume_attempts > max_resume_attempts)
     347             :                         {
     348           0 :                                 logp("no resume attempts remaining, will delete\n");
     349           0 :                                 recovery_method=RECOVERY_METHOD_DELETE;
     350             :                         }
     351             :                         else
     352             :                         {
     353           0 :                                 logp("resume attempts: %d\n", resume_attempts);
     354           0 :                                 logp("remaining resume attempts: %d\n",
     355             :                                         max_resume_attempts - resume_attempts);
     356             :                         }
     357             :                 }
     358             :         }
     359             : 
     360           0 :         if(recovery_method==RECOVERY_METHOD_DELETE)
     361             :         {
     362           0 :                 ret=working_delete(as, sdirs);
     363           0 :                 goto end;
     364             :         }
     365             : 
     366             :         // We are not deleting the old working directory - open the log inside
     367             :         // for appending.
     368           0 :         if(!(logpath=prepend_s(sdirs->rworking, "log"))
     369           0 :           || log_fzp_set(logpath, cconfs))
     370             :                 goto end;
     371             : 
     372           0 :         switch(recovery_method)
     373             :         {
     374             :                 case RECOVERY_METHOD_DELETE:
     375             :                         // Dealt with above.
     376             :                         break;
     377             :                 case RECOVERY_METHOD_RESUME:
     378           0 :                         ret=working_resume(as, sdirs, incexc, resume, cconfs);
     379           0 :                         break;
     380             :                 case RECOVERY_METHOD_UNSET:
     381             :                 default:
     382           0 :                         snprintf(msg, sizeof(msg),
     383             :                                 "Unknown working_dir_recovery_method: %d\n",
     384             :                                         (int)recovery_method);
     385           0 :                         log_and_send(as->asfd, msg);
     386           0 :                         break;
     387             :         }
     388             : 
     389             : end:
     390           0 :         free_w(&logpath);
     391           0 :         free_w(&phase1datatmp);
     392           0 :         log_fzp_set(NULL, cconfs); // fclose the logfzp
     393           0 :         return ret;
     394             : }
     395             : 
     396           0 : static int recover_currenttmp(struct sdirs *sdirs)
     397             : {
     398           0 :         logp("Found currenttmp symlink\n");
     399           0 :         switch(is_lnk_valid(sdirs->currenttmp))
     400             :         {
     401             :                 case 0:
     402           0 :                         logp("But currenttmp is not pointing at something valid.\n");
     403           0 :                         logp("Deleting it.\n");
     404           0 :                         if(append_to_resume_file(sdirs->currenttmp))
     405             :                                 return -1;
     406           0 :                         return unlink_w(sdirs->currenttmp, __func__);
     407             :                 case -1:
     408             :                         return -1;
     409             :         }
     410             : 
     411           0 :         switch(is_lnk_lstat(sdirs->current))
     412             :         {
     413             :                 case 0:
     414           0 :                         logp("But current already exists and is not a symlink!\n");
     415           0 :                         logp("Giving up.\n");
     416             :                         return -1;
     417             :                 case 1:
     418           0 :                         logp("But current symlink already exists!\n");
     419           0 :                         switch(is_lnk_valid(sdirs->current))
     420             :                         {
     421             :                                 case 0:
     422           0 :                                         logp("But current symlink is not pointing at something valid.\n");
     423           0 :                                         logp("Replacing current with currenttmp.\n");
     424           0 :                                         if(append_to_resume_file(
     425           0 :                                                 sdirs->currenttmp))
     426             :                                                         return -1;
     427           0 :                                         return do_rename(sdirs->currenttmp,
     428           0 :                                                 sdirs->current);
     429             :                                 case 1:
     430           0 :                                         logp("And current symlink points at something valid.\n");
     431           0 :                                         logp("Deleting currenttmp.\n");
     432           0 :                                         return unlink_w(sdirs->currenttmp, __func__);
     433             :                                 default:
     434             :                                         return -1;
     435             :                         }
     436             :                 default:
     437           0 :                         logp("Renaming currenttmp to current\n");
     438           0 :                         if(append_to_resume_file(sdirs->currenttmp))
     439             :                                 return -1;
     440           0 :                         return do_rename(sdirs->currenttmp, sdirs->current);
     441             :         }
     442             : }
     443             : 
     444           7 : int check_for_rubble(struct sdirs *sdirs)
     445             : {
     446           7 :         return is_lnk_lstat(sdirs->finishing)>0
     447           7 :           || is_lnk_lstat(sdirs->working)>0
     448          14 :           || is_lnk_lstat(sdirs->currenttmp)>0;
     449             : }
     450             : 
     451             : // Return 1 if the backup is now finalising.
     452           2 : int check_for_rubble_and_clean(struct async *as,
     453             :         struct sdirs *sdirs, const char *incexc,
     454             :         int *resume, struct conf **cconfs)
     455             : {
     456           2 :         struct asfd *asfd=as->asfd;
     457             : 
     458           2 :         switch(is_lnk_lstat(sdirs->finishing))
     459             :         {
     460             :                 case 1:
     461           0 :                         if(recover_finishing(as, sdirs, cconfs))
     462             :                                 return -1;
     463           0 :                         return 1;
     464             :                 case 0:
     465           0 :                         log_and_send(asfd,
     466             :                                 "Finishing directory is not a symlink.\n");
     467           0 :                         return -1;
     468             :         }
     469             : 
     470           2 :         switch(is_lnk_lstat(sdirs->working))
     471             :         {
     472             :                 case 1:
     473           0 :                         return recover_working(as,
     474             :                                 sdirs, incexc, resume, cconfs);
     475             :                 case 0:
     476           0 :                         log_and_send(asfd,
     477             :                                 "Working directory is not a symlink.\n");
     478           0 :                         return -1;
     479             :         }
     480             : 
     481           2 :         switch(is_lnk_lstat(sdirs->currenttmp))
     482             :         {
     483             :                 case 1:
     484           0 :                         return recover_currenttmp(sdirs);
     485             :                 case 0:
     486           0 :                         log_and_send(asfd,
     487             :                                 "Currenttmp directory is not a symlink.\n");
     488           0 :                         return -1;
     489             :         }
     490             : 
     491             :         return 0;
     492             : }

Generated by: LCOV version 1.13