LCOV - code coverage report
Current view: top level - src/server - rubble.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 5 132 3.8 %
Date: 2017-05-01 Functions: 1 9 11.1 %

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

Generated by: LCOV version 1.10