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(400, "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));
253 * The first step in the restore process is for the user to
254 * select a list of JobIds from which he will subsequently
255 * select which files are to be restored.
257 * Returns: 2 if filename list made
258 * 1 if jobid list made
261 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
264 char date[MAX_TIME_LENGTH];
265 bool have_date = false;
270 const char *list[] = {
271 _("List last 20 Jobs run"),
272 _("List Jobs where a given File is saved"),
273 _("Enter list of comma separated JobIds to select"),
274 _("Enter SQL list command"),
275 _("Select the most recent backup for a client"),
276 _("Select backup for a client before a specified time"),
277 _("Enter a list of files to restore"),
278 _("Enter a list of files to restore before a specified time"),
279 _("Find the JobIds of the most recent backup for a client"),
280 _("Find the JobIds for a backup for a client before a specified time"),
281 _("Enter a list of directories to restore for found JobIds"),
286 /* These keywords are handled in a for loop */
296 /* The keyword below are handled by individual arg lookups */
302 "bootstrap", /* 13 */
309 for (i=1; i<ua->argc; i++) { /* loop through arguments */
310 bool found_kw = false;
311 for (j=0; kw[j]; j++) { /* loop through keywords */
312 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
318 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
321 /* Found keyword in kw[] list, process it */
324 if (*rx->JobIds != 0) {
325 pm_strcat(rx->JobIds, ",");
327 pm_strcat(rx->JobIds, ua->argv[i]);
330 case 1: /* current */
331 bstrutime(date, sizeof(date), time(NULL));
335 if (str_to_utime(ua->argv[i]) == 0) {
336 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
339 bstrncpy(date, ua->argv[i], sizeof(date));
345 bstrutime(date, sizeof(date), time(NULL));
347 if (!get_client_name(ua, rx)) {
350 pm_strcpy(ua->cmd, ua->argv[i]);
351 insert_one_file_or_dir(ua, rx, date, j==4);
352 if (rx->name_list.num_ids) {
353 /* Check MediaType and select storage that corresponds */
354 get_storage_from_mediatype(ua, &rx->name_list, rx);
360 bstrutime(date, sizeof(date), time(NULL));
362 if (!select_backups_before_date(ua, rx, date)) {
367 case 6: /* pool specified */
368 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
370 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
373 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
375 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
379 case 7: /* all specified */
383 * All keywords 7 or greater are ignored or handled by a select prompt
389 if (rx->name_list.num_ids) {
390 return 2; /* filename list made */
394 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
395 "to be restored. You will be presented several methods\n"
396 "of specifying the JobIds. Then you will be allowed to\n"
397 "select which files from those JobIds are to be restored.\n\n"));
400 /* If choice not already made above, prompt */
406 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
407 for (int i=0; list[i]; i++) {
408 add_prompt(ua, list[i]);
411 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
414 case 0: /* list last 20 Jobs run */
415 gui_save = ua->jcr->gui;
417 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
418 ua->jcr->gui = gui_save;
421 case 1: /* list where a file is saved */
422 if (!get_client_name(ua, rx)) {
425 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
428 len = strlen(ua->cmd);
429 fname = (char *)malloc(len * 2 + 1);
430 db_escape_string(fname, ua->cmd, len);
431 Mmsg(rx->query, uar_file, rx->ClientName, fname);
433 gui_save = ua->jcr->gui;
435 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
436 ua->jcr->gui = gui_save;
439 case 2: /* enter a list of JobIds */
440 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
443 pm_strcpy(rx->JobIds, ua->cmd);
445 case 3: /* Enter an SQL list command */
446 if (!get_cmd(ua, _("Enter SQL list command: "))) {
449 gui_save = ua->jcr->gui;
451 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
452 ua->jcr->gui = gui_save;
455 case 4: /* Select the most recent backups */
456 bstrutime(date, sizeof(date), time(NULL));
457 if (!select_backups_before_date(ua, rx, date)) {
461 case 5: /* select backup at specified time */
462 if (!get_date(ua, date, sizeof(date))) {
465 if (!select_backups_before_date(ua, rx, date)) {
469 case 6: /* Enter files */
470 bstrutime(date, sizeof(date), time(NULL));
471 if (!get_client_name(ua, rx)) {
474 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
475 "containg a list of file names with paths, and terminate\n"
476 "them with a blank line.\n"));
478 if (!get_cmd(ua, _("Enter full filename: "))) {
481 len = strlen(ua->cmd);
485 insert_one_file_or_dir(ua, rx, date, false);
487 /* Check MediaType and select storage that corresponds */
488 if (rx->name_list.num_ids) {
489 get_storage_from_mediatype(ua, &rx->name_list, rx);
492 case 7: /* enter files backed up before specified time */
493 if (!get_date(ua, date, sizeof(date))) {
496 if (!get_client_name(ua, rx)) {
499 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
500 "containg a list of file names with paths, and terminate\n"
501 "them with a blank line.\n"));
503 if (!get_cmd(ua, _("Enter full filename: "))) {
506 len = strlen(ua->cmd);
510 insert_one_file_or_dir(ua, rx, date, false);
512 /* Check MediaType and select storage that corresponds */
513 if (rx->name_list.num_ids) {
514 get_storage_from_mediatype(ua, &rx->name_list, rx);
518 case 8: /* Find JobIds for current backup */
519 bstrutime(date, sizeof(date), time(NULL));
520 if (!select_backups_before_date(ua, rx, date)) {
526 case 9: /* Find JobIds for give date */
527 if (!get_date(ua, date, sizeof(date))) {
530 if (!select_backups_before_date(ua, rx, date)) {
536 case 10: /* Enter directories */
537 if (*rx->JobIds != 0) {
538 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
540 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
541 if (*rx->JobIds != 0 && *ua->cmd) {
542 pm_strcat(rx->JobIds, ",");
544 pm_strcat(rx->JobIds, ua->cmd);
546 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
547 return 0; /* nothing entered, return */
549 bstrutime(date, sizeof(date), time(NULL));
550 if (!get_client_name(ua, rx)) {
553 bsendmsg(ua, _("Enter full directory names or start the name\n"
554 "with a < to indicate it is a filename containg a list\n"
555 "of directories and terminate them with a blank line.\n"));
557 if (!get_cmd(ua, _("Enter directory name: "))) {
560 len = strlen(ua->cmd);
564 /* Add trailing slash to end of directory names */
565 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
566 strcat(ua->cmd, "/");
568 insert_one_file_or_dir(ua, rx, date, true);
570 /* Check MediaType and select storage that corresponds */
571 if (rx->name_list.num_ids) {
572 get_storage_from_mediatype(ua, &rx->name_list, rx);
576 case 11: /* Cancel or quit */
581 if (*rx->JobIds == 0) {
582 bsendmsg(ua, _("No Jobs selected.\n"));
585 if (strchr(rx->JobIds,',')) {
586 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
589 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
594 for (p=rx->JobIds; ; ) {
595 int stat = get_next_jobid_from_list(&p, &JobId);
597 bsendmsg(ua, _("Invalid JobId in list.\n"));
603 if (jr.JobId == JobId) {
604 continue; /* duplicate of last JobId */
606 memset(&jr, 0, sizeof(JOB_DBR));
608 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
610 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
611 edit_int64(JobId, ed1), db_strerror(ua->db));
614 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
615 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
619 rx->TotalFiles += jr.JobFiles;
627 static int get_date(UAContext *ua, char *date, int date_len)
629 bsendmsg(ua, _("The restored files will the most current backup\n"
630 "BEFORE the date you specify below.\n\n"));
632 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
635 if (str_to_utime(ua->cmd) != 0) {
638 bsendmsg(ua, _("Improper date format.\n"));
640 bstrncpy(date, ua->cmd, date_len);
645 * Insert a single file, or read a list of files from a file
647 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
657 if ((ffd = fopen(p, "r")) == NULL) {
659 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
663 while (fgets(file, sizeof(file), ffd)) {
666 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
667 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
670 if (!insert_file_into_findex_list(ua, rx, file, date)) {
671 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
679 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
681 insert_file_into_findex_list(ua, rx, ua->cmd, date);
688 * For a given file (path+filename), split into path and file, then
689 * lookup the most recent backup in the catalog to get the JobId
690 * and FileIndex, then insert them into the findex list.
692 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
697 strip_trailing_junk(file);
698 split_path_and_filename(rx, file);
699 if (*rx->JobIds == 0) {
700 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
703 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
704 rx->path, rx->fname, rx->ClientName);
707 /* Find and insert jobid and File Index */
708 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
709 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
710 rx->query, db_strerror(ua->db));
713 bsendmsg(ua, _("No database record found for: %s\n"), file);
717 * Find the MediaTypes for this JobId and add to the name_list
719 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
720 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
721 bsendmsg(ua, "%s", db_strerror(ua->db));
728 * For a given path lookup the most recent backup in the catalog
729 * to get the JobId and FileIndexes of all files in that directory.
731 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
736 strip_trailing_junk(dir);
737 if (*rx->JobIds == 0) {
738 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
741 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
742 dir, rx->ClientName);
745 /* Find and insert jobid and File Index */
746 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
747 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
748 rx->query, db_strerror(ua->db));
751 bsendmsg(ua, _("No database record found for: %s\n"), dir);
755 * Find the MediaTypes for this JobId and add to the name_list
757 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
758 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
759 bsendmsg(ua, "%s", db_strerror(ua->db));
766 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
770 /* Find path without the filename.
771 * I.e. everything after the last / is a "filename".
772 * OK, maybe it is a directory name, but we treat it like
773 * a filename. If we don't find a / then the whole name
774 * must be a path name (e.g. c:).
776 for (p=f=name; *p; p++) {
778 f = p; /* set pos of last slash */
781 if (*f == '/') { /* did we find a slash? */
782 f++; /* yes, point to filename */
783 } else { /* no, whole thing must be path name */
787 /* If filename doesn't exist (i.e. root directory), we
788 * simply create a blank name consisting of a single
789 * space. This makes handling zero length filenames
794 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
795 memcpy(rx->fname, f, rx->fnl); /* copy filename */
796 rx->fname[rx->fnl] = 0;
804 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
805 memcpy(rx->path, name, rx->pnl);
806 rx->path[rx->pnl] = 0;
812 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
815 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
818 JobId_t JobId, last_JobId;
823 memset(&tree, 0, sizeof(TREE_CTX));
825 * Build the directory tree containing JobIds user selected
827 tree.root = new_tree(rx->TotalFiles);
832 * For display purposes, the same JobId, with different volumes may
833 * appear more than once, however, we only insert it once.
837 tree.FileEstimate = 0;
838 if (get_next_jobid_from_list(&p, &JobId) > 0) {
839 /* Use first JobId as estimate of the number of files to restore */
840 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
841 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
842 bsendmsg(ua, "%s\n", db_strerror(ua->db));
845 /* Add about 25% more than this job for over estimate */
846 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
847 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
850 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
853 if (JobId == last_JobId) {
854 continue; /* eliminate duplicate JobIds */
857 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
858 edit_int64(JobId, ed1));
861 * Find files for this JobId and insert them in the tree
863 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
864 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
865 bsendmsg(ua, "%s", db_strerror(ua->db));
868 * Find the MediaTypes for this JobId and add to the name_list
870 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
871 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
872 bsendmsg(ua, "%s", db_strerror(ua->db));
875 if (tree.FileCount == 0) {
876 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
877 "is not possible.Most likely your retention policy pruned the files\n"));
878 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
882 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
883 if (JobId == last_JobId) {
884 continue; /* eliminate duplicate JobIds */
886 add_findex_all(rx->bsr, JobId);
894 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
895 edit_uint64_with_commas(tree.FileCount, ec1));
898 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
899 edit_uint64_with_commas(tree.FileCount, ec1));
904 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
905 items, edit_uint64_with_commas(tree.FileCount, ec1));
908 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
909 items, edit_uint64_with_commas(tree.FileCount, ec1));
913 /* Check MediaType and select storage that corresponds */
914 get_storage_from_mediatype(ua, &rx->name_list, rx);
916 if (find_arg(ua, N_("done")) < 0) {
917 /* Let the user interact in selecting which files to restore */
918 OK = user_select_files_from_tree(&tree);
922 * Walk down through the tree finding all files marked to be
923 * extracted making a bootstrap file.
926 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
927 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
928 if (node->extract || node->extract_dir) {
929 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
930 add_findex(rx->bsr, node->JobId, node->FileIndex);
931 if (node->extract && node->type != TN_NEWDIR) {
932 rx->selected_files++; /* count only saved files */
939 free_tree(tree.root); /* free the directory tree */
945 * This routine is used to get the current backup or a backup
946 * before the specified date.
948 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
953 char fileset_name[MAX_NAME_LENGTH];
954 char ed1[50], ed2[50];
955 char pool_select[MAX_NAME_LENGTH];
959 /* Create temp tables */
960 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
961 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
962 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
963 bsendmsg(ua, "%s\n", db_strerror(ua->db));
965 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
966 bsendmsg(ua, "%s\n", db_strerror(ua->db));
969 * Select Client from the Catalog
971 memset(&cr, 0, sizeof(cr));
972 if (!get_client_dbr(ua, &cr)) {
975 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
980 memset(&fsr, 0, sizeof(fsr));
981 i = find_arg_with_value(ua, "FileSet");
983 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
984 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
985 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
986 db_strerror(ua->db));
990 if (i < 0) { /* fileset not found */
991 edit_int64(cr.ClientId, ed1);
992 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
993 start_prompt(ua, _("The defined FileSet resources are:\n"));
994 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
995 bsendmsg(ua, "%s\n", db_strerror(ua->db));
997 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
998 fileset_name, sizeof(fileset_name)) < 0) {
1002 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1003 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1004 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1005 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1006 "Continuing anyway.\n"));
1010 /* If Pool specified, add PoolId specification */
1014 memset(&pr, 0, sizeof(pr));
1015 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1016 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1017 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1018 edit_int64(pr.PoolId, ed1));
1020 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1024 /* Find JobId of last Full backup for this client, fileset */
1025 edit_int64(cr.ClientId, ed1);
1026 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1028 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1029 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1033 /* Find all Volumes used by that JobId */
1034 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1035 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1038 /* Note, this is needed because I don't seem to get the callback
1039 * from the call just above.
1042 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1043 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1045 if (rx->JobTDate == 0) {
1046 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1050 /* Now find most recent Differental Job after Full save, if any */
1051 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1052 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1053 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1054 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1056 /* Now update JobTDate to lock onto Differental, if any */
1058 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1059 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1061 if (rx->JobTDate == 0) {
1062 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1066 /* Now find all Incremental Jobs after Full/dif save */
1067 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1068 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1069 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1070 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1073 /* Get the JobIds from that list */
1075 rx->last_jobid[0] = 0;
1076 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1077 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1080 if (rx->JobIds[0] != 0) {
1081 /* Display a list of Jobs selected for this restore */
1082 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1085 bsendmsg(ua, _("No jobs found.\n"));
1089 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1090 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1095 /* Return next JobId from comma separated list */
1096 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1102 for (int i=0; i<(int)sizeof(jobid); i++) {
1105 } else if (*q == ',') {
1112 if (jobid[0] == 0) {
1114 } else if (!is_a_number(jobid)) {
1115 return -1; /* error */
1118 *JobId = str_to_int64(jobid);
1122 static int count_handler(void *ctx, int num_fields, char **row)
1124 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1125 rx->JobId = str_to_int64(row[0]);
1131 * Callback handler to get JobId and FileIndex for files
1132 * can insert more than one depending on the caller.
1134 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1136 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1137 rx->JobId = str_to_int64(row[0]);
1138 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1140 rx->selected_files++;
1145 * Callback handler make list of JobIds
1147 static int jobid_handler(void *ctx, int num_fields, char **row)
1149 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1151 if (strcmp(rx->last_jobid, row[0]) == 0) {
1152 return 0; /* duplicate id */
1154 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1155 if (rx->JobIds[0] != 0) {
1156 pm_strcat(rx->JobIds, ",");
1158 pm_strcat(rx->JobIds, row[0]);
1164 * Callback handler to pickup last Full backup JobTDate
1166 static int last_full_handler(void *ctx, int num_fields, char **row)
1168 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1170 rx->JobTDate = str_to_int64(row[1]);
1175 * Callback handler build FileSet name prompt list
1177 static int fileset_handler(void *ctx, int num_fields, char **row)
1179 /* row[0] = FileSet (name) */
1181 add_prompt((UAContext *)ctx, row[0]);
1187 * Called here with each name to be added to the list. The name is
1188 * added to the list if it is not already in the list.
1190 * Used to make unique list of FileSets and MediaTypes
1192 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1194 NAME_LIST *name = (NAME_LIST *)ctx;
1196 if (name->num_ids == MAX_ID_LIST_LEN) {
1199 if (name->num_ids == name->max_ids) {
1200 if (name->max_ids == 0) {
1201 name->max_ids = 1000;
1202 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1204 name->max_ids = (name->max_ids * 3) / 2;
1205 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1208 for (int i=0; i<name->num_ids; i++) {
1209 if (strcmp(name->name[i], row[0]) == 0) {
1210 return 0; /* already in list, return */
1213 /* Add new name to list */
1214 name->name[name->num_ids++] = bstrdup(row[0]);
1220 * Print names in the list
1222 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1224 for (int i=0; i < name_list->num_ids; i++) {
1225 bsendmsg(ua, "%s\n", name_list->name[i]);
1231 * Free names in the list
1233 static void free_name_list(NAME_LIST *name_list)
1235 for (int i=0; i < name_list->num_ids; i++) {
1236 free(name_list->name[i]);
1238 if (name_list->name) {
1239 free(name_list->name);
1240 name_list->name = NULL;
1242 name_list->max_ids = 0;
1243 name_list->num_ids = 0;
1246 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1250 if (name_list->num_ids > 1) {
1251 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1252 "Restore is not possible. The MediaTypes used are:\n"));
1253 print_name_list(ua, name_list);
1254 rx->store = select_storage_resource(ua);
1258 if (name_list->num_ids == 0) {
1259 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1260 rx->store = select_storage_resource(ua);
1267 * We have a single MediaType, look it up in our Storage resource
1270 foreach_res(store, R_STORAGE) {
1271 if (strcmp(name_list->name[0], store->media_type) == 0) {
1272 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1281 /* Check if an explicit storage resource is given */
1283 int i = find_arg_with_value(ua, "storage");
1285 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1286 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1290 if (store && (store != rx->store)) {
1291 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1298 /* Take command line arg, or ask user if none */
1299 rx->store = get_storage_resource(ua, false /* don't use default */);
1302 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1303 "MediaType \"%s\", needed by the Jobs you selected.\n"
1304 "You will be allowed to select a Storage device later.\n"),
1305 name_list->name[0]);