/*
- Bacula® - The Network Backup Solution
+ Bacula(R) - The Network Backup Solution
- Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2017 Kern Sibbald
- The main author of Bacula is Kern Sibbald, with contributions from many
- others, a complete list can be found in the file AUTHORS.
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
You may use this file and others of this release according to the
license defined in the LICENSE file, which includes the Affero General
Public License, v3.0 ("AGPLv3") and some additional permissions and
terms pursuant to its AGPLv3 Section 7.
- Bacula® is a registered trademark of Kern Sibbald.
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
*/
/*
- *
* Bacula Director -- User Agent Database restore Command
* Creates a bootstrap file for restoring files and
* starts the restore job.
static void free_name_list(NAME_LIST *name_list);
static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
-static void free_rx(RESTORE_CTX *rx);
static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *fname);
static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx);
static bool get_date(UAContext *ua, char *date, int date_len);
static int restore_count_handler(void *ctx, int num_fields, char **row);
-static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx);
+void new_rx(RESTORE_CTX *rx)
+{
+ RBSR *bsr = NULL;
+ memset(rx, 0, sizeof(*rx));
+ rx->path = get_pool_memory(PM_FNAME);
+ rx->path[0] = 0;
+
+ rx->fname = get_pool_memory(PM_FNAME);
+ rx->fname[0] = 0;
+
+ rx->JobIds = get_pool_memory(PM_FNAME);
+ rx->JobIds[0] = 0;
+
+ rx->component_fname = get_pool_memory(PM_FNAME);
+ rx->component_fname[0] = 0;
+
+ rx->BaseJobIds = get_pool_memory(PM_FNAME);
+ rx->BaseJobIds[0] = 0;
+
+ rx->query = get_pool_memory(PM_FNAME);
+ rx->query[0] = 0;
+
+ rx->bsr_list = New(rblist(bsr, &bsr->link));
+ rx->hardlinks_in_mem = true;
+}
+
+
/*
* Restore files
*
char *strip_prefix, *add_prefix, *add_suffix, *regexp;
strip_prefix = add_prefix = add_suffix = regexp = NULL;
- memset(&rx, 0, sizeof(rx));
- rx.path = get_pool_memory(PM_FNAME);
- rx.path[0] = 0;
-
- rx.fname = get_pool_memory(PM_FNAME);
- rx.fname[0] = 0;
-
- rx.JobIds = get_pool_memory(PM_FNAME);
- rx.JobIds[0] = 0;
-
- rx.component_fname = get_pool_memory(PM_FNAME);
- rx.component_fname[0] = 0;
-
- rx.BaseJobIds = get_pool_memory(PM_FNAME);
- rx.BaseJobIds[0] = 0;
-
- rx.query = get_pool_memory(PM_FNAME);
- rx.query[0] = 0;
-
- rx.bsr = new_bsr();
- rx.hardlinks_in_mem = true;
-
+ new_rx(&rx); /* Initialize RESTORE_CTX */
+
if (!open_new_client_db(ua)) {
goto bail_out;
}
for (i = 0; i < ua->argc ; i++) {
+ if (strcasecmp(ua->argk[i], "fdcalled") == 0) {
+ rx.fdcalled = true;
+
+ } else if (strcasecmp(ua->argk[i], "noautoparent") == 0) {
+ rx.no_auto_parent = true;
+ }
if (!ua->argv[i]) {
continue; /* skip if no value given */
}
} else if (strcasecmp(ua->argk[i], "where") == 0) {
rx.where = ua->argv[i];
+ } else if (strcasecmp(ua->argk[i], "when") == 0) {
+ rx.when = ua->argv[i];
+
} else if (strcasecmp(ua->argk[i], "replace") == 0) {
rx.replace = ua->argv[i];
strcasecmp(ua->argv[i], "false")) {
rx.hardlinks_in_mem = false;
}
- }
+ }
}
if (strip_prefix || add_suffix || add_prefix) {
break;
}
- if (rx.bsr->JobId) {
+ if (rx.bsr_list->size() > 0) {
char ed1[50];
- if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
+ if (!complete_bsr(ua, rx.bsr_list)) { /* find Vol, SessId, SessTime from JobIds */
ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
goto bail_out;
}
ua->warning_msg(_("No files selected to be restored.\n"));
goto bail_out;
}
+
+ ua->send_msg(_("Bootstrap records written to %s\n"), ua->jcr->RestoreBootstrap);
display_bsr_info(ua, rx); /* display vols needed, etc */
if (rx.selected_files==1) {
}
get_client_name(ua, &rx);
- if (!rx.ClientName) {
+ if (!rx.ClientName[0]) {
ua->error_msg(_("No Client resource found!\n"));
goto bail_out;
}
pm_strcat(ua->cmd, buf);
}
+ if (rx.fdcalled) {
+ pm_strcat(ua->cmd, " fdcalled=yes");
+ }
+
+ if (rx.when) {
+ Mmsg(buf, " when=\"%s\"", rx.when);
+ pm_strcat(ua->cmd, buf);
+ }
+
if (rx.comment) {
Mmsg(buf, " comment=\"%s\"", rx.comment);
pm_strcat(ua->cmd, buf);
}
Dmsg1(200, "Submitting: %s\n", ua->cmd);
/*
- * Transfer jobids, to jcr to
+ * Transfer jobids, component stuff to jcr to
* pass to run_cmd(). Note, these are fields and
* other things that are not passed on the command
* line.
*/
/* ***FIXME*** pass jobids on command line */
+ if (jcr->JobIds) {
+ free_pool_memory(jcr->JobIds);
+ }
jcr->JobIds = rx.JobIds;
rx.JobIds = NULL;
+ jcr->component_fname = rx.component_fname;
+ rx.component_fname = NULL;
+ jcr->component_fd = rx.component_fd;
+ rx.component_fd = NULL;
parse_ua_args(ua);
run_cmd(ua, ua->cmd);
free_rx(&rx);
bfree(regexp);
}
+ /* Free the plugin config if needed, we don't want to re-use
+ * this part of the next try
+ */
+ free_plugin_config_items(jcr->plugin_config);
+ jcr->plugin_config = NULL;
+
free_rx(&rx);
garbage_collect_memory(); /* release unused memory */
return 0;
pm_strcpy(rx->BaseJobIds, jobids.list);
}
-static void free_rx(RESTORE_CTX *rx)
+void free_rx(RESTORE_CTX *rx)
{
- free_bsr(rx->bsr);
- rx->bsr = NULL;
+ free_bsr(rx->bsr_list);
+ rx->bsr_list = NULL;
free_and_null_pool_memory(rx->JobIds);
free_and_null_pool_memory(rx->BaseJobIds);
free_and_null_pool_memory(rx->fname);
free_and_null_pool_memory(rx->path);
free_and_null_pool_memory(rx->query);
+ if (rx->fileregex) {
+ free(rx->fileregex);
+ rx->fileregex = NULL;
+ }
+ if (rx->component_fd) {
+ fclose(rx->component_fd);
+ rx->component_fd = NULL;
+ }
+ if (rx->component_fname) {
+ unlink(rx->component_fname);
+ }
+ free_and_null_pool_memory(rx->component_fname);
free_name_list(&rx->name_list);
}
return 1;
}
memset(&cr, 0, sizeof(cr));
- if (!get_client_dbr(ua, &cr)) {
+ /* We want the name of the client where the backup was made */
+ if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
return 0;
}
bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
"comment", /* 21 */
"restorejob", /* 22 */
"replace", /* 23 */
+ "xxxxxxxxx", /* 24 */
+ "fdcalled", /* 25 */
+ "when", /* 26 */
+ "noautoparent", /* 27 */
NULL
};
ua->send_msg(_("Selecting jobs to build the Full state at %s\n"),
jr.cStartTime);
jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
- if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) {
+ if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &jobids)) {
return 0;
}
pm_strcpy(rx->JobIds, jobids.list);
pm_strcat(JobIds, edit_int64(JobId, ed1));
rx->TotalFiles += jr.JobFiles;
}
- free_pool_memory(rx->JobIds);
- rx->JobIds = JobIds; /* Set ACL filtered list */
+ pm_strcpy(rx->JobIds, JobIds); /* Set ACL filtered list */
+ free_pool_memory(JobIds);
if (*rx->JobIds == 0) {
ua->warning_msg(_("No Jobs selected.\n"));
return 0;
switch (*p) {
case '<':
p++;
- if ((ffd = fopen(p, "rb")) == NULL) {
+ if ((ffd = bfopen(p, "rb")) == NULL) {
berrno be;
ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
p, be.bstrerror());
} else {
Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
rx->path, rx->fname, rx->ClientName);
+ /*
+ * Note: we have just edited the JobIds into the query, so
+ * we need to clear JobIds, or they will be added
+ * back into JobIds with the query below, and then
+ * restored twice. Fixes bug #2212.
+ */
+ rx->JobIds[0] = 0;
}
rx->found = false;
/* Find and insert jobid and File Index */
/*
* Get the JobId and FileIndexes of all files in the specified table
*/
-static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
+bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
{
strip_trailing_junk(table);
Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
}
+static bool can_restore_all_files(UAContext *ua)
+{
+ alist *lst;
+ if (ua->cons) {
+ lst = ua->cons->ACL_lists[Directory_ACL];
+ /* ACL not defined, or the first entry is not *all* */
+ /* TODO: See if we search for *all* in all the list */
+ if (!lst || strcasecmp((char*)lst->get(0), "*all*") != 0) {
+ return false;
+ }
+ if (!lst || strcasecmp((char *)lst->get(0), "*all*") != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
{
- if (find_arg(ua, NT_("all")) >= 0) { /* if user enters all on command line */
+ bool can_restore=can_restore_all_files(ua);
+
+ if (can_restore && find_arg(ua, NT_("all")) >= 0) { /* if user enters all on command line */
return true; /* select everything */
}
+
ua->send_msg(_("\n\nFor one or more of the JobIds selected, no files were found,\n"
"so file selection is not possible.\n"
"Most likely your retention policy pruned the files.\n"));
+
+ if (!can_restore) {
+ ua->error_msg(_("\nThe current Console has UserId or Directory restrictions. "
+ "The full restore is not allowed.\n"));
+ return false;
+ }
+
if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
if (ua->pint32_val == 1)
return true;
if (*errmsg) {
ua->send_msg(_("Regex compile error: %s\n"), errmsg);
} else {
- rx->bsr->fileregex = bstrdup(ua->cmd);
+ rx->fileregex = bstrdup(ua->cmd);
return true;
}
}
if (lst->next) {
add_delta_list_findex(rx, lst->next);
}
- add_findex(rx->bsr, lst->JobId, lst->FileIndex);
+ add_findex(rx->bsr_list, lst->JobId, lst->FileIndex);
}
+/*
+ * This is a list of all the files (components) that the
+ * user has requested for restore. It is requested by
+ * the plugin (for now hard coded only for VSS).
+ * In the future, this will be requested by a RestoreObject
+ * and the plugin name will be sent to the FD.
+ */
+static bool write_component_file(UAContext *ua, RESTORE_CTX *rx, char *fname)
+{
+ int fd;
+ if (!rx->component_fd) {
+ Mmsg(rx->component_fname, "%s/%s.restore.sel.XXXXXX", working_directory, my_name);
+ fd = mkstemp(rx->component_fname);
+ if (fd < 0) {
+ berrno be;
+ ua->error_msg(_("Unable to create component file %s. ERR=%s\n"),
+ rx->component_fname, be.bstrerror());
+ return false;
+ }
+ rx->component_fd = fdopen(fd, "w+");
+ if (!rx->component_fd) {
+ berrno be;
+ ua->error_msg(_("Unable to fdopen component file %s. ERR=%s\n"),
+ rx->component_fname, be.bstrerror());
+ return false;
+ }
+ }
+ fprintf(rx->component_fd, "%s\n", fname);
+ if (ferror(rx->component_fd)) {
+ ua->error_msg(_("Error writing component file.\n"));
+ fclose(rx->component_fd);
+ unlink(rx->component_fname);
+ rx->component_fd = NULL;
+ return false;
+ }
+ return true;
+}
static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
{
tree.ua = ua;
tree.all = rx->all;
tree.hardlinks_in_mem = rx->hardlinks_in_mem;
+ tree.no_auto_parent = rx->no_auto_parent;
last_JobId = 0;
+ tree.last_dir_acl = NULL;
/*
* For display purposes, the same JobId, with different volumes may
* appear more than once, however, we only insert it once.
if (JobId == last_JobId) {
continue; /* eliminate duplicate JobIds */
}
- add_findex_all(rx->bsr, JobId);
+ add_findex_all(rx->bsr_list, JobId, rx->fileregex);
}
}
} else {
* extracted making a bootstrap file.
*/
if (OK) {
+ char cwd[2000];
for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
if (node->extract || node->extract_dir) {
Dmsg3(400, "JobId=%lld type=%d FI=%d\n", (uint64_t)node->JobId, node->type, node->FileIndex);
/* TODO: optimize bsr insertion when jobid are non sorted */
add_delta_list_findex(rx, node->delta_list);
- add_findex(rx->bsr, node->JobId, node->FileIndex);
+ add_findex(rx->bsr_list, node->JobId, node->FileIndex);
+ /*
+ * Special VSS plugin code to return selected
+ * components. For the moment, it is hard coded
+ * for the VSS plugin.
+ */
+ if (fnmatch(":component_info_*", node->fname, 0) == 0) {
+ tree_getpath(node, cwd, sizeof(cwd));
+ if (!write_component_file(ua, rx, cwd)) {
+ OK = false;
+ break;
+ }
+ }
if (node->extract && node->type != TN_NEWDIR) {
rx->selected_files++; /* count only saved files */
}
}
}
}
-
+ if (tree.uid_acl) {
+ delete tree.uid_acl;
+ delete tree.gid_acl;
+ delete tree.dir_acl;
+ }
free_tree(tree.root); /* free the directory tree */
return OK;
}
int i;
/* Create temp tables */
- db_sql_query(ua->db, uar_del_temp, NULL, NULL);
- db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
+ db_sql_query(ua->db, uar_del_temp, NULL, NULL);
+ db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
if (!db_sql_query(ua->db, uar_create_temp[db_get_type_index(ua->db)], NULL, NULL)) {
ua->error_msg("%s\n", db_strerror(ua->db));
}
* Select Client from the Catalog
*/
memset(&cr, 0, sizeof(cr));
- if (!get_client_dbr(ua, &cr)) {
+ if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
goto bail_out;
}
bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
}
bail_out:
- db_sql_query(ua->db, uar_del_temp, NULL, NULL);
- db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
+ db_sql_query(ua->db, uar_del_temp, NULL, NULL);
+ db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
return ok;
}
rx->JobId = JobId;
}
- add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
+ add_findex(rx->bsr_list, rx->JobId, str_to_int64(row[1]));
rx->found = true;
rx->selected_files++;
return 0;
if (acl_access_ok(ua, Storage_ACL, store->name())) {
rx.store = store;
Dmsg1(200, "Set store=%s\n", rx.store->name());
- if (Storage == NULL) {
+ if (Storage == NULL || Storage[0] == 0) {
ua->warning_msg(_("Using Storage \"%s\" from MediaType \"%s\".\n"),
store->name(), MediaType);
} else {