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 Bacula® - The Network Backup Solution
18 Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
20 The main author of Bacula is Kern Sibbald, with contributions from
21 many others, a complete list can be found in the file AUTHORS.
22 This program is Free Software; you can redistribute it and/or
23 modify it under the terms of version two of the GNU General Public
24 License as published by the Free Software Foundation plus additions
25 that are listed in the file LICENSE.
27 This program is distributed in the hope that it will be useful, but
28 WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
37 Bacula® is a registered trademark of John Walker.
38 The licensor of Bacula is the Free Software Foundation Europe
39 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
40 Switzerland, email:ftf@fsfeurope.org.
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 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 bsendmsg(ua, _("Forbidden \"where\" specified.\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 bsendmsg(ua, _("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 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
151 if (!(selected_files = write_bsr_file(ua, rx))) {
152 bsendmsg(ua, _("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 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
163 bsendmsg(ua, _("\n%s files selected to be restored.\n\n"),
164 edit_uint64_with_commas(rx.selected_files, ed1));
167 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("Forbidden \"where\" specified.\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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
427 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
429 bsendmsg(ua, _("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 bsendmsg(ua, _("\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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("You have already seleted 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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("No authorization for JobId=%s (Job \"%s\"). 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 bsendmsg(ua, _("No Jobs selected.\n"));
674 if (strchr(rx->JobIds,',')) {
675 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
677 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
685 static int get_date(UAContext *ua, char *date, int date_len)
687 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
728 if (!insert_file_into_findex_list(ua, rx, file, date)) {
729 bsendmsg(ua, _("Error occurred on line %d of %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 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
770 rx->query, db_strerror(ua->db));
773 bsendmsg(ua, _("No database record found for: %s\n"), file);
780 * For a given path lookup the most recent backup in the catalog
781 * to get the JobId and FileIndexes of all files in that directory.
783 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
786 strip_trailing_junk(dir);
787 if (*rx->JobIds == 0) {
788 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
791 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
792 dir, rx->ClientName);
795 /* Find and insert jobid and File Index */
796 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
797 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
798 rx->query, db_strerror(ua->db));
801 bsendmsg(ua, _("No database record found for: %s\n"), dir);
808 * Get the JobId and FileIndexes of all files in the specified table
810 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
812 strip_trailing_junk(table);
813 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
816 /* Find and insert jobid and File Index */
817 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
818 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
819 rx->query, db_strerror(ua->db));
822 bsendmsg(ua, _("No table found: %s\n"), table);
828 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
832 /* Find path without the filename.
833 * I.e. everything after the last / is a "filename".
834 * OK, maybe it is a directory name, but we treat it like
835 * a filename. If we don't find a / then the whole name
836 * must be a path name (e.g. c:).
838 for (p=f=name; *p; p++) {
839 if (IsPathSeparator(*p)) {
840 f = p; /* set pos of last slash */
843 if (IsPathSeparator(*f)) { /* did we find a slash? */
844 f++; /* yes, point to filename */
845 } else { /* no, whole thing must be path name */
849 /* If filename doesn't exist (i.e. root directory), we
850 * simply create a blank name consisting of a single
851 * space. This makes handling zero length filenames
856 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
857 memcpy(rx->fname, f, rx->fnl); /* copy filename */
858 rx->fname[rx->fnl] = 0;
866 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
867 memcpy(rx->path, name, rx->pnl);
868 rx->path[rx->pnl] = 0;
874 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
877 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
880 JobId_t JobId, last_JobId;
885 memset(&tree, 0, sizeof(TREE_CTX));
887 * Build the directory tree containing JobIds user selected
889 tree.root = new_tree(rx->TotalFiles);
894 * For display purposes, the same JobId, with different volumes may
895 * appear more than once, however, we only insert it once.
899 tree.FileEstimate = 0;
900 if (get_next_jobid_from_list(&p, &JobId) > 0) {
901 /* Use first JobId as estimate of the number of files to restore */
902 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
903 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
904 bsendmsg(ua, "%s\n", db_strerror(ua->db));
907 /* Add about 25% more than this job for over estimate */
908 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
909 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
912 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
915 if (JobId == last_JobId) {
916 continue; /* eliminate duplicate JobIds */
919 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
920 edit_int64(JobId, ed1));
923 * Find files for this JobId and insert them in the tree
925 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
926 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
927 bsendmsg(ua, "%s", db_strerror(ua->db));
930 if (tree.FileCount == 0) {
931 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
932 "is not possible.Most likely your retention policy pruned the files\n"));
933 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
937 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
938 if (JobId == last_JobId) {
939 continue; /* eliminate duplicate JobIds */
941 add_findex_all(rx->bsr, JobId);
949 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
950 edit_uint64_with_commas(tree.FileCount, ec1));
953 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
954 edit_uint64_with_commas(tree.FileCount, ec1));
959 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
960 items, edit_uint64_with_commas(tree.FileCount, ec1));
963 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
964 items, edit_uint64_with_commas(tree.FileCount, ec1));
968 if (find_arg(ua, NT_("done")) < 0) {
969 /* Let the user interact in selecting which files to restore */
970 OK = user_select_files_from_tree(&tree);
974 * Walk down through the tree finding all files marked to be
975 * extracted making a bootstrap file.
978 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
979 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
980 if (node->extract || node->extract_dir) {
981 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
982 add_findex(rx->bsr, node->JobId, node->FileIndex);
983 if (node->extract && node->type != TN_NEWDIR) {
984 rx->selected_files++; /* count only saved files */
991 free_tree(tree.root); /* free the directory tree */
997 * This routine is used to get the current backup or a backup
998 * before the specified date.
1000 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1005 char fileset_name[MAX_NAME_LENGTH];
1006 char ed1[50], ed2[50];
1007 char pool_select[MAX_NAME_LENGTH];
1011 /* Create temp tables */
1012 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1013 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1014 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
1015 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1017 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
1018 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1021 * Select Client from the Catalog
1023 memset(&cr, 0, sizeof(cr));
1024 if (!get_client_dbr(ua, &cr)) {
1027 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1032 memset(&fsr, 0, sizeof(fsr));
1033 i = find_arg_with_value(ua, "FileSet");
1035 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1036 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1037 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1038 db_strerror(ua->db));
1042 if (i < 0) { /* fileset not found */
1043 edit_int64(cr.ClientId, ed1);
1044 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1045 start_prompt(ua, _("The defined FileSet resources are:\n"));
1046 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1047 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1049 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1050 fileset_name, sizeof(fileset_name)) < 0) {
1051 bsendmsg(ua, _("No FileSet found for client \"%s\".\n"), cr.Name);
1055 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1056 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1057 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1058 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1059 "Continuing anyway.\n"));
1063 /* If Pool specified, add PoolId specification */
1067 memset(&pr, 0, sizeof(pr));
1068 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1069 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1070 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1071 edit_int64(pr.PoolId, ed1));
1073 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1077 /* Find JobId of last Full backup for this client, fileset */
1078 edit_int64(cr.ClientId, ed1);
1079 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1081 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1082 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1086 /* Find all Volumes used by that JobId */
1087 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1088 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1091 /* Note, this is needed because I don't seem to get the callback
1092 * from the call just above.
1095 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1096 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1098 if (rx->JobTDate == 0) {
1099 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1103 /* Now find most recent Differental Job after Full save, if any */
1104 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1105 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1106 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1107 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1109 /* Now update JobTDate to lock onto Differental, if any */
1111 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1112 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1114 if (rx->JobTDate == 0) {
1115 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1119 /* Now find all Incremental Jobs after Full/dif save */
1120 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1121 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1122 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1123 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1126 /* Get the JobIds from that list */
1128 rx->last_jobid[0] = 0;
1129 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1130 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1133 if (rx->JobIds[0] != 0) {
1134 /* Display a list of Jobs selected for this restore */
1135 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1138 bsendmsg(ua, _("No jobs found.\n"));
1142 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1143 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1149 * Return next JobId from comma separated list
1152 * 1 if next JobId returned
1153 * 0 if no more JobIds are in list
1154 * -1 there is an error
1156 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1162 for (int i=0; i<(int)sizeof(jobid); i++) {
1165 } else if (*q == ',') {
1172 if (jobid[0] == 0) {
1174 } else if (!is_a_number(jobid)) {
1175 return -1; /* error */
1178 *JobId = str_to_int64(jobid);
1182 static int count_handler(void *ctx, int num_fields, char **row)
1184 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1185 rx->JobId = str_to_int64(row[0]);
1191 * Callback handler to get JobId and FileIndex for files
1192 * can insert more than one depending on the caller.
1194 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1196 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1197 rx->JobId = str_to_int64(row[0]);
1198 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1200 rx->selected_files++;
1205 * Callback handler make list of JobIds
1207 static int jobid_handler(void *ctx, int num_fields, char **row)
1209 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1211 if (strcmp(rx->last_jobid, row[0]) == 0) {
1212 return 0; /* duplicate id */
1214 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1215 if (rx->JobIds[0] != 0) {
1216 pm_strcat(rx->JobIds, ",");
1218 pm_strcat(rx->JobIds, row[0]);
1224 * Callback handler to pickup last Full backup JobTDate
1226 static int last_full_handler(void *ctx, int num_fields, char **row)
1228 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1230 rx->JobTDate = str_to_int64(row[1]);
1235 * Callback handler build FileSet name prompt list
1237 static int fileset_handler(void *ctx, int num_fields, char **row)
1239 /* row[0] = FileSet (name) */
1241 add_prompt((UAContext *)ctx, row[0]);
1247 * Free names in the list
1249 static void free_name_list(NAME_LIST *name_list)
1251 for (int i=0; i < name_list->num_ids; i++) {
1252 free(name_list->name[i]);
1254 if (name_list->name) {
1255 free(name_list->name);
1256 name_list->name = NULL;
1258 name_list->max_ids = 0;
1259 name_list->num_ids = 0;
1262 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1267 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1271 * Try looking up Storage by name
1274 foreach_res(store, R_STORAGE) {
1275 if (strcmp(Storage, store->name()) == 0) {
1276 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1285 /* Check if an explicit storage resource is given */
1287 int i = find_arg_with_value(ua, "storage");
1289 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1290 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1294 if (store && (store != rx.store)) {
1295 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1298 Dmsg1(200, "Set store=%s\n", rx.store->name());
1303 /* If no storage resource, try to find one from MediaType */
1306 foreach_res(store, R_STORAGE) {
1307 if (strcmp(MediaType, store->media_type) == 0) {
1308 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1310 Dmsg1(200, "Set store=%s\n", rx.store->name());
1311 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1312 Storage, store->name(), MediaType);
1319 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1320 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1323 /* Take command line arg, or ask user if none */
1324 rx.store = get_storage_resource(ua, false /* don't use default */);
1325 Dmsg1(200, "Set store=%s\n", rx.store->name());