Esempio n. 1
0
int check_for_rubble_burp1(struct asfd *asfd,
	struct sdirs *sdirs, struct conf *cconf,
	const char *incexc, int *resume)
{
	int ret=0;
	ssize_t len=0;
	char msg[256]="";
	char realwork[256]="";
	struct stat statp;
	char *logpath=NULL;
	char *fullrealwork=NULL;
	char *phase1datatmp=NULL;
	const char *wdrm=cconf->recovery_method;

	// If there is a 'finishing' symlink, we need to
	// run the finish_backup stuff.
	if(!lstat(sdirs->finishing, &statp))
	{
		logp("Found finishing symlink - attempting to complete prior backup!\n");
		ret=backup_phase4_server(sdirs, cconf);
		if(!ret) logp("Prior backup completed OK.\n");
		else log_and_send(asfd, "Problem with prior backup. Please check the client log on the server.");
		goto end;
	}

	if(lstat(sdirs->working, &statp))
	{
		// No working directory - that is good.
		goto end;
	}
	if(!S_ISLNK(statp.st_mode))
	{
		log_and_send(asfd, "Working directory is not a symlink.\n");
		ret=-1;
		goto end;
	}

	// The working directory has not finished being populated.
	// Check what to do.
	if((len=readlink(sdirs->working, realwork, sizeof(realwork)-1))<0)
	{
		snprintf(msg, sizeof(msg), "Could not readlink on old working directory: %s\n", strerror(errno));
		log_and_send(asfd, msg);
		ret=-1;
		goto end;
	}
	realwork[len]='\0';
	if(!(fullrealwork=prepend_s(sdirs->client, realwork)))
	{
		ret=-1;
		goto end;
	}

	if(lstat(fullrealwork, &statp))
	{
		logp("removing dangling working symlink -> %s\n", realwork);
		unlink(sdirs->working);
		goto end;
	}

	if(!(phase1datatmp=get_tmp_filename(sdirs->phase1data)))
		goto end;

	// We have found an old working directory - open the log inside
	// for appending.
	if(!(logpath=prepend_s(fullrealwork, "log")))
	{
		ret=-1;
		goto end;
	}
	if(set_logfp(logpath, cconf))
	{
		ret=-1;
		goto end;
	}

	logp("found old working directory: %s\n", fullrealwork);
	logp("working_dir_recovery_method: %s\n", wdrm);

	if(!lstat(phase1datatmp, &statp))
	{
		// Phase 1 did not complete - delete everything.
		logp("Phase 1 has not completed.\n");
		wdrm="delete";
	}

	if(!strcmp(wdrm, "delete"))
	{
		// Try to remove it and start again.
		logp("deleting old working directory\n");
		if(recursive_delete(fullrealwork, NULL, 1 /* delete files */))
		{
			log_and_send(asfd,
				"Old working directory is in the way.\n");
			ret=-1;
			goto end;
		}
		unlink(sdirs->working); // get rid of the symlink.
		goto end;
	}
	if(!strcmp(wdrm, "resume"))
	{
		if(cconf->restore_client)
		{
			// This client is not the original client, resuming	
			// might cause all sorts of trouble.
			log_and_send(asfd, "Found interrupted backup - not resuming because the connected client is not the original");
			ret=-1;
			goto end;
		}

		logp("Found interrupted backup.\n");

		// Check that the current incexc configuration is the same
		// as before.
		if((ret=incexc_matches(fullrealwork, incexc))<0)
			goto end;
		if(ret)
		{
			// Attempt to resume on the next backup.
			logp("Will resume on the next backup request.\n");
			*resume=1;
			ret=0;
			goto end;
		}
		logp("Includes/excludes have changed since the last backup.\n");
		logp("Will treat last backup as finished.\n");
		wdrm="use";
	}
	if(!strcmp(wdrm, "use"))
	{
		// Use it as it is.
		logp("converting old working directory into the latest backup\n");
		free(fullrealwork); fullrealwork=NULL;

		// TODO: There might be a partial file written that is not
		// yet logged to the manifest. It does no harm other than
		// taking up some disk space. Detect this and remove it.

		// Get us a partial manifest from the files lying around.
		if(maybe_rebuild_manifest(sdirs, cconf, 1 /* compress */))
		{
			ret=-1;
			goto end;
		}

		// Now just rename the working link to be a finishing link,
		// then run this function again.
		if(do_rename(sdirs->working, sdirs->finishing))
		{
			ret=-1;
			goto end;
		}
		ret=check_for_rubble_burp1(asfd, sdirs, cconf, incexc, resume);
		goto end;
	}

	snprintf(msg, sizeof(msg),
		"Unknown working_dir_recovery_method: %s\n", wdrm);
	log_and_send(asfd, msg);
	ret=-1;

end:
	if(fullrealwork) free(fullrealwork);
	if(logpath) free(logpath);
	if(phase1datatmp) free(phase1datatmp);
	set_logfp(NULL, cconf); // fclose the logfp
	return ret;
}
Esempio n. 2
0
static int do_backup_server(struct async *as, struct sdirs *sdirs,
	struct conf **cconfs, const char *incexc, int resume)
{
	int ret=0;
	int do_phase2=1;
	struct asfd *asfd=as->asfd;
	enum protocol protocol=get_protocol(cconfs);
	struct cntr *cntr=get_cntr(cconfs);

