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 /* Forward referenced functions */
51 static int last_full_handler(void *ctx, int num_fields, char **row);
52 static int jobid_handler(void *ctx, int num_fields, char **row);
53 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
54 static int fileset_handler(void *ctx, int num_fields, char **row);
55 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
56 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
57 static void free_name_list(NAME_LIST *name_list);
58 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
59 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
60 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
61 static void free_rx(RESTORE_CTX *rx);
62 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
63 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
64 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
66 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
68 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
69 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
70 static int get_date(UAContext *ua, char *date, int date_len);
71 static int count_handler(void *ctx, int num_fields, char **row);
77 int restore_cmd(UAContext *ua, const char *cmd)
79 RESTORE_CTX rx; /* restore context */
84 memset(&rx, 0, sizeof(rx));
85 rx.path = get_pool_memory(PM_FNAME);
86 rx.fname = get_pool_memory(PM_FNAME);
87 rx.JobIds = get_pool_memory(PM_FNAME);
88 rx.query = get_pool_memory(PM_FNAME);
91 i = find_arg_with_value(ua, "where");
93 rx.where = ua->argv[i];
100 /* Ensure there is at least one Restore Job */
102 foreach_res(job, R_JOB) {
103 if (job->JobType == JT_RESTORE) {
104 if (!rx.restore_job) {
105 rx.restore_job = job;
111 if (!rx.restore_jobs) {
113 "No Restore Job Resource found in bacula-dir.conf.\n"
114 "You must create at least one before running this command.\n"));
119 * Request user to select JobIds or files by various different methods
120 * last 20 jobs, where File saved, most recent backup, ...
121 * In the end, a list of files are pumped into
124 switch (user_select_jobids_or_files(ua, &rx)) {
127 case 1: /* selected by jobid */
128 if (!build_directory_tree(ua, &rx)) {
129 bsendmsg(ua, _("Restore not done.\n"));
133 case 2: /* selected by filename, no tree needed */
138 uint32_t selected_files;
139 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
140 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
143 if (!(selected_files = write_bsr_file(ua, rx))) {
144 bsendmsg(ua, _("No files selected to be restored.\n"));
147 /* If no count of files, use bsr generated value (often wrong) */
148 if (rx.selected_files == 0) {
149 rx.selected_files = selected_files;
151 if (rx.selected_files==1) {
152 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
155 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
158 bsendmsg(ua, _("No files selected to be restored.\n"));
162 if (rx.restore_jobs == 1) {
163 job = rx.restore_job;
165 job = select_restore_job_resource(ua);
171 get_client_name(ua, &rx);
172 if (!rx.ClientName) {
173 bsendmsg(ua, _("No Restore Job resource found!\n"));
177 /* Build run command */
178 fname = get_pool_memory(PM_MESSAGE);
179 make_unique_restore_filename(ua, &fname);
182 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
183 " where=\"%s\" files=%d catalog=\"%s\"",
184 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
185 fname, rx.where, rx.selected_files, ua->catalog->hdr.name);
188 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
189 " files=%d catalog=\"%s\"",
190 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
191 fname, rx.selected_files, ua->catalog->hdr.name);
193 free_pool_memory(fname);
194 if (find_arg(ua, N_("yes")) > 0) {
195 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
197 Dmsg1(100, "Submitting: %s\n", ua->cmd);
199 run_cmd(ua, ua->cmd);
209 static void free_rx(RESTORE_CTX *rx)
214 free_pool_memory(rx->JobIds);
218 free_pool_memory(rx->fname);
222 free_pool_memory(rx->path);
226 free_pool_memory(rx->query);
229 free_name_list(&rx->name_list);
232 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
234 /* If no client name specified yet, get it now */
235 if (!rx->ClientName[0]) {
237 /* try command line argument */
238 int i = find_arg_with_value(ua, N_("client"));
240 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
243 memset(&cr, 0, sizeof(cr));
244 if (!get_client_dbr(ua, &cr)) {
247 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
252 static bool has_value(UAContext *ua, int i)
255 bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
262 * The first step in the restore process is for the user to
263 * select a list of JobIds from which he will subsequently
264 * select which files are to be restored.
266 * Returns: 2 if filename list made
267 * 1 if jobid list made
270 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
273 char date[MAX_TIME_LENGTH];
274 bool have_date = false;
279 const char *list[] = {
280 _("List last 20 Jobs run"),
281 _("List Jobs where a given File is saved"),
282 _("Enter list of comma separated JobIds to select"),
283 _("Enter SQL list command"),
284 _("Select the most recent backup for a client"),
285 _("Select backup for a client before a specified time"),
286 _("Enter a list of files to restore"),
287 _("Enter a list of files to restore before a specified time"),
288 _("Find the JobIds of the most recent backup for a client"),
289 _("Find the JobIds for a backup for a client before a specified time"),
290 _("Enter a list of directories to restore for found JobIds"),
295 /* These keywords are handled in a for loop */
305 /* The keyword below are handled by individual arg lookups */
311 "bootstrap", /* 13 */
318 for (i=1; i<ua->argc; i++) { /* loop through arguments */
319 bool found_kw = false;
320 for (j=0; kw[j]; j++) { /* loop through keywords */
321 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
327 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
330 /* Found keyword in kw[] list, process it */
333 if (!has_value(ua, i)) {
336 if (*rx->JobIds != 0) {
337 pm_strcat(rx->JobIds, ",");
339 pm_strcat(rx->JobIds, ua->argv[i]);
342 case 1: /* current */
343 bstrutime(date, sizeof(date), time(NULL));
347 if (!has_value(ua, i)) {
350 if (str_to_utime(ua->argv[i]) == 0) {
351 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
354 bstrncpy(date, ua->argv[i], sizeof(date));
359 if (!has_value(ua, i)) {
363 bstrutime(date, sizeof(date), time(NULL));
365 if (!get_client_name(ua, rx)) {
368 pm_strcpy(ua->cmd, ua->argv[i]);
369 insert_one_file_or_dir(ua, rx, date, j==4);
370 if (rx->name_list.num_ids) {
371 /* Check MediaType and select storage that corresponds */
372 get_storage_from_mediatype(ua, &rx->name_list, rx);
378 bstrutime(date, sizeof(date), time(NULL));
380 if (!select_backups_before_date(ua, rx, date)) {
385 case 6: /* pool specified */
386 if (!has_value(ua, i)) {
389 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
391 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
394 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
396 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
400 case 7: /* all specified */
404 * All keywords 7 or greater are ignored or handled by a select prompt
410 if (rx->name_list.num_ids) {
411 return 2; /* filename list made */
415 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
416 "to be restored. You will be presented several methods\n"
417 "of specifying the JobIds. Then you will be allowed to\n"
418 "select which files from those JobIds are to be restored.\n\n"));
421 /* If choice not already made above, prompt */
427 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
428 for (int i=0; list[i]; i++) {
429 add_prompt(ua, list[i]);
432 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
433 case -1: /* error or cancel */
435 case 0: /* list last 20 Jobs run */
436 gui_save = ua->jcr->gui;
438 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
439 ua->jcr->gui = gui_save;
442 case 1: /* list where a file is saved */
443 if (!get_client_name(ua, rx)) {
446 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
449 len = strlen(ua->cmd);
450 fname = (char *)malloc(len * 2 + 1);
451 db_escape_string(fname, ua->cmd, len);
452 Mmsg(rx->query, uar_file, rx->ClientName, fname);
454 gui_save = ua->jcr->gui;
456 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
457 ua->jcr->gui = gui_save;
460 case 2: /* enter a list of JobIds */
461 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
464 pm_strcpy(rx->JobIds, ua->cmd);
466 case 3: /* Enter an SQL list command */
467 if (!get_cmd(ua, _("Enter SQL list command: "))) {
470 gui_save = ua->jcr->gui;
472 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
473 ua->jcr->gui = gui_save;
476 case 4: /* Select the most recent backups */
477 bstrutime(date, sizeof(date), time(NULL));
478 if (!select_backups_before_date(ua, rx, date)) {
482 case 5: /* select backup at specified time */
483 if (!get_date(ua, date, sizeof(date))) {
486 if (!select_backups_before_date(ua, rx, date)) {
490 case 6: /* Enter files */
491 bstrutime(date, sizeof(date), time(NULL));
492 if (!get_client_name(ua, rx)) {
495 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
496 "containg a list of file names with paths, and terminate\n"
497 "them with a blank line.\n"));
499 if (!get_cmd(ua, _("Enter full filename: "))) {
502 len = strlen(ua->cmd);
506 insert_one_file_or_dir(ua, rx, date, false);
508 /* Check MediaType and select storage that corresponds */
509 if (rx->name_list.num_ids) {
510 get_storage_from_mediatype(ua, &rx->name_list, rx);
513 case 7: /* enter files backed up before specified time */
514 if (!get_date(ua, date, sizeof(date))) {
517 if (!get_client_name(ua, rx)) {
520 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
521 "containg a list of file names with paths, and terminate\n"
522 "them with a blank line.\n"));
524 if (!get_cmd(ua, _("Enter full filename: "))) {
527 len = strlen(ua->cmd);
531 insert_one_file_or_dir(ua, rx, date, false);
533 /* Check MediaType and select storage that corresponds */
534 if (rx->name_list.num_ids) {
535 get_storage_from_mediatype(ua, &rx->name_list, rx);
539 case 8: /* Find JobIds for current backup */
540 bstrutime(date, sizeof(date), time(NULL));
541 if (!select_backups_before_date(ua, rx, date)) {
547 case 9: /* Find JobIds for give date */
548 if (!get_date(ua, date, sizeof(date))) {
551 if (!select_backups_before_date(ua, rx, date)) {
557 case 10: /* Enter directories */
558 if (*rx->JobIds != 0) {
559 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
561 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
562 if (*rx->JobIds != 0 && *ua->cmd) {
563 pm_strcat(rx->JobIds, ",");
565 pm_strcat(rx->JobIds, ua->cmd);
567 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
568 return 0; /* nothing entered, return */
570 bstrutime(date, sizeof(date), time(NULL));
571 if (!get_client_name(ua, rx)) {
574 bsendmsg(ua, _("Enter full directory names or start the name\n"
575 "with a < to indicate it is a filename containg a list\n"
576 "of directories and terminate them with a blank line.\n"));
578 if (!get_cmd(ua, _("Enter directory name: "))) {
581 len = strlen(ua->cmd);
585 /* Add trailing slash to end of directory names */
586 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
587 strcat(ua->cmd, "/");
589 insert_one_file_or_dir(ua, rx, date, true);
591 /* Check MediaType and select storage that corresponds */
592 if (rx->name_list.num_ids) {
593 get_storage_from_mediatype(ua, &rx->name_list, rx);
597 case 11: /* Cancel or quit */
602 if (*rx->JobIds == 0) {
603 bsendmsg(ua, _("No Jobs selected.\n"));
606 if (strchr(rx->JobIds,',')) {
607 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
610 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
615 for (p=rx->JobIds; ; ) {
616 int stat = get_next_jobid_from_list(&p, &JobId);
618 bsendmsg(ua, _("Invalid JobId in list.\n"));
624 if (jr.JobId == JobId) {
625 continue; /* duplicate of last JobId */
627 memset(&jr, 0, sizeof(JOB_DBR));
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.Most likely your retention policy pruned the files\n"));
899 if (!get_yesno(ua, _("\nDo 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);
915 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
916 edit_uint64_with_commas(tree.FileCount, ec1));
919 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
920 edit_uint64_with_commas(tree.FileCount, ec1));
925 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
926 items, edit_uint64_with_commas(tree.FileCount, ec1));
929 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
930 items, edit_uint64_with_commas(tree.FileCount, ec1));
934 /* Check MediaType and select storage that corresponds */
935 get_storage_from_mediatype(ua, &rx->name_list, rx);
937 if (find_arg(ua, N_("done")) < 0) {
938 /* Let the user interact in selecting which files to restore */
939 OK = user_select_files_from_tree(&tree);
943 * Walk down through the tree finding all files marked to be
944 * extracted making a bootstrap file.
947 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
948 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
949 if (node->extract || node->extract_dir) {
950 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
951 add_findex(rx->bsr, node->JobId, node->FileIndex);
952 if (node->extract && node->type != TN_NEWDIR) {
953 rx->selected_files++; /* count only saved files */
960 free_tree(tree.root); /* free the directory tree */
966 * This routine is used to get the current backup or a backup
967 * before the specified date.
969 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
974 char fileset_name[MAX_NAME_LENGTH];
975 char ed1[50], ed2[50];
976 char pool_select[MAX_NAME_LENGTH];
980 /* Create temp tables */
981 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
982 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
983 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
984 bsendmsg(ua, "%s\n", db_strerror(ua->db));
986 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
987 bsendmsg(ua, "%s\n", db_strerror(ua->db));
990 * Select Client from the Catalog
992 memset(&cr, 0, sizeof(cr));
993 if (!get_client_dbr(ua, &cr)) {
996 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1001 memset(&fsr, 0, sizeof(fsr));
1002 i = find_arg_with_value(ua, "FileSet");
1004 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1005 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1006 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1007 db_strerror(ua->db));
1011 if (i < 0) { /* fileset not found */
1012 edit_int64(cr.ClientId, ed1);
1013 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1014 start_prompt(ua, _("The defined FileSet resources are:\n"));
1015 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1016 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1018 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1019 fileset_name, sizeof(fileset_name)) < 0) {
1023 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1024 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1025 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1026 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1027 "Continuing anyway.\n"));
1031 /* If Pool specified, add PoolId specification */
1035 memset(&pr, 0, sizeof(pr));
1036 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1037 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1038 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1039 edit_int64(pr.PoolId, ed1));
1041 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1045 /* Find JobId of last Full backup for this client, fileset */
1046 edit_int64(cr.ClientId, ed1);
1047 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1049 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1050 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1054 /* Find all Volumes used by that JobId */
1055 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1056 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1059 /* Note, this is needed because I don't seem to get the callback
1060 * from the call just above.
1063 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1064 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1066 if (rx->JobTDate == 0) {
1067 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1071 /* Now find most recent Differental Job after Full save, if any */
1072 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1073 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1074 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1075 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1077 /* Now update JobTDate to lock onto Differental, if any */
1079 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1080 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1082 if (rx->JobTDate == 0) {
1083 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1087 /* Now find all Incremental Jobs after Full/dif save */
1088 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1089 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1090 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1091 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1094 /* Get the JobIds from that list */
1096 rx->last_jobid[0] = 0;
1097 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1098 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1101 if (rx->JobIds[0] != 0) {
1102 /* Display a list of Jobs selected for this restore */
1103 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1106 bsendmsg(ua, _("No jobs found.\n"));
1110 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1111 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1116 /* Return next JobId from comma separated list */
1117 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1123 for (int i=0; i<(int)sizeof(jobid); i++) {
1126 } else if (*q == ',') {
1133 if (jobid[0] == 0) {
1135 } else if (!is_a_number(jobid)) {
1136 return -1; /* error */
1139 *JobId = str_to_int64(jobid);
1143 static int count_handler(void *ctx, int num_fields, char **row)
1145 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1146 rx->JobId = str_to_int64(row[0]);
1152 * Callback handler to get JobId and FileIndex for files
1153 * can insert more than one depending on the caller.
1155 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1157 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1158 rx->JobId = str_to_int64(row[0]);
1159 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1161 rx->selected_files++;
1166 * Callback handler make list of JobIds
1168 static int jobid_handler(void *ctx, int num_fields, char **row)
1170 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1172 if (strcmp(rx->last_jobid, row[0]) == 0) {
1173 return 0; /* duplicate id */
1175 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1176 if (rx->JobIds[0] != 0) {
1177 pm_strcat(rx->JobIds, ",");
1179 pm_strcat(rx->JobIds, row[0]);
1185 * Callback handler to pickup last Full backup JobTDate
1187 static int last_full_handler(void *ctx, int num_fields, char **row)
1189 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1191 rx->JobTDate = str_to_int64(row[1]);
1196 * Callback handler build FileSet name prompt list
1198 static int fileset_handler(void *ctx, int num_fields, char **row)
1200 /* row[0] = FileSet (name) */
1202 add_prompt((UAContext *)ctx, row[0]);
1208 * Called here with each name to be added to the list. The name is
1209 * added to the list if it is not already in the list.
1211 * Used to make unique list of FileSets and MediaTypes
1213 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1215 NAME_LIST *name = (NAME_LIST *)ctx;
1217 if (name->num_ids == MAX_ID_LIST_LEN) {
1220 if (name->num_ids == name->max_ids) {
1221 if (name->max_ids == 0) {
1222 name->max_ids = 1000;
1223 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1225 name->max_ids = (name->max_ids * 3) / 2;
1226 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1229 for (int i=0; i<name->num_ids; i++) {
1230 if (strcmp(name->name[i], row[0]) == 0) {
1231 return 0; /* already in list, return */
1234 /* Add new name to list */
1235 name->name[name->num_ids++] = bstrdup(row[0]);
1241 * Print names in the list
1243 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1245 for (int i=0; i < name_list->num_ids; i++) {
1246 bsendmsg(ua, "%s\n", name_list->name[i]);
1252 * Free names in the list
1254 static void free_name_list(NAME_LIST *name_list)
1256 for (int i=0; i < name_list->num_ids; i++) {
1257 free(name_list->name[i]);
1259 if (name_list->name) {
1260 free(name_list->name);
1261 name_list->name = NULL;
1263 name_list->max_ids = 0;
1264 name_list->num_ids = 0;
1267 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1271 if (name_list->num_ids > 1) {
1272 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1273 "Restore is not possible. The MediaTypes used are:\n"));
1274 print_name_list(ua, name_list);
1275 rx->store = select_storage_resource(ua);
1279 if (name_list->num_ids == 0) {
1280 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1281 rx->store = select_storage_resource(ua);
1288 * We have a single MediaType, look it up in our Storage resource
1291 foreach_res(store, R_STORAGE) {
1292 if (strcmp(name_list->name[0], store->media_type) == 0) {
1293 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1302 /* Check if an explicit storage resource is given */
1304 int i = find_arg_with_value(ua, "storage");
1306 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1307 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1311 if (store && (store != rx->store)) {
1312 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1319 /* Take command line arg, or ask user if none */
1320 rx->store = get_storage_resource(ua, false /* don't use default */);
1323 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1324 "MediaType \"%s\", needed by the Jobs you selected.\n"
1325 "You will be allowed to select a Storage device later.\n"),
1326 name_list->name[0]);