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-2005 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);
38 /* Imported variables */
39 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
40 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
41 extern char *uar_create_temp1, *uar_last_full, *uar_full;
42 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
43 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
44 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
45 extern char *uar_count_files, *uar_jobids_fileindex;
46 extern char *uar_jobid_fileindex_from_dir;
50 char **name; /* list of names */
51 int num_ids; /* ids stored */
52 int max_ids; /* size of array */
53 int num_del; /* number deleted */
54 int tot_ids; /* total to process */
58 /* Main structure for obtaining JobIds or Files to be restored */
63 char ClientName[MAX_NAME_LENGTH];
65 POOLMEM *JobIds; /* User entered string of JobIds */
70 uint32_t selected_files;
73 POOLMEM *fname; /* filename only */
74 POOLMEM *path; /* path only */
76 int fnl; /* filename length */
77 int pnl; /* path length */
79 bool all; /* mark all as default */
84 #define MAX_ID_LIST_LEN 1000000
87 /* Forward referenced functions */
88 static int last_full_handler(void *ctx, int num_fields, char **row);
89 static int jobid_handler(void *ctx, int num_fields, char **row);
90 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
91 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
92 static int fileset_handler(void *ctx, int num_fields, char **row);
93 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
94 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
95 static void free_name_list(NAME_LIST *name_list);
96 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
97 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
98 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
99 static void free_rx(RESTORE_CTX *rx);
100 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
101 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
102 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
104 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
106 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
107 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
108 static int get_date(UAContext *ua, char *date, int date_len);
109 static int count_handler(void *ctx, int num_fields, char **row);
115 int restore_cmd(UAContext *ua, const char *cmd)
117 RESTORE_CTX rx; /* restore context */
122 memset(&rx, 0, sizeof(rx));
123 rx.path = get_pool_memory(PM_FNAME);
124 rx.fname = get_pool_memory(PM_FNAME);
125 rx.JobIds = get_pool_memory(PM_FNAME);
126 rx.query = get_pool_memory(PM_FNAME);
129 i = find_arg_with_value(ua, "where");
131 rx.where = ua->argv[i];
138 /* Ensure there is at least one Restore Job */
140 foreach_res(job, R_JOB) {
141 if (job->JobType == JT_RESTORE) {
142 if (!rx.restore_job) {
143 rx.restore_job = job;
149 if (!rx.restore_jobs) {
151 "No Restore Job Resource found in bacula-dir.conf.\n"
152 "You must create at least one before running this command.\n"));
157 * Request user to select JobIds or files by various different methods
158 * last 20 jobs, where File saved, most recent backup, ...
159 * In the end, a list of files are pumped into
162 switch (user_select_jobids_or_files(ua, &rx)) {
165 case 1: /* selected by jobid */
166 if (!build_directory_tree(ua, &rx)) {
167 bsendmsg(ua, _("Restore not done.\n"));
171 case 2: /* selected by filename, no tree needed */
176 uint32_t selected_files;
177 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
178 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
181 if (!(selected_files = write_bsr_file(ua, rx.bsr))) {
182 bsendmsg(ua, _("No files selected to be restored.\n"));
185 /* If no count of files, use bsr generated value (often wrong) */
186 if (rx.selected_files == 0) {
187 rx.selected_files = selected_files;
189 if (rx.selected_files==1) {
190 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
193 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
196 bsendmsg(ua, _("No files selected to be restored.\n"));
200 if (rx.restore_jobs == 1) {
201 job = rx.restore_job;
203 job = select_restore_job_resource(ua);
209 get_client_name(ua, &rx);
210 if (!rx.ClientName) {
211 bsendmsg(ua, _("No Restore Job resource found!\n"));
215 /* Build run command */
216 fname = get_pool_memory(PM_MESSAGE);
217 make_unique_restore_filename(ua, &fname);
220 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
221 " where=\"%s\" files=%d catalog=\"%s\"",
222 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
223 fname, rx.where, rx.selected_files, ua->catalog->hdr.name);
226 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
227 " files=%d catalog=\"%s\"",
228 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
229 fname, rx.selected_files, ua->catalog->hdr.name);
231 free_pool_memory(fname);
232 if (find_arg(ua, N_("yes")) > 0) {
233 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
235 Dmsg1(400, "Submitting: %s\n", ua->cmd);
237 run_cmd(ua, ua->cmd);
247 static void free_rx(RESTORE_CTX *rx)
252 free_pool_memory(rx->JobIds);
256 free_pool_memory(rx->fname);
260 free_pool_memory(rx->path);
264 free_pool_memory(rx->query);
267 free_name_list(&rx->name_list);
270 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
272 /* If no client name specified yet, get it now */
273 if (!rx->ClientName[0]) {
275 /* try command line argument */
276 int i = find_arg_with_value(ua, N_("client"));
278 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
281 memset(&cr, 0, sizeof(cr));
282 if (!get_client_dbr(ua, &cr)) {
285 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
291 * The first step in the restore process is for the user to
292 * select a list of JobIds from which he will subsequently
293 * select which files are to be restored.
295 * Returns: 2 if filename list made
296 * 1 if jobid list made
299 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
302 char date[MAX_TIME_LENGTH];
303 bool have_date = false;
308 const char *list[] = {
309 _("List last 20 Jobs run"),
310 _("List Jobs where a given File is saved"),
311 _("Enter list of comma separated JobIds to select"),
312 _("Enter SQL list command"),
313 _("Select the most recent backup for a client"),
314 _("Select backup for a client before a specified time"),
315 _("Enter a list of files to restore"),
316 _("Enter a list of files to restore before a specified time"),
317 _("Find the JobIds of the most recent backup for a client"),
318 _("Find the JobIds for a backup for a client before a specified time"),
319 _("Enter a list of directories to restore for found JobIds"),
324 /* These keywords are handled in a for loop */
334 /* The keyword below are handled by individual arg lookups */
340 "bootstrap", /* 13 */
347 for (i=1; i<ua->argc; i++) { /* loop through arguments */
348 bool found_kw = false;
349 for (j=0; kw[j]; j++) { /* loop through keywords */
350 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
356 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
359 /* Found keyword in kw[] list, process it */
362 if (*rx->JobIds != 0) {
363 pm_strcat(rx->JobIds, ",");
365 pm_strcat(rx->JobIds, ua->argv[i]);
368 case 1: /* current */
369 bstrutime(date, sizeof(date), time(NULL));
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));
383 bstrutime(date, sizeof(date), time(NULL));
385 if (!get_client_name(ua, rx)) {
388 pm_strcpy(ua->cmd, ua->argv[i]);
389 insert_one_file_or_dir(ua, rx, date, j==4);
390 if (rx->name_list.num_ids) {
391 /* Check MediaType and select storage that corresponds */
392 get_storage_from_mediatype(ua, &rx->name_list, rx);
398 bstrutime(date, sizeof(date), time(NULL));
400 if (!select_backups_before_date(ua, rx, date)) {
405 case 6: /* pool specified */
406 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
408 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
411 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
413 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
417 case 7: /* all specified */
421 * All keywords 7 or greater are ignored or handled by a select prompt
427 if (rx->name_list.num_ids) {
428 return 2; /* filename list made */
432 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
433 "to be restored. You will be presented several methods\n"
434 "of specifying the JobIds. Then you will be allowed to\n"
435 "select which files from those JobIds are to be restored.\n\n"));
438 /* If choice not already made above, prompt */
444 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
445 for (int i=0; list[i]; i++) {
446 add_prompt(ua, list[i]);
449 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
452 case 0: /* list last 20 Jobs run */
453 gui_save = ua->jcr->gui;
455 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
456 ua->jcr->gui = gui_save;
459 case 1: /* list where a file is saved */
460 if (!get_client_name(ua, rx)) {
463 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
466 len = strlen(ua->cmd);
467 fname = (char *)malloc(len * 2 + 1);
468 db_escape_string(fname, ua->cmd, len);
469 Mmsg(rx->query, uar_file, rx->ClientName, fname);
471 gui_save = ua->jcr->gui;
473 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
474 ua->jcr->gui = gui_save;
477 case 2: /* enter a list of JobIds */
478 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
481 pm_strcpy(rx->JobIds, ua->cmd);
483 case 3: /* Enter an SQL list command */
484 if (!get_cmd(ua, _("Enter SQL list command: "))) {
487 gui_save = ua->jcr->gui;
489 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
490 ua->jcr->gui = gui_save;
493 case 4: /* Select the most recent backups */
494 bstrutime(date, sizeof(date), time(NULL));
495 if (!select_backups_before_date(ua, rx, date)) {
499 case 5: /* select backup at specified time */
500 if (!get_date(ua, date, sizeof(date))) {
503 if (!select_backups_before_date(ua, rx, date)) {
507 case 6: /* Enter files */
508 bstrutime(date, sizeof(date), time(NULL));
509 if (!get_client_name(ua, rx)) {
512 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
513 "containg a list of file names with paths, and terminate\n"
514 "them with a blank line.\n"));
516 if (!get_cmd(ua, _("Enter full filename: "))) {
519 len = strlen(ua->cmd);
523 insert_one_file_or_dir(ua, rx, date, false);
525 /* Check MediaType and select storage that corresponds */
526 if (rx->name_list.num_ids) {
527 get_storage_from_mediatype(ua, &rx->name_list, rx);
530 case 7: /* enter files backed up before specified time */
531 if (!get_date(ua, date, sizeof(date))) {
534 if (!get_client_name(ua, rx)) {
537 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
538 "containg a list of file names with paths, and terminate\n"
539 "them with a blank line.\n"));
541 if (!get_cmd(ua, _("Enter full filename: "))) {
544 len = strlen(ua->cmd);
548 insert_one_file_or_dir(ua, rx, date, false);
550 /* Check MediaType and select storage that corresponds */
551 if (rx->name_list.num_ids) {
552 get_storage_from_mediatype(ua, &rx->name_list, rx);
556 case 8: /* Find JobIds for current backup */
557 bstrutime(date, sizeof(date), time(NULL));
558 if (!select_backups_before_date(ua, rx, date)) {
564 case 9: /* Find JobIds for give date */
565 if (!get_date(ua, date, sizeof(date))) {
568 if (!select_backups_before_date(ua, rx, date)) {
574 case 10: /* Enter directories */
575 if (*rx->JobIds != 0) {
576 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
578 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
579 if (*rx->JobIds != 0 && *ua->cmd) {
580 pm_strcat(rx->JobIds, ",");
582 pm_strcat(rx->JobIds, ua->cmd);
584 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
585 return 0; /* nothing entered, return */
587 bstrutime(date, sizeof(date), time(NULL));
588 if (!get_client_name(ua, rx)) {
591 bsendmsg(ua, _("Enter full directory names or start the name\n"
592 "with a < to indicate it is a filename containg a list\n"
593 "of directories and terminate them with a blank line.\n"));
595 if (!get_cmd(ua, _("Enter directory name: "))) {
598 len = strlen(ua->cmd);
602 /* Add trailing slash to end of directory names */
603 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
604 strcat(ua->cmd, "/");
606 insert_one_file_or_dir(ua, rx, date, true);
608 /* Check MediaType and select storage that corresponds */
609 if (rx->name_list.num_ids) {
610 get_storage_from_mediatype(ua, &rx->name_list, rx);
614 case 11: /* Cancel or quit */
619 if (*rx->JobIds == 0) {
620 bsendmsg(ua, _("No Jobs selected.\n"));
623 if (strchr(rx->JobIds,',')) {
624 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
627 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
630 memset(&jr, 0, sizeof(JOB_DBR));
633 for (p=rx->JobIds; ; ) {
634 int stat = get_next_jobid_from_list(&p, &JobId);
636 bsendmsg(ua, _("Invalid JobId in list.\n"));
642 if (jr.JobId == JobId) {
643 continue; /* duplicate of last JobId */
646 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
648 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
649 edit_int64(JobId, ed1), db_strerror(ua->db));
652 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
653 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
657 rx->TotalFiles += jr.JobFiles;
665 static int get_date(UAContext *ua, char *date, int date_len)
667 bsendmsg(ua, _("The restored files will the most current backup\n"
668 "BEFORE the date you specify below.\n\n"));
670 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
673 if (str_to_utime(ua->cmd) != 0) {
676 bsendmsg(ua, _("Improper date format.\n"));
678 bstrncpy(date, ua->cmd, date_len);
683 * Insert a single file, or read a list of files from a file
685 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
695 if ((ffd = fopen(p, "r")) == NULL) {
697 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
701 while (fgets(file, sizeof(file), ffd)) {
704 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
705 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
708 if (!insert_file_into_findex_list(ua, rx, file, date)) {
709 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
717 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
719 insert_file_into_findex_list(ua, rx, ua->cmd, date);
726 * For a given file (path+filename), split into path and file, then
727 * lookup the most recent backup in the catalog to get the JobId
728 * and FileIndex, then insert them into the findex list.
730 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
735 strip_trailing_junk(file);
736 split_path_and_filename(rx, file);
737 if (*rx->JobIds == 0) {
738 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
741 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
742 rx->path, rx->fname, rx->ClientName);
745 /* Find and insert jobid and File Index */
746 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
747 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
748 rx->query, db_strerror(ua->db));
751 bsendmsg(ua, _("No database record found for: %s\n"), file);
755 * Find the MediaTypes for this JobId and add to the name_list
757 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
758 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
759 bsendmsg(ua, "%s", db_strerror(ua->db));
766 * For a given path lookup the most recent backup in the catalog
767 * to get the JobId and FileIndexes of all files in that directory.
769 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
774 strip_trailing_junk(dir);
775 if (*rx->JobIds == 0) {
776 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
779 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
780 dir, rx->ClientName);
783 /* Find and insert jobid and File Index */
784 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
785 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
786 rx->query, db_strerror(ua->db));
789 bsendmsg(ua, _("No database record found for: %s\n"), dir);
793 * Find the MediaTypes for this JobId and add to the name_list
795 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
796 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
797 bsendmsg(ua, "%s", db_strerror(ua->db));
804 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
808 /* Find path without the filename.
809 * I.e. everything after the last / is a "filename".
810 * OK, maybe it is a directory name, but we treat it like
811 * a filename. If we don't find a / then the whole name
812 * must be a path name (e.g. c:).
814 for (p=f=name; *p; p++) {
816 f = p; /* set pos of last slash */
819 if (*f == '/') { /* did we find a slash? */
820 f++; /* yes, point to filename */
821 } else { /* no, whole thing must be path name */
825 /* If filename doesn't exist (i.e. root directory), we
826 * simply create a blank name consisting of a single
827 * space. This makes handling zero length filenames
832 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
833 memcpy(rx->fname, f, rx->fnl); /* copy filename */
834 rx->fname[rx->fnl] = 0;
842 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
843 memcpy(rx->path, name, rx->pnl);
844 rx->path[rx->pnl] = 0;
850 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
853 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
856 JobId_t JobId, last_JobId;
861 memset(&tree, 0, sizeof(TREE_CTX));
863 * Build the directory tree containing JobIds user selected
865 tree.root = new_tree(rx->TotalFiles);
870 * For display purposes, the same JobId, with different volumes may
871 * appear more than once, however, we only insert it once.
875 tree.FileEstimate = 0;
876 if (get_next_jobid_from_list(&p, &JobId) > 0) {
877 /* Use first JobId as estimate of the number of files to restore */
878 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
879 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
880 bsendmsg(ua, "%s\n", db_strerror(ua->db));
883 /* Add about 25% more than this job for over estimate */
884 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
885 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
888 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
891 if (JobId == last_JobId) {
892 continue; /* eliminate duplicate JobIds */
895 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
896 edit_int64(JobId, ed1));
899 * Find files for this JobId and insert them in the tree
901 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
902 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
903 bsendmsg(ua, "%s", db_strerror(ua->db));
906 * Find the MediaTypes for this JobId and add to the name_list
908 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
909 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
910 bsendmsg(ua, "%s", db_strerror(ua->db));
913 if (tree.FileCount == 0) {
914 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
915 "is not possible.Most likely your retention policy pruned the files\n"));
916 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
920 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
921 if (JobId == last_JobId) {
922 continue; /* eliminate duplicate JobIds */
924 add_findex_all(rx->bsr, JobId);
932 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
933 edit_uint64_with_commas(tree.FileCount, ec1));
936 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
937 edit_uint64_with_commas(tree.FileCount, ec1));
942 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
943 items, edit_uint64_with_commas(tree.FileCount, ec1));
946 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
947 items, edit_uint64_with_commas(tree.FileCount, ec1));
951 /* Check MediaType and select storage that corresponds */
952 get_storage_from_mediatype(ua, &rx->name_list, rx);
954 if (find_arg(ua, N_("done")) < 0) {
955 /* Let the user interact in selecting which files to restore */
956 OK = user_select_files_from_tree(&tree);
960 * Walk down through the tree finding all files marked to be
961 * extracted making a bootstrap file.
964 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
965 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
966 if (node->extract || node->extract_dir) {
967 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
968 add_findex(rx->bsr, node->JobId, node->FileIndex);
969 if (node->extract && node->type != TN_NEWDIR) {
970 rx->selected_files++; /* count only saved files */
977 free_tree(tree.root); /* free the directory tree */
983 * This routine is used to get the current backup or a backup
984 * before the specified date.
986 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
991 char fileset_name[MAX_NAME_LENGTH];
992 char ed1[50], ed2[50];
993 char pool_select[MAX_NAME_LENGTH];
997 /* Create temp tables */
998 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
999 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1000 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
1001 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1003 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
1004 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1007 * Select Client from the Catalog
1009 memset(&cr, 0, sizeof(cr));
1010 if (!get_client_dbr(ua, &cr)) {
1013 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1018 memset(&fsr, 0, sizeof(fsr));
1019 i = find_arg_with_value(ua, "FileSet");
1021 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1022 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1023 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1024 db_strerror(ua->db));
1028 if (i < 0) { /* fileset not found */
1029 edit_int64(cr.ClientId, ed1);
1030 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1031 start_prompt(ua, _("The defined FileSet resources are:\n"));
1032 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1033 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1035 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1036 fileset_name, sizeof(fileset_name)) < 0) {
1040 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1041 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1042 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1043 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1044 "Continuing anyway.\n"));
1048 /* If Pool specified, add PoolId specification */
1052 memset(&pr, 0, sizeof(pr));
1053 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1054 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1055 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1056 edit_int64(pr.PoolId, ed1));
1058 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1062 /* Find JobId of last Full backup for this client, fileset */
1063 edit_int64(cr.ClientId, ed1);
1064 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1066 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1067 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1071 /* Find all Volumes used by that JobId */
1072 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1073 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1076 /* Note, this is needed because I don't seem to get the callback
1077 * from the call just above.
1080 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1081 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1083 if (rx->JobTDate == 0) {
1084 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1088 /* Now find most recent Differental Job after Full save, if any */
1089 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1090 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1091 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1092 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1094 /* Now update JobTDate to lock onto Differental, if any */
1096 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1097 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1099 if (rx->JobTDate == 0) {
1100 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1104 /* Now find all Incremental Jobs after Full/dif save */
1105 Mmsg(rx->query, uar_inc, 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 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1111 /* Get the JobIds from that list */
1113 rx->last_jobid[0] = 0;
1114 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1115 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1118 if (rx->JobIds[0] != 0) {
1119 /* Display a list of Jobs selected for this restore */
1120 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1123 bsendmsg(ua, _("No jobs found.\n"));
1127 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1128 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1133 /* Return next JobId from comma separated list */
1134 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
1140 for (int i=0; i<(int)sizeof(jobid); i++) {
1143 } else if (*q == ',') {
1150 if (jobid[0] == 0) {
1152 } else if (!is_a_number(jobid)) {
1153 return -1; /* error */
1156 *JobId = str_to_int64(jobid);
1160 static int count_handler(void *ctx, int num_fields, char **row)
1162 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1163 rx->JobId = str_to_int64(row[0]);
1169 * Callback handler to get JobId and FileIndex for files
1170 * can insert more than one depending on the caller.
1172 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1174 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1175 rx->JobId = str_to_int64(row[0]);
1176 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1178 rx->selected_files++;
1183 * Callback handler make list of JobIds
1185 static int jobid_handler(void *ctx, int num_fields, char **row)
1187 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1189 if (strcmp(rx->last_jobid, row[0]) == 0) {
1190 return 0; /* duplicate id */
1192 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1193 if (rx->JobIds[0] != 0) {
1194 pm_strcat(rx->JobIds, ",");
1196 pm_strcat(rx->JobIds, row[0]);
1202 * Callback handler to pickup last Full backup JobTDate
1204 static int last_full_handler(void *ctx, int num_fields, char **row)
1206 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1208 rx->JobTDate = str_to_int64(row[1]);
1213 * Callback handler build FileSet name prompt list
1215 static int fileset_handler(void *ctx, int num_fields, char **row)
1217 /* row[0] = FileSet (name) */
1219 add_prompt((UAContext *)ctx, row[0]);
1225 * Called here with each name to be added to the list. The name is
1226 * added to the list if it is not already in the list.
1228 * Used to make unique list of FileSets and MediaTypes
1230 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1232 NAME_LIST *name = (NAME_LIST *)ctx;
1234 if (name->num_ids == MAX_ID_LIST_LEN) {
1237 if (name->num_ids == name->max_ids) {
1238 if (name->max_ids == 0) {
1239 name->max_ids = 1000;
1240 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1242 name->max_ids = (name->max_ids * 3) / 2;
1243 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1246 for (int i=0; i<name->num_ids; i++) {
1247 if (strcmp(name->name[i], row[0]) == 0) {
1248 return 0; /* already in list, return */
1251 /* Add new name to list */
1252 name->name[name->num_ids++] = bstrdup(row[0]);
1258 * Print names in the list
1260 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1262 for (int i=0; i < name_list->num_ids; i++) {
1263 bsendmsg(ua, "%s\n", name_list->name[i]);
1269 * Free names in the list
1271 static void free_name_list(NAME_LIST *name_list)
1273 for (int i=0; i < name_list->num_ids; i++) {
1274 free(name_list->name[i]);
1276 if (name_list->name) {
1277 free(name_list->name);
1278 name_list->name = NULL;
1280 name_list->max_ids = 0;
1281 name_list->num_ids = 0;
1284 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1288 if (name_list->num_ids > 1) {
1289 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1290 "Restore is not possible. The MediaTypes used are:\n"));
1291 print_name_list(ua, name_list);
1292 rx->store = select_storage_resource(ua);
1296 if (name_list->num_ids == 0) {
1297 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1298 rx->store = select_storage_resource(ua);
1305 * We have a single MediaType, look it up in our Storage resource
1308 foreach_res(store, R_STORAGE) {
1309 if (strcmp(name_list->name[0], store->media_type) == 0) {
1310 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1319 /* Check if an explicit storage resource is given */
1321 int i = find_arg_with_value(ua, "storage");
1323 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1324 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1328 if (store && (store != rx->store)) {
1329 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1336 /* Take command line arg, or ask user if none */
1337 rx->store = get_storage_resource(ua, false /* don't use default */);
1340 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1341 "MediaType \"%s\", needed by the Jobs you selected.\n"
1342 "You will be allowed to select a Storage device later.\n"),
1343 name_list->name[0]);