Ejemplo n.º 1
0
int extra_comms(struct async *as,
	char **incexc, int *srestore, struct conf **confs, struct conf **cconfs)
{
	struct vers vers;
	struct asfd *asfd;
	asfd=as->asfd;
	//char *restorepath=NULL;
	const char *peer_version=NULL;

	if(vers_init(&vers, cconfs)) goto error;

	if(vers.cli<vers.directory_tree)
	{
		set_int(confs[OPT_DIRECTORY_TREE], 0);
		set_int(cconfs[OPT_DIRECTORY_TREE], 0);
	}

	// Clients before 1.2.7 did not know how to do extra comms, so skip
	// this section for them.
	if(vers.cli<vers.min) return 0;

	if(asfd->read_expect(asfd, CMD_GEN, "extra_comms_begin"))
	{
		logp("problem reading in extra_comms\n");
		goto error;
	}
	// Want to tell the clients the extra comms features that are
	// supported, so that new clients are more likely to work with old
	// servers.
	if(vers.cli==vers.feat_list)
	{
		// 1.3.0 did not support the feature list.
		if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin ok"))
		{
			logp("problem writing in extra_comms\n");
			goto error;
		}
	}
	else
	{
		if(send_features(asfd, cconfs)) goto error;
	}

	if(extra_comms_read(as, &vers, srestore, incexc, confs, cconfs))
		goto error;

	peer_version=get_string(cconfs[OPT_PEER_VERSION]);

	// This needs to come after extra_comms_read, as the client might
	// have set PROTO_1 or PROTO_2.
	switch(get_e_protocol(cconfs[OPT_PROTOCOL]))
	{
		case PROTO_AUTO:
			// The protocol has not been specified. Make a choice.
			if(vers.cli<vers.burp2)
			{
				// Client is burp-1.x.x, use protocol1.
				set_e_protocol(confs[OPT_PROTOCOL], PROTO_1);
				set_e_protocol(cconfs[OPT_PROTOCOL], PROTO_1);
				logp("Client is burp-%s - using protocol=%d\n",
					peer_version, PROTO_1);
			}
			else
			{
				// Client is burp-2.x.x, use protocol2.
				// This will probably never be reached because
				// the negotiation will take care of it.
				set_e_protocol(confs[OPT_PROTOCOL], PROTO_2);
				set_e_protocol(cconfs[OPT_PROTOCOL], PROTO_2);
				logp("Client is burp-%s - using protocol=%d\n",
					peer_version, PROTO_2);
			}
			break;
		case PROTO_1:
			// It is OK for the client to be burp1 and for the
			// server to be forced to protocol1.
			break;
		case PROTO_2:
			if(vers.cli>=vers.burp2) break;
			logp("protocol=%d is set server side, "
			  "but client is burp version %s\n",
			  peer_version);
			goto error;
	}

	if(get_e_protocol(cconfs[OPT_PROTOCOL])==PROTO_1)
	{
		if(get_e_rshash(cconfs[OPT_RSHASH])==RSHASH_UNSET)
		{
			set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4);
			set_e_rshash(cconfs[OPT_RSHASH], RSHASH_MD4);
		}
	}

	return 0;
