2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 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"),
424 /* These keywords are handled in a for loop */
434 /* The keyword below are handled by individual arg lookups */
440 "bootstrap", /* 13 */
442 "strip_prefix", /* 15 */
443 "add_prefix", /* 16 */
444 "add_suffix", /* 17 */
445 "regexwhere", /* 18 */
446 "restoreclient", /* 19 */
452 for (i=1; i<ua->argc; i++) { /* loop through arguments */
453 bool found_kw = false;
454 for (j=0; kw[j]; j++) { /* loop through keywords */
455 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
461 ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]);
464 /* Found keyword in kw[] list, process it */
467 if (!has_value(ua, i)) {
470 if (*rx->JobIds != 0) {
471 pm_strcat(rx->JobIds, ",");
473 pm_strcat(rx->JobIds, ua->argv[i]);
476 case 1: /* current */
478 * Note, we add one second here just to include any job
479 * that may have finished within the current second,
480 * which happens a lot in scripting small jobs.
482 bstrutime(date, sizeof(date), now);
486 if (have_date || !has_value(ua, i)) {
489 if (str_to_utime(ua->argv[i]) == 0) {
490 ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
493 bstrncpy(date, ua->argv[i], sizeof(date));
498 if (!has_value(ua, i)) {
502 bstrutime(date, sizeof(date), now);
504 if (!get_client_name(ua, rx)) {
507 pm_strcpy(ua->cmd, ua->argv[i]);
508 insert_one_file_or_dir(ua, rx, date, j==4);
512 bstrutime(date, sizeof(date), now);
514 if (!select_backups_before_date(ua, rx, date)) {
519 case 6: /* pool specified */
520 if (!has_value(ua, i)) {
523 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
525 ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
528 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
530 ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
534 case 7: /* all specified */
538 * All keywords 7 or greater are ignored or handled by a select prompt
546 ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
547 "to be restored. You will be presented several methods\n"
548 "of specifying the JobIds. Then you will be allowed to\n"
549 "select which files from those JobIds are to be restored.\n\n"));
552 /* If choice not already made above, prompt */
558 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
559 for (int i=0; list[i]; i++) {
560 add_prompt(ua, list[i]);
563 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
564 case -1: /* error or cancel */
566 case 0: /* list last 20 Jobs run */
567 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
568 ua->error_msg(_("SQL query not authorized.\n"));
571 gui_save = ua->jcr->gui;
573 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
574 ua->jcr->gui = gui_save;
577 case 1: /* list where a file is saved */
578 if (!get_client_name(ua, rx)) {
581 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
584 len = strlen(ua->cmd);
585 fname = (char *)malloc(len * 2 + 1);
586 db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len);
587 Mmsg(rx->query, uar_file[db_type], rx->ClientName, fname);
589 gui_save = ua->jcr->gui;
591 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
592 ua->jcr->gui = gui_save;
595 case 2: /* enter a list of JobIds */
596 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
599 pm_strcpy(rx->JobIds, ua->cmd);
601 case 3: /* Enter an SQL list command */
602 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
603 ua->error_msg(_("SQL query not authorized.\n"));
606 if (!get_cmd(ua, _("Enter SQL list command: "))) {
609 gui_save = ua->jcr->gui;
611 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
612 ua->jcr->gui = gui_save;
615 case 4: /* Select the most recent backups */
617 bstrutime(date, sizeof(date), now);
619 if (!select_backups_before_date(ua, rx, date)) {
623 case 5: /* select backup at specified time */
625 if (!get_date(ua, date, sizeof(date))) {
629 if (!select_backups_before_date(ua, rx, date)) {
633 case 6: /* Enter files */
635 bstrutime(date, sizeof(date), now);
637 if (!get_client_name(ua, rx)) {
640 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
641 "containing a list of file names with paths, and terminate\n"
642 "them with a blank line.\n"));
644 if (!get_cmd(ua, _("Enter full filename: "))) {
647 len = strlen(ua->cmd);
651 insert_one_file_or_dir(ua, rx, date, false);
654 case 7: /* enter files backed up before specified time */
656 if (!get_date(ua, date, sizeof(date))) {
660 if (!get_client_name(ua, rx)) {
663 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
664 "containing a list of file names with paths, and terminate\n"
665 "them with a blank line.\n"));
667 if (!get_cmd(ua, _("Enter full filename: "))) {
670 len = strlen(ua->cmd);
674 insert_one_file_or_dir(ua, rx, date, false);
678 case 8: /* Find JobIds for current backup */
680 bstrutime(date, sizeof(date), now);
682 if (!select_backups_before_date(ua, rx, date)) {
688 case 9: /* Find JobIds for give date */
690 if (!get_date(ua, date, sizeof(date))) {
694 if (!select_backups_before_date(ua, rx, date)) {
700 case 10: /* Enter directories */
701 if (*rx->JobIds != 0) {
702 ua->send_msg(_("You have already selected the following JobIds: %s\n"),
704 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
705 if (*rx->JobIds != 0 && *ua->cmd) {
706 pm_strcat(rx->JobIds, ",");
708 pm_strcat(rx->JobIds, ua->cmd);
710 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
711 return 0; /* nothing entered, return */
714 bstrutime(date, sizeof(date), now);
716 if (!get_client_name(ua, rx)) {
719 ua->send_msg(_("Enter full directory names or start the name\n"
720 "with a < to indicate it is a filename containing a list\n"
721 "of directories and terminate them with a blank line.\n"));
723 if (!get_cmd(ua, _("Enter directory name: "))) {
726 len = strlen(ua->cmd);
730 /* Add trailing slash to end of directory names */
731 if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
732 strcat(ua->cmd, "/");
734 insert_one_file_or_dir(ua, rx, date, true);
738 case 11: /* Cancel or quit */
743 POOLMEM *JobIds = get_pool_memory(PM_FNAME);
747 * Find total number of files to be restored, and filter the JobId
748 * list to contain only ones permitted by the ACL conditions.
750 for (p=rx->JobIds; ; ) {
752 int stat = get_next_jobid_from_list(&p, &JobId);
754 ua->error_msg(_("Invalid JobId in list.\n"));
755 free_pool_memory(JobIds);
761 if (jr.JobId == JobId) {
762 continue; /* duplicate of last JobId */
764 memset(&jr, 0, sizeof(JOB_DBR));
766 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
767 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
768 edit_int64(JobId, ed1), db_strerror(ua->db));
769 free_pool_memory(JobIds);
772 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
773 ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"),
774 edit_int64(JobId, ed1), jr.Name);
778 pm_strcat(JobIds, ",");
780 pm_strcat(JobIds, edit_int64(JobId, ed1));
781 rx->TotalFiles += jr.JobFiles;
783 free_pool_memory(rx->JobIds);
784 rx->JobIds = JobIds; /* Set ACL filtered list */
785 if (*rx->JobIds == 0) {
786 ua->warning_msg(_("No Jobs selected.\n"));
789 if (strchr(rx->JobIds,',')) {
790 ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds);
792 ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds);
800 static int get_date(UAContext *ua, char *date, int date_len)
802 ua->send_msg(_("The restored files will the most current backup\n"
803 "BEFORE the date you specify below.\n\n"));
805 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
808 if (str_to_utime(ua->cmd) != 0) {
811 ua->error_msg(_("Improper date format.\n"));
813 bstrncpy(date, ua->cmd, date_len);
818 * Insert a single file, or read a list of files from a file
820 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
830 if ((ffd = fopen(p, "rb")) == NULL) {
832 ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
836 while (fgets(file, sizeof(file), ffd)) {
839 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
840 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
843 if (!insert_file_into_findex_list(ua, rx, file, date)) {
844 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
852 insert_table_into_findex_list(ua, rx, p);
856 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
858 insert_file_into_findex_list(ua, rx, ua->cmd, date);
865 * For a given file (path+filename), split into path and file, then
866 * lookup the most recent backup in the catalog to get the JobId
867 * and FileIndex, then insert them into the findex list.
869 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
872 strip_trailing_newline(file);
873 split_path_and_filename(ua, rx, file);
874 if (*rx->JobIds == 0) {
875 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
878 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
879 rx->path, rx->fname, rx->ClientName);
882 /* Find and insert jobid and File Index */
883 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
884 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
885 rx->query, db_strerror(ua->db));
888 ua->error_msg(_("No database record found for: %s\n"), file);
889 // ua->error_msg("Query=%s\n", rx->query);
896 * For a given path lookup the most recent backup in the catalog
897 * to get the JobId and FileIndexes of all files in that directory.
899 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
902 strip_trailing_junk(dir);
903 if (*rx->JobIds == 0) {
904 ua->error_msg(_("No JobId specified cannot continue.\n"));
907 Mmsg(rx->query, uar_jobid_fileindex_from_dir[db_type], rx->JobIds, dir, rx->ClientName);
910 /* Find and insert jobid and File Index */
911 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
912 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
913 rx->query, db_strerror(ua->db));
916 ua->error_msg(_("No database record found for: %s\n"), dir);
923 * Get the JobId and FileIndexes of all files in the specified table
925 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
927 strip_trailing_junk(table);
928 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
931 /* Find and insert jobid and File Index */
932 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
933 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
934 rx->query, db_strerror(ua->db));
937 ua->error_msg(_("No table found: %s\n"), table);
943 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *name)
947 /* Find path without the filename.
948 * I.e. everything after the last / is a "filename".
949 * OK, maybe it is a directory name, but we treat it like
950 * a filename. If we don't find a / then the whole name
951 * must be a path name (e.g. c:).
953 for (p=f=name; *p; p++) {
954 if (IsPathSeparator(*p)) {
955 f = p; /* set pos of last slash */
958 if (IsPathSeparator(*f)) { /* did we find a slash? */
959 f++; /* yes, point to filename */
960 } else { /* no, whole thing must be path name */
964 /* If filename doesn't exist (i.e. root directory), we
965 * simply create a blank name consisting of a single
966 * space. This makes handling zero length filenames
971 rx->fname = check_pool_memory_size(rx->fname, 2*(rx->fnl)+1);
972 db_escape_string(ua->jcr, ua->db, rx->fname, f, rx->fnl);
980 rx->path = check_pool_memory_size(rx->path, 2*(rx->pnl)+1);
981 db_escape_string(ua->jcr, ua->db, rx->path, name, rx->pnl);
987 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
990 static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
992 ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
993 "is not possible.Most likely your retention policy pruned the files\n"));
994 if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
995 if (ua->pint32_val == 1)
997 while (get_cmd(ua, _("\nRegexp matching files to restore? (empty to abort): "))) {
998 if (ua->cmd[0] == '\0') {
1001 regex_t *fileregex_re = NULL;
1003 char errmsg[500] = "";
1005 fileregex_re = (regex_t *)bmalloc(sizeof(regex_t));
1006 rc = regcomp(fileregex_re, ua->cmd, REG_EXTENDED|REG_NOSUB);
1008 regerror(rc, fileregex_re, errmsg, sizeof(errmsg));
1009 regfree(fileregex_re);
1012 ua->send_msg(_("Regex compile error: %s\n"), errmsg);
1014 rx->bsr->fileregex = bstrdup(ua->cmd);
1023 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
1026 JobId_t JobId, last_JobId;
1031 memset(&tree, 0, sizeof(TREE_CTX));
1033 * Build the directory tree containing JobIds user selected
1035 tree.root = new_tree(rx->TotalFiles);
1040 * For display purposes, the same JobId, with different volumes may
1041 * appear more than once, however, we only insert it once.
1044 tree.FileEstimate = 0;
1045 if (get_next_jobid_from_list(&p, &JobId) > 0) {
1046 /* Use first JobId as estimate of the number of files to restore */
1047 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
1048 if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1049 ua->error_msg("%s\n", db_strerror(ua->db));
1052 /* Add about 25% more than this job for over estimate */
1053 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
1054 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
1058 ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ... "),
1061 #define new_get_file_list
1062 #ifdef new_get_file_list
1063 if (!db_get_file_list(ua->jcr, ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) {
1064 ua->error_msg("%s", db_strerror(ua->db));
1067 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1070 if (JobId == last_JobId) {
1071 continue; /* eliminate duplicate JobIds */
1075 * Find files for this JobId and insert them in the tree
1077 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
1078 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
1079 ua->error_msg("%s", db_strerror(ua->db));
1083 if (tree.FileCount == 0) {
1084 OK = ask_for_fileregex(ua, rx);
1087 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1088 if (JobId == last_JobId) {
1089 continue; /* eliminate duplicate JobIds */
1091 add_findex_all(rx->bsr, JobId);
1097 ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"),
1098 edit_uint64_with_commas(tree.FileCount, ec1));
1100 ua->info_msg(_("\n%s files inserted into the tree.\n"),
1101 edit_uint64_with_commas(tree.FileCount, ec1));
1104 if (find_arg(ua, NT_("done")) < 0) {
1105 /* Let the user interact in selecting which files to restore */
1106 OK = user_select_files_from_tree(&tree);
1110 * Walk down through the tree finding all files marked to be
1111 * extracted making a bootstrap file.
1114 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
1115 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
1116 if (node->extract || node->extract_dir) {
1117 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
1118 add_findex(rx->bsr, node->JobId, node->FileIndex);
1119 if (node->extract && node->type != TN_NEWDIR) {
1120 rx->selected_files++; /* count only saved files */
1127 free_tree(tree.root); /* free the directory tree */
1133 * This routine is used to get the current backup or a backup
1134 * before the specified date.
1136 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1141 char fileset_name[MAX_NAME_LENGTH];
1142 char ed1[50], ed2[50];
1143 char pool_select[MAX_NAME_LENGTH];
1146 /* Create temp tables */
1147 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1148 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1149 if (!db_sql_query(ua->db, uar_create_temp[db_type], NULL, NULL)) {
1150 ua->error_msg("%s\n", db_strerror(ua->db));
1152 if (!db_sql_query(ua->db, uar_create_temp1[db_type], NULL, NULL)) {
1153 ua->error_msg("%s\n", db_strerror(ua->db));
1156 * Select Client from the Catalog
1158 memset(&cr, 0, sizeof(cr));
1159 if (!get_client_dbr(ua, &cr)) {
1162 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1167 memset(&fsr, 0, sizeof(fsr));
1168 i = find_arg_with_value(ua, "FileSet");
1170 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1171 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1172 ua->error_msg(_("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1173 db_strerror(ua->db));
1177 if (i < 0) { /* fileset not found */
1178 edit_int64(cr.ClientId, ed1);
1179 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1180 start_prompt(ua, _("The defined FileSet resources are:\n"));
1181 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1182 ua->error_msg("%s\n", db_strerror(ua->db));
1184 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1185 fileset_name, sizeof(fileset_name)) < 0) {
1186 ua->error_msg(_("No FileSet found for client \"%s\".\n"), cr.Name);
1190 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1191 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1192 ua->warning_msg(_("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1193 ua->send_msg(_("This probably means you modified the FileSet.\n"
1194 "Continuing anyway.\n"));
1198 /* If Pool specified, add PoolId specification */
1202 memset(&pr, 0, sizeof(pr));
1203 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1204 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1205 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1206 edit_int64(pr.PoolId, ed1));
1208 ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1212 /* Find JobId of last Full backup for this client, fileset */
1213 edit_int64(cr.ClientId, ed1);
1214 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1216 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1217 ua->error_msg("%s\n", db_strerror(ua->db));
1221 /* Find all Volumes used by that JobId */
1222 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1223 ua->error_msg("%s\n", db_strerror(ua->db));
1227 /* Note, this is needed because I don't seem to get the callback
1228 * from the call just above.
1231 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1232 ua->warning_msg("%s\n", db_strerror(ua->db));
1234 if (rx->JobTDate == 0) {
1235 ua->error_msg(_("No Full backup before %s found.\n"), date);
1239 /* Now find most recent Differental Job after Full save, if any */
1240 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1241 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1242 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1243 ua->warning_msg("%s\n", db_strerror(ua->db));
1245 /* Now update JobTDate to lock onto Differental, if any */
1247 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1248 ua->warning_msg("%s\n", db_strerror(ua->db));
1250 if (rx->JobTDate == 0) {
1251 ua->error_msg(_("No Full backup before %s found.\n"), date);
1255 /* Now find all Incremental Jobs after Full/dif save */
1256 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1257 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1258 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1259 ua->warning_msg("%s\n", db_strerror(ua->db));
1262 /* Get the JobIds from that list */
1264 rx->last_jobid[0] = 0;
1265 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1266 ua->warning_msg("%s\n", db_strerror(ua->db));
1269 if (rx->JobIds[0] != 0) {
1270 /* Display a list of Jobs selected for this restore */
1271 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1274 ua->warning_msg(_("No jobs found.\n"));
1278 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1279 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1285 * Return next JobId from comma separated list
1288 * 1 if next JobId returned
1289 * 0 if no more JobIds are in list
1290 * -1 there is an error
1292 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1294 const int maxlen = 30;
1295 char jobid[maxlen+1];
1299 for (int i=0; i<maxlen; i++) {
1302 } else if (*q == ',') {
1309 if (jobid[0] == 0) {
1311 } else if (!is_a_number(jobid)) {
1312 return -1; /* error */
1315 *JobId = str_to_int64(jobid);
1319 static int restore_count_handler(void *ctx, int num_fields, char **row)
1321 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1322 rx->JobId = str_to_int64(row[0]);
1328 * Callback handler to get JobId and FileIndex for files
1329 * can insert more than one depending on the caller.
1331 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1333 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1335 Dmsg2(200, "JobId=%s FileIndex=%s\n", row[0], row[1]);
1336 rx->JobId = str_to_int64(row[0]);
1337 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1339 rx->selected_files++;
1344 * Callback handler make list of JobIds
1346 static int jobid_handler(void *ctx, int num_fields, char **row)
1348 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1350 if (strcmp(rx->last_jobid, row[0]) == 0) {
1351 return 0; /* duplicate id */
1353 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1354 if (rx->JobIds[0] != 0) {
1355 pm_strcat(rx->JobIds, ",");
1357 pm_strcat(rx->JobIds, row[0]);
1363 * Callback handler to pickup last Full backup JobTDate
1365 static int last_full_handler(void *ctx, int num_fields, char **row)
1367 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1369 rx->JobTDate = str_to_int64(row[1]);
1374 * Callback handler build FileSet name prompt list
1376 static int fileset_handler(void *ctx, int num_fields, char **row)
1378 /* row[0] = FileSet (name) */
1380 add_prompt((UAContext *)ctx, row[0]);
1386 * Free names in the list
1388 static void free_name_list(NAME_LIST *name_list)
1390 for (int i=0; i < name_list->num_ids; i++) {
1391 free(name_list->name[i]);
1393 if (name_list->name) {
1394 free(name_list->name);
1395 name_list->name = NULL;
1397 name_list->max_ids = 0;
1398 name_list->num_ids = 0;
1401 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1406 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1410 * Try looking up Storage by name
1413 foreach_res(store, R_STORAGE) {
1414 if (strcmp(Storage, store->name()) == 0) {
1415 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1424 /* Check if an explicit storage resource is given */
1426 int i = find_arg_with_value(ua, "storage");
1428 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1429 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1433 if (store && (store != rx.store)) {
1434 ua->info_msg(_("Warning default storage overridden by \"%s\" on command line.\n"),
1437 Dmsg1(200, "Set store=%s\n", rx.store->name());
1442 /* If no storage resource, try to find one from MediaType */
1445 foreach_res(store, R_STORAGE) {
1446 if (strcmp(MediaType, store->media_type) == 0) {
1447 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1449 Dmsg1(200, "Set store=%s\n", rx.store->name());
1450 ua->warning_msg(_("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1451 Storage, store->name(), MediaType);
1458 ua->warning_msg(_("\nUnable to find Storage resource for\n"
1459 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1462 /* Take command line arg, or ask user if none */
1463 rx.store = get_storage_resource(ua, false /* don't use default */);
1465 Dmsg1(200, "Set store=%s\n", rx.store->name());