	logp("in do_backup_server\n");

	log_rshash(cconfs);

	if(resume)
	{
		if(sdirs_get_real_working_from_symlink(sdirs)
		  || sdirs_get_real_manifest(sdirs, protocol)
		  || open_log(asfd, sdirs, cconfs))
			goto error;
	}
	else
	{
		// Not resuming - need to set everything up fresh.
		if(sdirs_create_real_working(sdirs,
			get_string(cconfs[OPT_TIMESTAMP_FORMAT]))
		  || sdirs_get_real_manifest(sdirs, protocol)
		  || open_log(asfd, sdirs, cconfs))
			goto error;

		if(write_incexc(sdirs->rworking, incexc))
		{
			logp("unable to write incexc\n");
			goto error;
		}

		if(backup_phase1_server(as, sdirs, cconfs))
		{
			logp("error in phase 1\n");
			goto error;
		}
	}

	if(resume)
	{
		struct stat statp;
		if(lstat(sdirs->phase1data, &statp)
		  && !lstat(sdirs->changed, &statp)
		  && !lstat(sdirs->unchanged, &statp))
		{
			// In this condition, it looks like there was an
			// interruption during phase3. Skip phase2.
			do_phase2=0;
		}
	}

	if(do_phase2)
	{
		if(backup_phase2_server(as, sdirs, incexc, resume, cconfs))
		{
			logp("error in backup phase 2\n");
			goto error;
		}

		asfd->write_str(asfd, CMD_GEN, "okbackupend");
	}

	// Close the connection with the client, the rest of the job we can do
	// by ourselves.
	logp("Backup ending - disconnect from client.\n");
	if(asfd_flush_asio(asfd)) goto end;
	as->asfd_remove(as, asfd);
	asfd_close(asfd);

	if(backup_phase3_server(sdirs, cconfs))
	{
		logp("error in backup phase 3\n");
		goto error;
	}

	if(do_rename(sdirs->working, sdirs->finishing))
		goto error;

	if(backup_phase4_server(sdirs, cconfs))
	{
		logp("error in backup phase 4\n");
		goto error;
	}

	cntr_print(cntr, ACTION_BACKUP, asfd);
	cntr_stats_to_file(cntr, sdirs->rworking, ACTION_BACKUP);

	if(protocol==PROTO_2)
	{
		// Regenerate dindex before the symlink is renamed, so that the
		// champ chooser cleanup does not try to remove data files
		// whilst the dindex regeneration is happening.
		if(regenerate_client_dindex(sdirs))
			goto error;
	}

	// Move the symlink to indicate that we are now in the end phase. The
	// rename() race condition is automatically recoverable here.
	if(do_rename(sdirs->finishing, sdirs->current))
		goto error;

	logp("Backup completed.\n");
	log_fzp_set(NULL, cconfs);
	compress_filename(sdirs->rworking,
		"log", "log.gz", get_int(cconfs[OPT_COMPRESSION]));

	goto end;
error:
	ret=-1;
end:

