Beispiel #1
0
int
ctfile_nextop_list(struct ct_global_state *state, char *ctfile, void *args)
{
	struct ct_extract_args	*cea = args;

	cea->cea_local_ctfile = ctfile;
	ct_add_operation(state, ct_list_op, ctfile_nextop_list_cleanup, cea);

	return (0);
}
void
ctfile_find_for_operation(struct ct_global_state *state, char *tag,
    ctfile_find_callback *nextop, void *nextop_args, int download_chain,
    int empty_ok)
{
	struct ct_ctfile_find_args  *ccfa;
	ccfa = e_calloc(1, sizeof(*ccfa));
	ccfa->ccfa_tag = tag;
	ccfa->ccfa_cachedir = state->ct_config->ct_ctfile_cachedir;
	ccfa->ccfa_nextop = nextop;
	ccfa->ccfa_nextop_args = nextop_args;
	ccfa->ccfa_download_chain = download_chain;
	ccfa->ccfa_empty_ok = empty_ok;

	ct_add_operation(state, ctfile_find_for_extract,
	    ctfile_find_for_extract_complete, ccfa);
}
Beispiel #3
0
/* Do a complete ct operation from start to finish */
int
ct_do_operation(struct ct_config *conf,  ct_op_cb *start,
    ct_op_complete_cb *complete, void *args, int flags)
{
	struct ct_global_state	*state;
	int		 	 ret;

	ct_prompt_for_login_password(conf);

	if ((ret = ct_init(&state, conf, flags, NULL)) != 0)
		return (ret);

	ct_add_operation(state, start, complete, args);
	ret = ct_event_dispatch(state->event_state);
	ct_cleanup(state);

	return (ret);
}
Beispiel #4
0
int
ct_main(int argc, char **argv)
{
	struct ct_extract_args		 cea;
	struct ct_archive_args		 caa;
	struct ct_ctfileop_args 	 cca;
	struct ct_ctfile_list_args	 ccla;
	struct ct_ctfile_delete_args	 ccda;
	struct ct_global_state		*state = NULL;
	struct ct_config		*conf;
	char				*ct_tdir = NULL;
	char				*ct_basisbackup = NULL;
	char				*ctfile = NULL;
	char				*ct_includefile = NULL;
	char				*ct_excludefile = NULL;
	char				*configfile = NULL, *config_file = NULL;
	char				*basisfile = NULL;
	char				*debugstring = NULL;
	char				**excludelist = NULL;
	char				**includelist = NULL;
	uint64_t			 debug_mask = 0;
	uint32_t			 cflags = CLOG_F_ENABLE | CLOG_F_STDERR;
	int				 ct_metadata = 0;
	int				 ct_match_mode = CT_MATCH_GLOB;
	int				 c;
	int				 ret = 0;
	int				 level0 = 0;
	int				 freeincludes = 0;
	int				 no_cross_mounts = 0;
	int				 strip_slash = 1;
	int				 follow_root_symlink = 0;
	int				 follow_symlinks = 0;
	int				 attr = 0;
	int				 verbose_ratios = 0;
	int				 ct_flags = 0;

	while ((c = getopt(argc, argv,
	    "AB:C:D:E:F:HI:PRVXacdef:hmprtvx0")) != -1) {
		switch (c) {
		case 'A':
			/* noop, deprecated */
			break;
		case 'B':
			basisfile = optarg;
			break;
		case 'C':
			ct_tdir = optarg;
			break;
		case 'D':
			if (debugstring != NULL)
				CFATALX("only one -D argument is valid");
			debugstring = optarg;
			break;
		case 'E':
			ct_excludefile = optarg;
			break;
		case 'F':
			configfile = optarg;
			break;
		case 'H':
			follow_root_symlink = 1;
			break;
		case 'I':
			ct_includefile = optarg;
			break;
		case 'P':
			strip_slash = 0;
			break;
		case 'R':
			verbose_ratios = 1;
			break;
		case 'V':
			show_version();
			exit(0);
			break;
		case 'X':
			no_cross_mounts = 1;
			break;
		case 'a':
			/* noop, deprecated */
			break;
		case 'c':
			if (ct_action)
				CFATALX("cannot mix operations, -c -e -t -x");
			ct_action = CT_A_ARCHIVE;
			break;
		case 'e':
			if (ct_action)
				CFATALX("cannot mix operations, -c -e -t -x");
			ct_action = CT_A_ERASE;
			break;
		case 'f': /* metadata file */
			ctfile = optarg;
			break;
		case 'h':
			follow_symlinks = 1;
			break;
		case 'm': /* metadata processing - XXX temporary? */
			ct_metadata = 1;
			break;
		case 'r':
			ct_match_mode = CT_MATCH_REGEX;
			break;
		case 'p':
			attr = 1;
			break;
		case 't':
			if (ct_action)
				CFATALX("cannot mix operations, -c -e -t -x");
			ct_action = CT_A_LIST;
			break;
		case 'v':
			ct_verbose++;
			break;
		case 'x':
			if (ct_action)
				CFATALX("cannot mix operations, -c -e -t -x");
			ct_action = CT_A_EXTRACT;
			break;
		case '0':
			level0 = 1;
			break;
		default:
			ct_usage();
			/* NOTREACHED */
		}
	}

	argc -= optind;
	argv += optind;

	if (debugstring) {
		cflags |= CLOG_F_DBGENABLE | CLOG_F_FILE | CLOG_F_FUNC |
		    CLOG_F_LINE | CLOG_F_DTIME;
		exude_enable(CT_LOG_EXUDE);
#if CT_ENABLE_THREADS
		exude_enable_threads();
#endif
		debug_mask |= ct_get_debugmask(debugstring);
	}

	/* please don't delete this line AGAIN! --mp */
	if (clog_set_flags(cflags))
		errx(1, "illegal clog flags");
	clog_set_mask(debug_mask);

	/* We can allocate these now that we've decided if we need exude */
	if (configfile)
		config_file = e_strdup(configfile);
	if (basisfile)
		ct_basisbackup = e_strdup(basisfile);

	if (ct_includefile != NULL) {
		int nentries;

		if ((ct_action == CT_A_LIST || ct_action == CT_A_EXTRACT) &&
		    argc != 0)
			CFATALX("-I is invalid when a pattern is "
			    "provided on the command line");
		includelist = ct_matchlist_fromfile(ct_includefile,
		    &nentries);
		if (nentries == -1)
			CFATAL("can't get includelist from %s",
			    ct_includefile);

		freeincludes = 1;
	} else if ((ct_action == CT_A_LIST || ct_action == CT_A_EXTRACT)) {
		includelist = argv;
	}
	if (ct_excludefile != NULL) {
		int	nentries;
		excludelist = ct_matchlist_fromfile(ct_excludefile, &nentries);
		if (nentries == -1)
			CFATAL("can't get excludelsit from %s", ct_excludefile);
	}


	if ((ret = ct_load_config(&conf, &config_file)) != 0) {
		CFATALX("%s", ct_strerror(ret));
	}

	if (!(ct_metadata && (ct_action == CT_A_LIST ||
	    ct_action == CT_A_ERASE))) {
		if (ctfile == NULL) {
			CWARNX("ctfile is required");
			ct_usage();
		}

		if (conf->ct_ctfile_mode == CT_MDMODE_REMOTE &&
		    ctfile_verify_name(ctfile))
			CFATALX("invalid ctfile: %s", ctfile);
	}

	/*
	 * !metadata extract with no args extracts everything.
	 * and all lists show everything if not filtered
	 */
	if (((ct_metadata == 0 && ct_action == CT_A_EXTRACT) ||
	    ct_action == CT_A_LIST) && argc == 0)
		ct_match_mode = CT_MATCH_EVERYTHING;

	if (level0)
		conf->ct_auto_incremental = 0; /* force incremental off */

	if (conf->ct_ctfile_mode == CT_MDMODE_REMOTE && ct_metadata == 0 &&
	    ct_basisbackup != NULL)
		CFATALX("incremental basis in remote mode");

	/* Don't bother starting a connection if just listing local files. */
	if (ct_action == CT_A_LIST &&
	    conf->ct_ctfile_mode == CT_MDMODE_LOCAL &&
	    ct_metadata == 0 ) {
		ret = ct_list(ctfile, includelist, excludelist,
		    ct_match_mode, NULL, strip_slash, ct_verbose);
		goto out;
	}

	ct_prompt_for_login_password(conf);

	if (ct_action == CT_A_EXTRACT ||
	    ct_action == CT_A_ARCHIVE || (ct_action == CT_A_LIST &&
	    conf->ct_ctfile_mode == CT_MDMODE_REMOTE && ct_metadata == 0) ||
	    ct_action == CT_A_ERASE)
		ct_flags |= CT_NEED_SECRETS;
	if (ct_action == CT_A_ARCHIVE)
		ct_flags |= CT_NEED_DB;


	if ((ret = ct_init(&state, conf, ct_flags, ct_info_sig)) != 0)
		CFATALX("failed to initialise cyphertite: %s",
		    ct_strerror(ret));

#if defined(CT_EXT_INIT)
	CT_EXT_INIT(state);
#endif

	if (conf->ct_crypto_passphrase != NULL &&
	    conf->ct_secrets_upload != 0) {
		ct_add_operation(state, ctfile_list_start,
		    ct_check_secrets_extract, conf->ct_crypto_secrets);
	}

	if (ct_action == CT_A_EXTRACT)
		ct_set_log_fns(state, &ct_verbose, ct_print_ctfile_info,
		    ct_print_file_start, ct_print_file_end,
		    ct_print_traverse_start, ct_print_traverse_end);
	else if (ct_action == CT_A_ARCHIVE)
		ct_set_log_fns(state, &ct_verbose, ct_print_ctfile_info,
		    ct_pr_fmt_file, ct_pr_fmt_file_end,
		    ct_print_traverse_start, ct_print_traverse_end);

	if (conf->ct_ctfile_mode == CT_MDMODE_REMOTE && ct_metadata == 0) {
		switch (ct_action) {
		case CT_A_EXTRACT:
		case CT_A_LIST:
			cea.cea_local_ctfile = NULL; /* to be found */
			cea.cea_filelist = includelist;
			cea.cea_excllist = excludelist;
			cea.cea_matchmode = ct_match_mode;
			cea.cea_ctfile_basedir = conf->ct_ctfile_cachedir;
			cea.cea_tdir = ct_tdir;
			cea.cea_strip_slash = strip_slash;
			cea.cea_attr = attr;
			cea.cea_follow_symlinks = follow_symlinks;
			cea.cea_log_state = &ct_verbose;
			cea.cea_log_chown_failed =
			    ct_print_extract_chown_failed;
			ctfile_find_for_operation(state, ctfile,
			    ((ct_action == CT_A_EXTRACT)  ?
			    ctfile_nextop_extract : ctfile_nextop_list),
			    &cea, 1, 0);
			break;
		case CT_A_ARCHIVE:
			ct_normalize_filelist(argv);
			caa.caa_filelist = argv;
			caa.caa_excllist = excludelist;
			caa.caa_matchmode = ct_match_mode;
			caa.caa_includelist = includelist;
			caa.caa_tdir = ct_tdir;
			caa.caa_tag = ctfile;
			caa.caa_ctfile_basedir = conf->ct_ctfile_cachedir;
			/* we want to encrypt as long as we have keys */
			caa.caa_no_cross_mounts = no_cross_mounts;
			caa.caa_strip_slash = strip_slash;
			caa.caa_follow_root_symlink = follow_root_symlink;
			caa.caa_follow_symlinks = follow_symlinks;
			caa.caa_max_incrementals = conf->ct_max_incrementals;
			if (conf->ct_auto_incremental)
				/*
				 * Need to work out basis filename and
				 * download it if necessary
				 */
				ctfile_find_for_operation(state, ctfile,
				    ctfile_nextop_archive, &caa, 0, 1);
			else   {
				/* No basis, just start the op */
				ctfile_nextop_archive(state, NULL, &caa);
			}
			break;
		default:
			CWARNX("invalid action");
			ct_usage();
			/* NOTREACHED */
			break;
		}
	} else if (ct_metadata != 0) {
		if (ct_action == CT_A_ARCHIVE || ct_action == CT_A_EXTRACT) {
			cca.cca_localname = ctfile;
			cca.cca_remotename = NULL;
			cca.cca_tdir = ct_tdir;
			cca.cca_cleartext = 0;
			cca.cca_ctfile = 1;
			/* only matters for archive */
			ct_add_operation(state,
			    ((ct_action == CT_A_ARCHIVE) ?
			    ctfile_archive : ctfile_extract),
			    ctfile_op_cleanup, &cca);
		} else if (ct_action == CT_A_ERASE) {
			if (ctfile != NULL)
				CFATALX("-f is not permitted with -me operation");
			if (argc == 0)
				CFATALX("no files specified");
			ccda.ccda_pattern = argv;
			ccda.ccda_matchmode = ct_match_mode;
			ccda.ccda_callback = ct_print_delete;
			ct_add_operation(state, ctfile_list_start,
			    ctfile_process_delete, &ccda);
		} else if (ct_action == CT_A_LIST) {
			ccla.ccla_search = includelist;
			ccla.ccla_exclude = excludelist;
			ccla.ccla_matchmode = ct_match_mode;
			ct_add_operation(state, ctfile_list_start,
			    ctfile_list_print, &ccla);
		} else {
			CWARNX("must specify action");
			ct_usage();
			/* NOTREACHED */
		}
	} else {
		/* list is handled above */
		if (ct_action == CT_A_ARCHIVE) {
			caa.caa_local_ctfile = ctfile;
			ct_normalize_filelist(argv);
			caa.caa_filelist = argv;
			caa.caa_excllist = excludelist;
			caa.caa_matchmode = ct_match_mode;
			caa.caa_includelist = includelist;
			caa.caa_tdir = ct_tdir;
			caa.caa_tag = ctfile;
			caa.caa_ctfile_basedir = NULL;
			/* we want to encrypt as long as we have keys */
			caa.caa_no_cross_mounts = no_cross_mounts;
			caa.caa_strip_slash = strip_slash;
			caa.caa_follow_root_symlink = follow_root_symlink;
			caa.caa_follow_symlinks = follow_symlinks;
			caa.caa_max_incrementals = 0; /* unlimited */
			caa.caa_basis = ct_basisbackup;

			ct_add_operation(state, ct_archive, NULL, &caa);
		} else if (ct_action == CT_A_EXTRACT) {
			cea.cea_local_ctfile = ctfile;
			cea.cea_filelist = includelist;
			cea.cea_excllist = excludelist;
			cea.cea_matchmode = ct_match_mode;
			cea.cea_ctfile_basedir = NULL;
			cea.cea_tdir = ct_tdir;
			cea.cea_strip_slash = strip_slash;
			cea.cea_attr = attr;
			cea.cea_follow_symlinks = follow_symlinks;
			cea.cea_log_state = &ct_verbose;
			cea.cea_log_chown_failed =
			    ct_print_extract_chown_failed;
			ct_add_operation(state, ct_extract, NULL, &cea);
		} else {
			CWARNX("must specify action");
			ct_usage();
			/* NOTREACHED */
		}
	}

	ct_wakeup_file(state->event_state);

	if ((ret = ct_run_eventloop(state)) != 0) {
		if (state->ct_errmsg[0] != '\0')
			CWARNX("%s: %s", state->ct_errmsg,
			    ct_strerror(ret));
		else
			CWARNX("%s", ct_strerror(ret));
		return (ret);
	}

	if (verbose_ratios)
		ct_dump_stats(state, stdout);
	ct_cleanup_login_cache();
	ct_cleanup(state);
out:
	if (includelist && freeincludes == 1)
		ct_matchlist_free(includelist);
	if (excludelist)
		ct_matchlist_free(excludelist);
	if (conf->ct_ctfile_mode == CT_MDMODE_REMOTE && ct_metadata == 0)
		ctfile_trim_cache(conf->ct_ctfile_cachedir,
		    conf->ct_ctfile_max_cachesize);

	ct_unload_config(config_file, conf);
#if CT_CHECK_MEMORY
	e_check_memory();
#endif
	exude_cleanup();

	return (ret);
}
int
ctfile_nextop_archive(struct ct_global_state *state, char *basis, void *args)
{
	struct ct_archive_args	*caa = args;
	struct ct_ctfileop_args	*cca;
	char			*ctfile;
	char	 		 buf[TIMEDATA_LEN], *fullname, *cachename;
	time_t	 		 now;

	CNDBG(CT_LOG_CTFILE, "setting basisname %s", basis ? basis : "<none>");
	caa->caa_basis = basis;

	/*
	 * We now have the basis found for us, cook and prepare the tag
	 * we wish to create then add the operation.
	 */
	if ((ctfile = ctfile_cook_name(caa->caa_tag)) == NULL) {
		CWARNX("%s: %s", caa->caa_tag,
		    ct_strerror(CTE_INVALID_CTFILE_NAME));
		return (CTE_INVALID_CTFILE_NAME);
	}

	if (ctfile_is_fullname(ctfile) != 0) {
		CWARNX("%s", ct_strerror(CTE_ARCHIVE_FULLNAME));
		e_free(&ctfile);
		return (CTE_ARCHIVE_FULLNAME);
	}

	now = time(NULL);
	if (strftime(buf, TIMEDATA_LEN, "%Y%m%d-%H%M%S",
	    localtime(&now)) == 0)
		CABORTX("can't format time");
	e_asprintf(&fullname, "%s-%s", buf, ctfile);
	CNDBG(CT_LOG_CTFILE, "backup file is %s", fullname);

	/* check it isn't already in the cache */
	cachename = ctfile_get_cachename(fullname,
	    state->ct_config->ct_ctfile_cachedir);
	if (ctfile_in_cache(fullname, state->ct_config->ct_ctfile_cachedir)) {
		CWARNX("%s: %s", fullname,
		    ct_strerror(CTE_BACKUP_ALREADY_EXISTS));
		e_free(&ctfile);
		e_free(&fullname);
		e_free(&cachename);
		return (CTE_BACKUP_ALREADY_EXISTS);
	}

	e_free(&ctfile);
	e_free(&fullname);

	caa->caa_local_ctfile = cachename;
	ct_add_operation(state, ct_archive, NULL, caa);
	/*
	 * set up an additional operation to upload the newly created
	 * ctfile after the archive is completed.
	 */
	cca = e_calloc(1, sizeof(*cca));
	cca->cca_localname = cachename;
	cca->cca_cleartext = 0;
	cca->cca_ctfile = 1;
	ct_add_operation(state, ctfile_archive, ctfile_nextop_archive_cleanup,
	    cca);
	return (0);
}
/*
 * List has completed.
 *
 * Select the best filename for download, and download it if missing.
 */
