LCOV - code coverage report
Current view: top level - src/server - rubble.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 9 175 5.1 %
Date: 2018-08-31 08:21:15 Functions: 2 12 16.7 %

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

Generated by: LCOV version 1.13