	log_fzp_set(NULL, cconfs);
	return ret;
}
Esempio n. 3
0
int do_backup_server_burp1(struct async *as,
	struct sdirs *sdirs, struct conf *cconf,
	const char *incexc, int resume)
{
	int ret=0;
	char msg[256]="";
	gzFile mzp=NULL;
	// Real path to the working directory
	char *realworking=NULL;
	char tstmp[64]="";
	struct asfd *asfd=as->asfd;

	struct dpthl dpthl;

	gzFile cmanfp=NULL;
	struct stat statp;

	logp("in do_backup_server\n");

	if(init_dpthl(&dpthl, asfd, sdirs, cconf))
	{
		log_and_send(asfd, "could not init_dpthl\n");
		goto error;
	}

	if(resume)
	{
		ssize_t len=0;
		char real[256]="";
		if((len=readlink(sdirs->working, real, sizeof(real)-1))<0)
			len=0;
		real[len]='\0';
		if(!(realworking=prepend_s(sdirs->client, real)))
		{
			log_and_send_oom(asfd, __func__);
			goto error;
		}
		if(open_log(asfd, realworking, cconf)) goto error;
	}
	else
	{
		// Not resuming - need to set everything up fresh.

		if(get_new_timestamp(asfd, sdirs, cconf, tstmp, sizeof(tstmp)))
			goto error;
		if(!(realworking=prepend_s(sdirs->client, tstmp)))
		{
			log_and_send_oom(asfd, __func__);
			goto error;
		}
		// Add the working symlink before creating the directory.
		// This is because bedup checks the working symlink before
		// going into a directory. If the directory got created first,
		// bedup might go into it in the moment before the symlink
		// gets added.
		if(symlink(tstmp, sdirs->working)) // relative link to the real work dir
		{
			snprintf(msg, sizeof(msg),
			  "could not point working symlink to: %s",
			  realworking);
			log_and_send(asfd, msg);
			goto error;
		}
		else if(mkdir(realworking, 0777))
		{
			snprintf(msg, sizeof(msg),
				"could not mkdir for next backup: %s",
				sdirs->working);
			log_and_send(asfd, msg);
			unlink(sdirs->working);
			goto error;
		}
		else if(open_log(asfd, realworking, cconf))
		{
			goto error;
		}
		else if(mkdir(sdirs->datadirtmp, 0777))
		{
			snprintf(msg, sizeof(msg),
			  "could not mkdir for datadir: %s", sdirs->datadirtmp);
			log_and_send(asfd, msg);
			goto error;
		}
		else if(write_timestamp(sdirs->timestamp, tstmp))
		{
			snprintf(msg, sizeof(msg),
			  "unable to write timestamp %s", sdirs->timestamp);
			log_and_send(asfd, msg);
			goto error;
		}
		else if(incexc && *incexc && write_incexc(realworking, incexc))
		{
			snprintf(msg, sizeof(msg), "unable to write incexc");
			log_and_send(asfd, msg);
			goto error;
		}

		if(backup_phase1_server(asfd, sdirs, cconf))
		{
			logp("error in phase 1\n");
			goto error;
		}
	}

	// Open the previous (current) manifest.
	// If the split_vss setting changed between the previous backup
	// and the new backup, do not open the previous manifest.
	// This will have the effect of making the client back up everything
	// fresh. Need to do this, otherwise toggling split_vss on and off
	// will result in backups that do not work.
	if(!lstat(sdirs->cmanifest, &statp)
	  && !vss_opts_changed(sdirs, cconf, incexc))
	{
		if(!(cmanfp=gzopen_file(sdirs->cmanifest, "rb")))
		{
			if(!lstat(sdirs->cmanifest, &statp))
			{
				logp("could not open old manifest %s\n",
					sdirs->cmanifest);
				goto error;
			}
		}
	}

	//if(cmanfp) logp("Current manifest: %s\n", sdirs->cmanifest);

	if(backup_phase2_server(asfd, sdirs, cconf, &cmanfp, &dpthl, resume))
	{
		logp("error in backup phase 2\n");
		goto error;
	}

	if(backup_phase3_server(sdirs, cconf,
		0 /* not recovery mode */, 1 /* compress */))
	{
		logp("error in backup phase 3\n");
		goto error;
	}

	// will not write anything more to
	// the new manifest
	// finish_backup will open it again
	// for reading
	if(gzclose_fp(&mzp))
	{
		logp("Error closing manifest after phase3\n");
		goto error;
	}

	asfd->write_str(asfd, CMD_GEN, "okbackupend");
	logp("Backup ending - disconnect from client.\n");

	// Close the connection with the client, the rest of the job
	// we can do by ourselves.
	asfd_free(&as->asfd);

	// Move the symlink to indicate that we are now in the end
	// phase. 
	if(do_rename(sdirs->working, sdirs->finishing))
		goto error;

	set_logfp(NULL, cconf); // does an fclose on logfp.
	// finish_backup will open logfp again
	ret=backup_phase4_server(sdirs, cconf);
	if(!ret && cconf->keep>0)
		ret=remove_old_backups(asfd, sdirs, cconf);

	goto end;
error:
	ret=-1;
end:
	gzclose_fp(&cmanfp);
	gzclose_fp(&mzp);
	set_logfp(NULL, cconf); // does an fclose on logfp.
	return ret;
}