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 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
190 rx.selected_files==1?"":"s");
192 bsendmsg(ua, _("No files selected to be restored.\n"));
196 if (rx.restore_jobs == 1) {
197 job = rx.restore_job;
199 job = select_restore_job_resource(ua);
205 get_client_name(ua, &rx);
206 if (!rx.ClientName) {
207 bsendmsg(ua, _("No Restore Job resource found!\n"));
211 /* Build run command */
212 fname = get_pool_memory(PM_MESSAGE);
213 make_unique_restore_filename(ua, &fname);
216 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
217 " where=\"%s\" files=%d catalog=\"%s\"",
218 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
219 fname, rx.where, rx.selected_files, ua->catalog->hdr.name);
222 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
223 " files=%d catalog=\"%s\"",
224 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
225 fname, rx.selected_files, ua->catalog->hdr.name);
227 free_pool_memory(fname);
228 if (find_arg(ua, _("yes")) > 0) {
229 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
231 Dmsg1(400, "Submitting: %s\n", ua->cmd);
233 run_cmd(ua, ua->cmd);
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 int get_client_name(UAContext *ua, RESTORE_CTX *rx)
268 /* If no client name specified yet, get it now */
269 if (!rx->ClientName[0]) {
271 /* try command line argument */
272 int i = find_arg_with_value(ua, _("client"));
274 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
277 memset(&cr, 0, sizeof(cr));
278 if (!get_client_dbr(ua, &cr)) {
281 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
287 * The first step in the restore process is for the user to
288 * select a list of JobIds from which he will subsequently
289 * select which files are to be restored.
291 * Returns: 2 if filename list made
292 * 1 if jobid list made
295 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
298 char date[MAX_TIME_LENGTH];
299 bool have_date = false;
304 const char *list[] = {
305 "List last 20 Jobs run",
306 "List Jobs where a given File is saved",
307 "Enter list of comma separated JobIds to select",
308 "Enter SQL list command",
309 "Select the most recent backup for a client",
310 "Select backup for a client before a specified time",
311 "Enter a list of files to restore",
312 "Enter a list of files to restore before a specified time",
313 "Find the JobIds of the most recent backup for a client",
314 "Find the JobIds for a backup for a client before a specified time",
315 "Enter a list of directories to restore for found JobIds",
320 /* These keywords are handled in a for loop */
330 /* The keyword below are handled by individual arg lookups */
336 "bootstrap", /* 13 */
343 for (i=1; i<ua->argc; i++) { /* loop through arguments */
344 bool found_kw = false;
345 for (j=0; kw[j]; j++) { /* loop through keywords */
346 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
352 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
355 /* Found keyword in kw[] list, process it */
358 if (*rx->JobIds != 0) {
359 pm_strcat(rx->JobIds, ",");
361 pm_strcat(rx->JobIds, ua->argv[i]);
364 case 1: /* current */
365 bstrutime(date, sizeof(date), time(NULL));
369 if (str_to_utime(ua->argv[i]) == 0) {
370 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
373 bstrncpy(date, ua->argv[i], sizeof(date));
379 bstrutime(date, sizeof(date), time(NULL));
381 if (!get_client_name(ua, rx)) {
384 pm_strcpy(ua->cmd, ua->argv[i]);
385 insert_one_file_or_dir(ua, rx, date, j==4);
386 if (rx->name_list.num_ids) {
387 /* Check MediaType and select storage that corresponds */
388 get_storage_from_mediatype(ua, &rx->name_list, rx);
394 bstrutime(date, sizeof(date), time(NULL));
396 if (!select_backups_before_date(ua, rx, date)) {
401 case 6: /* pool specified */
402 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
404 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
407 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
409 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
413 case 7: /* all specified */
417 * All keywords 7 or greater are ignored or handled by a select prompt
423 if (rx->name_list.num_ids) {
424 return 2; /* filename list made */
428 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
429 "to be restored. You will be presented several methods\n"
430 "of specifying the JobIds. Then you will be allowed to\n"
431 "select which files from those JobIds are to be restored.\n\n"));
434 /* If choice not already made above, prompt */
440 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
441 for (int i=0; list[i]; i++) {
442 add_prompt(ua, list[i]);
445 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
448 case 0: /* list last 20 Jobs run */
449 gui_save = ua->jcr->gui;
451 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
452 ua->jcr->gui = gui_save;
455 case 1: /* list where a file is saved */
456 if (!get_client_name(ua, rx)) {
459 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
462 len = strlen(ua->cmd);
463 fname = (char *)malloc(len * 2 + 1);
464 db_escape_string(fname, ua->cmd, len);
465 Mmsg(rx->query, uar_file, rx->ClientName, fname);
467 gui_save = ua->jcr->gui;
469 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
470 ua->jcr->gui = gui_save;
473 case 2: /* enter a list of JobIds */
474 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
477 pm_strcpy(rx->JobIds, ua->cmd);
479 case 3: /* Enter an SQL list command */
480 if (!get_cmd(ua, _("Enter SQL list command: "))) {
483 gui_save = ua->jcr->gui;
485 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
486 ua->jcr->gui = gui_save;
489 case 4: /* Select the most recent backups */
490 bstrutime(date, sizeof(date), time(NULL));
491 if (!select_backups_before_date(ua, rx, date)) {
495 case 5: /* select backup at specified time */
496 if (!get_date(ua, date, sizeof(date))) {
499 if (!select_backups_before_date(ua, rx, date)) {
503 case 6: /* Enter files */
504 bstrutime(date, sizeof(date), time(NULL));
505 if (!get_client_name(ua, rx)) {
508 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
509 "containg a list of file names with paths, and terminate\n"
510 "them with a blank line.\n"));
512 if (!get_cmd(ua, _("Enter full filename: "))) {
515 len = strlen(ua->cmd);
519 insert_one_file_or_dir(ua, rx, date, false);
521 /* Check MediaType and select storage that corresponds */
522 if (rx->name_list.num_ids) {
523 get_storage_from_mediatype(ua, &rx->name_list, rx);
526 case 7: /* enter files backed up before specified time */
527 if (!get_date(ua, date, sizeof(date))) {
530 if (!get_client_name(ua, rx)) {
533 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
534 "containg 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);
546 /* Check MediaType and select storage that corresponds */
547 if (rx->name_list.num_ids) {
548 get_storage_from_mediatype(ua, &rx->name_list, rx);
552 case 8: /* Find JobIds for current backup */
553 bstrutime(date, sizeof(date), time(NULL));
554 if (!select_backups_before_date(ua, rx, date)) {
560 case 9: /* Find JobIds for give date */
561 if (!get_date(ua, date, sizeof(date))) {
564 if (!select_backups_before_date(ua, rx, date)) {
570 case 10: /* Enter directories */
571 if (*rx->JobIds != 0) {
572 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
574 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
575 if (*rx->JobIds != 0 && *ua->cmd) {
576 pm_strcat(rx->JobIds, ",");
578 pm_strcat(rx->JobIds, ua->cmd);
580 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
581 return 0; /* nothing entered, return */
583 bstrutime(date, sizeof(date), time(NULL));
584 if (!get_client_name(ua, rx)) {
587 bsendmsg(ua, _("Enter full directory names or start the name\n"
588 "with a < to indicate it is a filename containg a list\n"
589 "of directories and terminate them with a blank line.\n"));
591 if (!get_cmd(ua, _("Enter directory name: "))) {
594 len = strlen(ua->cmd);
598 /* Add trailing slash to end of directory names */
599 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
600 strcat(ua->cmd, "/");
602 insert_one_file_or_dir(ua, rx, date, true);
604 /* Check MediaType and select storage that corresponds */
605 if (rx->name_list.num_ids) {
606 get_storage_from_mediatype(ua, &rx->name_list, rx);
610 case 11: /* Cancel or quit */
615 if (*rx->JobIds == 0) {
616 bsendmsg(ua, _("No Jobs selected.\n"));
619 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
620 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
622 memset(&jr, 0, sizeof(JOB_DBR));
625 for (p=rx->JobIds; ; ) {
626 int stat = get_next_jobid_from_list(&p, &JobId);
628 bsendmsg(ua, _("Invalid JobId in list.\n"));
634 if (jr.JobId == JobId) {
635 continue; /* duplicate of last JobId */
638 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
640 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
641 edit_int64(JobId, ed1), db_strerror(ua->db));
644 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
645 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
649 rx->TotalFiles += jr.JobFiles;
657 static int get_date(UAContext *ua, char *date, int date_len)
659 bsendmsg(ua, _("The restored files will the most current backup\n"
660 "BEFORE the date you specify below.\n\n"));
662 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
665 if (str_to_utime(ua->cmd) != 0) {
668 bsendmsg(ua, _("Improper date format.\n"));
670 bstrncpy(date, ua->cmd, date_len);
675 * Insert a single file, or read a list of files from a file
677 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
687 if ((ffd = fopen(p, "r")) == NULL) {
689 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
693 while (fgets(file, sizeof(file), ffd)) {
696 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
697 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
700 if (!insert_file_into_findex_list(ua, rx, file, date)) {
701 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
709 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
711 insert_file_into_findex_list(ua, rx, ua->cmd, date);
718 * For a given file (path+filename), split into path and file, then
719 * lookup the most recent backup in the catalog to get the JobId
720 * and FileIndex, then insert them into the findex list.
722 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
727 strip_trailing_junk(file);
728 split_path_and_filename(rx, file);
729 if (*rx->JobIds == 0) {
730 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
733 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
734 rx->path, rx->fname, rx->ClientName);
737 /* Find and insert jobid and File Index */
738 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
739 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
740 rx->query, db_strerror(ua->db));
743 bsendmsg(ua, _("No database record found for: %s\n"), file);
747 * Find the MediaTypes for this JobId and add to the name_list
749 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
750 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
751 bsendmsg(ua, "%s", db_strerror(ua->db));
758 * For a given path lookup the most recent backup in the catalog
759 * to get the JobId and FileIndexes of all files in that directory.
761 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
766 strip_trailing_junk(dir);
767 if (*rx->JobIds == 0) {
768 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
771 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
772 dir, rx->ClientName);
775 /* Find and insert jobid and File Index */
776 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
777 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
778 rx->query, db_strerror(ua->db));
781 bsendmsg(ua, _("No database record found for: %s\n"), dir);
785 * Find the MediaTypes for this JobId and add to the name_list
787 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
788 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
789 bsendmsg(ua, "%s", db_strerror(ua->db));
796 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
800 /* Find path without the filename.
801 * I.e. everything after the last / is a "filename".
802 * OK, maybe it is a directory name, but we treat it like
803 * a filename. If we don't find a / then the whole name
804 * must be a path name (e.g. c:).
806 for (p=f=name; *p; p++) {
808 f = p; /* set pos of last slash */
811 if (*f == '/') { /* did we find a slash? */
812 f++; /* yes, point to filename */
813 } else { /* no, whole thing must be path name */
817 /* If filename doesn't exist (i.e. root directory), we
818 * simply create a blank name consisting of a single
819 * space. This makes handling zero length filenames
824 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
825 memcpy(rx->fname, f, rx->fnl); /* copy filename */
826 rx->fname[rx->fnl] = 0;
834 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
835 memcpy(rx->path, name, rx->pnl);
836 rx->path[rx->pnl] = 0;
842 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
845 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
848 JobId_t JobId, last_JobId;
853 memset(&tree, 0, sizeof(TREE_CTX));
855 * Build the directory tree containing JobIds user selected
857 tree.root = new_tree(rx->TotalFiles);
862 * For display purposes, the same JobId, with different volumes may
863 * appear more than once, however, we only insert it once.
867 tree.FileEstimate = 0;
868 if (get_next_jobid_from_list(&p, &JobId) > 0) {
869 /* Use first JobId as estimate of the number of files to restore */
870 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
871 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
872 bsendmsg(ua, "%s\n", db_strerror(ua->db));
875 /* Add about 25% more than this job for over estimate */
876 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
877 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
880 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
883 if (JobId == last_JobId) {
884 continue; /* eliminate duplicate JobIds */
887 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
888 edit_int64(JobId, ed1));
891 * Find files for this JobId and insert them in the tree
893 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
894 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
895 bsendmsg(ua, "%s", db_strerror(ua->db));
898 * Find the MediaTypes for this JobId and add to the name_list
900 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
901 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
902 bsendmsg(ua, "%s", db_strerror(ua->db));
905 if (tree.FileCount == 0) {
906 bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
907 "is not possible.Most likely your retention policy pruned the files\n");
908 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
912 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
913 if (JobId == last_JobId) {
914 continue; /* eliminate duplicate JobIds */
916 add_findex_all(rx->bsr, JobId);
922 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
923 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
924 tree.all?" and marked for extraction":"");
926 /* Check MediaType and select storage that corresponds */
927 get_storage_from_mediatype(ua, &rx->name_list, rx);
929 if (find_arg(ua, _("done")) < 0) {
930 /* Let the user interact in selecting which files to restore */
931 OK = user_select_files_from_tree(&tree);
935 * Walk down through the tree finding all files marked to be
936 * extracted making a bootstrap file.
939 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
940 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
941 if (node->extract || node->extract_dir) {
942 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
943 add_findex(rx->bsr, node->JobId, node->FileIndex);
944 if (node->extract && node->type != TN_NEWDIR) {
945 rx->selected_files++; /* count only saved files */
952 free_tree(tree.root); /* free the directory tree */
958 * This routine is used to get the current backup or a backup
959 * before the specified date.
961 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
966 char fileset_name[MAX_NAME_LENGTH];
967 char ed1[50], ed2[50];
968 char pool_select[MAX_NAME_LENGTH];
972 /* Create temp tables */
973 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
974 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
975 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
976 bsendmsg(ua, "%s\n", db_strerror(ua->db));
978 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
979 bsendmsg(ua, "%s\n", db_strerror(ua->db));
982 * Select Client from the Catalog
984 memset(&cr, 0, sizeof(cr));
985 if (!get_client_dbr(ua, &cr)) {
988 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
993 memset(&fsr, 0, sizeof(fsr));
994 i = find_arg_with_value(ua, "FileSet");
996 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
997 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
998 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
999 db_strerror(ua->db));
1003 if (i < 0) { /* fileset not found */
1004 edit_int64(cr.ClientId, ed1);
1005 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1006 start_prompt(ua, _("The defined FileSet resources are:\n"));
1007 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1008 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1010 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1011 fileset_name, sizeof(fileset_name)) < 0) {
1015 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1016 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1017 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1018 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1019 "Continuing anyway.\n"));
1023 /* If Pool specified, add PoolId specification */
1027 memset(&pr, 0, sizeof(pr));
1028 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1029 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1030 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1031 edit_int64(pr.PoolId, ed1));
1033 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1037 /* Find JobId of last Full backup for this client, fileset */
1038 edit_int64(cr.ClientId, ed1);
1039 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1041 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1042 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1046 /* Find all Volumes used by that JobId */
1047 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1048 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1051 /* Note, this is needed because I don't seem to get the callback
1052 * from the call just above.
1055 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1056 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1058 if (rx->JobTDate == 0) {
1059 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1063 /* Now find most recent Differental Job after Full save, if any */
1064 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1065 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1066 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1067 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1069 /* Now update JobTDate to lock onto Differental, if any */
1071 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1072 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1074 if (rx->JobTDate == 0) {
1075 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1079 /* Now find all Incremental Jobs after Full/dif save */
1080 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1081 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1082 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1083 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1086 /* Get the JobIds from that list */
1088 rx->last_jobid[0] = 0;
1089 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1090 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1093 if (rx->JobIds[0] != 0) {
1094 /* Display a list of Jobs selected for this restore */
1095 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1098 bsendmsg(ua, _("No jobs found.\n"));
1102 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1103 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1108 /* Return next JobId from comma separated list */
1109 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
1115 for (int i=0; i<(int)sizeof(jobid); i++) {
1118 } else if (*q == ',') {
1125 if (jobid[0] == 0) {
1127 } else if (!is_a_number(jobid)) {
1128 return -1; /* error */
1131 *JobId = str_to_int64(jobid);
1135 static int count_handler(void *ctx, int num_fields, char **row)
1137 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1138 rx->JobId = str_to_int64(row[0]);
1144 * Callback handler to get JobId and FileIndex for files
1145 * can insert more than one depending on the caller.
1147 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1149 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1150 rx->JobId = str_to_int64(row[0]);
1151 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1153 rx->selected_files++;
1158 * Callback handler make list of JobIds
1160 static int jobid_handler(void *ctx, int num_fields, char **row)
1162 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1164 if (strcmp(rx->last_jobid, row[0]) == 0) {
1165 return 0; /* duplicate id */
1167 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1168 if (rx->JobIds[0] != 0) {
1169 pm_strcat(rx->JobIds, ",");
1171 pm_strcat(rx->JobIds, row[0]);
1177 * Callback handler to pickup last Full backup JobTDate
1179 static int last_full_handler(void *ctx, int num_fields, char **row)
1181 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1183 rx->JobTDate = str_to_int64(row[1]);
1188 * Callback handler build FileSet name prompt list
1190 static int fileset_handler(void *ctx, int num_fields, char **row)
1192 /* row[0] = FileSet (name) */
1194 add_prompt((UAContext *)ctx, row[0]);
1200 * Called here with each name to be added to the list. The name is
1201 * added to the list if it is not already in the list.
1203 * Used to make unique list of FileSets and MediaTypes
1205 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1207 NAME_LIST *name = (NAME_LIST *)ctx;
1209 if (name->num_ids == MAX_ID_LIST_LEN) {
1212 if (name->num_ids == name->max_ids) {
1213 if (name->max_ids == 0) {
1214 name->max_ids = 1000;
1215 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1217 name->max_ids = (name->max_ids * 3) / 2;
1218 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1221 for (int i=0; i<name->num_ids; i++) {
1222 if (strcmp(name->name[i], row[0]) == 0) {
1223 return 0; /* already in list, return */
1226 /* Add new name to list */
1227 name->name[name->num_ids++] = bstrdup(row[0]);
1233 * Print names in the list
1235 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1237 for (int i=0; i < name_list->num_ids; i++) {
1238 bsendmsg(ua, "%s\n", name_list->name[i]);
1244 * Free names in the list
1246 static void free_name_list(NAME_LIST *name_list)
1248 for (int i=0; i < name_list->num_ids; i++) {
1249 free(name_list->name[i]);
1251 if (name_list->name) {
1252 free(name_list->name);
1253 name_list->name = NULL;
1255 name_list->max_ids = 0;
1256 name_list->num_ids = 0;
1259 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1263 if (name_list->num_ids > 1) {
1264 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1265 "Restore is not possible. The MediaTypes used are:\n"));
1266 print_name_list(ua, name_list);
1267 rx->store = select_storage_resource(ua);
1271 if (name_list->num_ids == 0) {
1272 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1273 rx->store = select_storage_resource(ua);
1280 * We have a single MediaType, look it up in our Storage resource
1283 foreach_res(store, R_STORAGE) {
1284 if (strcmp(name_list->name[0], store->media_type) == 0) {
1285 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1294 /* Check if an explicit storage resource is given */
1296 int i = find_arg_with_value(ua, "storage");
1298 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1299 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1303 if (store && (store != rx->store)) {
1304 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1311 /* Take command line arg, or ask user if none */
1312 rx->store = get_storage_resource(ua, false /* don't use default */);
1315 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1316 "MediaType \"%s\", needed by the Jobs you selected.\n"
1317 "You will be allowed to select a Storage device later.\n"),
1318 name_list->name[0]);