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

Generated by: LCOV version 1.13