3 * Bacula Director -- User Agent Database restore Command
4 * Creates a bootstrap file for restoring files and
5 * starts the restore job.
7 * Tree handling routines split into ua_tree.c July MMIII.
8 * BSR (bootstrap record) handling routines split into
11 * Kern Sibbald, July MMII
16 Copyright (C) 2002-2005 Kern Sibbald
18 This program is free software; you can redistribute it and/or
19 modify it under the terms of the GNU General Public License
20 version 2 as amended with additional clauses defined in the
21 file LICENSE in the main source directory.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 the file LICENSE for additional details.
35 /* Imported functions */
36 extern void print_bsr(UAContext *ua, RBSR *bsr);
38 /* Imported variables */
39 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
40 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
41 extern char *uar_create_temp1, *uar_last_full, *uar_full;
42 extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
43 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
44 extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
45 extern char *uar_count_files, *uar_jobids_fileindex;
46 extern char *uar_jobid_fileindex_from_dir;
50 char **name; /* list of names */
51 int num_ids; /* ids stored */
52 int max_ids; /* size of array */
53 int num_del; /* number deleted */
54 int tot_ids; /* total to process */
58 /* Main structure for obtaining JobIds or Files to be restored */
63 char ClientName[MAX_NAME_LENGTH];
65 POOLMEM *JobIds; /* User entered string of JobIds */
70 uint32_t selected_files;
73 POOLMEM *fname; /* filename only */
74 POOLMEM *path; /* path only */
76 int fnl; /* filename length */
77 int pnl; /* path length */
79 bool all; /* mark all as default */
84 #define MAX_ID_LIST_LEN 1000000
87 /* Forward referenced functions */
88 static int last_full_handler(void *ctx, int num_fields, char **row);
89 static int jobid_handler(void *ctx, int num_fields, char **row);
90 static int get_next_jobid_from_list(char **p, uint32_t *JobId);
91 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
92 static int fileset_handler(void *ctx, int num_fields, char **row);
93 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
94 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
95 static void free_name_list(NAME_LIST *name_list);
96 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
97 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
98 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
99 static void free_rx(RESTORE_CTX *rx);
100 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
101 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
102 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
104 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
106 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
107 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
108 static int get_date(UAContext *ua, char *date, int date_len);
109 static int count_handler(void *ctx, int num_fields, char **row);
115 int restore_cmd(UAContext *ua, const char *cmd)
117 RESTORE_CTX rx; /* restore context */
122 memset(&rx, 0, sizeof(rx));
123 rx.path = get_pool_memory(PM_FNAME);
124 rx.fname = get_pool_memory(PM_FNAME);
125 rx.JobIds = get_pool_memory(PM_FNAME);
126 rx.query = get_pool_memory(PM_FNAME);
129 i = find_arg_with_value(ua, "where");
131 rx.where = ua->argv[i];
138 /* Ensure there is at least one Restore Job */
140 foreach_res(job, R_JOB) {
141 if (job->JobType == JT_RESTORE) {
142 if (!rx.restore_job) {
143 rx.restore_job = job;
149 if (!rx.restore_jobs) {
151 "No Restore Job Resource found in bacula-dir.conf.\n"
152 "You must create at least one before running this command.\n"));
157 * Request user to select JobIds or files by various different methods
158 * last 20 jobs, where File saved, most recent backup, ...
159 * In the end, a list of files are pumped into
162 switch (user_select_jobids_or_files(ua, &rx)) {
165 case 1: /* selected by jobid */
166 if (!build_directory_tree(ua, &rx)) {
167 bsendmsg(ua, _("Restore not done.\n"));
171 case 2: /* selected by filename, no tree needed */
176 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
177 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
180 if (!(rx.selected_files = write_bsr_file(ua, rx.bsr))) {
181 bsendmsg(ua, _("No files selected to be restored.\n"));
184 bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
185 rx.selected_files==1?"":"s");
187 bsendmsg(ua, _("No files selected to be restored.\n"));
191 if (rx.restore_jobs == 1) {
192 job = rx.restore_job;
194 job = select_restore_job_resource(ua);
200 get_client_name(ua, &rx);
201 if (!rx.ClientName) {
202 bsendmsg(ua, _("No Restore Job resource found!\n"));
206 /* Build run command */
207 fname = get_pool_memory(PM_MESSAGE);
208 make_unique_restore_filename(ua, &fname);
211 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
212 " where=\"%s\" files=%d catalog=\"%s\"",
213 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
214 fname, rx.where, rx.selected_files, ua->catalog->hdr.name);
217 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
218 " files=%d catalog=\"%s\"",
219 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
220 fname, rx.selected_files, ua->catalog->hdr.name);
222 free_pool_memory(fname);
223 if (find_arg(ua, _("yes")) > 0) {
224 pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
226 Dmsg1(400, "Submitting: %s\n", ua->cmd);
228 run_cmd(ua, ua->cmd);
238 static void free_rx(RESTORE_CTX *rx)
243 free_pool_memory(rx->JobIds);
247 free_pool_memory(rx->fname);
251 free_pool_memory(rx->path);
255 free_pool_memory(rx->query);
258 free_name_list(&rx->name_list);
261 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
263 /* If no client name specified yet, get it now */
264 if (!rx->ClientName[0]) {
266 /* try command line argument */
267 int i = find_arg_with_value(ua, _("client"));
269 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
272 memset(&cr, 0, sizeof(cr));
273 if (!get_client_dbr(ua, &cr)) {
276 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
282 * The first step in the restore process is for the user to
283 * select a list of JobIds from which he will subsequently
284 * select which files are to be restored.
286 * Returns: 2 if filename list made
287 * 1 if jobid list made
290 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
293 char date[MAX_TIME_LENGTH];
294 bool have_date = false;
299 const char *list[] = {
300 "List last 20 Jobs run",
301 "List Jobs where a given File is saved",
302 "Enter list of comma separated JobIds to select",
303 "Enter SQL list command",
304 "Select the most recent backup for a client",
305 "Select backup for a client before a specified time",
306 "Enter a list of files to restore",
307 "Enter a list of files to restore before a specified time",
308 "Find the JobIds of the most recent backup for a client",
309 "Find the JobIds for a backup for a client before a specified time",
310 "Enter a list of directories to restore for found JobIds",
315 /* These keywords are handled in a for loop */
325 /* The keyword below are handled by individual arg lookups */
331 "bootstrap", /* 13 */
338 for (i=1; i<ua->argc; i++) { /* loop through arguments */
339 bool found_kw = false;
340 for (j=0; kw[j]; j++) { /* loop through keywords */
341 if (strcasecmp(kw[j], ua->argk[i]) == 0) {
347 bsendmsg(ua, _("Unknown keyword: %s\n"), ua->argk[i]);
350 /* Found keyword in kw[] list, process it */
353 if (*rx->JobIds != 0) {
354 pm_strcat(rx->JobIds, ",");
356 pm_strcat(rx->JobIds, ua->argv[i]);
359 case 1: /* current */
360 bstrutime(date, sizeof(date), time(NULL));
364 if (str_to_utime(ua->argv[i]) == 0) {
365 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
368 bstrncpy(date, ua->argv[i], sizeof(date));
374 bstrutime(date, sizeof(date), time(NULL));
376 if (!get_client_name(ua, rx)) {
379 pm_strcpy(ua->cmd, ua->argv[i]);
380 insert_one_file_or_dir(ua, rx, date, j==4);
381 if (rx->name_list.num_ids) {
382 /* Check MediaType and select storage that corresponds */
383 get_storage_from_mediatype(ua, &rx->name_list, rx);
389 bstrutime(date, sizeof(date), time(NULL));
391 if (!select_backups_before_date(ua, rx, date)) {
396 case 6: /* pool specified */
397 rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
399 bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
402 if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
404 bsendmsg(ua, _("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
408 case 7: /* all specified */
412 * All keywords 7 or greater are ignored or handled by a select prompt
418 if (rx->name_list.num_ids) {
419 return 2; /* filename list made */
423 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
424 "to be restored. You will be presented several methods\n"
425 "of specifying the JobIds. Then you will be allowed to\n"
426 "select which files from those JobIds are to be restored.\n\n"));
429 /* If choice not already made above, prompt */
435 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
436 for (int i=0; list[i]; i++) {
437 add_prompt(ua, list[i]);
440 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
443 case 0: /* list last 20 Jobs run */
444 gui_save = ua->jcr->gui;
446 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
447 ua->jcr->gui = gui_save;
450 case 1: /* list where a file is saved */
451 if (!get_client_name(ua, rx)) {
454 if (!get_cmd(ua, _("Enter Filename (no path):"))) {
457 len = strlen(ua->cmd);
458 fname = (char *)malloc(len * 2 + 1);
459 db_escape_string(fname, ua->cmd, len);
460 Mmsg(rx->query, uar_file, rx->ClientName, fname);
462 gui_save = ua->jcr->gui;
464 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
465 ua->jcr->gui = gui_save;
468 case 2: /* enter a list of JobIds */
469 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
472 pm_strcpy(rx->JobIds, ua->cmd);
474 case 3: /* Enter an SQL list command */
475 if (!get_cmd(ua, _("Enter SQL list command: "))) {
478 gui_save = ua->jcr->gui;
480 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
481 ua->jcr->gui = gui_save;
484 case 4: /* Select the most recent backups */
485 bstrutime(date, sizeof(date), time(NULL));
486 if (!select_backups_before_date(ua, rx, date)) {
490 case 5: /* select backup at specified time */
491 if (!get_date(ua, date, sizeof(date))) {
494 if (!select_backups_before_date(ua, rx, date)) {
498 case 6: /* Enter files */
499 bstrutime(date, sizeof(date), time(NULL));
500 if (!get_client_name(ua, rx)) {
503 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
504 "containg a list of file names with paths, and terminate\n"
505 "them with a blank line.\n"));
507 if (!get_cmd(ua, _("Enter full filename: "))) {
510 len = strlen(ua->cmd);
514 insert_one_file_or_dir(ua, rx, date, false);
516 /* Check MediaType and select storage that corresponds */
517 if (rx->name_list.num_ids) {
518 get_storage_from_mediatype(ua, &rx->name_list, rx);
521 case 7: /* enter files backed up before specified time */
522 if (!get_date(ua, date, sizeof(date))) {
525 if (!get_client_name(ua, rx)) {
528 bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"
529 "containg a list of file names with paths, and terminate\n"
530 "them with a blank line.\n"));
532 if (!get_cmd(ua, _("Enter full filename: "))) {
535 len = strlen(ua->cmd);
539 insert_one_file_or_dir(ua, rx, date, false);
541 /* Check MediaType and select storage that corresponds */
542 if (rx->name_list.num_ids) {
543 get_storage_from_mediatype(ua, &rx->name_list, rx);
547 case 8: /* Find JobIds for current backup */
548 bstrutime(date, sizeof(date), time(NULL));
549 if (!select_backups_before_date(ua, rx, date)) {
555 case 9: /* Find JobIds for give date */
556 if (!get_date(ua, date, sizeof(date))) {
559 if (!select_backups_before_date(ua, rx, date)) {
565 case 10: /* Enter directories */
566 if (*rx->JobIds != 0) {
567 bsendmsg(ua, _("You have already seleted the following JobIds: %s\n"),
569 } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
570 if (*rx->JobIds != 0 && *ua->cmd) {
571 pm_strcat(rx->JobIds, ",");
573 pm_strcat(rx->JobIds, ua->cmd);
575 if (*rx->JobIds == 0 || *rx->JobIds == '.') {
576 return 0; /* nothing entered, return */
578 bstrutime(date, sizeof(date), time(NULL));
579 if (!get_client_name(ua, rx)) {
582 bsendmsg(ua, _("Enter full directory names or start the name\n"
583 "with a < to indicate it is a filename containg a list\n"
584 "of directories and terminate them with a blank line.\n"));
586 if (!get_cmd(ua, _("Enter directory name: "))) {
589 len = strlen(ua->cmd);
593 /* Add trailing slash to end of directory names */
594 if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
595 strcat(ua->cmd, "/");
597 insert_one_file_or_dir(ua, rx, date, true);
599 /* Check MediaType and select storage that corresponds */
600 if (rx->name_list.num_ids) {
601 get_storage_from_mediatype(ua, &rx->name_list, rx);
605 case 11: /* Cancel or quit */
610 if (*rx->JobIds == 0) {
611 bsendmsg(ua, _("No Jobs selected.\n"));
614 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
615 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
617 memset(&jr, 0, sizeof(JOB_DBR));
620 for (p=rx->JobIds; ; ) {
621 int stat = get_next_jobid_from_list(&p, &JobId);
623 bsendmsg(ua, _("Invalid JobId in list.\n"));
629 if (jr.JobId == JobId) {
630 continue; /* duplicate of last JobId */
633 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
635 bsendmsg(ua, _("Unable to get Job record for JobId=%s: ERR=%s\n"),
636 edit_int64(JobId, ed1), db_strerror(ua->db));
639 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
640 bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
644 rx->TotalFiles += jr.JobFiles;
652 static int get_date(UAContext *ua, char *date, int date_len)
654 bsendmsg(ua, _("The restored files will the most current backup\n"
655 "BEFORE the date you specify below.\n\n"));
657 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
660 if (str_to_utime(ua->cmd) != 0) {
663 bsendmsg(ua, _("Improper date format.\n"));
665 bstrncpy(date, ua->cmd, date_len);
670 * Insert a single file, or read a list of files from a file
672 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
682 if ((ffd = fopen(p, "r")) == NULL) {
684 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
688 while (fgets(file, sizeof(file), ffd)) {
691 if (!insert_dir_into_findex_list(ua, rx, file, date)) {
692 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
695 if (!insert_file_into_findex_list(ua, rx, file, date)) {
696 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
704 insert_dir_into_findex_list(ua, rx, ua->cmd, date);
706 insert_file_into_findex_list(ua, rx, ua->cmd, date);
713 * For a given file (path+filename), split into path and file, then
714 * lookup the most recent backup in the catalog to get the JobId
715 * and FileIndex, then insert them into the findex list.
717 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
722 strip_trailing_junk(file);
723 split_path_and_filename(rx, file);
724 if (*rx->JobIds == 0) {
725 Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname,
728 Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
729 rx->path, rx->fname, rx->ClientName);
732 /* Find and insert jobid and File Index */
733 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
734 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
735 rx->query, db_strerror(ua->db));
738 bsendmsg(ua, _("No database record found for: %s\n"), file);
742 * Find the MediaTypes for this JobId and add to the name_list
744 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
745 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
746 bsendmsg(ua, "%s", db_strerror(ua->db));
753 * For a given path lookup the most recent backup in the catalog
754 * to get the JobId and FileIndexes of all files in that directory.
756 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
761 strip_trailing_junk(dir);
762 if (*rx->JobIds == 0) {
763 bsendmsg(ua, _("No JobId specified cannot continue.\n"));
766 Mmsg(rx->query, uar_jobid_fileindex_from_dir, rx->JobIds,
767 dir, rx->ClientName);
770 /* Find and insert jobid and File Index */
771 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
772 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
773 rx->query, db_strerror(ua->db));
776 bsendmsg(ua, _("No database record found for: %s\n"), dir);
780 * Find the MediaTypes for this JobId and add to the name_list
782 Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
783 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
784 bsendmsg(ua, "%s", db_strerror(ua->db));
791 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
795 /* Find path without the filename.
796 * I.e. everything after the last / is a "filename".
797 * OK, maybe it is a directory name, but we treat it like
798 * a filename. If we don't find a / then the whole name
799 * must be a path name (e.g. c:).
801 for (p=f=name; *p; p++) {
803 f = p; /* set pos of last slash */
806 if (*f == '/') { /* did we find a slash? */
807 f++; /* yes, point to filename */
808 } else { /* no, whole thing must be path name */
812 /* If filename doesn't exist (i.e. root directory), we
813 * simply create a blank name consisting of a single
814 * space. This makes handling zero length filenames
819 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
820 memcpy(rx->fname, f, rx->fnl); /* copy filename */
821 rx->fname[rx->fnl] = 0;
829 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
830 memcpy(rx->path, name, rx->pnl);
831 rx->path[rx->pnl] = 0;
837 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
840 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
843 JobId_t JobId, last_JobId;
848 memset(&tree, 0, sizeof(TREE_CTX));
850 * Build the directory tree containing JobIds user selected
852 tree.root = new_tree(rx->TotalFiles);
857 * For display purposes, the same JobId, with different volumes may
858 * appear more than once, however, we only insert it once.
862 tree.FileEstimate = 0;
863 if (get_next_jobid_from_list(&p, &JobId) > 0) {
864 /* Use first JobId as estimate of the number of files to restore */
865 Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
866 if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
867 bsendmsg(ua, "%s\n", db_strerror(ua->db));
870 /* Add about 25% more than this job for over estimate */
871 tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
872 tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
875 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
878 if (JobId == last_JobId) {
879 continue; /* eliminate duplicate JobIds */
882 bsendmsg(ua, _("\nBuilding directory tree for JobId %s ... "),
883 edit_int64(JobId, ed1));
886 * Find files for this JobId and insert them in the tree
888 Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
889 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
890 bsendmsg(ua, "%s", db_strerror(ua->db));
893 * Find the MediaTypes for this JobId and add to the name_list
895 Mmsg(rx->query, uar_mediatype, edit_int64(JobId, ed1));
896 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
897 bsendmsg(ua, "%s", db_strerror(ua->db));
900 if (tree.FileCount == 0) {
901 bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
902 "is not possible.Most likely your retention policy pruned the files\n");
903 if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
907 for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
908 if (JobId == last_JobId) {
909 continue; /* eliminate duplicate JobIds */
911 add_findex_all(rx->bsr, JobId);
917 bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
918 items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
919 tree.all?" and marked for extraction":"");
921 /* Check MediaType and select storage that corresponds */
922 get_storage_from_mediatype(ua, &rx->name_list, rx);
924 if (find_arg(ua, _("done")) < 0) {
925 /* Let the user interact in selecting which files to restore */
926 OK = user_select_files_from_tree(&tree);
930 * Walk down through the tree finding all files marked to be
931 * extracted making a bootstrap file.
934 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
935 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
936 if (node->extract || node->extract_dir) {
937 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
938 add_findex(rx->bsr, node->JobId, node->FileIndex);
939 if (node->extract && node->type != TN_NEWDIR) {
940 rx->selected_files++; /* count only saved files */
947 free_tree(tree.root); /* free the directory tree */
953 * This routine is used to get the current backup or a backup
954 * before the specified date.
956 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
961 char fileset_name[MAX_NAME_LENGTH];
962 char ed1[50], ed2[50];
963 char pool_select[MAX_NAME_LENGTH];
967 /* Create temp tables */
968 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
969 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
970 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
971 bsendmsg(ua, "%s\n", db_strerror(ua->db));
973 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
974 bsendmsg(ua, "%s\n", db_strerror(ua->db));
977 * Select Client from the Catalog
979 memset(&cr, 0, sizeof(cr));
980 if (!get_client_dbr(ua, &cr)) {
983 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
988 memset(&fsr, 0, sizeof(fsr));
989 i = find_arg_with_value(ua, "FileSet");
991 bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
992 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
993 bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
994 db_strerror(ua->db));
998 if (i < 0) { /* fileset not found */
999 edit_int64(cr.ClientId, ed1);
1000 Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1001 start_prompt(ua, _("The defined FileSet resources are:\n"));
1002 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1003 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1005 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1006 fileset_name, sizeof(fileset_name)) < 0) {
1010 bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1011 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1012 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1013 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
1014 "Continuing anyway.\n"));
1018 /* If Pool specified, add PoolId specification */
1022 memset(&pr, 0, sizeof(pr));
1023 bstrncpy(pr.Name, rx->pool->hdr.name, sizeof(pr.Name));
1024 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1025 bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ",
1026 edit_int64(pr.PoolId, ed1));
1028 bsendmsg(ua, _("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1032 /* Find JobId of last Full backup for this client, fileset */
1033 edit_int64(cr.ClientId, ed1);
1034 Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1036 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1037 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1041 /* Find all Volumes used by that JobId */
1042 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1043 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1046 /* Note, this is needed because I don't seem to get the callback
1047 * from the call just above.
1050 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1051 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1053 if (rx->JobTDate == 0) {
1054 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1058 /* Now find most recent Differental Job after Full save, if any */
1059 Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1060 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1061 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1062 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1064 /* Now update JobTDate to lock onto Differental, if any */
1066 if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1067 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1069 if (rx->JobTDate == 0) {
1070 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
1074 /* Now find all Incremental Jobs after Full/dif save */
1075 Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1076 edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1077 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1078 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1081 /* Get the JobIds from that list */
1083 rx->last_jobid[0] = 0;
1084 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1085 bsendmsg(ua, "%s\n", db_strerror(ua->db));
1088 if (rx->JobIds[0] != 0) {
1089 /* Display a list of Jobs selected for this restore */
1090 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
1093 bsendmsg(ua, _("No jobs found.\n"));
1097 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1098 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1103 /* Return next JobId from comma separated list */
1104 static int get_next_jobid_from_list(char **p, uint32_t *JobId)
1110 for (int i=0; i<(int)sizeof(jobid); i++) {
1113 } else if (*q == ',') {
1120 if (jobid[0] == 0) {
1122 } else if (!is_a_number(jobid)) {
1123 return -1; /* error */
1126 *JobId = str_to_int64(jobid);
1130 static int count_handler(void *ctx, int num_fields, char **row)
1132 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1133 rx->JobId = str_to_int64(row[0]);
1139 * Callback handler to get JobId and FileIndex for files
1140 * can insert more than one depending on the caller.
1142 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1144 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1145 rx->JobId = str_to_int64(row[0]);
1146 add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1148 rx->selected_files++;
1153 * Callback handler make list of JobIds
1155 static int jobid_handler(void *ctx, int num_fields, char **row)
1157 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1159 if (strcmp(rx->last_jobid, row[0]) == 0) {
1160 return 0; /* duplicate id */
1162 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1163 if (rx->JobIds[0] != 0) {
1164 pm_strcat(rx->JobIds, ",");
1166 pm_strcat(rx->JobIds, row[0]);
1172 * Callback handler to pickup last Full backup JobTDate
1174 static int last_full_handler(void *ctx, int num_fields, char **row)
1176 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1178 rx->JobTDate = str_to_int64(row[1]);
1183 * Callback handler build FileSet name prompt list
1185 static int fileset_handler(void *ctx, int num_fields, char **row)
1187 /* row[0] = FileSet (name) */
1189 add_prompt((UAContext *)ctx, row[0]);
1195 * Called here with each name to be added to the list. The name is
1196 * added to the list if it is not already in the list.
1198 * Used to make unique list of FileSets and MediaTypes
1200 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
1202 NAME_LIST *name = (NAME_LIST *)ctx;
1204 if (name->num_ids == MAX_ID_LIST_LEN) {
1207 if (name->num_ids == name->max_ids) {
1208 if (name->max_ids == 0) {
1209 name->max_ids = 1000;
1210 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
1212 name->max_ids = (name->max_ids * 3) / 2;
1213 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
1216 for (int i=0; i<name->num_ids; i++) {
1217 if (strcmp(name->name[i], row[0]) == 0) {
1218 return 0; /* already in list, return */
1221 /* Add new name to list */
1222 name->name[name->num_ids++] = bstrdup(row[0]);
1228 * Print names in the list
1230 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
1232 for (int i=0; i < name_list->num_ids; i++) {
1233 bsendmsg(ua, "%s\n", name_list->name[i]);
1239 * Free names in the list
1241 static void free_name_list(NAME_LIST *name_list)
1243 for (int i=0; i < name_list->num_ids; i++) {
1244 free(name_list->name[i]);
1246 if (name_list->name) {
1247 free(name_list->name);
1248 name_list->name = NULL;
1250 name_list->max_ids = 0;
1251 name_list->num_ids = 0;
1254 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
1258 if (name_list->num_ids > 1) {
1259 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
1260 "Restore is not possible. The MediaTypes used are:\n"));
1261 print_name_list(ua, name_list);
1262 rx->store = select_storage_resource(ua);
1266 if (name_list->num_ids == 0) {
1267 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
1268 rx->store = select_storage_resource(ua);
1275 * We have a single MediaType, look it up in our Storage resource
1278 foreach_res(store, R_STORAGE) {
1279 if (strcmp(name_list->name[0], store->media_type) == 0) {
1280 if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1289 /* Check if an explicit storage resource is given */
1291 int i = find_arg_with_value(ua, "storage");
1293 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1294 if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
1298 if (store && (store != rx->store)) {
1299 bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
1306 /* Take command line arg, or ask user if none */
1307 rx->store = get_storage_resource(ua, false /* don't use default */);
1310 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
1311 "MediaType \"%s\", needed by the Jobs you selected.\n"
1312 "You will be allowed to select a Storage device later.\n"),
1313 name_list->name[0]);