int
ctfile_find_for_extract_complete(struct ct_global_state *state,
    struct ct_op *op)
{
	struct ct_ctfile_find_args		*ccfa = op->op_args;
	struct ct_ctfile_find_fileop_args	*ccffa;
	struct ct_op				*list_fakeop = op->op_priv;
	struct ct_ctfile_list_args		*ccla = list_fakeop->op_args;
	struct ctfile_list_tree			 result;
	struct ctfile_list_file			*tmp;
	char	 				*best = NULL;
	int					 ret = 0;

	RB_INIT(&result);
	ctfile_list_complete(&state->ctfile_list_files, ccla->ccla_matchmode,
	    ccla->ccla_search, ccla->ccla_exclude, &result);
	e_free(ccla->ccla_search);
	e_free(&ccla->ccla_search);
	e_free(&ccla);
	e_free(&list_fakeop);

	/*
	 * Prepare arguments for next operation.
	 * either we'll download the next file, or skip straight to
	 * the callback for after the download, either way we need the nextop
	 */
	ccffa = e_calloc(1, sizeof(*ccffa));
	ccffa->ccffa_nextop = ccfa->ccfa_nextop;
	ccffa->ccffa_nextop_args = ccfa->ccfa_nextop_args;
	ccffa->ccffa_download_chain = ccfa->ccfa_download_chain;

