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

Generated by: LCOV version 1.13