2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 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 plus additions
11 that are listed in the file LICENSE.
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 John Walker.
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
48 /* Imported functions */
49 extern void print_bsr(UAContext *ua, RBSR *bsr);
53 /* Forward referenced functions */
54 static int last_full_handler(void *ctx, int num_fields, char **row);
55 static int jobid_handler(void *ctx, int num_fields, char **row);
56 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
57 static int fileset_handler(void *ctx, int num_fields, char **row);
58 static void free_name_list(NAME_LIST *name_list);
59 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
60 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
61 static void free_rx(RESTORE_CTX *rx);
62 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
63 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
64 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
66 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
68 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
69 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
70 static int get_date(UAContext *ua, char *date, int date_len);
71 static int 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;
87 memset(&rx, 0, sizeof(rx));
88 rx.path = get_pool_memory(PM_FNAME);
89 rx.fname = get_pool_memory(PM_FNAME);
90 rx.JobIds = get_pool_memory(PM_FNAME);
91 rx.query = get_pool_memory(PM_FNAME);
94 i = find_arg_with_value(ua, "where");
96 rx.where = ua->argv[i];
97 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
98 ua->error_msg(_("\"where\" specification not authorized.\n"));
103 if (!open_client_db(ua)) {
107 /* Ensure there is at least one Restore Job */
109 foreach_res(job, R_JOB) {
110 if (job->JobType == JT_RESTORE) {
111 if (!rx.restore_job) {
112 rx.restore_job = job;
118 if (!rx.restore_jobs) {
120 "No Restore Job Resource found in bacula-dir.conf.\n"
121 "You must create at least one before running this command.\n"));
126 * Request user to select JobIds or files by various different methods
127 * last 20 jobs, where File saved, most recent backup, ...
128 * In the end, a list of files are pumped into
131 switch (user_select_jobids_or_files(ua, &rx)) {
134 case 1: /* selected by jobid */
135 if (!build_directory_tree(ua, &rx)) {
136 ua->send_msg(_("Restore not done.\n"));
140 case 2: /* selected by filename, no tree needed */
145 uint32_t selected_files;
147 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
148 ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
151 if (!(selected_files = write_bsr_file(ua, rx))) {
152 ua->warning_msg(_("No files selected to be restored.\n"));
155 /* If no count of files, use bsr generated value (often wrong) */
156 if (rx.selected_files == 0) {
157 rx.selected_files = selected_files;
159 if (rx.selected_files==1) {
160 ua->info_msg(_("\n1 file selected to be restored.\n\n"));
163 ua->info_msg(_("\n%s files selected to be restored.\n\n"),
164 edit_uint64_with_commas(rx.selected_files, ed1));
167 ua->warning_msg(_("No files selected to be restored.\n"));
171 if (rx.restore_jobs == 1) {
172 job = rx.restore_job;
174 job = select_restore_job_resource(ua);
180 get_client_name(ua, &rx);
181 if (!rx.ClientName) {
182 ua->error_msg(_("No Client resource found!\n"));
186 escaped_bsr_name = escape_filename(jcr->RestoreBootstrap);
187 escaped_where_name = escape_filename(rx.where);
189 /* Build run command */
191 if (!acl_access_ok(ua, Where_ACL, rx.where)) {
192 ua->error_msg(_("\"where\" specification not authorized.\n"));
197 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
198 " where=\"%s\" files=%d catalog=\"%s\"",
199 job->name(), rx.ClientName, rx.store?rx.store->name():"",
200 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
201 escaped_where_name ? escaped_where_name : rx.where,
202 rx.selected_files, ua->catalog->name());
205 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
206 " files=%d catalog=\"%s\"",
207 job->name(), rx.ClientName, rx.store?rx.store->name():"",
208 escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
209 rx.selected_files, ua->catalog->name());
212 if (escaped_bsr_name != NULL) {
213 bfree(escaped_bsr_name);
216 if (escaped_where_name != NULL) {
217 bfree(escaped_where_name);
220 if (find_arg(ua, NT_("yes")) > 0) {
221 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
223 Dmsg1(200, "Submitting: %s\n", ua->cmd);
225 run_cmd(ua, ua->cmd);
230 if (escaped_bsr_name != NULL) {
231 bfree(escaped_bsr_name);
234 if (escaped_where_name != NULL) {
235 bfree(escaped_where_name);
243 static void free_rx(RESTORE_CTX *rx)
248 free_pool_memory(rx->JobIds);
252 free_pool_memory(rx->fname);
256 free_pool_memory(rx->path);
260 free_pool_memory(rx->query);
263 free_name_list(&rx->name_list);
266 static bool has_value(UAContext *ua, int i)
269 ua->error_msg(_("Missing value for keyword: %s\n"), ua->argk[i]);
275 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
277 /* If no client name specified yet, get it now */
278 if (!rx->ClientName[0]) {
280 /* try command line argument */
281 int i = find_arg_with_value(ua, NT_("client"));
283 if (!has_value(ua, i)) {
286 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
289 memset(&cr, 0, sizeof(cr));
290 if (!get_client_dbr(ua, &cr)) {
293 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
300 * The first step in the restore process is for the user to
301 * select a list of JobIds from which he will subsequently
302 * select which files are to be restored.
304 * Returns: 2 if filename list made
305 * 1 if jobid list made
308 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
311 char date[MAX_TIME_LENGTH];
312 bool have_date = false;
313 /* Include current second if using current time */
314 utime_t now = time(NULL) + 1;
316 JOB_DBR jr = { (JobId_t)-1 };
319 const char *list[] = {
320 _("List last 20 Jobs run"),
321 _("List Jobs where a given File is saved"),
322 _("Enter list of comma separated JobIds to select"),
323 _("Enter SQL list command"),
324 _("Select the most recent backup for a client"),
325 _("Select backup for a client before a specified time"),
326 _("Enter a list of files to restore"),
327 _("Enter a list of files to restore before a specified time"),
328 _("Find the JobIds of the most recent backup for a client"),
329 _("Find the JobIds for a backup for a client before a specified time"),
330 _("Enter a list of directories to restore for found JobIds"),
335 /* These keywords are handled in a for loop */
345 /* The keyword below are handled by individual arg lookups */
351 "bootstrap", /* 13 */
358 for (i=1; i<ua->argc; i++) { /* loop through arguments */
359 bool found_kw = false;
360 for (j=0; kw[j]; j++) { /* loop through keywords */
361 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
367 ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]);
370 /* Found keyword in kw[] list, process it */
373 if (!has_value(ua, i)) {
376 if (*rx->JobIds != 0) {
377 pm_strcat(rx->JobIds, ",");
379 pm_strcat(rx->JobIds, ua->argv[i]);
382 case 1: /* current */
384 * Note, we add one second here just to include any job
385 * that may have finished within the current second,
386 * which happens a lot in scripting small jobs.
388 bstrutime(date, sizeof(date), now);
392 if (!has_value(ua, i)) {
395 if (str_to_utime(ua->argv[i]) == 0) {
396 ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
399 bstrncpy(date, ua->argv[i], sizeof(date));
404 if (!has_value(ua, i)) {
408 bstrutime(date, sizeof(date), now);
410 if (!get_client_name(ua, rx)) {
413 pm_strcpy(ua->cmd, ua->argv[i]);
414 insert_one_file_or_dir(ua, rx, date, j==4);
418 bstrutime(date, sizeof(date), now);
420 if (!select_backups_before_date(ua, rx, date)) {
425 case 6: /* pool specified */
426 if (!has_value(ua, i)) {
429 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
431 ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
434 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
436 ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
440 case 7: /* all specified */
444 * All keywords 7 or greater are ignored or handled by a select prompt
452 ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
453 "to be restored. You will be presented several methods\n"
454 "of specifying the JobIds. Then you will be allowed to\n"
455 "select which files from those JobIds are to be restored.\n\n"));
458 /* If choice not already made above, prompt */
464 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
465 for (int i=0; list[i]; i++) {
466 add_prompt(ua, list[i]);
469 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
470 case -1: /* error or cancel */
472 case 0: /* list last 20 Jobs run */
473 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
474 ua->error_msg(_("SQL query not authorized.\n"));
477 gui_save = ua->jcr->gui;
479 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
480 ua->jcr->gui = gui_save;
483 case 1: /* list where a file is saved */
484 if (!get_client_name(ua, rx)) {
487 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
490 len = strlen(ua->cmd);
491 fname = (char *)malloc(len * 2 + 1);
492 db_escape_string(fname, ua->cmd, len);
493 Mmsg(rx->query, uar_file, rx->ClientName, fname);
495 gui_save = ua->jcr->gui;
497 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
498 ua->jcr->gui = gui_save;
501 case 2: /* enter a list of JobIds */
502 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
505 pm_strcpy(rx->JobIds, ua->cmd);
507 case 3: /* Enter an SQL list command */
508 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
509 ua->error_msg(_("SQL query not authorized.\n"));
512 if (!get_cmd(ua, _("Enter SQL list command: "))) {
515 gui_save = ua->jcr->gui;
517 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
518 ua->jcr->gui = gui_save;
521 case 4: /* Select the most recent backups */
522 bstrutime(date, sizeof(date), now);
523 if (!select_backups_before_date(ua, rx, date)) {
527 case 5: /* select backup at specified time */
528 if (!get_date(ua, date, sizeof(date))) {
531 if (!select_backups_before_date(ua, rx, date)) {
535 case 6: /* Enter files */
536 bstrutime(date, sizeof(date), now);
537 if (!get_client_name(ua, rx)) {
540 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
541 "containing a list of file names with paths, and terminate\n"
542 "them with a blank line.\n"));
544 if (!get_cmd(ua, _("Enter full filename: "))) {
547 len = strlen(ua->cmd);
551 insert_one_file_or_dir(ua, rx, date, false);
554 case 7: /* enter files backed up before specified time */
555 if (!get_date(ua, date, sizeof(date))) {
558 if (!get_client_name(ua, rx)) {
561 ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
562 "containing a list of file names with paths, and terminate\n"
563 "them with a blank line.\n"));
565 if (!get_cmd(ua, _("Enter full filename: "))) {
568 len = strlen(ua->cmd);
572 insert_one_file_or_dir(ua, rx, date, false);
576 case 8: /* Find JobIds for current backup */
577 bstrutime(date, sizeof(date), now);
578 if (!select_backups_before_date(ua, rx, date)) {
584 case 9: /* Find JobIds for give date */
585 if (!get_date(ua, date, sizeof(date))) {
588 if (!select_backups_before_date(ua, rx, date)) {
594 case 10: /* Enter directories */
595 if (*rx->JobIds != 0) {
596 ua->send_msg(_("You have already selected the following JobIds: %s\n"),
598 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
599 if (*rx->JobIds != 0 && *ua->cmd) {
600 pm_strcat(rx->JobIds, ",");
602 pm_strcat(rx->JobIds, ua->cmd);
604 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
605 return 0; /* nothing entered, return */
607 bstrutime(date, sizeof(date), now);
608 if (!get_client_name(ua, rx)) {
611 ua->send_msg(_("Enter full directory names or start the name\n"
612 "with a < to indicate it is a filename containing a list\n"
613 "of directories and terminate them with a blank line.\n"));
615 if (!get_cmd(ua, _("Enter directory name: "))) {
618 len = strlen(ua->cmd);
622 /* Add trailing slash to end of directory names */
623 if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
624 strcat(ua->cmd, "/");
626 insert_one_file_or_dir(ua, rx, date, true);
630 case 11: /* Cancel or quit */
635 POOLMEM *JobIds = get_pool_memory(PM_FNAME);
639 * Find total number of files to be restored, and filter the JobId
640 * list to contain only ones permitted by the ACL conditions.
642 for (p=rx->JobIds; ; ) {
644 int stat = get_next_jobid_from_list(&p, &JobId);
646 ua->error_msg(_("Invalid JobId in list.\n"));
647 free_pool_memory(JobIds);
653 if (jr.JobId == JobId) {
654 continue; /* duplicate of last JobId */
656 memset(&jr, 0, sizeof(JOB_DBR));
658 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
659 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
660 edit_int64(JobId, ed1), db_strerror(ua->db));
661 free_pool_memory(JobIds);
664 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
665 ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"),
666 edit_int64(JobId, ed1), jr.Name);
670 pm_strcat(JobIds, ",");
672 pm_strcat(JobIds, edit_int64(JobId, ed1));
673 rx->TotalFiles += jr.JobFiles;
675 free_pool_memory(rx->JobIds);
676 rx->JobIds = JobIds; /* Set ACL filtered list */
677 if (*rx->JobIds == 0) {
678 ua->warning_msg(_("No Jobs selected.\n"));
681 if (strchr(rx->JobIds,',')) {
682 ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds);
684 ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds);
692 static int get_date(UAContext *ua, char *date, int date_len)
694 ua->send_msg(_("The restored files will the most current backup\n"
695 "BEFORE the date you specify below.\n\n"));
697 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
700 if (str_to_utime(ua->cmd) != 0) {
703 ua->error_msg(_("Improper date format.\n"));
705 bstrncpy(date, ua->cmd, date_len);
710 * Insert a single file, or read a list of files from a file
712 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
722 if ((ffd = fopen(p, "rb")) == NULL) {
724 ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
728 while (fgets(file, sizeof(file), ffd)) {
731 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
732 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
735 if (!insert_file_into_findex_list(ua, rx, file, date)) {
736 ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
744 insert_table_into_findex_list(ua, rx, p);
748 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
750 insert_file_into_findex_list(ua, rx, ua->cmd, date);
757 * For a given file (path+filename), split into path and file, then
758 * lookup the most recent backup in the catalog to get the JobId
759 * and FileIndex, then insert them into the findex list.
761 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
764 strip_trailing_newline(file);
765 split_path_and_filename(rx, file);
766 if (*rx->JobIds == 0) {
767 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
770 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
771 rx->path, rx->fname, rx->ClientName);
774 /* Find and insert jobid and File Index */
775 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
776 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
777 rx->query, db_strerror(ua->db));
780 ua->error_msg(_("No database record found for: %s\n"), file);
781 // ua->error_msg("Query=%s\n", rx->query);
788 * For a given path lookup the most recent backup in the catalog
789 * to get the JobId and FileIndexes of all files in that directory.
791 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
794 strip_trailing_junk(dir);
795 if (*rx->JobIds == 0) {
796 ua->error_msg(_("No JobId specified cannot continue.\n"));
799 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
800 dir, rx->ClientName);
803 /* Find and insert jobid and File Index */
804 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
805 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
806 rx->query, db_strerror(ua->db));
809 ua->error_msg(_("No database record found for: %s\n"), dir);
816 * Get the JobId and FileIndexes of all files in the specified table
818 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
820 strip_trailing_junk(table);
821 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
824 /* Find and insert jobid and File Index */
825 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
826 ua->error_msg(_("Query failed: %s. ERR=%s\n"),
827 rx->query, db_strerror(ua->db));
830 ua->error_msg(_("No table found: %s\n"), table);
836 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
840 /* Find path without the filename.
841 * I.e. everything after the last / is a "filename".
842 * OK, maybe it is a directory name, but we treat it like
843 * a filename. If we don't find a / then the whole name
844 * must be a path name (e.g. c:).
846 for (p=f=name; *p; p++) {
847 if (IsPathSeparator(*p)) {
848 f = p; /* set pos of last slash */
851 if (IsPathSeparator(*f)) { /* did we find a slash? */
852 f++; /* yes, point to filename */
853 } else { /* no, whole thing must be path name */
857 /* If filename doesn't exist (i.e. root directory), we
858 * simply create a blank name consisting of a single
859 * space. This makes handling zero length filenames
864 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
865 memcpy(rx->fname, f, rx->fnl); /* copy filename */
866 rx->fname[rx->fnl] = 0;
874 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
875 memcpy(rx->path, name, rx->pnl);
876 rx->path[rx->pnl] = 0;
882 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
885 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
888 JobId_t JobId, last_JobId;
893 memset(&tree, 0, sizeof(TREE_CTX));
895 * Build the directory tree containing JobIds user selected
897 tree.root = new_tree(rx->TotalFiles);
902 * For display purposes, the same JobId, with different volumes may
903 * appear more than once, however, we only insert it once.
907 tree.FileEstimate = 0;
908 if (get_next_jobid_from_list(&p, &JobId) > 0) {
909 /* Use first JobId as estimate of the number of files to restore */
910 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
911 if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
912 ua->error_msg("%s\n", db_strerror(ua->db));
915 /* Add about 25% more than this job for over estimate */
916 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
917 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
920 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
923 if (JobId == last_JobId) {
924 continue; /* eliminate duplicate JobIds */
927 ua->info_msg(_("\nBuilding directory tree for JobId %s ... "),
928 edit_int64(JobId, ed1));
931 * Find files for this JobId and insert them in the tree
933 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
934 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
935 ua->error_msg("%s", db_strerror(ua->db));
938 if (tree.FileCount == 0) {
939 ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
940 "is not possible.Most likely your retention policy pruned the files\n"));
941 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
945 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
946 if (JobId == last_JobId) {
947 continue; /* eliminate duplicate JobIds */
949 add_findex_all(rx->bsr, JobId);
957 ua->info_msg(_("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
958 edit_uint64_with_commas(tree.FileCount, ec1));
961 ua->info_msg(_("\n1 Job, %s files inserted into the tree.\n"),
962 edit_uint64_with_commas(tree.FileCount, ec1));
967 ua->info_msg(_("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
968 items, edit_uint64_with_commas(tree.FileCount, ec1));
971 ua->info_msg(_("\n%d Jobs, %s files inserted into the tree.\n"),
972 items, edit_uint64_with_commas(tree.FileCount, ec1));
976 if (find_arg(ua, NT_("done")) < 0) {
977 /* Let the user interact in selecting which files to restore */
978 OK = user_select_files_from_tree(&tree);
982 * Walk down through the tree finding all files marked to be
983 * extracted making a bootstrap file.
986 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
987 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
988 if (node->extract || node->extract_dir) {
989 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
990 add_findex(rx->bsr, node->JobId, node->FileIndex);
991 if (node->extract && node->type != TN_NEWDIR) {
992 rx->selected_files++; /* count only saved files */
999 free_tree(tree.root); /* free the directory tree */
1005 * This routine is used to get the current backup or a backup
1006 * before the specified date.
1008 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1013 char fileset_name[MAX_NAME_LENGTH];
1014 char ed1[50], ed2[50];
1015 char pool_select[MAX_NAME_LENGTH];
1019 /* Create temp tables */
1020 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1021 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1022 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
1023 ua->error_msg("%s\n", db_strerror(ua->db));
1025 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
1026 ua->error_msg("%s\n", db_strerror(ua->db));
1029 * Select Client from the Catalog
1031 memset(&cr, 0, sizeof(cr));
1032 if (!get_client_dbr(ua, &cr)) {
1035 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1040 memset(&fsr, 0, sizeof(fsr));
1041 i = find_arg_with_value(ua, "FileSet");
1043 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1044 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1045 ua->error_msg(_("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1046 db_strerror(ua->db));
1050 if (i < 0) { /* fileset not found */
1051 edit_int64(cr.ClientId, ed1);
1052 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1053 start_prompt(ua, _("The defined FileSet resources are:\n"));
1054 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1055 ua->error_msg("%s\n", db_strerror(ua->db));
1057 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1058 fileset_name, sizeof(fileset_name)) < 0) {
1059 ua->error_msg(_("No FileSet found for client \"%s\".\n"), cr.Name);
1063 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1064 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1065 ua->warning_msg(_("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1066 ua->send_msg(_("This probably means you modified the FileSet.\n"
1067 "Continuing anyway.\n"));
1071 /* If Pool specified, add PoolId specification */
1075 memset(&pr, 0, sizeof(pr));
1076 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1077 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1078 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1079 edit_int64(pr.PoolId, ed1));
1081 ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1085 /* Find JobId of last Full backup for this client, fileset */
1086 edit_int64(cr.ClientId, ed1);
1087 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1089 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1090 ua->error_msg("%s\n", db_strerror(ua->db));
1094 /* Find all Volumes used by that JobId */
1095 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1096 ua->error_msg("%s\n", db_strerror(ua->db));
1099 /* Note, this is needed because I don't seem to get the callback
1100 * from the call just above.
1103 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1104 ua->warning_msg("%s\n", db_strerror(ua->db));
1106 if (rx->JobTDate == 0) {
1107 ua->error_msg(_("No Full backup before %s found.\n"), date);
1111 /* Now find most recent Differental Job after Full save, if any */
1112 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1113 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1114 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1115 ua->warning_msg("%s\n", db_strerror(ua->db));
1117 /* Now update JobTDate to lock onto Differental, if any */
1119 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1120 ua->warning_msg("%s\n", db_strerror(ua->db));
1122 if (rx->JobTDate == 0) {
1123 ua->error_msg(_("No Full backup before %s found.\n"), date);
1127 /* Now find all Incremental Jobs after Full/dif save */
1128 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1129 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1130 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1131 ua->warning_msg("%s\n", db_strerror(ua->db));
1134 /* Get the JobIds from that list */
1136 rx->last_jobid[0] = 0;
1137 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1138 ua->warning_msg("%s\n", db_strerror(ua->db));
1141 if (rx->JobIds[0] != 0) {
1142 /* Display a list of Jobs selected for this restore */
1143 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1146 ua->warning_msg(_("No jobs found.\n"));
1150 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1151 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1157 * Return next JobId from comma separated list
1160 * 1 if next JobId returned
1161 * 0 if no more JobIds are in list
1162 * -1 there is an error
1164 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1170 for (int i=0; i<(int)sizeof(jobid); i++) {
1173 } else if (*q == ',') {
1180 if (jobid[0] == 0) {
1182 } else if (!is_a_number(jobid)) {
1183 return -1; /* error */
1186 *JobId = str_to_int64(jobid);
1190 static int restore_count_handler(void *ctx, int num_fields, char **row)
1192 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1193 rx->JobId = str_to_int64(row[0]);
1199 * Callback handler to get JobId and FileIndex for files
1200 * can insert more than one depending on the caller.
1202 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1204 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1205 rx->JobId = str_to_int64(row[0]);
1206 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1208 rx->selected_files++;
1213 * Callback handler make list of JobIds
1215 static int jobid_handler(void *ctx, int num_fields, char **row)
1217 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1219 if (strcmp(rx->last_jobid, row[0]) == 0) {
1220 return 0; /* duplicate id */
1222 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1223 if (rx->JobIds[0] != 0) {
1224 pm_strcat(rx->JobIds, ",");
1226 pm_strcat(rx->JobIds, row[0]);
1232 * Callback handler to pickup last Full backup JobTDate
1234 static int last_full_handler(void *ctx, int num_fields, char **row)
1236 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1238 rx->JobTDate = str_to_int64(row[1]);
1243 * Callback handler build FileSet name prompt list
1245 static int fileset_handler(void *ctx, int num_fields, char **row)
1247 /* row[0] = FileSet (name) */
1249 add_prompt((UAContext *)ctx, row[0]);
1255 * Free names in the list
1257 static void free_name_list(NAME_LIST *name_list)
1259 for (int i=0; i < name_list->num_ids; i++) {
1260 free(name_list->name[i]);
1262 if (name_list->name) {
1263 free(name_list->name);
1264 name_list->name = NULL;
1266 name_list->max_ids = 0;
1267 name_list->num_ids = 0;
1270 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1275 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1279 * Try looking up Storage by name
1282 foreach_res(store, R_STORAGE) {
1283 if (strcmp(Storage, store->name()) == 0) {
1284 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1293 /* Check if an explicit storage resource is given */
1295 int i = find_arg_with_value(ua, "storage");
1297 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1298 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1302 if (store && (store != rx.store)) {
1303 ua->info_msg(_("Warning default storage overridden by \"%s\" on command line.\n"),
1306 Dmsg1(200, "Set store=%s\n", rx.store->name());
1311 /* If no storage resource, try to find one from MediaType */
1314 foreach_res(store, R_STORAGE) {
1315 if (strcmp(MediaType, store->media_type) == 0) {
1316 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1318 Dmsg1(200, "Set store=%s\n", rx.store->name());
1319 ua->warning_msg(_("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1320 Storage, store->name(), MediaType);
1327 ua->warning_msg(_("\nUnable to find Storage resource for\n"
1328 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1331 /* Take command line arg, or ask user if none */
1332 rx.store = get_storage_resource(ua, false /* don't use default */);
1333 Dmsg1(200, "Set store=%s\n", rx.store->name());