2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Database restore Command
31 * Creates a bootstrap file for restoring files and
32 * starts the restore job.
34 * Tree handling routines split into ua_tree.c July MMIII.
35 * BSR (bootstrap record) handling routines split into
38 * Kern Sibbald, July MMII
47 /* Imported functions */
48 extern void print_bsr(UAContext *ua, RBSR *bsr);
52 /* Forward referenced functions */
53 static int last_full_handler(void *ctx, int num_fields, char **row);
54 static int jobid_handler(void *ctx, int num_fields, char **row);
55 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
56 static int fileset_handler(void *ctx, int num_fields, char **row);
57 static void free_name_list(NAME_LIST *name_list);
58 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
59 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
60 static void free_rx(RESTORE_CTX *rx);
61 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *fname);
62 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
63 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
65 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
67 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
68 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
69 static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx);
70 static int get_date(UAContext *ua, char *date, int date_len);
71 static int restore_count_handler(void *ctx, int num_fields, char **row);
72 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
78 int restore_cmd(UAContext *ua, const char *cmd)
80 RESTORE_CTX rx; /* restore context */
84 char *escaped_bsr_name = NULL;
85 char *escaped_where_name = NULL;
86 char *strip_prefix, *add_prefix, *add_suffix, *regexp;
87 strip_prefix = add_prefix = add_suffix = regexp = NULL;
89 memset(&rx, 0, sizeof(rx));
90 rx.path = get_pool_memory(PM_FNAME);
91 rx.fname = get_pool_memory(PM_FNAME);
92 rx.JobIds = get_pool_memory(PM_FNAME);
93 rx.query = get_pool_memory(PM_FNAME);
96 i = find_arg_with_value(ua, "where");
98 rx.where = ua->argv[i];
101 i = find_arg_with_value(ua, "strip_prefix");
103 strip_prefix = ua->argv[i];
106 i = find_arg_with_value(ua, "add_prefix");
108 add_prefix = ua->argv[i];
111 i = find_arg_with_value(ua, "add_suffix");
113 add_suffix = ua->argv[i];
116 i = find_arg_with_value(ua, "regexwhere");
118 rx.RegexWhere = ua->argv[i];
121 if (strip_prefix || add_suffix || add_prefix) {
122 int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
123 regexp = (char *)bmalloc(len * sizeof(char));
125 bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
126 rx.RegexWhere = regexp;
129 /* TODO: add acl for regexwhere ? */
132 if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere)) {
133 ua->error_msg(_("\"RegexWhere\" specification not authorized.\n"));
139 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
140 ua->error_msg(_("\"where\" specification not authorized.\n"));
145 if (!open_client_db(ua)) {
149 /* Ensure there is at least one Restore Job */
151 foreach_res(job, R_JOB) {
152 if (job->JobType == JT_RESTORE) {
153 if (!rx.restore_job) {
154 rx.restore_job = job;
160 if (!rx.restore_jobs) {
162 "No Restore Job Resource found in bacula-dir.conf.\n"
163 "You must create at least one before running this command.\n"));
168 * Request user to select JobIds or files by various different methods
169 * last 20 jobs, where File saved, most recent backup, ...
170 * In the end, a list of files are pumped into
173 switch (user_select_jobids_or_files(ua, &rx)) {
176 case 1: /* selected by jobid */
177 if (!build_directory_tree(ua, &rx)) {
178 ua->send_msg(_("Restore not done.\n"));
182 case 2: /* selected by filename, no tree needed */
187 uint32_t selected_files;
189 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
190 ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
193 if (!(selected_files = write_bsr_file(ua, rx))) {
194 ua->warning_msg(_("No files selected to be restored.\n"));
197 display_bsr_info(ua, rx); /* display vols needed, etc */
199 /* If no count of files, use bsr generated value (often wrong) */
200 if (rx.selected_files == 0) {
201 rx.selected_files = selected_files;
203 if (rx.selected_files==1) {
204 ua->info_msg(_("\n1 file selected to be restored.\n\n"));
207 ua->info_msg(_("\n%s files selected to be restored.\n\n"),
208 edit_uint64_with_commas(rx.selected_files, ed1));
211 ua->warning_msg(_("No files selected to be restored.\n"));
215 if (rx.restore_jobs == 1) {
216 job = rx.restore_job;
218 job = select_restore_job_resource(ua);
224 get_client_name(ua, &rx);
225 if (!rx.ClientName) {
226 ua->error_msg(_("No Client resource found!\n"));
229 get_restore_client_name(ua, rx);
231 escaped_bsr_name = escape_filename(jcr->RestoreBootstrap);
233 /* Build run command */
235 escaped_where_name = escape_filename(rx.RegexWhere);
237 "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
238 " bootstrap=\"%s\" regexwhere=\"%s\" files=%u catalog=\"%s\"",
239 job->name(), rx.ClientName, rx.RestoreClientName,
240 rx.store?rx.store->name():"",
241 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
242 escaped_where_name ? escaped_where_name : rx.RegexWhere,
243 rx.selected_files, ua->catalog->name());
245 } else if (rx.where) {
246 escaped_where_name = escape_filename(rx.where);
248 "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
249 " bootstrap=\"%s\" where=\"%s\" files=%u catalog=\"%s\"",
250 job->name(), rx.ClientName, rx.RestoreClientName,
251 rx.store?rx.store->name():"",
252 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
253 escaped_where_name ? escaped_where_name : rx.where,
254 rx.selected_files, ua->catalog->name());
258 "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
259 " bootstrap=\"%s\" files=%u catalog=\"%s\"",
260 job->name(), rx.ClientName, rx.RestoreClientName,
261 rx.store?rx.store->name():"",
262 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
263 rx.selected_files, ua->catalog->name());
266 if (escaped_bsr_name != NULL) {
267 bfree(escaped_bsr_name);
270 if (escaped_where_name != NULL) {
271 bfree(escaped_where_name);
278 if (find_arg(ua, NT_("yes")) > 0) {
279 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
281 Dmsg1(200, "Submitting: %s\n", ua->cmd);
283 run_cmd(ua, ua->cmd);
288 if (escaped_bsr_name != NULL) {
289 bfree(escaped_bsr_name);
292 if (escaped_where_name != NULL) {
293 bfree(escaped_where_name);
305 static void free_rx(RESTORE_CTX *rx)
310 free_pool_memory(rx->JobIds);
314 free_pool_memory(rx->fname);
318 free_pool_memory(rx->path);
322 free_pool_memory(rx->query);
325 free_name_list(&rx->name_list);
328 static bool has_value(UAContext *ua, int i)
331 ua->error_msg(_("Missing value for keyword: %s\n"), ua->argk[i]);
338 * This gets the client name from which the backup was made
340 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
342 /* If no client name specified yet, get it now */
343 if (!rx->ClientName[0]) {
345 /* try command line argument */
346 int i = find_arg_with_value(ua, NT_("client"));
348 i = find_arg_with_value(ua, NT_("backupclient"));
351 if (!has_value(ua, i)) {
354 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
357 memset(&cr, 0, sizeof(cr));
358 if (!get_client_dbr(ua, &cr)) {
361 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
367 * This is where we pick up a client name to restore to.
369 static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx)
371 /* Start with same name as backup client */
372 bstrncpy(rx.RestoreClientName, rx.ClientName, sizeof(rx.RestoreClientName));
374 /* try command line argument */
375 int i = find_arg_with_value(ua, NT_("restoreclient"));
377 if (!has_value(ua, i)) {
380 bstrncpy(rx.RestoreClientName, ua->argv[i], sizeof(rx.RestoreClientName));
389 * The first step in the restore process is for the user to
390 * select a list of JobIds from which he will subsequently
391 * select which files are to be restored.
393 * Returns: 2 if filename list made
394 * 1 if jobid list made
397 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
400 char date[MAX_TIME_LENGTH];
401 bool have_date = false;
402 /* Include current second if using current time */
403 utime_t now = time(NULL) + 1;
405 JOB_DBR jr = { (JobId_t)-1 };
408 const char *list[] = {
409 _("List last 20 Jobs run"),
410 _("List Jobs where a given File is saved"),
411 _("Enter list of comma separated JobIds to select"),
412 _("Enter SQL list command"),
413 _("Select the most recent backup for a client"),
414 _("Select backup for a client before a specified time"),
415 _("Enter a list of files to restore"),
416 _("Enter a list of files to restore before a specified time"),
417 _("Find the JobIds of the most recent backup for a client"),
418 _("Find the JobIds for a backup for a client before a specified time"),
419 _("Enter a list of directories to restore for found JobIds"),
420 _("Select full restore to a specified JobId"),
425 /* These keywords are handled in a for loop */
435 /* The keyword below are handled by individual arg lookups */
441 "bootstrap", /* 13 */
443 "strip_prefix", /* 15 */
444 "add_prefix", /* 16 */
445 "add_suffix", /* 17 */
446 "regexwhere", /* 18 */
447 "restoreclient", /* 19 */
454 for (i=1; i<ua->argc; i++) { /* loop through arguments */
455 bool found_kw = false;
456 for (j=0; kw[j]; j++) { /* loop through keywords */
457 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
463 ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]);
466 /* Found keyword in kw[] list, process it */
469 if (!has_value(ua, i)) {
472 if (*rx->JobIds != 0) {
473 pm_strcat(rx->JobIds, ",");
475 pm_strcat(rx->JobIds, ua->argv[i]);
478 case 1: /* current */
480 * Note, we add one second here just to include any job
481 * that may have finished within the current second,
482 * which happens a lot in scripting small jobs.
484 bstrutime(date, sizeof(date), now);
488 if (have_date || !has_value(ua, i)) {
491 if (str_to_utime(ua->argv[i]) == 0) {
492 ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
495 bstrncpy(date, ua->argv[i], sizeof(date));
500 if (!has_value(ua, i)) {
504 bstrutime(date, sizeof(date), now);
506 if (!get_client_name(ua, rx)) {
509 pm_strcpy(ua->cmd, ua->argv[i]);
510 insert_one_file_or_dir(ua, rx, date, j==4);
514 bstrutime(date, sizeof(date), now);
516 if (!select_backups_before_date(ua, rx, date)) {
521 case 6: /* pool specified */
522 if (!has_value(ua, i)) {
525 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
527 ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
530 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
532 ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
536 case 7: /* all specified */
540 * All keywords 7 or greater are ignored or handled by a select prompt
548 ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
549 "to be restored. You will be presented several methods\n"
550 "of specifying the JobIds. Then you will be allowed to\n"
551 "select which files from those JobIds are to be restored.\n\n"));
554 /* If choice not already made above, prompt */
561 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
562 for (int i=0; list[i]; i++) {
563 add_prompt(ua, list[i]);
566 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
567 case -1: /* error or cancel */
569 case 0: /* list last 20 Jobs run */
570 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
571 ua->error_msg(_("SQL query not authorized.\n"));
574 gui_save = ua->jcr->gui;
576 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
577 ua->jcr->gui = gui_save;
580 case 1: /* list where a file is saved */
581 if (!get_client_name(ua, rx)) {
584 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
587 len = strlen(ua->cmd);
588 fname = (char *)malloc(len * 2 + 1);
589 db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len);
590 Mmsg(rx->query, uar_file[db_type], rx->ClientName, fname);
592 gui_save = ua->jcr->gui;
594 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
595 ua->jcr->gui = gui_save;
598 case 2: /* enter a list of JobIds */
599 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
602 pm_strcpy(rx->JobIds, ua->cmd);
604 case 3: /* Enter an SQL list command */
605 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
606 ua->error_msg(_("SQL query not authorized.\n"));
609 if (!get_cmd(ua, _("Enter SQL list command: "))) {
612 gui_save = ua->jcr->gui;
614 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
615 ua->jcr->gui = gui_save;
618 case 4: /* Select the most recent backups */
620 bstrutime(date, sizeof(date), now);
622 if (!select_backups_before_date(ua, rx, date)) {
626 case 5: /* select backup at specified time */
628 if (!get_date(ua, date, sizeof(date))) {
632 if (!select_backups_before_date(ua, rx, date)) {
636 case 6: /* Enter files */
638 bstrutime(date, sizeof(date), now);
640 if (!get_client_name(ua, rx)) {
643 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
644 "containing a list of file names with paths, and terminate\n"
645 "them with a blank line.\n"));
647 if (!get_cmd(ua, _("Enter full filename: "))) {
650 len = strlen(ua->cmd);
654 insert_one_file_or_dir(ua, rx, date, false);
657 case 7: /* enter files backed up before specified time */
659 if (!get_date(ua, date, sizeof(date))) {
663 if (!get_client_name(ua, rx)) {
666 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
667 "containing a list of file names with paths, and terminate\n"
668 "them with a blank line.\n"));
670 if (!get_cmd(ua, _("Enter full filename: "))) {
673 len = strlen(ua->cmd);
677 insert_one_file_or_dir(ua, rx, date, false);
681 case 8: /* Find JobIds for current backup */
683 bstrutime(date, sizeof(date), now);
685 if (!select_backups_before_date(ua, rx, date)) {
691 case 9: /* Find JobIds for give date */
693 if (!get_date(ua, date, sizeof(date))) {
697 if (!select_backups_before_date(ua, rx, date)) {
703 case 10: /* Enter directories */
704 if (*rx->JobIds != 0) {
705 ua->send_msg(_("You have already selected the following JobIds: %s\n"),
707 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
708 if (*rx->JobIds != 0 && *ua->cmd) {
709 pm_strcat(rx->JobIds, ",");
711 pm_strcat(rx->JobIds, ua->cmd);
713 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
714 return 0; /* nothing entered, return */
717 bstrutime(date, sizeof(date), now);
719 if (!get_client_name(ua, rx)) {
722 ua->send_msg(_("Enter full directory names or start the name\n"
723 "with a < to indicate it is a filename containing a list\n"
724 "of directories and terminate them with a blank line.\n"));
726 if (!get_cmd(ua, _("Enter directory name: "))) {
729 len = strlen(ua->cmd);
733 /* Add trailing slash to end of directory names */
734 if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
735 strcat(ua->cmd, "/");
737 insert_one_file_or_dir(ua, rx, date, true);
741 case 11: /* Choose a jobid and select jobs */
742 if (!get_cmd(ua, _("Enter JobId to restore: ")) ||
743 !is_an_integer(ua->cmd))
748 memset(&jr, 0, sizeof(JOB_DBR));
749 jr.JobId = str_to_int64(ua->cmd);
750 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
751 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
752 ua->cmd, db_strerror(ua->db));
755 jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
756 if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) {
759 pm_strcpy(rx->JobIds, jobids.list);
760 Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds);
762 case 12: /* Cancel or quit */
767 memset(&jr, 0, sizeof(JOB_DBR));
768 POOLMEM *JobIds = get_pool_memory(PM_FNAME);
772 * Find total number of files to be restored, and filter the JobId
773 * list to contain only ones permitted by the ACL conditions.
775 for (p=rx->JobIds; ; ) {
777 int stat = get_next_jobid_from_list(&p, &JobId);
779 ua->error_msg(_("Invalid JobId in list.\n"));
780 free_pool_memory(JobIds);
786 if (jr.JobId == JobId) {
787 continue; /* duplicate of last JobId */
789 memset(&jr, 0, sizeof(JOB_DBR));
791 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
792 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
793 edit_int64(JobId, ed1), db_strerror(ua->db));
794 free_pool_memory(JobIds);
797 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
798 ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"),
799 edit_int64(JobId, ed1), jr.Name);
803 pm_strcat(JobIds, ",");
805 pm_strcat(JobIds, edit_int64(JobId, ed1));
806 rx->TotalFiles += jr.JobFiles;
808 free_pool_memory(rx->JobIds);
809 rx->JobIds = JobIds; /* Set ACL filtered list */
810 if (*rx->JobIds == 0) {
811 ua->warning_msg(_("No Jobs selected.\n"));
814 if (strchr(rx->JobIds,',')) {
815 ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds);
817 ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds);
825 static int get_date(UAContext *ua, char *date, int date_len)
827 ua->send_msg(_("The restored files will the most current backup\n"
828 "BEFORE the date you specify below.\n\n"));
830 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
833 if (str_to_utime(ua->cmd) != 0) {
836 ua->error_msg(_("Improper date format.\n"));
838 bstrncpy(date, ua->cmd, date_len);
843 * Insert a single file, or read a list of files from a file
845 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
855 if ((ffd = fopen(p, "rb")) == NULL) {
857 ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
861 while (fgets(file, sizeof(file), ffd)) {
864 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
865 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
868 if (!insert_file_into_findex_list(ua, rx, file, date)) {
869 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
877 insert_table_into_findex_list(ua, rx, p);
881 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
883 insert_file_into_findex_list(ua, rx, ua->cmd, date);
890 * For a given file (path+filename), split into path and file, then
891 * lookup the most recent backup in the catalog to get the JobId
892 * and FileIndex, then insert them into the findex list.
894 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
897 strip_trailing_newline(file);
898 split_path_and_filename(ua, rx, file);
899 if (*rx->JobIds == 0) {
900 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
903 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
904 rx->path, rx->fname, rx->ClientName);
907 /* Find and insert jobid and File Index */
908 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
909 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
910 rx->query, db_strerror(ua->db));
913 ua->error_msg(_("No database record found for: %s\n"), file);
914 // ua->error_msg("Query=%s\n", rx->query);
921 * For a given path lookup the most recent backup in the catalog
922 * to get the JobId and FileIndexes of all files in that directory.
924 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
927 strip_trailing_junk(dir);
928 if (*rx->JobIds == 0) {
929 ua->error_msg(_("No JobId specified cannot continue.\n"));
932 Mmsg(rx->query, uar_jobid_fileindex_from_dir[db_type], rx->JobIds, dir, rx->ClientName);
935 /* Find and insert jobid and File Index */
936 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
937 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
938 rx->query, db_strerror(ua->db));
941 ua->error_msg(_("No database record found for: %s\n"), dir);
948 * Get the JobId and FileIndexes of all files in the specified table
950 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
952 strip_trailing_junk(table);
953 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
956 /* Find and insert jobid and File Index */
957 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
958 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
959 rx->query, db_strerror(ua->db));
962 ua->error_msg(_("No table found: %s\n"), table);
968 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *name)
972 /* Find path without the filename.
973 * I.e. everything after the last / is a "filename".
974 * OK, maybe it is a directory name, but we treat it like
975 * a filename. If we don't find a / then the whole name
976 * must be a path name (e.g. c:).
978 for (p=f=name; *p; p++) {
979 if (IsPathSeparator(*p)) {
980 f = p; /* set pos of last slash */
983 if (IsPathSeparator(*f)) { /* did we find a slash? */
984 f++; /* yes, point to filename */
985 } else { /* no, whole thing must be path name */
989 /* If filename doesn't exist (i.e. root directory), we
990 * simply create a blank name consisting of a single
991 * space. This makes handling zero length filenames
996 rx->fname = check_pool_memory_size(rx->fname, 2*(rx->fnl)+1);
997 db_escape_string(ua->jcr, ua->db, rx->fname, f, rx->fnl);
1005 rx->path = check_pool_memory_size(rx->path, 2*(rx->pnl)+1);
1006 db_escape_string(ua->jcr, ua->db, rx->path, name, rx->pnl);
1012 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
1015 static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
1017 if (find_arg(ua, NT_("all")) >= 0) { /* if user enters all on command line */
1018 return true; /* select everything */
1020 ua->send_msg(_("\n\nFor one or more of the JobIds selected, no files were found,\n"
1021 "so file selection is not possible.\n"
1022 "Most likely your retention policy pruned the files.\n"));
1023 if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
1024 if (ua->pint32_val == 1)
1026 while (get_cmd(ua, _("\nRegexp matching files to restore? (empty to abort): "))) {
1027 if (ua->cmd[0] == '\0') {
1030 regex_t *fileregex_re = NULL;
1032 char errmsg[500] = "";
1034 fileregex_re = (regex_t *)bmalloc(sizeof(regex_t));
1035 rc = regcomp(fileregex_re, ua->cmd, REG_EXTENDED|REG_NOSUB);
1037 regerror(rc, fileregex_re, errmsg, sizeof(errmsg));
1039 regfree(fileregex_re);
1042 ua->send_msg(_("Regex compile error: %s\n"), errmsg);
1044 rx->bsr->fileregex = bstrdup(ua->cmd);
1053 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
1056 JobId_t JobId, last_JobId;
1061 memset(&tree, 0, sizeof(TREE_CTX));
1063 * Build the directory tree containing JobIds user selected
1065 tree.root = new_tree(rx->TotalFiles);
1070 * For display purposes, the same JobId, with different volumes may
1071 * appear more than once, however, we only insert it once.
1074 tree.FileEstimate = 0;
1075 if (get_next_jobid_from_list(&p, &JobId) > 0) {
1076 /* Use first JobId as estimate of the number of files to restore */
1077 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
1078 if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1079 ua->error_msg("%s\n", db_strerror(ua->db));
1082 /* Add about 25% more than this job for over estimate */
1083 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
1084 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
1088 ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ... "),
1091 #define new_get_file_list
1092 #ifdef new_get_file_list
1093 if (!db_get_file_list(ua->jcr, ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) {
1094 ua->error_msg("%s", db_strerror(ua->db));
1097 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1100 if (JobId == last_JobId) {
1101 continue; /* eliminate duplicate JobIds */
1105 * Find files for this JobId and insert them in the tree
1107 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
1108 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
1109 ua->error_msg("%s", db_strerror(ua->db));
1114 * Look at the first JobId on the list (presumably the oldest) and
1115 * if it is marked purged, don't do the manual selection because
1116 * the Job was pruned, so the tree is incomplete.
1118 if (tree.FileCount != 0) {
1119 /* Find out if any Job is purged */
1120 Mmsg(rx->query, "SELECT SUM(PurgedFiles) FROM Job WHERE JobId IN (%s)", rx->JobIds);
1121 if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1122 ua->error_msg("%s\n", db_strerror(ua->db));
1124 /* rx->JobId is the PurgedFiles flag */
1125 if (rx->found && rx->JobId > 0) {
1126 tree.FileCount = 0; /* set count to zero, no tree selection */
1129 if (tree.FileCount == 0) {
1130 OK = ask_for_fileregex(ua, rx);
1133 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1134 if (JobId == last_JobId) {
1135 continue; /* eliminate duplicate JobIds */
1137 add_findex_all(rx->bsr, JobId);
1143 ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"),
1144 edit_uint64_with_commas(tree.FileCount, ec1));
1146 ua->info_msg(_("\n%s files inserted into the tree.\n"),
1147 edit_uint64_with_commas(tree.FileCount, ec1));
1150 if (find_arg(ua, NT_("done")) < 0) {
1151 /* Let the user interact in selecting which files to restore */
1152 OK = user_select_files_from_tree(&tree);
1156 * Walk down through the tree finding all files marked to be
1157 * extracted making a bootstrap file.
1160 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
1161 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
1162 if (node->extract || node->extract_dir) {
1163 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
1164 add_findex(rx->bsr, node->JobId, node->FileIndex);
1165 if (node->extract && node->type != TN_NEWDIR) {
1166 rx->selected_files++; /* count only saved files */
1173 free_tree(tree.root); /* free the directory tree */
1179 * This routine is used to get the current backup or a backup
1180 * before the specified date.
1182 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1187 char fileset_name[MAX_NAME_LENGTH];
1188 char ed1[50], ed2[50];
1189 char pool_select[MAX_NAME_LENGTH];
1192 /* Create temp tables */
1193 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1194 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1195 if (!db_sql_query(ua->db, uar_create_temp[db_type], NULL, NULL)) {
1196 ua->error_msg("%s\n", db_strerror(ua->db));
1198 if (!db_sql_query(ua->db, uar_create_temp1[db_type], NULL, NULL)) {
1199 ua->error_msg("%s\n", db_strerror(ua->db));
1202 * Select Client from the Catalog
1204 memset(&cr, 0, sizeof(cr));
1205 if (!get_client_dbr(ua, &cr)) {
1208 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1213 memset(&fsr, 0, sizeof(fsr));
1214 i = find_arg_with_value(ua, "FileSet");
1216 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1217 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1218 ua->error_msg(_("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1219 db_strerror(ua->db));
1223 if (i < 0) { /* fileset not found */
1224 edit_int64(cr.ClientId, ed1);
1225 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1226 start_prompt(ua, _("The defined FileSet resources are:\n"));
1227 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1228 ua->error_msg("%s\n", db_strerror(ua->db));
1230 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1231 fileset_name, sizeof(fileset_name)) < 0) {
1232 ua->error_msg(_("No FileSet found for client \"%s\".\n"), cr.Name);
1236 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1237 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1238 ua->warning_msg(_("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1239 ua->send_msg(_("This probably means you modified the FileSet.\n"
1240 "Continuing anyway.\n"));
1244 /* If Pool specified, add PoolId specification */
1248 memset(&pr, 0, sizeof(pr));
1249 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1250 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1251 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1252 edit_int64(pr.PoolId, ed1));
1254 ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1258 /* Find JobId of last Full backup for this client, fileset */
1259 edit_int64(cr.ClientId, ed1);
1260 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1262 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1263 ua->error_msg("%s\n", db_strerror(ua->db));
1267 /* Find all Volumes used by that JobId */
1268 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1269 ua->error_msg("%s\n", db_strerror(ua->db));
1273 /* Note, this is needed because I don't seem to get the callback
1274 * from the call just above.
1277 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1278 ua->warning_msg("%s\n", db_strerror(ua->db));
1280 if (rx->JobTDate == 0) {
1281 ua->error_msg(_("No Full backup before %s found.\n"), date);
1285 /* Now find most recent Differental Job after Full save, if any */
1286 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1287 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1288 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1289 ua->warning_msg("%s\n", db_strerror(ua->db));
1291 /* Now update JobTDate to look into Differental, if any */
1293 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1294 ua->warning_msg("%s\n", db_strerror(ua->db));
1296 if (rx->JobTDate == 0) {
1297 ua->error_msg(_("No Full backup before %s found.\n"), date);
1301 /* Now find all Incremental Jobs after Full/dif save */
1302 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1303 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1304 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1305 ua->warning_msg("%s\n", db_strerror(ua->db));
1308 /* Get the JobIds from that list */
1310 rx->last_jobid[0] = 0;
1311 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1312 ua->warning_msg("%s\n", db_strerror(ua->db));
1315 if (rx->JobIds[0] != 0) {
1316 if (find_arg(ua, NT_("copies")) > 0) {
1317 /* Display a list of all copies */
1318 db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds,
1319 prtit, ua, HORZ_LIST);
1321 /* Display a list of Jobs selected for this restore */
1322 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1325 ua->warning_msg(_("No jobs found.\n"));
1329 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1330 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1334 static int restore_count_handler(void *ctx, int num_fields, char **row)
1336 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1337 rx->JobId = str_to_int64(row[0]);
1343 * Callback handler to get JobId and FileIndex for files
1344 * can insert more than one depending on the caller.
1346 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1348 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1350 Dmsg2(200, "JobId=%s FileIndex=%s\n", row[0], row[1]);
1351 rx->JobId = str_to_int64(row[0]);
1352 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1354 rx->selected_files++;
1359 * Callback handler make list of JobIds
1361 static int jobid_handler(void *ctx, int num_fields, char **row)
1363 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1365 if (strcmp(rx->last_jobid, row[0]) == 0) {
1366 return 0; /* duplicate id */
1368 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1369 if (rx->JobIds[0] != 0) {
1370 pm_strcat(rx->JobIds, ",");
1372 pm_strcat(rx->JobIds, row[0]);
1378 * Callback handler to pickup last Full backup JobTDate
1380 static int last_full_handler(void *ctx, int num_fields, char **row)
1382 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1384 rx->JobTDate = str_to_int64(row[1]);
1389 * Callback handler build FileSet name prompt list
1391 static int fileset_handler(void *ctx, int num_fields, char **row)
1393 /* row[0] = FileSet (name) */
1395 add_prompt((UAContext *)ctx, row[0]);
1401 * Free names in the list
1403 static void free_name_list(NAME_LIST *name_list)
1405 for (int i=0; i < name_list->num_ids; i++) {
1406 free(name_list->name[i]);
1408 if (name_list->name) {
1409 free(name_list->name);
1410 name_list->name = NULL;
1412 name_list->max_ids = 0;
1413 name_list->num_ids = 0;
1416 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1421 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1425 * Try looking up Storage by name
1428 foreach_res(store, R_STORAGE) {
1429 if (strcmp(Storage, store->name()) == 0) {
1430 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1439 /* Check if an explicit storage resource is given */
1441 int i = find_arg_with_value(ua, "storage");
1443 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1444 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1448 if (store && (store != rx.store)) {
1449 ua->info_msg(_("Warning default storage overridden by \"%s\" on command line.\n"),
1452 Dmsg1(200, "Set store=%s\n", rx.store->name());
1457 /* If no storage resource, try to find one from MediaType */
1460 foreach_res(store, R_STORAGE) {
1461 if (strcmp(MediaType, store->media_type) == 0) {
1462 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1464 Dmsg1(200, "Set store=%s\n", rx.store->name());
1465 ua->warning_msg(_("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1466 Storage, store->name(), MediaType);
1473 ua->warning_msg(_("\nUnable to find Storage resource for\n"
1474 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1477 /* Take command line arg, or ask user if none */
1478 rx.store = get_storage_resource(ua, false /* don't use default */);
1480 Dmsg1(200, "Set store=%s\n", rx.store->name());