	/* grab the newest one */
	if ((tmp = RB_MAX(ctfile_list_tree, &result)) == NULL) {
		if (ccfa->ccfa_empty_ok)
			goto do_operation;
		else {
			CWARNX("%s: %s", ccfa->ccfa_tag,
			    ct_strerror(CTE_NO_SUCH_BACKUP));
			ret = CTE_NO_SUCH_BACKUP;
			e_free(&ccffa);
			goto out;
		}
	}

	/* pick the newest one */
	best = e_strdup(tmp->mlf_name);
	CNDBG(CT_LOG_CTFILE, "backup file is %s", best);

	while ((tmp = RB_ROOT(&result)) != NULL) {
		RB_REMOVE(ctfile_list_tree, &result, tmp);
		e_free(&tmp);
	}

	/*
	 * if the metadata file is not in the cache directory then we
	 * need to download it first. if we need to recursively download
	 * an incremental chain then that code will handle scheduling
	 * those operations too. If we have it, we still need to check
	 * that all others in the chain exist, however.
	 */
	if (!ctfile_in_cache(best, ccfa->ccfa_cachedir)) {
		ccffa->ccffa_base.cca_localname = best;
		ccffa->ccffa_base.cca_tdir = ccfa->ccfa_cachedir;
		ccffa->ccffa_base.cca_remotename = e_strdup(best);
		ccffa->ccffa_base.cca_ctfile = 1;
		ct_add_operation(state, ctfile_extract, ctfile_extract_nextop,
		    ccffa);
	} else {
do_operation:
		/*
		 * No download needed, fake the next operation callback
		 * to see if we need anymore.
		 */
		ccffa->ccffa_base.cca_localname = best;
		ccffa->ccffa_base.cca_tdir = ccfa->ccfa_cachedir;
		ccffa->ccffa_base.cca_ctfile = 1;
		op->op_args = ccffa;
		ctfile_extract_nextop(state, op);
	}
out:
	e_free(&ccfa);

	return (ret);
}