LCOV - code coverage report
Current view: top level - src/server - rubble.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 9 150 6.0 %
Date: 2018-03-30 Functions: 2 11 18.2 %

          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 "protocol1/backup_phase4.h"
      12             : #include "protocol2/backup_phase4.h"
      13             : #include "sdirs.h"
      14             : #include "rubble.h"
      15             : #include "timestamp.h"
      16             : 
      17           0 : int append_to_resume_file(const char *path)
      18             : {
      19           0 :         int ret=-1;
      20           0 :         char tstmp[48]="";
      21           0 :         char *resume_path=NULL;
      22             : 
      23           0 :         if(timestamp_get_new(/*index*/0,
      24             :                 tstmp, sizeof(tstmp),
      25             :                 /*bufforfile*/NULL, /*bs*/0,
      26             :                 /*format*/NULL))
      27             :                         goto end;
      28           0 :         if(!(resume_path=prepend_s(path, "resumed")))
      29             :                 goto end;
      30           0 :         ret=timestamp_write(resume_path, tstmp);
      31             : end:
      32           0 :         free_w(&resume_path);
      33           0 :         return ret;
      34             : }
      35             : 
      36           0 : static int incexc_matches(const char *fullrealwork, const char *incexc)
      37             : {
      38           0 :         int ret=0;
      39           0 :         int got=0;
      40           0 :         struct fzp *fzp=NULL;
      41           0 :         char buf[4096]="";
      42           0 :         const char *inc=NULL;
      43           0 :         char *old_incexc_path=NULL;
      44           0 :         if(!(old_incexc_path=prepend_s(fullrealwork, "incexc")))
      45             :                 return -1;
      46           0 :         if(!(fzp=fzp_open(old_incexc_path, "rb")))
      47             :         {
      48             :                 // Assume that no incexc file could be found because the client
      49             :                 // was on an old version. Assume resume is OK and return 1.
      50             :                 ret=1;
      51             :                 goto end;
      52             :         }
      53             :         inc=incexc;
      54           0 :         while((got=fzp_read(fzp, buf, sizeof(buf)))>0)
      55             :         {
      56           0 :                 if(strlen(inc)<(size_t)got) break;
      57           0 :                 if(strncmp(buf, inc, got)) break;
      58           0 :                 inc+=got;
      59             :         }
      60           0 :         if(inc && strlen(inc)) ret=0;
      61           0 :         else ret=1;
      62             : end:
      63           0 :         fzp_close(&fzp);
      64           0 :         free_w(&old_incexc_path);
      65           0 :         return ret;
      66             : }
      67             : 
      68           0 : static int working_delete(struct async *as, struct sdirs *sdirs)
      69             : {
      70             :         // Try to remove it and start again.
      71           0 :         logp("deleting old working directory\n");
      72           0 :         if(recursive_delete(sdirs->rworking))
      73             :         {
      74           0 :                 log_and_send(as->asfd,
      75             :                         "Old working directory is in the way.\n");
      76             :                 return -1;
      77             :         }
      78             :         // Get rid of the symlink.
      79           0 :         unlink(sdirs->working);
      80             :         return 0;
      81             : }
      82             : 
      83           0 : static int working_resume(struct async *as, struct sdirs *sdirs,
      84             :         const char *incexc, int *resume, struct conf **cconfs)
      85             : {
      86           0 :         if(get_string(cconfs[OPT_RESTORE_CLIENT]))
      87             :         {
      88             :                 // This client is not the original client, resuming might cause
      89             :                 // all sorts of trouble.
      90           0 :                 log_and_send(as->asfd, "Found interrupted backup - not resuming because the connected client is not the original");
      91           0 :                 return -1;
      92             :         }
      93             : 
      94           0 :         logp("Found interrupted backup.\n");
      95             : 
      96             :         // Check that the current incexc configuration is the same
      97             :         // as before.
      98           0 :         switch(incexc_matches(sdirs->rworking, incexc))
      99             :         {
     100             :                 case 1:
     101             :                         // Attempt to resume on the next backup.
     102           0 :                         logp("Will resume on the next backup request.\n");
     103           0 :                         *resume=1;
     104           0 :                         return 0;
     105             :                 case 0:
     106           0 :                         logp("Includes/excludes changed since last backup.\n");
     107           0 :                         logp("Will delete instead of resuming.\n");
     108           0 :                         return working_delete(as, sdirs);
     109             :                 case -1:
     110             :                 default:
     111             :                         return -1;
     112             :         }
     113             : }
     114             : 
     115           0 : static int get_fullrealwork(struct sdirs *sdirs)
     116             : {
     117             :         struct stat statp;
     118             : 
     119           0 :         if(sdirs_get_real_working_from_symlink(sdirs))
     120             :                 return -1;
     121             : 
     122           0 :         if(lstat(sdirs->rworking, &statp))
     123             :         {
     124           0 :                 logp("removing dangling working symlink %s -> %s\n",
     125             :                         sdirs->working, sdirs->rworking);
     126           0 :                 unlink(sdirs->working);
     127           0 :                 free_w(&sdirs->rworking);
     128             :         }
     129             :         return 0;
     130             : }
     131             : 
     132           0 : static int recover_finishing(struct async *as,
     133             :         struct sdirs *sdirs, struct conf **cconfs)
     134             : {
     135             :         int r;
     136           0 :         char msg[128]="";
     137           0 :         struct asfd *asfd=as->asfd;
     138           0 :         logp("Found finishing symlink - attempting to complete prior backup!\n");
     139             : 
     140           0 :         if(append_to_resume_file(sdirs->finishing))
     141             :                 return -1;
     142             : 
     143             :         snprintf(msg, sizeof(msg),
     144             :                 "Now finalising previous backup of client. "
     145             :                 "Please try again later.");
     146           0 :         asfd->write_str(asfd, CMD_ERROR, msg);
     147             : 
     148             :         // Do not need the client connected any more.
     149             :         // Disconnect.
     150           0 :         logp("Disconnect from client.\n");
     151           0 :         as->asfd_remove(as, asfd);
     152           0 :         asfd_close(asfd);
     153             : 
     154           0 :         switch(get_protocol(cconfs))
     155             :         {
     156             :                 case PROTO_1:
     157           0 :                         r=backup_phase4_server_protocol1(sdirs, cconfs);
     158           0 :                         break;
     159             :                 case PROTO_2:
     160             :                 default:
     161           0 :                         r=backup_phase4_server_protocol2(sdirs, cconfs);
     162           0 :                         break;
     163             :         }
     164           0 :         if(r)
     165             :         {
     166           0 :                 logp("Problem with prior backup. Please check the client log on the server.");
     167           0 :                 return -1;
     168             :         }
     169           0 :         logp("Prior backup completed OK.\n");
     170             : 
     171             :         // Move the symlink to indicate that we are now in the end
     172             :         // phase.
     173             :         // FIX THIS: Check whether the rename race condition is recoverable
     174             :         // here.
     175           0 :         if(do_rename(sdirs->finishing, sdirs->current)) return -1;
     176           0 :         return 0;
     177             : }
     178             : 
     179           0 : static void log_recovery_method(struct sdirs *sdirs,
     180             :         enum recovery_method recovery_method)
     181             : {
     182           0 :         logp("found old working directory: %s\n", sdirs->rworking);
     183           0 :         logp("working_dir_recovery_method: %s\n",
     184             :                 recovery_method_to_str(recovery_method));
     185           0 : }
     186             : 
     187           0 : static int recover_working(struct async *as,
     188             :         struct sdirs *sdirs, const char *incexc,
     189             :         int *resume, struct conf **cconfs)
     190             : {
     191           0 :         int ret=-1;
     192           0 :         char msg[256]="";
     193           0 :         char *logpath=NULL;
     194             :         struct stat statp;
     195           0 :         char *phase1datatmp=NULL;
     196           0 :         enum recovery_method recovery_method=get_e_recovery_method(
     197             :                 cconfs[OPT_WORKING_DIR_RECOVERY_METHOD]);
     198             : 
     199             :         // The working directory has not finished being populated.
     200             :         // Check what to do.
     201           0 :         if(get_fullrealwork(sdirs)) goto end;
     202           0 :         if(!sdirs->rworking) goto end;
     203             : 
     204           0 :         log_recovery_method(sdirs, recovery_method);
     205             : 
     206           0 :         if(!(phase1datatmp=get_tmp_filename(sdirs->phase1data)))
     207             :                 goto end;
     208             :         // If there is still a phase1 tmp file...
     209           0 :         if(!lstat(phase1datatmp, &statp)
     210           0 :           ||
     211             :                 // ...or phase1 has not even got underway yet...
     212           0 :                 (lstat(phase1datatmp, &statp)
     213           0 :                   && lstat(sdirs->phase1data, &statp)
     214           0 :                   && lstat(sdirs->changed, &statp)
     215           0 :                   && lstat(sdirs->unchanged, &statp)))
     216             :         {
     217             :                 // ...phase 1 did not complete - delete everything.
     218           0 :                 logp("Phase 1 has not completed.\n");
     219           0 :                 recovery_method=RECOVERY_METHOD_DELETE;
     220             :         }
     221             : 
     222           0 :         if(recovery_method==RECOVERY_METHOD_DELETE)
     223             :         {
     224           0 :                 ret=working_delete(as, sdirs);
     225           0 :                 goto end;
     226             :         }
     227             : 
     228             :         // We are not deleting the old working directory - open the log inside
     229             :         // for appending.
     230           0 :         if(!(logpath=prepend_s(sdirs->rworking, "log"))
     231           0 :           || log_fzp_set(logpath, cconfs))
     232             :                 goto end;
     233             : 
     234           0 :         switch(recovery_method)
     235             :         {
     236             :                 case RECOVERY_METHOD_DELETE:
     237             :                         // Dealt with above.
     238             :                         break;
     239             :                 case RECOVERY_METHOD_RESUME:
     240           0 :                         ret=working_resume(as, sdirs, incexc, resume, cconfs);
     241           0 :                         break;
     242             :                 case RECOVERY_METHOD_UNSET:
     243             :                 default:
     244           0 :                         snprintf(msg, sizeof(msg),
     245             :                                 "Unknown working_dir_recovery_method: %d\n",
     246             :                                         (int)recovery_method);
     247           0 :                         log_and_send(as->asfd, msg);
     248           0 :                         break;
     249             :         }
     250             : 
     251             : end:
     252           0 :         free_w(&logpath);
     253           0 :         free_w(&phase1datatmp);
     254           0 :         log_fzp_set(NULL, cconfs); // fclose the logfzp
     255           0 :         return ret;
     256             : }
     257             : 
     258           0 : static int recover_currenttmp(struct sdirs *sdirs)
     259             : {
     260           0 :         logp("Found currenttmp symlink\n");
     261           0 :         switch(is_lnk_valid(sdirs->currenttmp))
     262             :         {
     263             :                 case 0:
     264           0 :                         logp("But currenttmp is not pointing at something valid.\n");
     265           0 :                         logp("Deleting it.\n");
     266           0 :                         if(append_to_resume_file(sdirs->currenttmp))
     267             :                                 return -1;
     268           0 :                         return unlink_w(sdirs->currenttmp, __func__);
     269             :                 case -1:
     270             :                         return -1;
     271             :         }
     272             : 
     273           0 :         switch(is_lnk_lstat(sdirs->current))
     274             :         {
     275             :                 case 0:
     276           0 :                         logp("But current already exists and is not a symlink!\n");
     277           0 :                         logp("Giving up.\n");
     278             :                         return -1;
     279             :                 case 1:
     280           0 :                         logp("But current symlink already exists!\n");
     281           0 :                         switch(is_lnk_valid(sdirs->current))
     282             :                         {
     283             :                                 case 0:
     284           0 :                                         logp("But current symlink is not pointing at something valid.\n");
     285           0 :                                         logp("Replacing current with currenttmp.\n");
     286           0 :                                         if(append_to_resume_file(
     287           0 :                                                 sdirs->currenttmp))
     288             :                                                         return -1;
     289           0 :                                         return do_rename(sdirs->currenttmp,
     290           0 :                                                 sdirs->current);
     291             :                                 case 1:
     292           0 :                                         logp("And current symlink points at something valid.\n");
     293           0 :                                         logp("Deleting currenttmp.\n");
     294           0 :                                         return unlink_w(sdirs->currenttmp, __func__);
     295             :                                 default:
     296             :                                         return -1;
     297             :                         }
     298             :                 default:
     299           0 :                         logp("Renaming currenttmp to current\n");
     300           0 :                         if(append_to_resume_file(sdirs->currenttmp))
     301             :                                 return -1;
     302           0 :                         return do_rename(sdirs->currenttmp, sdirs->current);
     303             :         }
     304             : }
     305             : 
     306           7 : int check_for_rubble(struct sdirs *sdirs)
     307             : {
     308          14 :         return is_lnk_lstat(sdirs->finishing)>0
     309           7 :           || is_lnk_lstat(sdirs->working)>0
     310          14 :           || is_lnk_lstat(sdirs->currenttmp)>0;
     311             : }
     312             : 
     313             : // Return 1 if the backup is now finalising.
     314           2 : int check_for_rubble_and_clean(struct async *as,
     315             :         struct sdirs *sdirs, const char *incexc,
     316             :         int *resume, struct conf **cconfs)
     317             : {
     318           2 :         struct asfd *asfd=as->asfd;
     319             : 
     320           2 :         switch(is_lnk_lstat(sdirs->finishing))
     321             :         {
     322             :                 case 1:
     323           0 :                         if(recover_finishing(as, sdirs, cconfs))
     324             :                                 return -1;
     325           0 :                         return 1;
     326             :                 case 0:
     327           0 :                         log_and_send(asfd,
     328             :                                 "Finishing directory is not a symlink.\n");
     329           0 :                         return -1;
     330             :         }
     331             : 
     332           2 :         switch(is_lnk_lstat(sdirs->working))
     333             :         {
     334             :                 case 1:
     335           0 :                         return recover_working(as,
     336             :                                 sdirs, incexc, resume, cconfs);
     337             :                 case 0:
     338           0 :                         log_and_send(asfd,
     339             :                                 "Working directory is not a symlink.\n");
     340           0 :                         return -1;
     341             :         }
     342             : 
     343           2 :         switch(is_lnk_lstat(sdirs->currenttmp))
     344             :         {
     345             :                 case 1:
     346           0 :                         return recover_currenttmp(sdirs);
     347             :                 case 0:
     348           0 :                         log_and_send(asfd,
     349             :                                 "Currenttmp directory is not a symlink.\n");
     350           0 :                         return -1;
     351             :         }
     352             : 
     353             :         return 0;
     354             : }

Generated by: LCOV version 1.10