3 * Bacula Director -- User Agent Database restore Command
4 * Creates a bootstrap file for restoring files and
5 * starts the restore job.
7 * Tree handling routines split into ua_tree.c July MMIII.
8 * BSR (bootstrap record) handling routines split into
11 * Kern Sibbald, July MMII
16 Copyright (C) 2002-2006 Kern Sibbald
18 This program is free software; you can redistribute it and/or
19 modify it under the terms of the GNU General Public License
20 version 2 as amended with additional clauses defined in the
21 file LICENSE in the main source directory.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 the file LICENSE for additional details.
35 /* Imported functions */
36 extern void print_bsr(UAContext *ua, RBSR *bsr);
40 /* Forward referenced functions */
41 static int last_full_handler(void *ctx, int num_fields, char **row);
42 static int jobid_handler(void *ctx, int num_fields, char **row);
43 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
44 static int fileset_handler(void *ctx, int num_fields, char **row);
45 static void free_name_list(NAME_LIST *name_list);
46 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
47 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
48 static void free_rx(RESTORE_CTX *rx);
49 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
50 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
51 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
53 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
55 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
56 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
57 static int get_date(UAContext *ua, char *date, int date_len);
58 static int count_handler(void *ctx, int num_fields, char **row);
59 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
65 int restore_cmd(UAContext *ua, const char *cmd)
67 RESTORE_CTX rx; /* restore context */
71 char *escaped_bsr_name = NULL;
72 char *escaped_where_name = NULL;
74 memset(&rx, 0, sizeof(rx));
75 rx.path = get_pool_memory(PM_FNAME);
76 rx.fname = get_pool_memory(PM_FNAME);
77 rx.JobIds = get_pool_memory(PM_FNAME);
78 rx.query = get_pool_memory(PM_FNAME);
81 i = find_arg_with_value(ua, "where");
83 rx.where = ua->argv[i];
84 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
85 bsendmsg(ua, _("Forbidden \"where\" specified.\n"));
94 /* Ensure there is at least one Restore Job */
96 foreach_res(job, R_JOB) {
97 if (job->JobType == JT_RESTORE) {
98 if (!rx.restore_job) {
105 if (!rx.restore_jobs) {
107 "No Restore Job Resource found in bacula-dir.conf.\n"
108 "You must create at least one before running this command.\n"));
113 * Request user to select JobIds or files by various different methods
114 * last 20 jobs, where File saved, most recent backup, ...
115 * In the end, a list of files are pumped into
118 switch (user_select_jobids_or_files(ua, &rx)) {
121 case 1: /* selected by jobid */
122 if (!build_directory_tree(ua, &rx)) {
123 bsendmsg(ua, _("Restore not done.\n"));
127 case 2: /* selected by filename, no tree needed */
132 uint32_t selected_files;
133 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
134 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
137 if (!(selected_files = write_bsr_file(ua, rx))) {
138 bsendmsg(ua, _("No files selected to be restored.\n"));
141 /* If no count of files, use bsr generated value (often wrong) */
142 if (rx.selected_files == 0) {
143 rx.selected_files = selected_files;
145 if (rx.selected_files==1) {
146 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
149 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
152 bsendmsg(ua, _("No files selected to be restored.\n"));
156 if (rx.restore_jobs == 1) {
157 job = rx.restore_job;
159 job = select_restore_job_resource(ua);
165 get_client_name(ua, &rx);
166 if (!rx.ClientName) {
167 bsendmsg(ua, _("No Restore Job resource found!\n"));
171 escaped_bsr_name = escape_filename(jcr->RestoreBootstrap);
172 escaped_where_name = escape_filename(rx.where);
174 /* Build run command */
176 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
177 bsendmsg(ua, _("Forbidden \"where\" specified.\n"));
182 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
183 " where=\"%s\" files=%d catalog=\"%s\"",
184 job->name(), rx.ClientName, rx.store?rx.store->name():"",
185 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
186 escaped_where_name ? escaped_where_name : rx.where,
187 rx.selected_files, ua->catalog->name());
190 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
191 " files=%d catalog=\"%s\"",
192 job->name(), rx.ClientName, rx.store?rx.store->name():"",
193 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
194 rx.selected_files, ua->catalog->name());
197 if (escaped_bsr_name != NULL) {
198 bfree(escaped_bsr_name);
201 if (escaped_where_name != NULL) {
202 bfree(escaped_where_name);
205 if (find_arg(ua, NT_("yes")) > 0) {
206 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
208 Dmsg1(200, "Submitting: %s\n", ua->cmd);
210 run_cmd(ua, ua->cmd);
215 if (escaped_bsr_name != NULL) {
216 bfree(escaped_bsr_name);
219 if (escaped_where_name != NULL) {
220 bfree(escaped_where_name);
228 static void free_rx(RESTORE_CTX *rx)
233 free_pool_memory(rx->JobIds);
237 free_pool_memory(rx->fname);
241 free_pool_memory(rx->path);
245 free_pool_memory(rx->query);
248 free_name_list(&rx->name_list);
251 static bool has_value(UAContext *ua, int i)
254 bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
260 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
262 /* If no client name specified yet, get it now */
263 if (!rx->ClientName[0]) {
265 /* try command line argument */
266 int i = find_arg_with_value(ua, NT_("client"));
268 if (!has_value(ua, i)) {
271 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
274 memset(&cr, 0, sizeof(cr));
275 if (!get_client_dbr(ua, &cr)) {
278 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
285 * The first step in the restore process is for the user to
286 * select a list of JobIds from which he will subsequently
287 * select which files are to be restored.
289 * Returns: 2 if filename list made
290 * 1 if jobid list made
293 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
296 char date[MAX_TIME_LENGTH];
297 bool have_date = false;
299 JOB_DBR jr = { (JobId_t)-1 };
302 const char *list[] = {
303 _("List last 20 Jobs run"),
304 _("List Jobs where a given File is saved"),
305 _("Enter list of comma separated JobIds to select"),
306 _("Enter SQL list command"),
307 _("Select the most recent backup for a client"),
308 _("Select backup for a client before a specified time"),
309 _("Enter a list of files to restore"),
310 _("Enter a list of files to restore before a specified time"),
311 _("Find the JobIds of the most recent backup for a client"),
312 _("Find the JobIds for a backup for a client before a specified time"),
313 _("Enter a list of directories to restore for found JobIds"),
318 /* These keywords are handled in a for loop */
328 /* The keyword below are handled by individual arg lookups */
334 "bootstrap", /* 13 */
341 for (i=1; i<ua->argc; i++) { /* loop through arguments */
342 bool found_kw = false;
343 for (j=0; kw[j]; j++) { /* loop through keywords */
344 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
350 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
353 /* Found keyword in kw[] list, process it */
356 if (!has_value(ua, i)) {
359 if (*rx->JobIds != 0) {
360 pm_strcat(rx->JobIds, ",");
362 pm_strcat(rx->JobIds, ua->argv[i]);
365 case 1: /* current */
366 bstrutime(date, sizeof(date), time(NULL));
370 if (!has_value(ua, i)) {
373 if (str_to_utime(ua->argv[i]) == 0) {
374 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
377 bstrncpy(date, ua->argv[i], sizeof(date));
382 if (!has_value(ua, i)) {
386 bstrutime(date, sizeof(date), time(NULL));
388 if (!get_client_name(ua, rx)) {
391 pm_strcpy(ua->cmd, ua->argv[i]);
392 insert_one_file_or_dir(ua, rx, date, j==4);
396 bstrutime(date, sizeof(date), time(NULL));
398 if (!select_backups_before_date(ua, rx, date)) {
403 case 6: /* pool specified */
404 if (!has_value(ua, i)) {
407 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
409 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
412 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
414 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
418 case 7: /* all specified */
422 * All keywords 7 or greater are ignored or handled by a select prompt
430 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
431 "to be restored. You will be presented several methods\n"
432 "of specifying the JobIds. Then you will be allowed to\n"
433 "select which files from those JobIds are to be restored.\n\n"));
436 /* If choice not already made above, prompt */
442 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
443 for (int i=0; list[i]; i++) {
444 add_prompt(ua, list[i]);
447 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
448 case -1: /* error or cancel */
450 case 0: /* list last 20 Jobs run */
451 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
452 bsendmsg(ua, _("SQL query not authorized.\n"));
455 gui_save = ua->jcr->gui;
457 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
458 ua->jcr->gui = gui_save;
461 case 1: /* list where a file is saved */
462 if (!get_client_name(ua, rx)) {
465 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
468 len = strlen(ua->cmd);
469 fname = (char *)malloc(len * 2 + 1);
470 db_escape_string(fname, ua->cmd, len);
471 Mmsg(rx->query, uar_file, rx->ClientName, fname);
473 gui_save = ua->jcr->gui;
475 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
476 ua->jcr->gui = gui_save;
479 case 2: /* enter a list of JobIds */
480 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
483 pm_strcpy(rx->JobIds, ua->cmd);
485 case 3: /* Enter an SQL list command */
486 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
487 bsendmsg(ua, _("SQL query not authorized.\n"));
490 if (!get_cmd(ua, _("Enter SQL list command: "))) {
493 gui_save = ua->jcr->gui;
495 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
496 ua->jcr->gui = gui_save;
499 case 4: /* Select the most recent backups */
500 bstrutime(date, sizeof(date), time(NULL));
501 if (!select_backups_before_date(ua, rx, date)) {
505 case 5: /* select backup at specified time */
506 if (!get_date(ua, date, sizeof(date))) {
509 if (!select_backups_before_date(ua, rx, date)) {
513 case 6: /* Enter files */
514 bstrutime(date, sizeof(date), time(NULL));
515 if (!get_client_name(ua, rx)) {
518 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
519 "containing a list of file names with paths, and terminate\n"
520 "them with a blank line.\n"));
522 if (!get_cmd(ua, _("Enter full filename: "))) {
525 len = strlen(ua->cmd);
529 insert_one_file_or_dir(ua, rx, date, false);
532 case 7: /* enter files backed up before specified time */
533 if (!get_date(ua, date, sizeof(date))) {
536 if (!get_client_name(ua, rx)) {
539 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
540 "containing a list of file names with paths, and terminate\n"
541 "them with a blank line.\n"));
543 if (!get_cmd(ua, _("Enter full filename: "))) {
546 len = strlen(ua->cmd);
550 insert_one_file_or_dir(ua, rx, date, false);
554 case 8: /* Find JobIds for current backup */
555 bstrutime(date, sizeof(date), time(NULL));
556 if (!select_backups_before_date(ua, rx, date)) {
562 case 9: /* Find JobIds for give date */
563 if (!get_date(ua, date, sizeof(date))) {
566 if (!select_backups_before_date(ua, rx, date)) {
572 case 10: /* Enter directories */
573 if (*rx->JobIds != 0) {
574 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
576 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
577 if (*rx->JobIds != 0 && *ua->cmd) {
578 pm_strcat(rx->JobIds, ",");
580 pm_strcat(rx->JobIds, ua->cmd);
582 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
583 return 0; /* nothing entered, return */
585 bstrutime(date, sizeof(date), time(NULL));
586 if (!get_client_name(ua, rx)) {
589 bsendmsg(ua, _("Enter full directory names or start the name\n"
590 "with a < to indicate it is a filename containing a list\n"
591 "of directories and terminate them with a blank line.\n"));
593 if (!get_cmd(ua, _("Enter directory name: "))) {
596 len = strlen(ua->cmd);
600 /* Add trailing slash to end of directory names */
601 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
602 strcat(ua->cmd, "/");
604 insert_one_file_or_dir(ua, rx, date, true);
608 case 11: /* Cancel or quit */
613 if (*rx->JobIds == 0) {
614 bsendmsg(ua, _("No Jobs selected.\n"));
617 if (strchr(rx->JobIds,',')) {
618 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
621 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
626 for (p=rx->JobIds; ; ) {
627 int stat = get_next_jobid_from_list(&p, &JobId);
629 bsendmsg(ua, _("Invalid JobId in list.\n"));
635 if (jr.JobId == JobId) {
636 continue; /* duplicate of last JobId */
638 memset(&jr, 0, sizeof(JOB_DBR));
640 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
642 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
643 edit_int64(JobId, ed1), db_strerror(ua->db));
646 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
647 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
651 rx->TotalFiles += jr.JobFiles;
659 static int get_date(UAContext *ua, char *date, int date_len)
661 bsendmsg(ua, _("The restored files will the most current backup\n"
662 "BEFORE the date you specify below.\n\n"));
664 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
667 if (str_to_utime(ua->cmd) != 0) {
670 bsendmsg(ua, _("Improper date format.\n"));
672 bstrncpy(date, ua->cmd, date_len);
677 * Insert a single file, or read a list of files from a file
679 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
689 if ((ffd = fopen(p, "rb")) == NULL) {
691 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
695 while (fgets(file, sizeof(file), ffd)) {
698 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
699 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
702 if (!insert_file_into_findex_list(ua, rx, file, date)) {
703 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
711 insert_table_into_findex_list(ua, rx, p);
715 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
717 insert_file_into_findex_list(ua, rx, ua->cmd, date);
724 * For a given file (path+filename), split into path and file, then
725 * lookup the most recent backup in the catalog to get the JobId
726 * and FileIndex, then insert them into the findex list.
728 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
731 strip_trailing_newline(file);
732 split_path_and_filename(rx, file);
733 if (*rx->JobIds == 0) {
734 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
737 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
738 rx->path, rx->fname, rx->ClientName);
741 /* Find and insert jobid and File Index */
742 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
743 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
744 rx->query, db_strerror(ua->db));
747 bsendmsg(ua, _("No database record found for: %s\n"), file);
754 * For a given path lookup the most recent backup in the catalog
755 * to get the JobId and FileIndexes of all files in that directory.
757 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
760 strip_trailing_junk(dir);
761 if (*rx->JobIds == 0) {
762 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
765 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
766 dir, rx->ClientName);
769 /* Find and insert jobid and File Index */
770 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
771 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
772 rx->query, db_strerror(ua->db));
775 bsendmsg(ua, _("No database record found for: %s\n"), dir);
782 * Get the JobId and FileIndexes of all files in the specified table
784 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
786 strip_trailing_junk(table);
787 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
790 /* Find and insert jobid and File Index */
791 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
792 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
793 rx->query, db_strerror(ua->db));
796 bsendmsg(ua, _("No table found: %s\n"), table);
802 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
806 /* Find path without the filename.
807 * I.e. everything after the last / is a "filename".
808 * OK, maybe it is a directory name, but we treat it like
809 * a filename. If we don't find a / then the whole name
810 * must be a path name (e.g. c:).
812 for (p=f=name; *p; p++) {
814 f = p; /* set pos of last slash */
817 if (*f == '/') { /* did we find a slash? */
818 f++; /* yes, point to filename */
819 } else { /* no, whole thing must be path name */
823 /* If filename doesn't exist (i.e. root directory), we
824 * simply create a blank name consisting of a single
825 * space. This makes handling zero length filenames
830 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
831 memcpy(rx->fname, f, rx->fnl); /* copy filename */
832 rx->fname[rx->fnl] = 0;
840 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
841 memcpy(rx->path, name, rx->pnl);
842 rx->path[rx->pnl] = 0;
848 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
851 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
854 JobId_t JobId, last_JobId;
859 memset(&tree, 0, sizeof(TREE_CTX));
861 * Build the directory tree containing JobIds user selected
863 tree.root = new_tree(rx->TotalFiles);
868 * For display purposes, the same JobId, with different volumes may
869 * appear more than once, however, we only insert it once.
873 tree.FileEstimate = 0;
874 if (get_next_jobid_from_list(&p, &JobId) > 0) {
875 /* Use first JobId as estimate of the number of files to restore */
876 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
877 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
878 bsendmsg(ua, "%s\n", db_strerror(ua->db));
881 /* Add about 25% more than this job for over estimate */
882 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
883 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
886 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
889 if (JobId == last_JobId) {
890 continue; /* eliminate duplicate JobIds */
893 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
894 edit_int64(JobId, ed1));
897 * Find files for this JobId and insert them in the tree
899 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
900 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
901 bsendmsg(ua, "%s", db_strerror(ua->db));
904 if (tree.FileCount == 0) {
905 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
906 "is not possible.Most likely your retention policy pruned the files\n"));
907 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
911 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
912 if (JobId == last_JobId) {
913 continue; /* eliminate duplicate JobIds */
915 add_findex_all(rx->bsr, JobId);
923 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
924 edit_uint64_with_commas(tree.FileCount, ec1));
927 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
928 edit_uint64_with_commas(tree.FileCount, ec1));
933 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
934 items, edit_uint64_with_commas(tree.FileCount, ec1));
937 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
938 items, edit_uint64_with_commas(tree.FileCount, ec1));
942 if (find_arg(ua, NT_("done")) < 0) {
943 /* Let the user interact in selecting which files to restore */
944 OK = user_select_files_from_tree(&tree);
948 * Walk down through the tree finding all files marked to be
949 * extracted making a bootstrap file.
952 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
953 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
954 if (node->extract || node->extract_dir) {
955 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
956 add_findex(rx->bsr, node->JobId, node->FileIndex);
957 if (node->extract && node->type != TN_NEWDIR) {
958 rx->selected_files++; /* count only saved files */
965 free_tree(tree.root); /* free the directory tree */
971 * This routine is used to get the current backup or a backup
972 * before the specified date.
974 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
979 char fileset_name[MAX_NAME_LENGTH];
980 char ed1[50], ed2[50];
981 char pool_select[MAX_NAME_LENGTH];
985 /* Create temp tables */
986 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
987 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
988 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
989 bsendmsg(ua, "%s\n", db_strerror(ua->db));
991 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
992 bsendmsg(ua, "%s\n", db_strerror(ua->db));
995 * Select Client from the Catalog
997 memset(&cr, 0, sizeof(cr));
998 if (!get_client_dbr(ua, &cr)) {
1001 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1006 memset(&fsr, 0, sizeof(fsr));
1007 i = find_arg_with_value(ua, "FileSet");
1009 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1010 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1011 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1012 db_strerror(ua->db));
1016 if (i < 0) { /* fileset not found */
1017 edit_int64(cr.ClientId, ed1);
1018 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1019 start_prompt(ua, _("The defined FileSet resources are:\n"));
1020 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1021 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1023 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1024 fileset_name, sizeof(fileset_name)) < 0) {
1028 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1029 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1030 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1031 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1032 "Continuing anyway.\n"));
1036 /* If Pool specified, add PoolId specification */
1040 memset(&pr, 0, sizeof(pr));
1041 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1042 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1043 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1044 edit_int64(pr.PoolId, ed1));
1046 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1050 /* Find JobId of last Full backup for this client, fileset */
1051 edit_int64(cr.ClientId, ed1);
1052 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1054 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1055 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1059 /* Find all Volumes used by that JobId */
1060 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1061 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1064 /* Note, this is needed because I don't seem to get the callback
1065 * from the call just above.
1068 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1069 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1071 if (rx->JobTDate == 0) {
1072 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1076 /* Now find most recent Differental Job after Full save, if any */
1077 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1078 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1079 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1080 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1082 /* Now update JobTDate to lock onto Differental, if any */
1084 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1085 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1087 if (rx->JobTDate == 0) {
1088 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1092 /* Now find all Incremental Jobs after Full/dif save */
1093 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1094 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1095 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1096 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1099 /* Get the JobIds from that list */
1101 rx->last_jobid[0] = 0;
1102 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1103 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1106 if (rx->JobIds[0] != 0) {
1107 /* Display a list of Jobs selected for this restore */
1108 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1111 bsendmsg(ua, _("No jobs found.\n"));
1115 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1116 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1122 * Return next JobId from comma separated list
1125 * 1 if next JobId returned
1126 * 0 if no more JobIds are in list
1127 * -1 there is an error
1129 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1135 for (int i=0; i<(int)sizeof(jobid); i++) {
1138 } else if (*q == ',') {
1145 if (jobid[0] == 0) {
1147 } else if (!is_a_number(jobid)) {
1148 return -1; /* error */
1151 *JobId = str_to_int64(jobid);
1155 static int count_handler(void *ctx, int num_fields, char **row)
1157 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1158 rx->JobId = str_to_int64(row[0]);
1164 * Callback handler to get JobId and FileIndex for files
1165 * can insert more than one depending on the caller.
1167 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1169 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1170 rx->JobId = str_to_int64(row[0]);
1171 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1173 rx->selected_files++;
1178 * Callback handler make list of JobIds
1180 static int jobid_handler(void *ctx, int num_fields, char **row)
1182 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1184 if (strcmp(rx->last_jobid, row[0]) == 0) {
1185 return 0; /* duplicate id */
1187 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1188 if (rx->JobIds[0] != 0) {
1189 pm_strcat(rx->JobIds, ",");
1191 pm_strcat(rx->JobIds, row[0]);
1197 * Callback handler to pickup last Full backup JobTDate
1199 static int last_full_handler(void *ctx, int num_fields, char **row)
1201 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1203 rx->JobTDate = str_to_int64(row[1]);
1208 * Callback handler build FileSet name prompt list
1210 static int fileset_handler(void *ctx, int num_fields, char **row)
1212 /* row[0] = FileSet (name) */
1214 add_prompt((UAContext *)ctx, row[0]);
1220 * Free names in the list
1222 static void free_name_list(NAME_LIST *name_list)
1224 for (int i=0; i < name_list->num_ids; i++) {
1225 free(name_list->name[i]);
1227 if (name_list->name) {
1228 free(name_list->name);
1229 name_list->name = NULL;
1231 name_list->max_ids = 0;
1232 name_list->num_ids = 0;
1235 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1240 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1244 * Try looking up Storage by name
1247 foreach_res(store, R_STORAGE) {
1248 if (strcmp(Storage, store->name()) == 0) {
1249 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1258 /* Check if an explicit storage resource is given */
1260 int i = find_arg_with_value(ua, "storage");
1262 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1263 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1267 if (store && (store != rx.store)) {
1268 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1271 Dmsg1(200, "Set store=%s\n", rx.store->name());
1276 /* If no storage resource, try to find one from MediaType */
1279 foreach_res(store, R_STORAGE) {
1280 if (strcmp(MediaType, store->media_type) == 0) {
1281 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1283 Dmsg1(200, "Set store=%s\n", rx.store->name());
1284 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1285 Storage, store->name(), MediaType);
1292 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1293 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1296 /* Take command line arg, or ask user if none */
1297 rx.store = get_storage_resource(ua, false /* don't use default */);
1298 Dmsg1(200, "Set store=%s\n", rx.store->name());