3 * Bacula Director -- User Agent Database restore Command
4 * Creates a bootstrap file for restoring files and
5 * starts the restore job.
7 * Tree handling routines split into ua_tree.c July MMIII.
8 * BSR (bootstrap record) handling routines split into
11 * Kern Sibbald, July MMII
16 Bacula® - The Network Backup Solution
18 Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
20 The main author of Bacula is Kern Sibbald, with contributions from
21 many others, a complete list can be found in the file AUTHORS.
22 This program is Free Software; you can redistribute it and/or
23 modify it under the terms of version two of the GNU General Public
24 License as published by the Free Software Foundation plus additions
25 that are listed in the file LICENSE.
27 This program is distributed in the hope that it will be useful, but
28 WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
37 Bacula® is a registered trademark of John Walker.
38 The licensor of Bacula is the Free Software Foundation Europe
39 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
40 Switzerland, email:ftf@fsfeurope.org.
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 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 bsendmsg(ua, _("Forbidden \"where\" specified.\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 bsendmsg(ua, _("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 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
151 if (!(selected_files = write_bsr_file(ua, rx))) {
152 bsendmsg(ua, _("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 bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
163 bsendmsg(ua, _("\n%s files selected to be restored.\n\n"),
164 edit_uint64_with_commas(rx.selected_files, ed1));
167 bsendmsg(ua, _("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 bsendmsg(ua, _("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 bsendmsg(ua, _("Forbidden \"where\" specified.\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 bsendmsg(ua, _("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;
314 JOB_DBR jr = { (JobId_t)-1 };
317 const char *list[] = {
318 _("List last 20 Jobs run"),
319 _("List Jobs where a given File is saved"),
320 _("Enter list of comma separated JobIds to select"),
321 _("Enter SQL list command"),
322 _("Select the most recent backup for a client"),
323 _("Select backup for a client before a specified time"),
324 _("Enter a list of files to restore"),
325 _("Enter a list of files to restore before a specified time"),
326 _("Find the JobIds of the most recent backup for a client"),
327 _("Find the JobIds for a backup for a client before a specified time"),
328 _("Enter a list of directories to restore for found JobIds"),
333 /* These keywords are handled in a for loop */
343 /* The keyword below are handled by individual arg lookups */
349 "bootstrap", /* 13 */
356 for (i=1; i<ua->argc; i++) { /* loop through arguments */
357 bool found_kw = false;
358 for (j=0; kw[j]; j++) { /* loop through keywords */
359 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
365 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
368 /* Found keyword in kw[] list, process it */
371 if (!has_value(ua, i)) {
374 if (*rx->JobIds != 0) {
375 pm_strcat(rx->JobIds, ",");
377 pm_strcat(rx->JobIds, ua->argv[i]);
380 case 1: /* current */
381 bstrutime(date, sizeof(date), time(NULL));
385 if (!has_value(ua, i)) {
388 if (str_to_utime(ua->argv[i]) == 0) {
389 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
392 bstrncpy(date, ua->argv[i], sizeof(date));
397 if (!has_value(ua, i)) {
401 bstrutime(date, sizeof(date), time(NULL));
403 if (!get_client_name(ua, rx)) {
406 pm_strcpy(ua->cmd, ua->argv[i]);
407 insert_one_file_or_dir(ua, rx, date, j==4);
411 bstrutime(date, sizeof(date), time(NULL));
413 if (!select_backups_before_date(ua, rx, date)) {
418 case 6: /* pool specified */
419 if (!has_value(ua, i)) {
422 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
424 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
427 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
429 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
433 case 7: /* all specified */
437 * All keywords 7 or greater are ignored or handled by a select prompt
445 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
446 "to be restored. You will be presented several methods\n"
447 "of specifying the JobIds. Then you will be allowed to\n"
448 "select which files from those JobIds are to be restored.\n\n"));
451 /* If choice not already made above, prompt */
457 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
458 for (int i=0; list[i]; i++) {
459 add_prompt(ua, list[i]);
462 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
463 case -1: /* error or cancel */
465 case 0: /* list last 20 Jobs run */
466 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
467 bsendmsg(ua, _("SQL query not authorized.\n"));
470 gui_save = ua->jcr->gui;
472 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
473 ua->jcr->gui = gui_save;
476 case 1: /* list where a file is saved */
477 if (!get_client_name(ua, rx)) {
480 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
483 len = strlen(ua->cmd);
484 fname = (char *)malloc(len * 2 + 1);
485 db_escape_string(fname, ua->cmd, len);
486 Mmsg(rx->query, uar_file, rx->ClientName, fname);
488 gui_save = ua->jcr->gui;
490 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
491 ua->jcr->gui = gui_save;
494 case 2: /* enter a list of JobIds */
495 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
498 pm_strcpy(rx->JobIds, ua->cmd);
500 case 3: /* Enter an SQL list command */
501 if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
502 bsendmsg(ua, _("SQL query not authorized.\n"));
505 if (!get_cmd(ua, _("Enter SQL list command: "))) {
508 gui_save = ua->jcr->gui;
510 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
511 ua->jcr->gui = gui_save;
514 case 4: /* Select the most recent backups */
515 bstrutime(date, sizeof(date), time(NULL));
516 if (!select_backups_before_date(ua, rx, date)) {
520 case 5: /* select backup at specified time */
521 if (!get_date(ua, date, sizeof(date))) {
524 if (!select_backups_before_date(ua, rx, date)) {
528 case 6: /* Enter files */
529 bstrutime(date, sizeof(date), time(NULL));
530 if (!get_client_name(ua, rx)) {
533 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
534 "containing a list of file names with paths, and terminate\n"
535 "them with a blank line.\n"));
537 if (!get_cmd(ua, _("Enter full filename: "))) {
540 len = strlen(ua->cmd);
544 insert_one_file_or_dir(ua, rx, date, false);
547 case 7: /* enter files backed up before specified time */
548 if (!get_date(ua, date, sizeof(date))) {
551 if (!get_client_name(ua, rx)) {
554 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
555 "containing a list of file names with paths, and terminate\n"
556 "them with a blank line.\n"));
558 if (!get_cmd(ua, _("Enter full filename: "))) {
561 len = strlen(ua->cmd);
565 insert_one_file_or_dir(ua, rx, date, false);
569 case 8: /* Find JobIds for current backup */
570 bstrutime(date, sizeof(date), time(NULL));
571 if (!select_backups_before_date(ua, rx, date)) {
577 case 9: /* Find JobIds for give date */
578 if (!get_date(ua, date, sizeof(date))) {
581 if (!select_backups_before_date(ua, rx, date)) {
587 case 10: /* Enter directories */
588 if (*rx->JobIds != 0) {
589 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
591 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
592 if (*rx->JobIds != 0 && *ua->cmd) {
593 pm_strcat(rx->JobIds, ",");
595 pm_strcat(rx->JobIds, ua->cmd);
597 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
598 return 0; /* nothing entered, return */
600 bstrutime(date, sizeof(date), time(NULL));
601 if (!get_client_name(ua, rx)) {
604 bsendmsg(ua, _("Enter full directory names or start the name\n"
605 "with a < to indicate it is a filename containing a list\n"
606 "of directories and terminate them with a blank line.\n"));
608 if (!get_cmd(ua, _("Enter directory name: "))) {
611 len = strlen(ua->cmd);
615 /* Add trailing slash to end of directory names */
616 if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
617 strcat(ua->cmd, "/");
619 insert_one_file_or_dir(ua, rx, date, true);
623 case 11: /* Cancel or quit */
628 if (*rx->JobIds == 0) {
629 bsendmsg(ua, _("No Jobs selected.\n"));
632 if (strchr(rx->JobIds,',')) {
633 bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
636 bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
641 for (p=rx->JobIds; ; ) {
642 int stat = get_next_jobid_from_list(&p, &JobId);
644 bsendmsg(ua, _("Invalid JobId in list.\n"));
650 if (jr.JobId == JobId) {
651 continue; /* duplicate of last JobId */
653 memset(&jr, 0, sizeof(JOB_DBR));
655 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
657 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
658 edit_int64(JobId, ed1), db_strerror(ua->db));
661 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
662 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
666 rx->TotalFiles += jr.JobFiles;
674 static int get_date(UAContext *ua, char *date, int date_len)
676 bsendmsg(ua, _("The restored files will the most current backup\n"
677 "BEFORE the date you specify below.\n\n"));
679 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
682 if (str_to_utime(ua->cmd) != 0) {
685 bsendmsg(ua, _("Improper date format.\n"));
687 bstrncpy(date, ua->cmd, date_len);
692 * Insert a single file, or read a list of files from a file
694 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
704 if ((ffd = fopen(p, "rb")) == NULL) {
706 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
710 while (fgets(file, sizeof(file), ffd)) {
713 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
714 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
717 if (!insert_file_into_findex_list(ua, rx, file, date)) {
718 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
726 insert_table_into_findex_list(ua, rx, p);
730 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
732 insert_file_into_findex_list(ua, rx, ua->cmd, date);
739 * For a given file (path+filename), split into path and file, then
740 * lookup the most recent backup in the catalog to get the JobId
741 * and FileIndex, then insert them into the findex list.
743 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
746 strip_trailing_newline(file);
747 split_path_and_filename(rx, file);
748 if (*rx->JobIds == 0) {
749 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
752 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
753 rx->path, rx->fname, rx->ClientName);
756 /* Find and insert jobid and File Index */
757 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
758 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
759 rx->query, db_strerror(ua->db));
762 bsendmsg(ua, _("No database record found for: %s\n"), file);
769 * For a given path lookup the most recent backup in the catalog
770 * to get the JobId and FileIndexes of all files in that directory.
772 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
775 strip_trailing_junk(dir);
776 if (*rx->JobIds == 0) {
777 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
780 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
781 dir, rx->ClientName);
784 /* Find and insert jobid and File Index */
785 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
786 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
787 rx->query, db_strerror(ua->db));
790 bsendmsg(ua, _("No database record found for: %s\n"), dir);
797 * Get the JobId and FileIndexes of all files in the specified table
799 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
801 strip_trailing_junk(table);
802 Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
805 /* Find and insert jobid and File Index */
806 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
807 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
808 rx->query, db_strerror(ua->db));
811 bsendmsg(ua, _("No table found: %s\n"), table);
817 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
821 /* Find path without the filename.
822 * I.e. everything after the last / is a "filename".
823 * OK, maybe it is a directory name, but we treat it like
824 * a filename. If we don't find a / then the whole name
825 * must be a path name (e.g. c:).
827 for (p=f=name; *p; p++) {
828 if (IsPathSeparator(*p)) {
829 f = p; /* set pos of last slash */
832 if (IsPathSeparator(*f)) { /* did we find a slash? */
833 f++; /* yes, point to filename */
834 } else { /* no, whole thing must be path name */
838 /* If filename doesn't exist (i.e. root directory), we
839 * simply create a blank name consisting of a single
840 * space. This makes handling zero length filenames
845 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
846 memcpy(rx->fname, f, rx->fnl); /* copy filename */
847 rx->fname[rx->fnl] = 0;
855 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
856 memcpy(rx->path, name, rx->pnl);
857 rx->path[rx->pnl] = 0;
863 Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
866 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
869 JobId_t JobId, last_JobId;
874 memset(&tree, 0, sizeof(TREE_CTX));
876 * Build the directory tree containing JobIds user selected
878 tree.root = new_tree(rx->TotalFiles);
883 * For display purposes, the same JobId, with different volumes may
884 * appear more than once, however, we only insert it once.
888 tree.FileEstimate = 0;
889 if (get_next_jobid_from_list(&p, &JobId) > 0) {
890 /* Use first JobId as estimate of the number of files to restore */
891 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
892 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
893 bsendmsg(ua, "%s\n", db_strerror(ua->db));
896 /* Add about 25% more than this job for over estimate */
897 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
898 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
901 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
904 if (JobId == last_JobId) {
905 continue; /* eliminate duplicate JobIds */
908 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
909 edit_int64(JobId, ed1));
912 * Find files for this JobId and insert them in the tree
914 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
915 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
916 bsendmsg(ua, "%s", db_strerror(ua->db));
919 if (tree.FileCount == 0) {
920 bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
921 "is not possible.Most likely your retention policy pruned the files\n"));
922 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
926 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
927 if (JobId == last_JobId) {
928 continue; /* eliminate duplicate JobIds */
930 add_findex_all(rx->bsr, JobId);
938 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
939 edit_uint64_with_commas(tree.FileCount, ec1));
942 bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
943 edit_uint64_with_commas(tree.FileCount, ec1));
948 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
949 items, edit_uint64_with_commas(tree.FileCount, ec1));
952 bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
953 items, edit_uint64_with_commas(tree.FileCount, ec1));
957 if (find_arg(ua, NT_("done")) < 0) {
958 /* Let the user interact in selecting which files to restore */
959 OK = user_select_files_from_tree(&tree);
963 * Walk down through the tree finding all files marked to be
964 * extracted making a bootstrap file.
967 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
968 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
969 if (node->extract || node->extract_dir) {
970 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
971 add_findex(rx->bsr, node->JobId, node->FileIndex);
972 if (node->extract && node->type != TN_NEWDIR) {
973 rx->selected_files++; /* count only saved files */
980 free_tree(tree.root); /* free the directory tree */
986 * This routine is used to get the current backup or a backup
987 * before the specified date.
989 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
994 char fileset_name[MAX_NAME_LENGTH];
995 char ed1[50], ed2[50];
996 char pool_select[MAX_NAME_LENGTH];
1000 /* Create temp tables */
1001 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1002 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1003 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
1004 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1006 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
1007 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1010 * Select Client from the Catalog
1012 memset(&cr, 0, sizeof(cr));
1013 if (!get_client_dbr(ua, &cr)) {
1016 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1021 memset(&fsr, 0, sizeof(fsr));
1022 i = find_arg_with_value(ua, "FileSet");
1024 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1025 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1026 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1027 db_strerror(ua->db));
1031 if (i < 0) { /* fileset not found */
1032 edit_int64(cr.ClientId, ed1);
1033 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1034 start_prompt(ua, _("The defined FileSet resources are:\n"));
1035 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1036 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1038 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1039 fileset_name, sizeof(fileset_name)) < 0) {
1040 bsendmsg(ua, _("No FileSet found for client \"%s\".\n"), cr.Name);
1044 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1045 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1046 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1047 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1048 "Continuing anyway.\n"));
1052 /* If Pool specified, add PoolId specification */
1056 memset(&pr, 0, sizeof(pr));
1057 bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1058 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1059 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1060 edit_int64(pr.PoolId, ed1));
1062 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1066 /* Find JobId of last Full backup for this client, fileset */
1067 edit_int64(cr.ClientId, ed1);
1068 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1070 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1071 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1075 /* Find all Volumes used by that JobId */
1076 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1077 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1080 /* Note, this is needed because I don't seem to get the callback
1081 * from the call just above.
1084 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1085 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1087 if (rx->JobTDate == 0) {
1088 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1092 /* Now find most recent Differental Job after Full save, if any */
1093 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1094 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1095 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1096 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1098 /* Now update JobTDate to lock onto Differental, if any */
1100 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1101 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1103 if (rx->JobTDate == 0) {
1104 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1108 /* Now find all Incremental Jobs after Full/dif save */
1109 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1110 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1111 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1112 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1115 /* Get the JobIds from that list */
1117 rx->last_jobid[0] = 0;
1118 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1119 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1122 if (rx->JobIds[0] != 0) {
1123 /* Display a list of Jobs selected for this restore */
1124 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1127 bsendmsg(ua, _("No jobs found.\n"));
1131 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1132 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1138 * Return next JobId from comma separated list
1141 * 1 if next JobId returned
1142 * 0 if no more JobIds are in list
1143 * -1 there is an error
1145 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1151 for (int i=0; i<(int)sizeof(jobid); i++) {
1154 } else if (*q == ',') {
1161 if (jobid[0] == 0) {
1163 } else if (!is_a_number(jobid)) {
1164 return -1; /* error */
1167 *JobId = str_to_int64(jobid);
1171 static int count_handler(void *ctx, int num_fields, char **row)
1173 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1174 rx->JobId = str_to_int64(row[0]);
1180 * Callback handler to get JobId and FileIndex for files
1181 * can insert more than one depending on the caller.
1183 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1185 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1186 rx->JobId = str_to_int64(row[0]);
1187 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1189 rx->selected_files++;
1194 * Callback handler make list of JobIds
1196 static int jobid_handler(void *ctx, int num_fields, char **row)
1198 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1200 if (strcmp(rx->last_jobid, row[0]) == 0) {
1201 return 0; /* duplicate id */
1203 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1204 if (rx->JobIds[0] != 0) {
1205 pm_strcat(rx->JobIds, ",");
1207 pm_strcat(rx->JobIds, row[0]);
1213 * Callback handler to pickup last Full backup JobTDate
1215 static int last_full_handler(void *ctx, int num_fields, char **row)
1217 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1219 rx->JobTDate = str_to_int64(row[1]);
1224 * Callback handler build FileSet name prompt list
1226 static int fileset_handler(void *ctx, int num_fields, char **row)
1228 /* row[0] = FileSet (name) */
1230 add_prompt((UAContext *)ctx, row[0]);
1236 * Free names in the list
1238 static void free_name_list(NAME_LIST *name_list)
1240 for (int i=0; i < name_list->num_ids; i++) {
1241 free(name_list->name[i]);
1243 if (name_list->name) {
1244 free(name_list->name);
1245 name_list->name = NULL;
1247 name_list->max_ids = 0;
1248 name_list->num_ids = 0;
1251 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
1256 Dmsg1(200, "Already have store=%s\n", rx.store->name());
1260 * Try looking up Storage by name
1263 foreach_res(store, R_STORAGE) {
1264 if (strcmp(Storage, store->name()) == 0) {
1265 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1274 /* Check if an explicit storage resource is given */
1276 int i = find_arg_with_value(ua, "storage");
1278 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1279 if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1283 if (store && (store != rx.store)) {
1284 bsendmsg(ua, _("Warning default storage overridden by \"%s\" on command line.\n"),
1287 Dmsg1(200, "Set store=%s\n", rx.store->name());
1292 /* If no storage resource, try to find one from MediaType */
1295 foreach_res(store, R_STORAGE) {
1296 if (strcmp(MediaType, store->media_type) == 0) {
1297 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1299 Dmsg1(200, "Set store=%s\n", rx.store->name());
1300 bsendmsg(ua, _("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1301 Storage, store->name(), MediaType);
1308 bsendmsg(ua, _("\nUnable to find Storage resource for\n"
1309 "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1312 /* Take command line arg, or ask user if none */
1313 rx.store = get_storage_resource(ua, false /* don't use default */);
1314 Dmsg1(200, "Set store=%s\n", rx.store->name());