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
17 Copyright (C) 2002-2005 Kern Sibbald
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation; either version 2 of
22 the License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public
30 License along with this program; if not, write to the Free
31 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
40 /* Imported functions */
41 extern void print_bsr(UAContext *ua, RBSR *bsr);
43 /* Imported variables */
44 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
45 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
46 extern char *uar_create_temp1, *uar_last_full, *uar_full;
47 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
48 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
49 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
50 extern char *uar_count_files, *uar_jobids_fileindex;
51 extern char *uar_jobid_fileindex_from_dir;
55 char **name; /* list of names */
56 int num_ids; /* ids stored */
57 int max_ids; /* size of array */
58 int num_del; /* number deleted */
59 int tot_ids; /* total to process */
63 /* Main structure for obtaining JobIds or Files to be restored */
68 char ClientName[MAX_NAME_LENGTH];
70 POOLMEM *JobIds; /* User entered string of JobIds */
75 uint32_t selected_files;
78 POOLMEM *fname; /* filename only */
79 POOLMEM *path; /* path only */
81 int fnl; /* filename length */
82 int pnl; /* path length */
84 bool all; /* mark all as default */
89 #define MAX_ID_LIST_LEN 1000000
92 /* Forward referenced functions */
93 static int last_full_handler(void *ctx, int num_fields, char **row);
94 static int jobid_handler(void *ctx, int num_fields, char **row);
95 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
96 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
97 static int fileset_handler(void *ctx, int num_fields, char **row);
98 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
99 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
100 static void free_name_list(NAME_LIST *name_list);
101 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
102 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
103 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
104 static void free_rx(RESTORE_CTX *rx);
105 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
106 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
107 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
109 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
111 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
112 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
113 static int get_date(UAContext *ua, char *date, int date_len);
114 static int count_handler(void *ctx, int num_fields, char **row);
120 int restore_cmd(UAContext *ua, const char *cmd)
122 RESTORE_CTX rx; /* restore context */
126 memset(&rx, 0, sizeof(rx));
127 rx.path = get_pool_memory(PM_FNAME);
128 rx.fname = get_pool_memory(PM_FNAME);
129 rx.JobIds = get_pool_memory(PM_FNAME);
130 rx.query = get_pool_memory(PM_FNAME);
133 i = find_arg_with_value(ua, "where");
135 rx.where = ua->argv[i];
142 /* Ensure there is at least one Restore Job */
144 foreach_res(job, R_JOB) {
145 if (job->JobType == JT_RESTORE) {
146 if (!rx.restore_job) {
147 rx.restore_job = job;
153 if (!rx.restore_jobs) {
155 "No Restore Job Resource found in bacula-dir.conf.\n"
156 "You must create at least one before running this command.\n"));
161 * Request user to select JobIds or files by various different methods
162 * last 20 jobs, where File saved, most recent backup, ...
163 * In the end, a list of files are pumped into
166 switch (user_select_jobids_or_files(ua, &rx)) {
169 case 1: /* selected by jobid */
170 if (!build_directory_tree(ua, &rx)) {
171 bsendmsg(ua, _("Restore not done.\n"));
175 case 2: /* selected by filename, no tree needed */
180 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
181 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
184 if (!(rx.selected_files = write_bsr_file(ua, rx.bsr))) {
185 bsendmsg(ua, _("No files selected to be restored.\n"));
188 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
189 rx.selected_files==1?"":"s");
191 bsendmsg(ua, _("No files selected to be restored.\n"));
195 if (rx.restore_jobs == 1) {
196 job = rx.restore_job;
198 job = select_restore_job_resource(ua);
204 get_client_name(ua, &rx);
205 if (!rx.ClientName) {
206 bsendmsg(ua, _("No Restore Job resource found!\n"));
210 /* Build run command */
213 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
214 " where=\"%s\" files=%d catalog=\"%s\"",
215 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
216 working_directory, rx.where, rx.selected_files, ua->catalog->hdr.name);
219 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
220 " files=%d catalog=\"%s\"",
221 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
222 working_directory, rx.selected_files, ua->catalog->hdr.name);
224 if (find_arg(ua, _("yes")) > 0) {
225 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
227 Dmsg1(400, "Submitting: %s\n", ua->cmd);
229 run_cmd(ua, ua->cmd);
239 static void free_rx(RESTORE_CTX *rx)
244 free_pool_memory(rx->JobIds);
248 free_pool_memory(rx->fname);
252 free_pool_memory(rx->path);
256 free_pool_memory(rx->query);
259 free_name_list(&rx->name_list);
262 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
264 /* If no client name specified yet, get it now */
265 if (!rx->ClientName[0]) {
267 /* try command line argument */
268 int i = find_arg_with_value(ua, _("client"));
270 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
273 memset(&cr, 0, sizeof(cr));
274 if (!get_client_dbr(ua, &cr)) {
277 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
283 * The first step in the restore process is for the user to
284 * select a list of JobIds from which he will subsequently
285 * select which files are to be restored.
287 * Returns: 2 if filename list made
288 * 1 if jobid list made
291 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
294 char date[MAX_TIME_LENGTH];
295 bool have_date = false;
300 const char *list[] = {
301 "List last 20 Jobs run",
302 "List Jobs where a given File is saved",
303 "Enter list of comma separated JobIds to select",
304 "Enter SQL list command",
305 "Select the most recent backup for a client",
306 "Select backup for a client before a specified time",
307 "Enter a list of files to restore",
308 "Enter a list of files to restore before a specified time",
309 "Find the JobIds of the most recent backup for a client",
310 "Find the JobIds for a backup for a client before a specified time",
311 "Enter a list of directories to restore for given JobIds",
316 /* These keywords are handled in a for loop */
326 /* The keyword below are handled by individual arg lookups */
338 for (i=1; i<ua->argc; i++) { /* loop through arguments */
339 bool found_kw = false;
340 for (j=0; kw[j]; j++) { /* loop through keywords */
341 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
347 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
350 /* Found keyword in kw[] list, process it */
353 if (*rx->JobIds != 0) {
354 pm_strcat(rx->JobIds, ",");
356 pm_strcat(rx->JobIds, ua->argv[i]);
359 case 1: /* current */
360 bstrutime(date, sizeof(date), time(NULL));
364 if (str_to_utime(ua->argv[i]) == 0) {
365 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
368 bstrncpy(date, ua->argv[i], sizeof(date));
374 bstrutime(date, sizeof(date), time(NULL));
376 if (!get_client_name(ua, rx)) {
379 pm_strcpy(ua->cmd, ua->argv[i]);
380 insert_one_file_or_dir(ua, rx, date, j==4);
381 if (rx->name_list.num_ids) {
382 /* Check MediaType and select storage that corresponds */
383 get_storage_from_mediatype(ua, &rx->name_list, rx);
389 bstrutime(date, sizeof(date), time(NULL));
391 if (!select_backups_before_date(ua, rx, date)) {
396 case 6: /* pool specified */
397 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
399 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
402 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
404 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
408 case 7: /* all specified */
412 * All keywords 7 or greater are ignored or handled by a select prompt
418 if (rx->name_list.num_ids) {
419 return 2; /* filename list made */
423 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
424 "to be restored. You will be presented several methods\n"
425 "of specifying the JobIds. Then you will be allowed to\n"
426 "select which files from those JobIds are to be restored.\n\n"));
429 /* If choice not already made above, prompt */
435 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
436 for (int i=0; list[i]; i++) {
437 add_prompt(ua, list[i]);
440 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
443 case 0: /* list last 20 Jobs run */
444 gui_save = ua->jcr->gui;
446 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
447 ua->jcr->gui = gui_save;
450 case 1: /* list where a file is saved */
451 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
454 len = strlen(ua->cmd);
455 fname = (char *)malloc(len * 2 + 1);
456 db_escape_string(fname, ua->cmd, len);
457 Mmsg(rx->query, uar_file, fname);
459 gui_save = ua->jcr->gui;
461 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
462 ua->jcr->gui = gui_save;
465 case 2: /* enter a list of JobIds */
466 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
469 pm_strcpy(rx->JobIds, ua->cmd);
471 case 3: /* Enter an SQL list command */
472 if (!get_cmd(ua, _("Enter SQL list command: "))) {
475 gui_save = ua->jcr->gui;
477 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
478 ua->jcr->gui = gui_save;
481 case 4: /* Select the most recent backups */
482 bstrutime(date, sizeof(date), time(NULL));
483 if (!select_backups_before_date(ua, rx, date)) {
487 case 5: /* select backup at specified time */
488 if (!get_date(ua, date, sizeof(date))) {
491 if (!select_backups_before_date(ua, rx, date)) {
495 case 6: /* Enter files */
496 bstrutime(date, sizeof(date), time(NULL));
497 if (!get_client_name(ua, rx)) {
500 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
501 "containg a list of file names with paths, and terminate\n"
502 "them with a blank line.\n"));
504 if (!get_cmd(ua, _("Enter full filename: "))) {
507 len = strlen(ua->cmd);
511 insert_one_file_or_dir(ua, rx, date, false);
513 /* Check MediaType and select storage that corresponds */
514 if (rx->name_list.num_ids) {
515 get_storage_from_mediatype(ua, &rx->name_list, rx);
518 case 7: /* enter files backed up before specified time */
519 if (!get_date(ua, date, sizeof(date))) {
522 if (!get_client_name(ua, rx)) {
525 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
526 "containg a list of file names with paths, and terminate\n"
527 "them with a blank line.\n"));
529 if (!get_cmd(ua, _("Enter full filename: "))) {
532 len = strlen(ua->cmd);
536 insert_one_file_or_dir(ua, rx, date, false);
538 /* Check MediaType and select storage that corresponds */
539 if (rx->name_list.num_ids) {
540 get_storage_from_mediatype(ua, &rx->name_list, rx);
544 case 8: /* Find JobIds for current backup */
545 bstrutime(date, sizeof(date), time(NULL));
546 if (!select_backups_before_date(ua, rx, date)) {
552 case 9: /* Find JobIds for give date */
553 if (!get_date(ua, date, sizeof(date))) {
556 if (!select_backups_before_date(ua, rx, date)) {
562 case 10: /* Enter directories */
563 if (*rx->JobIds != 0) {
564 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
566 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
567 if (*rx->JobIds != 0 && *ua->cmd) {
568 pm_strcat(rx->JobIds, ",");
570 pm_strcat(rx->JobIds, ua->cmd);
572 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
573 return 0; /* nothing entered, return */
575 bstrutime(date, sizeof(date), time(NULL));
576 if (!get_client_name(ua, rx)) {
579 bsendmsg(ua, _("Enter directory names with a trailing /, or < to enter a filename\n"
580 "containg a list of directories and terminate\n"
581 "them with a blank line.\n"));
583 if (!get_cmd(ua, _("Enter directory name: "))) {
586 len = strlen(ua->cmd);
590 if (ua->cmd[len-1] != '/') {
591 strcat(ua->cmd, "/");
593 insert_one_file_or_dir(ua, rx, date, true);
595 /* Check MediaType and select storage that corresponds */
596 if (rx->name_list.num_ids) {
597 get_storage_from_mediatype(ua, &rx->name_list, rx);
601 case 11: /* Cancel or quit */
606 if (*rx->JobIds == 0) {
607 bsendmsg(ua, _("No Jobs selected.\n"));
610 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
611 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
613 memset(&jr, 0, sizeof(JOB_DBR));
616 for (p=rx->JobIds; ; ) {
617 int stat = get_next_jobid_from_list(&p, &JobId);
619 bsendmsg(ua, _("Invalid JobId in list.\n"));
625 if (jr.JobId == JobId) {
626 continue; /* duplicate of last JobId */
629 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
631 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
632 edit_int64(JobId, ed1), db_strerror(ua->db));
635 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
636 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
640 rx->TotalFiles += jr.JobFiles;
648 static int get_date(UAContext *ua, char *date, int date_len)
650 bsendmsg(ua, _("The restored files will the most current backup\n"
651 "BEFORE the date you specify below.\n\n"));
653 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
656 if (str_to_utime(ua->cmd) != 0) {
659 bsendmsg(ua, _("Improper date format.\n"));
661 bstrncpy(date, ua->cmd, date_len);
666 * Insert a single file, or read a list of files from a file
668 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
678 if ((ffd = fopen(p, "r")) == NULL) {
680 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
684 while (fgets(file, sizeof(file), ffd)) {
687 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
688 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
691 if (!insert_file_into_findex_list(ua, rx, file, date)) {
692 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
700 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
702 insert_file_into_findex_list(ua, rx, ua->cmd, date);
709 * For a given file (path+filename), split into path and file, then
710 * lookup the most recent backup in the catalog to get the JobId
711 * and FileIndex, then insert them into the findex list.
713 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
718 strip_trailing_junk(file);
719 split_path_and_filename(rx, file);
720 if (*rx->JobIds == 0) {
721 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
724 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
725 rx->path, rx->fname, rx->ClientName);
728 /* Find and insert jobid and File Index */
729 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
730 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
731 rx->query, db_strerror(ua->db));
734 bsendmsg(ua, _("No database record found for: %s\n"), file);
738 * Find the MediaTypes for this JobId and add to the name_list
740 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
741 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
742 bsendmsg(ua, "%s", db_strerror(ua->db));
749 * For a given path lookup the most recent backup in the catalog
750 * to get the JobId and FileIndexes of all files in that directory.
752 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
757 strip_trailing_junk(dir);
758 if (*rx->JobIds == 0) {
759 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
762 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
763 dir, rx->ClientName);
766 /* Find and insert jobid and File Index */
767 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
768 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
769 rx->query, db_strerror(ua->db));
772 bsendmsg(ua, _("No database record found for: %s\n"), dir);
776 * Find the MediaTypes for this JobId and add to the name_list
778 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
779 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
780 bsendmsg(ua, "%s", db_strerror(ua->db));
787 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
791 /* Find path without the filename.
792 * I.e. everything after the last / is a "filename".
793 * OK, maybe it is a directory name, but we treat it like
794 * a filename. If we don't find a / then the whole name
795 * must be a path name (e.g. c:).
797 for (p=f=name; *p; p++) {
799 f = p; /* set pos of last slash */
802 if (*f == '/') { /* did we find a slash? */
803 f++; /* yes, point to filename */
804 } else { /* no, whole thing must be path name */
808 /* If filename doesn't exist (i.e. root directory), we
809 * simply create a blank name consisting of a single
810 * space. This makes handling zero length filenames
815 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
816 memcpy(rx->fname, f, rx->fnl); /* copy filename */
817 rx->fname[rx->fnl] = 0;
825 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
826 memcpy(rx->path, name, rx->pnl);
827 rx->path[rx->pnl] = 0;
833 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
836 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
839 JobId_t JobId, last_JobId;
844 memset(&tree, 0, sizeof(TREE_CTX));
846 * Build the directory tree containing JobIds user selected
848 tree.root = new_tree(rx->TotalFiles);
853 * For display purposes, the same JobId, with different volumes may
854 * appear more than once, however, we only insert it once.
858 tree.FileEstimate = 0;
859 if (get_next_jobid_from_list(&p, &JobId) > 0) {
860 /* Use first JobId as estimate of the number of files to restore */
861 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
862 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
863 bsendmsg(ua, "%s\n", db_strerror(ua->db));
866 /* Add about 25% more than this job for over estimate */
867 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
868 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
871 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
874 if (JobId == last_JobId) {
875 continue; /* eliminate duplicate JobIds */
878 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
879 edit_int64(JobId, ed1));
882 * Find files for this JobId and insert them in the tree
884 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
885 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
886 bsendmsg(ua, "%s", db_strerror(ua->db));
889 * Find the MediaTypes for this JobId and add to the name_list
891 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
892 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
893 bsendmsg(ua, "%s", db_strerror(ua->db));
896 if (tree.FileCount == 0) {
897 bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
898 "is not possible.\nMost likely your retention policy pruned the files\n");
899 if (!get_yesno(ua, _("Do you want to restore all the files? (yes|no): "))) {
903 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
904 if (JobId == last_JobId) {
905 continue; /* eliminate duplicate JobIds */
907 add_findex_all(rx->bsr, JobId);
913 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
914 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
915 tree.all?" and marked for extraction":"");
917 /* Check MediaType and select storage that corresponds */
918 get_storage_from_mediatype(ua, &rx->name_list, rx);
920 if (find_arg(ua, _("done")) < 0) {
921 /* Let the user interact in selecting which files to restore */
922 OK = user_select_files_from_tree(&tree);
926 * Walk down through the tree finding all files marked to be
927 * extracted making a bootstrap file.
930 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
931 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
932 if (node->extract || node->extract_dir) {
933 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
934 add_findex(rx->bsr, node->JobId, node->FileIndex);
935 if (node->extract && node->type != TN_NEWDIR) {
936 rx->selected_files++; /* count only saved files */
943 free_tree(tree.root); /* free the directory tree */
949 * This routine is used to get the current backup or a backup
950 * before the specified date.
952 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
957 char fileset_name[MAX_NAME_LENGTH];
958 char ed1[50], ed2[50];
959 char pool_select[MAX_NAME_LENGTH];
963 /* Create temp tables */
964 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
965 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
966 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
967 bsendmsg(ua, "%s\n", db_strerror(ua->db));
969 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
970 bsendmsg(ua, "%s\n", db_strerror(ua->db));
973 * Select Client from the Catalog
975 memset(&cr, 0, sizeof(cr));
976 if (!get_client_dbr(ua, &cr)) {
979 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
984 memset(&fsr, 0, sizeof(fsr));
985 i = find_arg_with_value(ua, "FileSet");
987 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
988 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
989 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
990 db_strerror(ua->db));
994 if (i < 0) { /* fileset not found */
995 edit_int64(cr.ClientId, ed1);
996 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
997 start_prompt(ua, _("The defined FileSet resources are:\n"));
998 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
999 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1001 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1002 fileset_name, sizeof(fileset_name)) < 0) {
1006 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1007 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1008 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1009 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1010 "Continuing anyway.\n"));
1014 /* If Pool specified, add PoolId specification */
1018 memset(&pr, 0, sizeof(pr));
1019 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1020 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1021 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1022 edit_int64(pr.PoolId, ed1));
1024 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1028 /* Find JobId of last Full backup for this client, fileset */
1029 edit_int64(cr.ClientId, ed1);
1030 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1032 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1033 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1037 /* Find all Volumes used by that JobId */
1038 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1039 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1042 /* Note, this is needed because I don't seem to get the callback
1043 * from the call just above.
1046 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1047 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1049 if (rx->JobTDate == 0) {
1050 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1054 /* Now find most recent Differental Job after Full save, if any */
1055 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1056 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1057 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1058 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1060 /* Now update JobTDate to lock onto Differental, if any */
1062 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1063 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1065 if (rx->JobTDate == 0) {
1066 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1070 /* Now find all Incremental Jobs after Full/dif save */
1071 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1072 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1073 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1074 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1077 /* Get the JobIds from that list */
1079 rx->last_jobid[0] = 0;
1080 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1081 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1084 if (rx->JobIds[0] != 0) {
1085 /* Display a list of Jobs selected for this restore */
1086 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1089 bsendmsg(ua, _("No jobs found.\n"));
1093 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1094 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1099 /* Return next JobId from comma separated list */
1100 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
1106 for (int i=0; i<(int)sizeof(jobid); i++) {
1109 } else if (*q == ',') {
1116 if (jobid[0] == 0) {
1118 } else if (!is_a_number(jobid)) {
1119 return -1; /* error */
1122 *JobId = str_to_int64(jobid);
1126 static int count_handler(void *ctx, int num_fields, char **row)
1128 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1129 rx->JobId = str_to_int64(row[0]);
1135 * Callback handler to get JobId and FileIndex for files
1136 * can insert more than one depending on the caller.
1138 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1140 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1141 rx->JobId = str_to_int64(row[0]);
1142 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1144 rx->selected_files++;
1149 * Callback handler make list of JobIds
1151 static int jobid_handler(void *ctx, int num_fields, char **row)
1153 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1155 if (strcmp(rx->last_jobid, row[0]) == 0) {
1156 return 0; /* duplicate id */
1158 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1159 if (rx->JobIds[0] != 0) {
1160 pm_strcat(rx->JobIds, ",");
1162 pm_strcat(rx->JobIds, row[0]);
1168 * Callback handler to pickup last Full backup JobTDate
1170 static int last_full_handler(void *ctx, int num_fields, char **row)
1172 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1174 rx->JobTDate = str_to_int64(row[1]);
1179 * Callback handler build FileSet name prompt list
1181 static int fileset_handler(void *ctx, int num_fields, char **row)
1183 /* row[0] = FileSet (name) */
1185 add_prompt((UAContext *)ctx, row[0]);
1191 * Called here with each name to be added to the list. The name is
1192 * added to the list if it is not already in the list.
1194 * Used to make unique list of FileSets and MediaTypes
1196 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1198 NAME_LIST *name = (NAME_LIST *)ctx;
1200 if (name->num_ids == MAX_ID_LIST_LEN) {
1203 if (name->num_ids == name->max_ids) {
1204 if (name->max_ids == 0) {
1205 name->max_ids = 1000;
1206 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1208 name->max_ids = (name->max_ids * 3) / 2;
1209 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1212 for (int i=0; i<name->num_ids; i++) {
1213 if (strcmp(name->name[i], row[0]) == 0) {
1214 return 0; /* already in list, return */
1217 /* Add new name to list */
1218 name->name[name->num_ids++] = bstrdup(row[0]);
1224 * Print names in the list
1226 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1228 for (int i=0; i < name_list->num_ids; i++) {
1229 bsendmsg(ua, "%s\n", name_list->name[i]);
1235 * Free names in the list
1237 static void free_name_list(NAME_LIST *name_list)
1239 for (int i=0; i < name_list->num_ids; i++) {
1240 free(name_list->name[i]);
1242 if (name_list->name) {
1243 free(name_list->name);
1244 name_list->name = NULL;
1246 name_list->max_ids = 0;
1247 name_list->num_ids = 0;
1250 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1254 if (name_list->num_ids > 1) {
1255 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1256 "Restore is not possible. The MediaTypes used are:\n"));
1257 print_name_list(ua, name_list);
1258 rx->store = select_storage_resource(ua);
1262 if (name_list->num_ids == 0) {
1263 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1264 rx->store = select_storage_resource(ua);
1271 * We have a single MediaType, look it up in our Storage resource
1274 foreach_res(store, R_STORAGE) {
1275 if (strcmp(name_list->name[0], store->media_type) == 0) {
1276 if (acl_access_ok(ua, Storage_ACL, store->hdr.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->hdr.name)) {
1294 if (store && (store != rx->store)) {
1295 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1302 /* Take command line arg, or ask user if none */
1303 rx->store = get_storage_resource(ua, false /* don't use default */);
1306 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1307 "MediaType \"%s\", needed by the Jobs you selected.\n"
1308 "You will be allowed to select a Storage device later.\n"),
1309 name_list->name[0]);