error:
	return -1;
}
Ejemplo n.º 2
0
static int send_features(struct asfd *asfd, struct conf **cconfs)
{
	int ret=-1;
	char *feat=NULL;
	struct stat statp;
	const char *restorepath=NULL;
	enum protocol protocol=get_e_protocol(cconfs[OPT_PROTOCOL]);
	struct strlist *startdir=get_strlist(cconfs[OPT_STARTDIR]);
	struct strlist *incglob=get_strlist(cconfs[OPT_INCGLOB]);

	if(append_to_feat(&feat, "extra_comms_begin ok:")
		/* clients can autoupgrade */
	  || append_to_feat(&feat, "autoupgrade:")
		/* clients can give server incexc conf so that the
		   server knows better what to do on resume */
	  || append_to_feat(&feat, "incexc:")
		/* clients can give the server an alternative client
		   to restore from */
	  || append_to_feat(&feat, "orig_client:")
		/* clients can tell the server what kind of system they are. */
          || append_to_feat(&feat, "uname:"))
		goto end;

	/* Clients can receive restore initiated from the server. */
	if(!(restorepath=get_restorepath(cconfs))
	  || set_string(cconfs[OPT_RESTORE_PATH], restorepath))
		goto end;
	if(!lstat(restorepath, &statp) && S_ISREG(statp.st_mode)
	  && append_to_feat(&feat, "srestore:"))
		goto end;

	/* Clients can receive incexc conf from the server.
	   Only give it as an option if the server has some starting
	   directory configured in the clientconfdir. */
	if((startdir || incglob)
	  && append_to_feat(&feat, "sincexc:"))
		goto end;

	/* Clients can be sent cntrs on resume/verify/restore. */
/* FIX THIS: Disabled until I rewrite a better protocol.
	if(append_to_feat(&feat, "counters:"))
		goto end;
*/
	// We support CMD_MESSAGE.
	if(append_to_feat(&feat, "msg:"))
		goto end;

	if(protocol==PROTO_AUTO)
	{
		/* If the server is configured to use either protocol, let the
		   client know that it can choose. */
		logp("Server is using protocol=0 (auto)\n");
		if(append_to_feat(&feat, "csetproto:"))
			goto end;
	}
	else
	{
		char p[32]="";
		/* Tell the client what we are going to use. */
		logp("Server is using protocol=%d\n", (int)protocol);
		snprintf(p, sizeof(p), "forceproto=%d:", (int)protocol);
		if(append_to_feat(&feat, p))
			goto end;
	}

#ifndef RS_DEFAULT_STRONG_LEN
	if(append_to_feat(&feat, "rshash=blake2:"))
		goto end;
#endif

	//printf("feat: %s\n", feat);

	if(asfd->write_str(asfd, CMD_GEN, feat))
	{
		logp("problem in extra_comms\n");
		goto end;
	}

	ret=0;
end:
	if(feat) free(feat);
	return ret;
}
Ejemplo n.º 3
0
static int extra_comms_read(struct async *as,
	struct vers *vers, int *srestore,
	char **incexc, struct conf **globalcs, struct conf **cconfs)
{
	int ret=-1;
	struct asfd *asfd;
	struct iobuf *rbuf;
	asfd=as->asfd;
	rbuf=asfd->rbuf;

	while(1)
	{
		iobuf_free_content(rbuf);
		if(asfd->read(asfd)) goto end;

		if(rbuf->cmd!=CMD_GEN)
		{
			iobuf_log_unexpected(rbuf, __func__);
			goto end;
		}

		if(!strcmp(rbuf->buf, "extra_comms_end"))
		{
			if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end ok"))
				goto end;
			break;
		}
		else if(!strncmp_w(rbuf->buf, "autoupgrade:"))
		{
			char *os=NULL;
			os=rbuf->buf+strlen("autoupgrade:");
			iobuf_free_content(rbuf);
			if(os && *os && autoupgrade_server(as, vers->ser,
				vers->cli, os, globalcs)) goto end;
		}
		else if(!strcmp(rbuf->buf, "srestore ok"))
		{
			iobuf_free_content(rbuf);
			// Client can accept the restore.
			// Load the restore config, then send it.
			*srestore=1;
			if(conf_parse_incexcs_path(cconfs,
				get_string(cconfs[OPT_RESTORE_PATH]))
			  || incexc_send_server_restore(asfd, cconfs))
				goto end;
			// Do not unlink it here - wait until
			// the client says that it wants to do the
			// restore.
			// Also need to leave it around if the
			// restore is to an alternative client, so
			// that the code below that reloads the config
			// can read it again.
			//unlink(get_string(cconfs[OPT_RESTORE_PATH]));
		}
		else if(!strcmp(rbuf->buf, "srestore not ok"))
		{
			const char *restore_path=get_string(
				cconfs[OPT_RESTORE_PATH]);
			// Client will not accept the restore.
			unlink(restore_path);
			if(set_string(cconfs[OPT_RESTORE_PATH], NULL))
				goto end;
			logp("Client not accepting server initiated restore.\n");
		}
		else if(!strcmp(rbuf->buf, "sincexc ok"))
		{
			// Client can accept incexc conf from the
			// server.
			iobuf_free_content(rbuf);
			if(incexc_send_server(asfd, cconfs)) goto end;
		}
		else if(!strcmp(rbuf->buf, "incexc"))
		{
			// Client is telling server its incexc
			// configuration so that it can better decide
			// what to do on resume.
			iobuf_free_content(rbuf);
			if(incexc_recv_server(asfd, incexc, globalcs)) goto end;
			if(*incexc)
			{
				char *tmp=NULL;
				char comp[32]="";
				snprintf(comp, sizeof(comp),
					"compression = %d\n",
					get_int(cconfs[OPT_COMPRESSION]));
				if(!(tmp=prepend(*incexc, comp)))
					goto end;
				free_w(incexc);
				*incexc=tmp;
			}
		}
		else if(!strcmp(rbuf->buf, "countersok"))
		{
			// Client can accept counters on
			// resume/verify/restore.
			logp("Client supports being sent counters.\n");
			set_int(cconfs[OPT_SEND_CLIENT_CNTR], 1);
		}
		else if(!strncmp_w(rbuf->buf, "uname=")
		  && strlen(rbuf->buf)>strlen("uname="))
		{
			char *uname=rbuf->buf+strlen("uname=");
			if(!strncasecmp("Windows", uname, strlen("Windows")))
				set_int(cconfs[OPT_CLIENT_IS_WINDOWS], 1);
		}
		else if(!strncmp_w(rbuf->buf, "orig_client=")
		  && strlen(rbuf->buf)>strlen("orig_client="))
		{
			if(conf_switch_to_orig_client(globalcs, cconfs,
				rbuf->buf+strlen("orig_client=")))
					goto end;
			// If this started out as a server-initiated
			// restore, need to load the restore file
			// again.
			if(*srestore)
			{
				if(conf_parse_incexcs_path(cconfs,
					get_string(cconfs[OPT_RESTORE_PATH])))
						goto end;
			}
			if(asfd->write_str(asfd, CMD_GEN, "orig_client ok"))
				goto end;
		}
		else if(!strncmp_w(rbuf->buf, "restore_spool="))
		{
			// Client supports temporary spool directory
			// for restores.
			if(set_string(cconfs[OPT_RESTORE_SPOOL],
				rbuf->buf+strlen("restore_spool=")))
					goto end;
		}
		else if(!strncmp_w(rbuf->buf, "protocol="))
		{
			char msg[128]="";
			// Client wants to set protocol.
			enum protocol protocol=get_e_protocol(
				cconfs[OPT_PROTOCOL]);
			if(protocol!=PROTO_AUTO)
			{
				snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s but server is set to protocol=%d\n", rbuf->buf, protocol);
				log_and_send_oom(asfd, __func__);
				goto end;
			}
			else if(!strcmp(rbuf->buf+strlen("protocol="), "1"))
			{
				set_e_protocol(cconfs[OPT_PROTOCOL], PROTO_1);
				set_e_protocol(globalcs[OPT_PROTOCOL], PROTO_1);
			}
			else if(!strcmp(rbuf->buf+strlen("protocol="), "2"))
			{
				set_e_protocol(cconfs[OPT_PROTOCOL], PROTO_2);
				set_e_protocol(globalcs[OPT_PROTOCOL], PROTO_2);
			}
			else
			{
				snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s, which is unknown\n", rbuf->buf);
				log_and_send_oom(asfd, __func__);
				goto end;
			}
			logp("Client has set protocol=%d\n",
				(int)get_e_protocol(cconfs[OPT_PROTOCOL]));
		}
		else if(!strncmp_w(rbuf->buf, "rshash=blake2"))
		{
#ifdef RS_DEFAULT_STRONG_LEN
			logp("Client is trying to use librsync hash blake2, but server does not support it.\n");
			goto end;
#else
			set_e_rshash(cconfs[OPT_RSHASH], RSHASH_BLAKE2);
			set_e_rshash(globalcs[OPT_RSHASH], RSHASH_BLAKE2);
#endif
		}
		else if(!strncmp_w(rbuf->buf, "msg"))
		{
			set_int(cconfs[OPT_MESSAGE], 1);
			set_int(globalcs[OPT_MESSAGE], 1);
		}
		else
		{
			iobuf_log_unexpected(rbuf, __func__);
			goto end;
		}
	}

	ret=0;
end:
	iobuf_free_content(rbuf);
	return ret;
}
Ejemplo n.º 4
0
Archivo: backup.c Proyecto: patvdv/burp
// Return 0 for OK, -1 for error.
int do_backup_client(struct asfd *asfd, struct conf **confs, enum action action,
                     int resume)
{
    int ret=-1;

    if(action==ACTION_ESTIMATE)
        logp("do estimate client\n");
    else
    {
        logp("do backup client\n");
        if(get_e_protocol(confs[OPT_PROTOCOL])==PROTO_1)
            logp("Using librsync hash %s\n",
                 rshash_to_str(get_e_rshash(confs[OPT_RSHASH])));
    }

#ifdef HAVE_WIN32
    win32_enable_backup_privileges();
#ifdef WIN32_VSS
    if(win32_start_vss(confs)) return ret;
#endif
    if(action==ACTION_BACKUP_TIMED) set_low_priority();
#endif

    // Scan the file system and send the results to the server.
    // Skip phase1 if the server wanted to resume.
    if(!resume)
    {
        if(get_int(confs[OPT_BREAKPOINT])==1)
        {
            breakpoint(confs, __func__);
            goto end;
        }
        if(backup_phase1_client(asfd, confs, action==ACTION_ESTIMATE))
            goto end;
    }

    switch(action)
    {
    case ACTION_DIFF:
    case ACTION_DIFF_LONG:
        ret=1;
        goto end;
    case ACTION_ESTIMATE:
        cntr_print(get_cntr(confs[OPT_CNTR]), ACTION_ESTIMATE);
        break;
    default:
        // Now, the server will be telling us what data we need
        // to send.
        if(get_int(confs[OPT_BREAKPOINT])==2)
        {
            breakpoint(confs, __func__);
            goto end;
        }

        if(get_e_protocol(confs[OPT_PROTOCOL])==PROTO_1)
            ret=backup_phase2_client_protocol1(asfd,
                                               confs, resume);
        else
            ret=backup_phase2_client_protocol2(asfd,
                                               confs, resume);
        if(ret) goto end;
        break;
    }

    ret=0;
end:
#if defined(HAVE_WIN32)
    if(action==ACTION_BACKUP_TIMED) unset_low_priority();
#if defined(WIN32_VSS)
    win32_stop_vss();
#endif
#endif
    return ret;
}
Ejemplo n.º 5
0
static void check_default(struct conf **c, enum conf_opt o)
{
	switch(o)
	{
		case OPT_BURP_MODE:
			fail_unless(get_e_burp_mode(c[o])==BURP_MODE_UNSET);
			break;
		case OPT_LOCKFILE:
		case OPT_PIDFILE:
		case OPT_ADDRESS:
		case OPT_PORT:
		case OPT_STATUS_ADDRESS:
		case OPT_STATUS_PORT:
        	case OPT_SSL_CERT_CA:
		case OPT_SSL_CERT:
		case OPT_SSL_KEY:
		case OPT_SSL_KEY_PASSWORD:
		case OPT_SSL_PEER_CN:
		case OPT_SSL_CIPHERS:
		case OPT_SSL_DHFILE:
		case OPT_CA_CONF:
		case OPT_CA_NAME:
		case OPT_CA_SERVER_NAME:
		case OPT_CA_BURP_CA:
		case OPT_CA_CSR_DIR:
		case OPT_PEER_VERSION:
		case OPT_CLIENT_LOCKDIR:
		case OPT_MONITOR_LOGFILE:
		case OPT_CNAME:
		case OPT_PASSWORD:
		case OPT_PASSWD:
		case OPT_SERVER:
		case OPT_ENCRYPTION_PASSWORD:
		case OPT_AUTOUPGRADE_OS:
		case OPT_AUTOUPGRADE_DIR:
		case OPT_BACKUP:
		case OPT_BACKUP2:
		case OPT_RESTOREPREFIX:
		case OPT_RESTORE_SPOOL:
		case OPT_BROWSEFILE:
		case OPT_BROWSEDIR:
		case OPT_B_SCRIPT_PRE:
		case OPT_B_SCRIPT_POST:
		case OPT_R_SCRIPT_PRE:
		case OPT_R_SCRIPT_POST:
		case OPT_B_SCRIPT:
		case OPT_R_SCRIPT:
		case OPT_RESTORE_PATH:
		case OPT_ORIG_CLIENT:
		case OPT_CONFFILE:
		case OPT_USER:
		case OPT_GROUP:
		case OPT_DIRECTORY:
		case OPT_TIMESTAMP_FORMAT:
		case OPT_CLIENTCONFDIR:
		case OPT_S_SCRIPT_PRE:
		case OPT_S_SCRIPT_POST:
		case OPT_MANUAL_DELETE:
		case OPT_S_SCRIPT:
		case OPT_TIMER_SCRIPT:
		case OPT_N_SUCCESS_SCRIPT:
		case OPT_N_FAILURE_SCRIPT:
		case OPT_DEDUP_GROUP:
		case OPT_VSS_DRIVES:
		case OPT_REGEX:
		case OPT_RESTORE_CLIENT:
			fail_unless(get_string(c[o])==NULL);
			break;
		case OPT_RATELIMIT:
			fail_unless(get_float(c[o])==0);
			break;
		case OPT_CLIENT_IS_WINDOWS:
		case OPT_RANDOMISE:
		case OPT_B_SCRIPT_POST_RUN_ON_FAIL:
		case OPT_R_SCRIPT_POST_RUN_ON_FAIL:
		case OPT_SEND_CLIENT_CNTR:
		case OPT_BREAKPOINT:
		case OPT_SYSLOG:
		case OPT_PROGRESS_COUNTER:
		case OPT_MONITOR_BROWSE_CACHE:
		case OPT_S_SCRIPT_PRE_NOTIFY:
		case OPT_S_SCRIPT_POST_RUN_ON_FAIL:
		case OPT_S_SCRIPT_POST_NOTIFY:
		case OPT_S_SCRIPT_NOTIFY:
		case OPT_HARDLINKED_ARCHIVE:
        	case OPT_N_SUCCESS_WARNINGS_ONLY:
        	case OPT_N_SUCCESS_CHANGES_ONLY:
		case OPT_CROSS_ALL_FILESYSTEMS:
		case OPT_READ_ALL_FIFOS:
		case OPT_READ_ALL_BLOCKDEVS:
		case OPT_SPLIT_VSS:
		case OPT_STRIP_VSS:
		case OPT_ATIME:
		case OPT_SCAN_PROBLEM_RAISES_ERROR:
		case OPT_OVERWRITE:
		case OPT_STRIP:
		case OPT_MESSAGE:
			fail_unless(get_int(c[o])==0);
			break;
		case OPT_DAEMON:
		case OPT_STDOUT:
		case OPT_FORK:
		case OPT_DIRECTORY_TREE:
		case OPT_PASSWORD_CHECK:
		case OPT_LIBRSYNC:
		case OPT_VERSION_WARN:
		case OPT_PATH_LENGTH_WARN:
		case OPT_CLIENT_CAN_DELETE:
		case OPT_CLIENT_CAN_DIFF:
		case OPT_CLIENT_CAN_FORCE_BACKUP:
		case OPT_CLIENT_CAN_LIST:
		case OPT_CLIENT_CAN_RESTORE:
		case OPT_CLIENT_CAN_VERIFY:
		case OPT_SERVER_CAN_RESTORE:
		case OPT_B_SCRIPT_RESERVED_ARGS:
		case OPT_R_SCRIPT_RESERVED_ARGS:
		case OPT_ACL:
		case OPT_XATTR:
			fail_unless(get_int(c[o])==1);
			break;
		case OPT_NETWORK_TIMEOUT:
			fail_unless(get_int(c[o])==60*60*2);
			break;
		case OPT_SSL_COMPRESSION:
		case OPT_MAX_CHILDREN:
		case OPT_MAX_STATUS_CHILDREN:
			fail_unless(get_int(c[o])==5);
			break;
        	case OPT_COMPRESSION:
			fail_unless(get_int(c[o])==9);
			break;
		case OPT_MAX_STORAGE_SUBDIRS:
			fail_unless(get_int(c[o])==30000);
			break;
		case OPT_MAX_HARDLINKS:
			fail_unless(get_int(c[o])==10000);
			break;
		case OPT_UMASK:
			fail_unless(get_mode_t(c[o])==0022);
			break;
		case OPT_STARTDIR:
		case OPT_B_SCRIPT_PRE_ARG:
		case OPT_B_SCRIPT_POST_ARG:
		case OPT_R_SCRIPT_PRE_ARG:
		case OPT_R_SCRIPT_POST_ARG:
		case OPT_B_SCRIPT_ARG:
		case OPT_R_SCRIPT_ARG:
		case OPT_S_SCRIPT_PRE_ARG:
		case OPT_S_SCRIPT_POST_ARG:
		case OPT_S_SCRIPT_ARG:
		case OPT_TIMER_ARG:
		case OPT_N_SUCCESS_ARG:
		case OPT_N_FAILURE_ARG:
		case OPT_RESTORE_CLIENTS:
		case OPT_KEEP:
		case OPT_INCEXCDIR:
		case OPT_INCLUDE:
		case OPT_EXCLUDE:
		case OPT_FSCHGDIR:
		case OPT_NOBACKUP:
		case OPT_INCEXT:
		case OPT_EXCEXT:
		case OPT_INCREG:
		case OPT_EXCREG:
		case OPT_EXCFS:
		case OPT_EXCOM:
		case OPT_INCGLOB:
        	case OPT_FIFOS:
        	case OPT_BLOCKDEVS:
			fail_unless(get_strlist(c[o])==NULL);
			break;
		case OPT_PROTOCOL:
			fail_unless(get_e_protocol(c[o])==PROTO_AUTO);
			break;
		case OPT_HARD_QUOTA:
		case OPT_SOFT_QUOTA:
		case OPT_MIN_FILE_SIZE:
		case OPT_MAX_FILE_SIZE:
			fail_unless(get_uint64_t(c[o])==0);
			break;
        	case OPT_WORKING_DIR_RECOVERY_METHOD:
			fail_unless(get_e_recovery_method(c[o])==
				RECOVERY_METHOD_DELETE);
			break;
        	case OPT_RSHASH:
			fail_unless(get_e_rshash(c[o])==RSHASH_UNSET);
			break;
		case OPT_CNTR:
			fail_unless(get_cntr(c)==NULL);
			break;
        	case OPT_MAX:
			break;
		// No default, so we get compiler warnings if something was
		// missed.
	}
}
Ejemplo n.º 6
0
int do_restore_client(struct asfd *asfd,
	struct conf **confs, enum action act, int vss_restore)
{
	int ret=-1;
	char msg[512]="";
	struct sbuf *sb=NULL;
	struct blk *blk=NULL;
	BFILE *bfd=NULL;
	char *fullpath=NULL;
	char *style=NULL;
	char *datpath=NULL;
	enum protocol protocol=get_e_protocol(confs[OPT_PROTOCOL]);
	const char *backup=get_string(confs[OPT_BACKUP]);
	const char *regex=get_string(confs[OPT_REGEX]);

