2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Database restore Command
31 * Creates a bootstrap file for restoring files and
32 * starts the restore job.
34 * Tree handling routines split into ua_tree.c July MMIII.
35 * BSR (bootstrap record) handling routines split into
38 * Kern Sibbald, July MMII
48 /* Imported functions */
49 extern void print_bsr(UAContext *ua, RBSR *bsr);
53 /* Forward referenced functions */
54 static int last_full_handler(void *ctx, int num_fields, char **row);
55 static int jobid_handler(void *ctx, int num_fields, char **row);
56 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
57 static int fileset_handler(void *ctx, int num_fields, char **row);
58 static void free_name_list(NAME_LIST *name_list);
59 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
60 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
61 static void free_rx(RESTORE_CTX *rx);
62 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
63 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
64 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
66 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
68 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
69 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
70 static int get_date(UAContext *ua, char *date, int date_len);
71 static int restore_count_handler(void *ctx, int num_fields, char **row);
72 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
78 int restore_cmd(UAContext *ua, const char *cmd)
80 RESTORE_CTX rx; /* restore context */
84 char *escaped_bsr_name = NULL;
85 char *escaped_where_name = NULL;
87 memset(&rx, 0, sizeof(rx));
88 rx.path = get_pool_memory(PM_FNAME);
89 rx.fname = get_pool_memory(PM_FNAME);
90 rx.JobIds = get_pool_memory(PM_FNAME);
91 rx.query = get_pool_memory(PM_FNAME);
94 i = find_arg_with_value(ua, "where");
96 rx.where = ua->argv[i];
97 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
98 ua->error_msg(_("\"where\" specification not authorized.\n"));
103 if (!open_client_db(ua)) {
107 /* Ensure there is at least one Restore Job */
109 foreach_res(job, R_JOB) {
110 if (job->JobType == JT_RESTORE) {
111 if (!rx.restore_job) {
112 rx.restore_job = job;
118 if (!rx.restore_jobs) {
120 "No Restore Job Resource found in bacula-dir.conf.\n"
121 "You must create at least one before running this command.\n"));
126 * Request user to select JobIds or files by various different methods
127 * last 20 jobs, where File saved, most recent backup, ...
128 * In the end, a list of files are pumped into
131 switch (user_select_jobids_or_files(ua, &rx)) {
134 case 1: /* selected by jobid */
135 if (!build_directory_tree(ua, &rx)) {
136 ua->send_msg(_("Restore not done.\n"));
140 case 2: /* selected by filename, no tree needed */
145 uint32_t selected_files;
147 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
148 ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
151 if (!(selected_files = write_bsr_file(ua, rx))) {
152 ua->warning_msg(_("No files selected to be restored.\n"));
155 /* If no count of files, use bsr generated value (often wrong) */
156 if (rx.selected_files == 0) {
157 rx.selected_files = selected_files;
159 if (rx.selected_files==1) {
160 ua->info_msg(_("\n1 file selected to be restored.\n\n"));
163 ua->info_msg(_("\n%s files selected to be restored.\n\n"),
164 edit_uint64_with_commas(rx.selected_files, ed1));
167 ua->warning_msg(_("No files selected to be restored.\n"));
171 if (rx.restore_jobs == 1) {
172 job = rx.restore_job;
174 job = select_restore_job_resource(ua);
180 get_client_name(ua, &rx);
181 if (!rx.ClientName) {
182 ua->error_msg(_("No Client resource found!\n"));
186 escaped_bsr_name = escape_filename(jcr->RestoreBootstrap);
187 escaped_where_name = escape_filename(rx.where);
189 /* Build run command */
191 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
192 ua->error_msg(_("\"where\" specification not authorized.\n"));
197 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
198 " where=\"%s\" files=%d catalog=\"%s\"",
199 job->name(), rx.ClientName, rx.store?rx.store->name():"",
200 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
201 escaped_where_name ? escaped_where_name : rx.where,
202 rx.selected_files, ua->catalog->name());
205 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
206 " files=%d catalog=\"%s\"",
207 job->name(), rx.ClientName, rx.store?rx.store->name():"",
208 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
209 rx.selected_files, ua->catalog->name());
212 if (escaped_bsr_name != NULL) {
213 bfree(escaped_bsr_name);
216 if (escaped_where_name != NULL) {
217 bfree(escaped_where_name);
220 if (find_arg(ua, NT_("yes")) > 0) {
221 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
223 Dmsg1(200, "Submitting: %s\n", ua->cmd);
225 run_cmd(ua, ua->cmd);
230 if (escaped_bsr_name != NULL) {
231 bfree(escaped_bsr_name);
234 if (escaped_where_name != NULL) {
235 bfree(escaped_where_name);
243 static void free_rx(RESTORE_CTX *rx)
248 free_pool_memory(rx->JobIds);
252 free_pool_memory(rx->fname);
256 free_pool_memory(rx->path);
260 free_pool_memory(rx->query);
263 free_name_list(&rx->name_list);
266 static bool has_value(UAContext *ua, int i)
269 ua->error_msg(_("Missing value for keyword: %s\n"), ua->argk[i]);
275 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
277 /* If no client name specified yet, get it now */
278 if (!rx->ClientName[0]) {
280 /* try command line argument */
281 int i = find_arg_with_value(ua, NT_("client"));
283 if (!has_value(ua, i)) {
286 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
289 memset(&cr, 0, sizeof(cr));
290 if (!get_client_dbr(ua, &cr)) {
293 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
300 * The first step in the restore process is for the user to
301 * select a list of JobIds from which he will subsequently
302 * select which files are to be restored.
304 * Returns: 2 if filename list made
305 * 1 if jobid list made
308 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
311 char date[MAX_TIME_LENGTH];
312 bool have_date = false;
314 JOB_DBR jr = { (JobId_t)-1 };
317 const char *list[] = {
318 _("List last 20 Jobs run"),
319 _("List Jobs where a given File is saved"),
320 _("Enter list of comma separated JobIds to select"),
321 _("Enter SQL list command"),
322 _("Select the most recent backup for a client"),
323 _("Select backup for a client before a specified time"),
324 _("Enter a list of files to restore"),
325 _("Enter a list of files to restore before a specified time"),
326 _("Find the JobIds of the most recent backup for a client"),
327 _("Find the JobIds for a backup for a client before a specified time"),
328 _("Enter a list of directories to restore for found JobIds"),
333 /* These keywords are handled in a for loop */
343 /* The keyword below are handled by individual arg lookups */
349 "bootstrap", /* 13 */
356 for (i=1; i<ua->argc; i++) { /* loop through arguments */
357 bool found_kw = false;
358 for (j=0; kw[j]; j++) { /* loop through keywords */
359 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
365 ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]);
368 /* Found keyword in kw[] list, process it */
371 if (!has_value(ua, i)) {
374 if (*rx->JobIds != 0) {
375 pm_strcat(rx->JobIds, ",");
377 pm_strcat(rx->JobIds, ua->argv[i]);
380 case 1: /* current */
381 bstrutime(date, sizeof(date), time(NULL));
385 if (!has_value(ua, i)) {
388 if (str_to_utime(ua->argv[i]) == 0) {
389 ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
392 bstrncpy(date, ua->argv[i], sizeof(date));
397 if (!has_value(ua, i)) {
401 bstrutime(date, sizeof(date), time(NULL));
403 if (!get_client_name(ua, rx)) {
406 pm_strcpy(ua->cmd, ua->argv[i]);
407 insert_one_file_or_dir(ua, rx, date, j==4);
411 bstrutime(date, sizeof(date), time(NULL));
413 if (!select_backups_before_date(ua, rx, date)) {
418 case 6: /* pool specified */
419 if (!has_value(ua, i)) {
422 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
424 ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
427 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
429 ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
433 case 7: /* all specified */
437 * All keywords 7 or greater are ignored or handled by a select prompt
445 ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
446 "to be restored. You will be presented several methods\n"
447 "of specifying the JobIds. Then you will be allowed to\n"
448 "select which files from those JobIds are to be restored.\n\n"));
451 /* If choice not already made above, prompt */
457 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
458 for (int i=0; list[i]; i++) {
459 add_prompt(ua, list[i]);
462 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
463 case -1: /* error or cancel */
465 case 0: /* list last 20 Jobs run */
466 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
467 ua->error_msg(_("SQL query not authorized.\n"));
470 gui_save = ua->jcr->gui;
472 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
473 ua->jcr->gui = gui_save;
476 case 1: /* list where a file is saved */
477 if (!get_client_name(ua, rx)) {
480 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
483 len = strlen(ua->cmd);
484 fname = (char *)malloc(len * 2 + 1);
485 db_escape_string(fname, ua->cmd, len);
486 Mmsg(rx->query, uar_file, rx->ClientName, fname);
488 gui_save = ua->jcr->gui;
490 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
491 ua->jcr->gui = gui_save;
494 case 2: /* enter a list of JobIds */
495 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
498 pm_strcpy(rx->JobIds, ua->cmd);
500 case 3: /* Enter an SQL list command */
501 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
502 ua->error_msg(_("SQL query not authorized.\n"));
505 if (!get_cmd(ua, _("Enter SQL list command: "))) {
508 gui_save = ua->jcr->gui;
510 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
511 ua->jcr->gui = gui_save;
514 case 4: /* Select the most recent backups */
515 bstrutime(date, sizeof(date), time(NULL));
516 if (!select_backups_before_date(ua, rx, date)) {
520 case 5: /* select backup at specified time */
521 if (!get_date(ua, date, sizeof(date))) {
524 if (!select_backups_before_date(ua, rx, date)) {
528 case 6: /* Enter files */
529 bstrutime(date, sizeof(date), time(NULL));
530 if (!get_client_name(ua, rx)) {
533 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
534 "containing a list of file names with paths, and terminate\n"
535 "them with a blank line.\n"));
537 if (!get_cmd(ua, _("Enter full filename: "))) {
540 len = strlen(ua->cmd);
544 insert_one_file_or_dir(ua, rx, date, false);
547 case 7: /* enter files backed up before specified time */
548 if (!get_date(ua, date, sizeof(date))) {
551 if (!get_client_name(ua, rx)) {
554 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
555 "containing a list of file names with paths, and terminate\n"
556 "them with a blank line.\n"));
558 if (!get_cmd(ua, _("Enter full filename: "))) {
561 len = strlen(ua->cmd);
565 insert_one_file_or_dir(ua, rx, date, false);
569 case 8: /* Find JobIds for current backup */
570 bstrutime(date, sizeof(date), time(NULL));
571 if (!select_backups_before_date(ua, rx, date)) {
577 case 9: /* Find JobIds for give date */
578 if (!get_date(ua, date, sizeof(date))) {
581 if (!select_backups_before_date(ua, rx, date)) {
587 case 10: /* Enter directories */
588 if (*rx->JobIds != 0) {
589 ua->send_msg(_("You have already selected the following JobIds: %s\n"),
591 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
592 if (*rx->JobIds != 0 && *ua->cmd) {
593 pm_strcat(rx->JobIds, ",");
595 pm_strcat(rx->JobIds, ua->cmd);
597 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
598 return 0; /* nothing entered, return */
600 bstrutime(date, sizeof(date), time(NULL));
601 if (!get_client_name(ua, rx)) {
604 ua->send_msg(_("Enter full directory names or start the name\n"
605 "with a < to indicate it is a filename containing a list\n"
606 "of directories and terminate them with a blank line.\n"));
608 if (!get_cmd(ua, _("Enter directory name: "))) {
611 len = strlen(ua->cmd);
615 /* Add trailing slash to end of directory names */
616 if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
617 strcat(ua->cmd, "/");
619 insert_one_file_or_dir(ua, rx, date, true);
623 case 11: /* Cancel or quit */
628 POOLMEM *JobIds = get_pool_memory(PM_FNAME);
632 * Find total number of files to be restored, and filter the JobId
633 * list to contain only ones permitted by the ACL conditions.
635 for (p=rx->JobIds; ; ) {
637 int stat = get_next_jobid_from_list(&p, &JobId);
639 ua->error_msg(_("Invalid JobId in list.\n"));
640 free_pool_memory(JobIds);
646 if (jr.JobId == JobId) {
647 continue; /* duplicate of last JobId */
649 memset(&jr, 0, sizeof(JOB_DBR));
651 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
652 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
653 edit_int64(JobId, ed1), db_strerror(ua->db));
654 free_pool_memory(JobIds);
657 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
658 ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"),
659 edit_int64(JobId, ed1), jr.Name);
663 pm_strcat(JobIds, ",");
665 pm_strcat(JobIds, edit_int64(JobId, ed1));
666 rx->TotalFiles += jr.JobFiles;
668 free_pool_memory(rx->JobIds);
669 rx->JobIds = JobIds; /* Set ACL filtered list */
670 if (*rx->JobIds == 0) {
671 ua->warning_msg(_("No Jobs selected.\n"));
674 if (strchr(rx->JobIds,',')) {
675 ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds);
677 ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds);
685 static int get_date(UAContext *ua, char *date, int date_len)
687 ua->send_msg(_("The restored files will the most current backup\n"
688 "BEFORE the date you specify below.\n\n"));
690 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
693 if (str_to_utime(ua->cmd) != 0) {
696 ua->error_msg(_("Improper date format.\n"));
698 bstrncpy(date, ua->cmd, date_len);
703 * Insert a single file, or read a list of files from a file
705 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
715 if ((ffd = fopen(p, "rb")) == NULL) {
717 ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
721 while (fgets(file, sizeof(file), ffd)) {
724 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
725 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
728 if (!insert_file_into_findex_list(ua, rx, file, date)) {
729 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
737 insert_table_into_findex_list(ua, rx, p);
741 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
743 insert_file_into_findex_list(ua, rx, ua->cmd, date);
750 * For a given file (path+filename), split into path and file, then
751 * lookup the most recent backup in the catalog to get the JobId
752 * and FileIndex, then insert them into the findex list.
754 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
757 strip_trailing_newline(file);
758 split_path_and_filename(rx, file);
759 if (*rx->JobIds == 0) {
760 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
763 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
764 rx->path, rx->fname, rx->ClientName);
767 /* Find and insert jobid and File Index */
768 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
769 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
770 rx->query, db_strerror(ua->db));
773 ua->error_msg(_("No database record found for: %s\n"), file);
774 // ua->error_msg("Query=%s\n", rx->query);
781 * For a given path lookup the most recent backup in the catalog
782 * to get the JobId and FileIndexes of all files in that directory.
784 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
787 strip_trailing_junk(dir);
788 if (*rx->JobIds == 0) {
789 ua->error_msg(_("No JobId specified cannot continue.\n"));
792 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
793 dir, rx->ClientName);
796 /* Find and insert jobid and File Index */
797 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
798 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
799 rx->query, db_strerror(ua->db));
802 ua->error_msg(_("No database record found for: %s\n"), dir);
809 * Get the JobId and FileIndexes of all files in the specified table
811 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
813 strip_trailing_junk(table);
814 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
817 /* Find and insert jobid and File Index */
818 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
819 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
820 rx->query, db_strerror(ua->db));
823 ua->error_msg(_("No table found: %s\n"), table);
829 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
833 /* Find path without the filename.
834 * I.e. everything after the last / is a "filename".
835 * OK, maybe it is a directory name, but we treat it like
836 * a filename. If we don't find a / then the whole name
837 * must be a path name (e.g. c:).
839 for (p=f=name; *p; p++) {
840 if (IsPathSeparator(*p)) {
841 f = p; /* set pos of last slash */
844 if (IsPathSeparator(*f)) { /* did we find a slash? */
845 f++; /* yes, point to filename */
846 } else { /* no, whole thing must be path name */
850 /* If filename doesn't exist (i.e. root directory), we
851 * simply create a blank name consisting of a single
852 * space. This makes handling zero length filenames
857 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
858 memcpy(rx->fname, f, rx->fnl); /* copy filename */
859 rx->fname[rx->fnl] = 0;
867 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
868 memcpy(rx->path, name, rx->pnl);
869 rx->path[rx->pnl] = 0;
875 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
878 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
881 JobId_t JobId, last_JobId;
886 memset(&tree, 0, sizeof(TREE_CTX));
888 * Build the directory tree containing JobIds user selected
890 tree.root = new_tree(rx->TotalFiles);
895 * For display purposes, the same JobId, with different volumes may
896 * appear more than once, however, we only insert it once.
900 tree.FileEstimate = 0;
901 if (get_next_jobid_from_list(&p, &JobId) > 0) {
902 /* Use first JobId as estimate of the number of files to restore */
903 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
904 if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
905 ua->error_msg("%s\n", db_strerror(ua->db));
908 /* Add about 25% more than this job for over estimate */
909 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
910 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
913 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
916 if (JobId == last_JobId) {
917 continue; /* eliminate duplicate JobIds */
920 ua->info_msg(_("\nBuilding directory tree for JobId %s ... "),
921 edit_int64(JobId, ed1));
924 * Find files for this JobId and insert them in the tree
926 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
927 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
928 ua->error_msg("%s", db_strerror(ua->db));
931 if (tree.FileCount == 0) {
932 ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
933 "is not possible.Most likely your retention policy pruned the files\n"));
934 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
938 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
939 if (JobId == last_JobId) {
940 continue; /* eliminate duplicate JobIds */
942 add_findex_all(rx->bsr, JobId);
950 ua->info_msg(_("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
951 edit_uint64_with_commas(tree.FileCount, ec1));
954 ua->info_msg(_("\n1 Job, %s files inserted into the tree.\n"),
955 edit_uint64_with_commas(tree.FileCount, ec1));
960 ua->info_msg(_("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
961 items, edit_uint64_with_commas(tree.FileCount, ec1));
964 ua->info_msg(_("\n%d Jobs, %s files inserted into the tree.\n"),
965 items, edit_uint64_with_commas(tree.FileCount, ec1));
969 if (find_arg(ua, NT_("done")) < 0) {
970 /* Let the user interact in selecting which files to restore */
971 OK = user_select_files_from_tree(&tree);
975 * Walk down through the tree finding all files marked to be
976 * extracted making a bootstrap file.
979 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
980 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
981 if (node->extract || node->extract_dir) {
982 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
983 add_findex(rx->bsr, node->JobId, node->FileIndex);
984 if (node->extract && node->type != TN_NEWDIR) {
985 rx->selected_files++; /* count only saved files */
992 free_tree(tree.root); /* free the directory tree */
998 * This routine is used to get the current backup or a backup
999 * before the specified date.
1001 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1006 char fileset_name[MAX_NAME_LENGTH];
1007 char ed1[50], ed2[50];
1008 char pool_select[MAX_NAME_LENGTH];
1012 /* Create temp tables */
1013 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1014 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1015 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
1016 ua->error_msg("%s\n", db_strerror(ua->db));
1018 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
1019 ua->error_msg("%s\n", db_strerror(ua->db));
1022 * Select Client from the Catalog
1024 memset(&cr, 0, sizeof(cr));
1025 if (!get_client_dbr(ua, &cr)) {
1028 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1033 memset(&fsr, 0, sizeof(fsr));
1034 i = find_arg_with_value(ua, "FileSet");
1036 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1037 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1038 ua->error_msg(_("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1039 db_strerror(ua->db));
1043 if (i < 0) { /* fileset not found */
1044 edit_int64(cr.ClientId, ed1);
1045 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1046 start_prompt(ua, _("The defined FileSet resources are:\n"));
1047 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1048 ua->error_msg("%s\n", db_strerror(ua->db));
1050 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1051 fileset_name, sizeof(fileset_name)) < 0) {
1052 ua->error_msg(_("No FileSet found for client \"%s\".\n"), cr.Name);
1056 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1057 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1058 ua->warning_msg(_("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1059 ua->send_msg(_("This probably means you modified the FileSet.\n"
1060 "Continuing anyway.\n"));
1064 /* If Pool specified, add PoolId specification */
1068 memset(&pr, 0, sizeof(pr));
1069 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1070 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1071 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1072 edit_int64(pr.PoolId, ed1));
1074 ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1078 /* Find JobId of last Full backup for this client, fileset */
1079 edit_int64(cr.ClientId, ed1);
1080 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1082 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1083 ua->error_msg("%s\n", db_strerror(ua->db));
1087 /* Find all Volumes used by that JobId */
1088 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1089 ua->error_msg("%s\n", db_strerror(ua->db));
1092 /* Note, this is needed because I don't seem to get the callback
1093 * from the call just above.
1096 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1097 ua->warning_msg("%s\n", db_strerror(ua->db));
1099 if (rx->JobTDate == 0) {
1100 ua->error_msg(_("No Full backup before %s found.\n"), date);
1104 /* Now find most recent Differental Job after Full save, if any */
1105 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1106 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1107 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1108 ua->warning_msg("%s\n", db_strerror(ua->db));
1110 /* Now update JobTDate to lock onto Differental, if any */
1112 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1113 ua->warning_msg("%s\n", db_strerror(ua->db));
1115 if (rx->JobTDate == 0) {
1116 ua->error_msg(_("No Full backup before %s found.\n"), date);
1120 /* Now find all Incremental Jobs after Full/dif save */
1121 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1122 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1123 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1124 ua->warning_msg("%s\n", db_strerror(ua->db));
1127 /* Get the JobIds from that list */
1129 rx->last_jobid[0] = 0;
1130 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1131 ua->warning_msg("%s\n", db_strerror(ua->db));
1134 if (rx->JobIds[0] != 0) {
1135 /* Display a list of Jobs selected for this restore */
1136 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1139 ua->warning_msg(_("No jobs found.\n"));
1143 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1144 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1150 * Return next JobId from comma separated list
1153 * 1 if next JobId returned
1154 * 0 if no more JobIds are in list
1155 * -1 there is an error
1157 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1163 for (int i=0; i<(int)sizeof(jobid); i++) {
1166 } else if (*q == ',') {
1173 if (jobid[0] == 0) {
1175 } else if (!is_a_number(jobid)) {
1176 return -1; /* error */
1179 *JobId = str_to_int64(jobid);
1183 static int restore_count_handler(void *ctx, int num_fields, char **row)
1185 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1186 rx->JobId = str_to_int64(row[0]);
1192 * Callback handler to get JobId and FileIndex for files
1193 * can insert more than one depending on the caller.
1195 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1197 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1198 rx->JobId = str_to_int64(row[0]);
1199 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1201 rx->selected_files++;
1206 * Callback handler make list of JobIds
1208 static int jobid_handler(void *ctx, int num_fields, char **row)
1210 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1212 if (strcmp(rx->last_jobid, row[0]) == 0) {
1213 return 0; /* duplicate id */
1215 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1216 if (rx->JobIds[0] != 0) {
1217 pm_strcat(rx->JobIds, ",");
1219 pm_strcat(rx->JobIds, row[0]);
1225 * Callback handler to pickup last Full backup JobTDate
1227 static int last_full_handler(void *ctx, int num_fields, char **row)
1229 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1231 rx->JobTDate = str_to_int64(row[1]);
1236 * Callback handler build FileSet name prompt list
1238 static int fileset_handler(void *ctx, int num_fields, char **row)
1240 /* row[0] = FileSet (name) */
1242 add_prompt((UAContext *)ctx, row[0]);
1248 * Free names in the list
1250 static void free_name_list(NAME_LIST *name_list)
1252 for (int i=0; i < name_list->num_ids; i++) {
1253 free(name_list->name[i]);
1255 if (name_list->name) {
1256 free(name_list->name);
1257 name_list->name = NULL;
1259 name_list->max_ids = 0;
1260 name_list->num_ids = 0;
1263 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1268 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1272 * Try looking up Storage by name
1275 foreach_res(store, R_STORAGE) {
1276 if (strcmp(Storage, store->name()) == 0) {
1277 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1286 /* Check if an explicit storage resource is given */
1288 int i = find_arg_with_value(ua, "storage");
1290 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1291 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1295 if (store && (store != rx.store)) {
1296 ua->info_msg(_("Warning default storage overridden by \"%s\" on command line.\n"),
1299 Dmsg1(200, "Set store=%s\n", rx.store->name());
1304 /* If no storage resource, try to find one from MediaType */
1307 foreach_res(store, R_STORAGE) {
1308 if (strcmp(MediaType, store->media_type) == 0) {
1309 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1311 Dmsg1(200, "Set store=%s\n", rx.store->name());
1312 ua->warning_msg(_("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1313 Storage, store->name(), MediaType);
1320 ua->warning_msg(_("\nUnable to find Storage resource for\n"
1321 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1324 /* Take command line arg, or ask user if none */
1325 rx.store = get_storage_resource(ua, false /* don't use default */);
1326 Dmsg1(200, "Set store=%s\n", rx.store->name());