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-2006 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;
47 extern char *uar_jobid_fileindex_from_table;
51 /* Forward referenced functions */
52 static int last_full_handler(void *ctx, int num_fields, char **row);
53 static int jobid_handler(void *ctx, int num_fields, char **row);
54 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
55 static int fileset_handler(void *ctx, int num_fields, char **row);
56 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
57 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
58 static void free_name_list(NAME_LIST *name_list);
59 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
60 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
61 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
62 static void free_rx(RESTORE_CTX *rx);
63 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
64 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
65 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
67 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
69 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
70 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
71 static int get_date(UAContext *ua, char *date, int date_len);
72 static int count_handler(void *ctx, int num_fields, char **row);
73 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
79 int restore_cmd(UAContext *ua, const char *cmd)
81 RESTORE_CTX rx; /* restore context */
86 memset(&rx, 0, sizeof(rx));
87 rx.path = get_pool_memory(PM_FNAME);
88 rx.fname = get_pool_memory(PM_FNAME);
89 rx.JobIds = get_pool_memory(PM_FNAME);
90 rx.query = get_pool_memory(PM_FNAME);
93 i = find_arg_with_value(ua, "where");
95 rx.where = ua->argv[i];
102 /* Ensure there is at least one Restore Job */
104 foreach_res(job, R_JOB) {
105 if (job->JobType == JT_RESTORE) {
106 if (!rx.restore_job) {
107 rx.restore_job = job;
113 if (!rx.restore_jobs) {
115 "No Restore Job Resource found in bacula-dir.conf.\n"
116 "You must create at least one before running this command.\n"));
121 * Request user to select JobIds or files by various different methods
122 * last 20 jobs, where File saved, most recent backup, ...
123 * In the end, a list of files are pumped into
126 switch (user_select_jobids_or_files(ua, &rx)) {
129 case 1: /* selected by jobid */
130 if (!build_directory_tree(ua, &rx)) {
131 bsendmsg(ua, _("Restore not done.\n"));
135 case 2: /* selected by filename, no tree needed */
140 uint32_t selected_files;
141 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
142 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
145 if (!(selected_files = write_bsr_file(ua, rx))) {
146 bsendmsg(ua, _("No files selected to be restored.\n"));
149 /* If no count of files, use bsr generated value (often wrong) */
150 if (rx.selected_files == 0) {
151 rx.selected_files = selected_files;
153 if (rx.selected_files==1) {
154 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
157 bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
160 bsendmsg(ua, _("No files selected to be restored.\n"));
164 if (rx.restore_jobs == 1) {
165 job = rx.restore_job;
167 job = select_restore_job_resource(ua);
173 get_client_name(ua, &rx);
174 if (!rx.ClientName) {
175 bsendmsg(ua, _("No Restore Job resource found!\n"));
179 /* Build run command */
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 jcr->RestoreBootstrap, 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 jcr->RestoreBootstrap, rx.selected_files, ua->catalog->hdr.name);
193 if (find_arg(ua, NT_("yes")) > 0) {
194 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
196 Dmsg1(100, "Submitting: %s\n", ua->cmd);
198 run_cmd(ua, ua->cmd);
208 static void free_rx(RESTORE_CTX *rx)
213 free_pool_memory(rx->JobIds);
217 free_pool_memory(rx->fname);
221 free_pool_memory(rx->path);
225 free_pool_memory(rx->query);
228 free_name_list(&rx->name_list);
231 static bool has_value(UAContext *ua, int i)
234 bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
240 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
242 /* If no client name specified yet, get it now */
243 if (!rx->ClientName[0]) {
245 /* try command line argument */
246 int i = find_arg_with_value(ua, NT_("client"));
248 if (!has_value(ua, i)) {
251 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
254 memset(&cr, 0, sizeof(cr));
255 if (!get_client_dbr(ua, &cr)) {
258 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
265 * The first step in the restore process is for the user to
266 * select a list of JobIds from which he will subsequently
267 * select which files are to be restored.
269 * Returns: 2 if filename list made
270 * 1 if jobid list made
273 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
276 char date[MAX_TIME_LENGTH];
277 bool have_date = false;
282 const char *list[] = {
283 _("List last 20 Jobs run"),
284 _("List Jobs where a given File is saved"),
285 _("Enter list of comma separated JobIds to select"),
286 _("Enter SQL list command"),
287 _("Select the most recent backup for a client"),
288 _("Select backup for a client before a specified time"),
289 _("Enter a list of files to restore"),
290 _("Enter a list of files to restore before a specified time"),
291 _("Find the JobIds of the most recent backup for a client"),
292 _("Find the JobIds for a backup for a client before a specified time"),
293 _("Enter a list of directories to restore for found JobIds"),
298 /* These keywords are handled in a for loop */
308 /* The keyword below are handled by individual arg lookups */
314 "bootstrap", /* 13 */
321 for (i=1; i<ua->argc; i++) { /* loop through arguments */
322 bool found_kw = false;
323 for (j=0; kw[j]; j++) { /* loop through keywords */
324 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
330 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
333 /* Found keyword in kw[] list, process it */
336 if (!has_value(ua, i)) {
339 if (*rx->JobIds != 0) {
340 pm_strcat(rx->JobIds, ",");
342 pm_strcat(rx->JobIds, ua->argv[i]);
345 case 1: /* current */
346 bstrutime(date, sizeof(date), time(NULL));
350 if (!has_value(ua, i)) {
353 if (str_to_utime(ua->argv[i]) == 0) {
354 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
357 bstrncpy(date, ua->argv[i], sizeof(date));
362 if (!has_value(ua, i)) {
366 bstrutime(date, sizeof(date), time(NULL));
368 if (!get_client_name(ua, rx)) {
371 pm_strcpy(ua->cmd, ua->argv[i]);
372 insert_one_file_or_dir(ua, rx, date, j==4);
373 if (rx->name_list.num_ids) {
374 /* Check MediaType and select storage that corresponds */
375 get_storage_from_mediatype(ua, &rx->name_list, rx);
381 bstrutime(date, sizeof(date), time(NULL));
383 if (!select_backups_before_date(ua, rx, date)) {
388 case 6: /* pool specified */
389 if (!has_value(ua, i)) {
392 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
394 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
397 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
399 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
403 case 7: /* all specified */
407 * All keywords 7 or greater are ignored or handled by a select prompt
413 if (rx->name_list.num_ids) {
414 return 2; /* filename list made */
418 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
419 "to be restored. You will be presented several methods\n"
420 "of specifying the JobIds. Then you will be allowed to\n"
421 "select which files from those JobIds are to be restored.\n\n"));
424 /* If choice not already made above, prompt */
430 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
431 for (int i=0; list[i]; i++) {
432 add_prompt(ua, list[i]);
435 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
436 case -1: /* error or cancel */
438 case 0: /* list last 20 Jobs run */
439 gui_save = ua->jcr->gui;
441 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
442 ua->jcr->gui = gui_save;
445 case 1: /* list where a file is saved */
446 if (!get_client_name(ua, rx)) {
449 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
452 len = strlen(ua->cmd);
453 fname = (char *)malloc(len * 2 + 1);
454 db_escape_string(fname, ua->cmd, len);
455 Mmsg(rx->query, uar_file, rx->ClientName, fname);
457 gui_save = ua->jcr->gui;
459 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
460 ua->jcr->gui = gui_save;
463 case 2: /* enter a list of JobIds */
464 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
467 pm_strcpy(rx->JobIds, ua->cmd);
469 case 3: /* Enter an SQL list command */
470 if (!get_cmd(ua, _("Enter SQL list command: "))) {
473 gui_save = ua->jcr->gui;
475 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
476 ua->jcr->gui = gui_save;
479 case 4: /* Select the most recent backups */
480 bstrutime(date, sizeof(date), time(NULL));
481 if (!select_backups_before_date(ua, rx, date)) {
485 case 5: /* select backup at specified time */
486 if (!get_date(ua, date, sizeof(date))) {
489 if (!select_backups_before_date(ua, rx, date)) {
493 case 6: /* Enter files */
494 bstrutime(date, sizeof(date), time(NULL));
495 if (!get_client_name(ua, rx)) {
498 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
499 "containg a list of file names with paths, and terminate\n"
500 "them with a blank line.\n"));
502 if (!get_cmd(ua, _("Enter full filename: "))) {
505 len = strlen(ua->cmd);
509 insert_one_file_or_dir(ua, rx, date, false);
511 /* Check MediaType and select storage that corresponds */
512 if (rx->name_list.num_ids) {
513 get_storage_from_mediatype(ua, &rx->name_list, rx);
516 case 7: /* enter files backed up before specified time */
517 if (!get_date(ua, date, sizeof(date))) {
520 if (!get_client_name(ua, rx)) {
523 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
524 "containg a list of file names with paths, and terminate\n"
525 "them with a blank line.\n"));
527 if (!get_cmd(ua, _("Enter full filename: "))) {
530 len = strlen(ua->cmd);
534 insert_one_file_or_dir(ua, rx, date, false);
536 /* Check MediaType and select storage that corresponds */
537 if (rx->name_list.num_ids) {
538 get_storage_from_mediatype(ua, &rx->name_list, rx);
542 case 8: /* Find JobIds for current backup */
543 bstrutime(date, sizeof(date), time(NULL));
544 if (!select_backups_before_date(ua, rx, date)) {
550 case 9: /* Find JobIds for give date */
551 if (!get_date(ua, date, sizeof(date))) {
554 if (!select_backups_before_date(ua, rx, date)) {
560 case 10: /* Enter directories */
561 if (*rx->JobIds != 0) {
562 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
564 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
565 if (*rx->JobIds != 0 && *ua->cmd) {
566 pm_strcat(rx->JobIds, ",");
568 pm_strcat(rx->JobIds, ua->cmd);
570 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
571 return 0; /* nothing entered, return */
573 bstrutime(date, sizeof(date), time(NULL));
574 if (!get_client_name(ua, rx)) {
577 bsendmsg(ua, _("Enter full directory names or start the name\n"
578 "with a < to indicate it is a filename containg a list\n"
579 "of directories and terminate them with a blank line.\n"));
581 if (!get_cmd(ua, _("Enter directory name: "))) {
584 len = strlen(ua->cmd);
588 /* Add trailing slash to end of directory names */
589 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
590 strcat(ua->cmd, "/");
592 insert_one_file_or_dir(ua, rx, date, true);
594 /* Check MediaType and select storage that corresponds */
595 if (rx->name_list.num_ids) {
596 get_storage_from_mediatype(ua, &rx->name_list, rx);
600 case 11: /* Cancel or quit */
605 if (*rx->JobIds == 0) {
606 bsendmsg(ua, _("No Jobs selected.\n"));
609 if (strchr(rx->JobIds,',')) {
610 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
613 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
618 for (p=rx->JobIds; ; ) {
619 int stat = get_next_jobid_from_list(&p, &JobId);
621 bsendmsg(ua, _("Invalid JobId in list.\n"));
627 if (jr.JobId == JobId) {
628 continue; /* duplicate of last JobId */
630 memset(&jr, 0, sizeof(JOB_DBR));
632 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
634 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
635 edit_int64(JobId, ed1), db_strerror(ua->db));
638 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
639 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
643 rx->TotalFiles += jr.JobFiles;
651 static int get_date(UAContext *ua, char *date, int date_len)
653 bsendmsg(ua, _("The restored files will the most current backup\n"
654 "BEFORE the date you specify below.\n\n"));
656 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
659 if (str_to_utime(ua->cmd) != 0) {
662 bsendmsg(ua, _("Improper date format.\n"));
664 bstrncpy(date, ua->cmd, date_len);
669 * Insert a single file, or read a list of files from a file
671 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
681 if ((ffd = fopen(p, "r")) == NULL) {
683 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
687 while (fgets(file, sizeof(file), ffd)) {
690 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
691 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
694 if (!insert_file_into_findex_list(ua, rx, file, date)) {
695 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
703 insert_table_into_findex_list(ua, rx, p);
707 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
709 insert_file_into_findex_list(ua, rx, ua->cmd, date);
716 * For a given file (path+filename), split into path and file, then
717 * lookup the most recent backup in the catalog to get the JobId
718 * and FileIndex, then insert them into the findex list.
720 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
725 strip_trailing_junk(file);
726 split_path_and_filename(rx, file);
727 if (*rx->JobIds == 0) {
728 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
731 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
732 rx->path, rx->fname, rx->ClientName);
735 /* Find and insert jobid and File Index */
736 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
737 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
738 rx->query, db_strerror(ua->db));
741 bsendmsg(ua, _("No database record found for: %s\n"), file);
745 * Find the MediaTypes for this JobId and add to the name_list
747 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
748 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
749 bsendmsg(ua, "%s", db_strerror(ua->db));
756 * For a given path lookup the most recent backup in the catalog
757 * to get the JobId and FileIndexes of all files in that directory.
759 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
764 strip_trailing_junk(dir);
765 if (*rx->JobIds == 0) {
766 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
769 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
770 dir, rx->ClientName);
773 /* Find and insert jobid and File Index */
774 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
775 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
776 rx->query, db_strerror(ua->db));
779 bsendmsg(ua, _("No database record found for: %s\n"), dir);
783 * Find the MediaTypes for this JobId and add to the name_list
785 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
786 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
787 bsendmsg(ua, "%s", db_strerror(ua->db));
794 * Get the JobId and FileIndexes of all files in the specified table
796 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
800 strip_trailing_junk(table);
801 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
804 /* Find and insert jobid and File Index */
805 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
806 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
807 rx->query, db_strerror(ua->db));
810 bsendmsg(ua, _("No table found: %s\n"), table);
814 * Find the MediaTypes for this JobId and add to the name_list
816 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
817 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
818 bsendmsg(ua, "%s", db_strerror(ua->db));
824 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
828 /* Find path without the filename.
829 * I.e. everything after the last / is a "filename".
830 * OK, maybe it is a directory name, but we treat it like
831 * a filename. If we don't find a / then the whole name
832 * must be a path name (e.g. c:).
834 for (p=f=name; *p; p++) {
836 f = p; /* set pos of last slash */
839 if (*f == '/') { /* did we find a slash? */
840 f++; /* yes, point to filename */
841 } else { /* no, whole thing must be path name */
845 /* If filename doesn't exist (i.e. root directory), we
846 * simply create a blank name consisting of a single
847 * space. This makes handling zero length filenames
852 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
853 memcpy(rx->fname, f, rx->fnl); /* copy filename */
854 rx->fname[rx->fnl] = 0;
862 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
863 memcpy(rx->path, name, rx->pnl);
864 rx->path[rx->pnl] = 0;
870 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
873 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
876 JobId_t JobId, last_JobId;
881 memset(&tree, 0, sizeof(TREE_CTX));
883 * Build the directory tree containing JobIds user selected
885 tree.root = new_tree(rx->TotalFiles);
890 * For display purposes, the same JobId, with different volumes may
891 * appear more than once, however, we only insert it once.
895 tree.FileEstimate = 0;
896 if (get_next_jobid_from_list(&p, &JobId) > 0) {
897 /* Use first JobId as estimate of the number of files to restore */
898 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
899 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
900 bsendmsg(ua, "%s\n", db_strerror(ua->db));
903 /* Add about 25% more than this job for over estimate */
904 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
905 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
908 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
911 if (JobId == last_JobId) {
912 continue; /* eliminate duplicate JobIds */
915 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
916 edit_int64(JobId, ed1));
919 * Find files for this JobId and insert them in the tree
921 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
922 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
923 bsendmsg(ua, "%s", db_strerror(ua->db));
926 * Find the MediaTypes for this JobId and add to the name_list
928 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
929 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
930 bsendmsg(ua, "%s", db_strerror(ua->db));
933 if (tree.FileCount == 0) {
934 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
935 "is not possible.Most likely your retention policy pruned the files\n"));
936 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
940 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
941 if (JobId == last_JobId) {
942 continue; /* eliminate duplicate JobIds */
944 add_findex_all(rx->bsr, JobId);
952 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
953 edit_uint64_with_commas(tree.FileCount, ec1));
956 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
957 edit_uint64_with_commas(tree.FileCount, ec1));
962 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
963 items, edit_uint64_with_commas(tree.FileCount, ec1));
966 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
967 items, edit_uint64_with_commas(tree.FileCount, ec1));
971 /* Check MediaType and select storage that corresponds */
972 get_storage_from_mediatype(ua, &rx->name_list, rx);
974 if (find_arg(ua, NT_("done")) < 0) {
975 /* Let the user interact in selecting which files to restore */
976 OK = user_select_files_from_tree(&tree);
980 * Walk down through the tree finding all files marked to be
981 * extracted making a bootstrap file.
984 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
985 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
986 if (node->extract || node->extract_dir) {
987 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
988 add_findex(rx->bsr, node->JobId, node->FileIndex);
989 if (node->extract && node->type != TN_NEWDIR) {
990 rx->selected_files++; /* count only saved files */
997 free_tree(tree.root); /* free the directory tree */
1003 * This routine is used to get the current backup or a backup
1004 * before the specified date.
1006 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1011 char fileset_name[MAX_NAME_LENGTH];
1012 char ed1[50], ed2[50];
1013 char pool_select[MAX_NAME_LENGTH];
1017 /* Create temp tables */
1018 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1019 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1020 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
1021 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1023 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
1024 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1027 * Select Client from the Catalog
1029 memset(&cr, 0, sizeof(cr));
1030 if (!get_client_dbr(ua, &cr)) {
1033 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1038 memset(&fsr, 0, sizeof(fsr));
1039 i = find_arg_with_value(ua, "FileSet");
1041 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1042 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1043 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1044 db_strerror(ua->db));
1048 if (i < 0) { /* fileset not found */
1049 edit_int64(cr.ClientId, ed1);
1050 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1051 start_prompt(ua, _("The defined FileSet resources are:\n"));
1052 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1053 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1055 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1056 fileset_name, sizeof(fileset_name)) < 0) {
1060 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1061 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1062 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1063 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1064 "Continuing anyway.\n"));
1068 /* If Pool specified, add PoolId specification */
1072 memset(&pr, 0, sizeof(pr));
1073 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1074 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1075 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1076 edit_int64(pr.PoolId, ed1));
1078 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1082 /* Find JobId of last Full backup for this client, fileset */
1083 edit_int64(cr.ClientId, ed1);
1084 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1086 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1087 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1091 /* Find all Volumes used by that JobId */
1092 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1093 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1096 /* Note, this is needed because I don't seem to get the callback
1097 * from the call just above.
1100 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1101 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1103 if (rx->JobTDate == 0) {
1104 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1108 /* Now find most recent Differental Job after Full save, if any */
1109 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1110 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1111 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1112 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1114 /* Now update JobTDate to lock onto Differental, if any */
1116 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1117 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1119 if (rx->JobTDate == 0) {
1120 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1124 /* Now find all Incremental Jobs after Full/dif save */
1125 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1126 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1127 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1128 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1131 /* Get the JobIds from that list */
1133 rx->last_jobid[0] = 0;
1134 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1135 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1138 if (rx->JobIds[0] != 0) {
1139 /* Display a list of Jobs selected for this restore */
1140 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1143 bsendmsg(ua, _("No jobs found.\n"));
1147 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1148 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1154 * Return next JobId from comma separated list
1157 * 1 if next JobId returned
1158 * 0 if no more JobIds are in list
1159 * -1 there is an error
1161 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1167 for (int i=0; i<(int)sizeof(jobid); i++) {
1170 } else if (*q == ',') {
1177 if (jobid[0] == 0) {
1179 } else if (!is_a_number(jobid)) {
1180 return -1; /* error */
1183 *JobId = str_to_int64(jobid);
1187 static int count_handler(void *ctx, int num_fields, char **row)
1189 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1190 rx->JobId = str_to_int64(row[0]);
1196 * Callback handler to get JobId and FileIndex for files
1197 * can insert more than one depending on the caller.
1199 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1201 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1202 rx->JobId = str_to_int64(row[0]);
1203 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1205 rx->selected_files++;
1210 * Callback handler make list of JobIds
1212 static int jobid_handler(void *ctx, int num_fields, char **row)
1214 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1216 if (strcmp(rx->last_jobid, row[0]) == 0) {
1217 return 0; /* duplicate id */
1219 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1220 if (rx->JobIds[0] != 0) {
1221 pm_strcat(rx->JobIds, ",");
1223 pm_strcat(rx->JobIds, row[0]);
1229 * Callback handler to pickup last Full backup JobTDate
1231 static int last_full_handler(void *ctx, int num_fields, char **row)
1233 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1235 rx->JobTDate = str_to_int64(row[1]);
1240 * Callback handler build FileSet name prompt list
1242 static int fileset_handler(void *ctx, int num_fields, char **row)
1244 /* row[0] = FileSet (name) */
1246 add_prompt((UAContext *)ctx, row[0]);
1252 * Called here with each name to be added to the list. The name is
1253 * added to the list if it is not already in the list.
1255 * Used to make unique list of FileSets and MediaTypes
1257 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1259 NAME_LIST *name = (NAME_LIST *)ctx;
1261 if (name->num_ids == MAX_ID_LIST_LEN) {
1264 if (name->num_ids == name->max_ids) {
1265 if (name->max_ids == 0) {
1266 name->max_ids = 1000;
1267 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1269 name->max_ids = (name->max_ids * 3) / 2;
1270 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1273 for (int i=0; i<name->num_ids; i++) {
1274 if (strcmp(name->name[i], row[0]) == 0) {
1275 return 0; /* already in list, return */
1278 /* Add new name to list */
1279 name->name[name->num_ids++] = bstrdup(row[0]);
1285 * Print names in the list
1287 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1289 for (int i=0; i < name_list->num_ids; i++) {
1290 bsendmsg(ua, "%s\n", name_list->name[i]);
1296 * Free names in the list
1298 static void free_name_list(NAME_LIST *name_list)
1300 for (int i=0; i < name_list->num_ids; i++) {
1301 free(name_list->name[i]);
1303 if (name_list->name) {
1304 free(name_list->name);
1305 name_list->name = NULL;
1307 name_list->max_ids = 0;
1308 name_list->num_ids = 0;
1311 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1315 if (name_list->num_ids > 1) {
1316 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1317 "Restore is not possible. The MediaTypes used are:\n"));
1318 print_name_list(ua, name_list);
1319 rx->store = select_storage_resource(ua);
1323 if (name_list->num_ids == 0) {
1324 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1325 rx->store = select_storage_resource(ua);
1332 * We have a single MediaType, look it up in our Storage resource
1335 foreach_res(store, R_STORAGE) {
1336 if (strcmp(name_list->name[0], store->media_type) == 0) {
1337 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1346 /* Check if an explicit storage resource is given */
1348 int i = find_arg_with_value(ua, "storage");
1350 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1351 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1355 if (store && (store != rx->store)) {
1356 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1363 /* Take command line arg, or ask user if none */
1364 rx->store = get_storage_resource(ua, false /* don't use default */);
1367 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1368 "MediaType \"%s\", needed by the Jobs you selected.\n"
1369 "You will be allowed to select a Storage device later.\n"),
1370 name_list->name[0]);