	if(!(bfd=bfile_alloc())) goto end;

	bfile_init(bfd, 0, confs);

	snprintf(msg, sizeof(msg), "%s %s:%s", act_str(act),
		backup?backup:"", regex?regex:"");
	logp("doing %s\n", msg);
	if(asfd->write_str(asfd, CMD_GEN, msg)
	  || asfd->read_expect(asfd, CMD_GEN, "ok"))
		goto error;
	logp("doing %s confirmed\n", act_str(act));

#if defined(HAVE_WIN32)
	if(act==ACTION_RESTORE) win32_enable_backup_privileges();
#endif

	if(!(style=get_restore_style(asfd, confs)))
		goto error;
	if(!strcmp(style, RESTORE_SPOOL))
	{
		if(restore_spool(asfd, confs, &datpath))
			goto error;
	}
	else
		logp("Streaming restore direct\n");

	printf("\n");

//	if(get_int(confs[OPT_SEND_CLIENT_CNTR]) && cntr_recv(confs))
//		goto error;

	if(!(sb=sbuf_alloc(confs))
	  || (protocol==PROTO_2 && !(blk=blk_alloc())))
	{
		log_and_send_oom(asfd, __func__);
		goto error;
	}

	while(1)
	{
		sbuf_free_content(sb);

		switch(sbuf_fill_w(sb, asfd, blk, datpath, confs))
		{
			case 0: break;
			case 1: if(asfd->write_str(asfd, CMD_GEN,
				"restoreend_ok")) goto error;
				goto end; // It was OK.
			default:
			case -1: goto error;
		}

		if(protocol==PROTO_2 && blk->data)
		{
			int wret;
			if(act==ACTION_VERIFY)
				cntr_add(get_cntr(confs[OPT_CNTR]), CMD_DATA, 1);
			else
				wret=write_data(asfd, bfd, blk);
			if(!datpath) free(blk->data);
			blk->data=NULL;
			if(wret) goto error;
			continue;
		}

		switch(sb->path.cmd)
		{
			case CMD_DIRECTORY:
			case CMD_FILE:
			case CMD_ENC_FILE:
			case CMD_SOFT_LINK:
			case CMD_HARD_LINK:
			case CMD_SPECIAL:
			case CMD_METADATA:
			case CMD_ENC_METADATA:
			case CMD_VSS:
			case CMD_ENC_VSS:
			case CMD_VSS_T:
			case CMD_ENC_VSS_T:
			case CMD_EFS_FILE:
				if(get_int(confs[OPT_STRIP]))
				{
					int s;
					s=strip_path_components(asfd, sb, confs);
					if(s<0) goto error;
					if(s==0)
					{
						// Too many components stripped
						// - carry on.
						continue;
					}
					// It is OK, sb.path is now stripped.
				}
				free_w(&fullpath);
				if(!(fullpath=prepend_s(
					get_string(confs[OPT_RESTOREPREFIX]),
					sb->path.buf)))
				{
					log_and_send_oom(asfd, __func__);
					goto error;
				}
				if(act==ACTION_RESTORE)
				{
				  strip_invalid_characters(&fullpath);
				  if(!overwrite_ok(sb, confs,
#ifdef HAVE_WIN32
					bfd,
#endif
					fullpath))
				  {
					char msg[512]="";
					// Something exists at that path.
					snprintf(msg, sizeof(msg),
						"Path exists: %s", fullpath);
					if(restore_interrupt(asfd,
						sb, msg, confs))
							goto error;
					else
						continue;
				  }
				}
				break;
			case CMD_MESSAGE:
			case CMD_WARNING:
				log_recvd(&sb->path, confs, 1);
				printf("\n");
				continue;
			default:
				break;
		}

		switch(sb->path.cmd)
		{
			// These are the same in both protocol1 and protocol2.
			case CMD_DIRECTORY:
				if(restore_dir(asfd, sb, fullpath, act, confs))
					goto error;
				continue;
			case CMD_SOFT_LINK:
			case CMD_HARD_LINK:
				if(restore_link(asfd, sb, fullpath, act, confs))
					goto error;
				continue;
			case CMD_SPECIAL:
				if(restore_special(asfd, sb,
					fullpath, act, confs)) goto error;
				continue;
			default:
				break;
		}

		if(protocol==PROTO_2)
		{
			if(restore_switch_protocol2(asfd, sb, fullpath, act,
				bfd, vss_restore, confs))
					goto error;
		}
		else
		{
			if(restore_switch_protocol1(asfd, sb, fullpath, act,
				bfd, vss_restore, confs))
					goto error;
		}
	}

end:
	ret=0;
error:
	// It is possible for a fd to still be open.
	bfd->close(bfd, asfd);
	bfile_free(&bfd);

	cntr_print_end(get_cntr(confs[OPT_CNTR]));
	cntr_print(get_cntr(confs[OPT_CNTR]), act);

	if(!ret) logp("%s finished\n", act_str(act));
	else logp("ret: %d\n", ret);

	sbuf_free(&sb);
	free_w(&style);
	if(datpath)
	{
		recursive_delete(datpath, NULL, 1);
		free(datpath);
	}
	free_w(&fullpath);

	return ret;
}