]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_restore.c
Fix File and Job Retention in show pool command
[bacula/bacula] / bacula / src / dird / ua_restore.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Database restore Command
31  *      Creates a bootstrap file for restoring files and
32  *      starts the restore job.
33  *
34  *      Tree handling routines split into ua_tree.c July MMIII.
35  *      BSR (bootstrap record) handling routines split into
36  *        bsr.c July MMIII
37  *
38  *     Kern Sibbald, July MMII
39  *
40  *   Version $Id$
41  */
42
43
44 #include "bacula.h"
45 #include "dird.h"
46
47 /* Imported functions */
48 extern void print_bsr(UAContext *ua, RBSR *bsr);
49
50
51
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,
64                                          char *date);
65 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
66                                         char *date);
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);
73 static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx);
74
75 /*
76  *   Restore files
77  *
78  */
79 int restore_cmd(UAContext *ua, const char *cmd)
80 {
81    RESTORE_CTX rx;                    /* restore context */
82    JOB *job;
83    int i;
84    JCR *jcr = ua->jcr;
85    char *escaped_bsr_name = NULL;
86    char *escaped_where_name = NULL;
87    char *strip_prefix, *add_prefix, *add_suffix, *regexp;
88    strip_prefix = add_prefix = add_suffix = regexp = NULL;
89
90    memset(&rx, 0, sizeof(rx));
91    rx.path = get_pool_memory(PM_FNAME);
92    rx.fname = get_pool_memory(PM_FNAME);
93    rx.JobIds = get_pool_memory(PM_FNAME);
94    rx.BaseJobIds = get_pool_memory(PM_FNAME);
95    rx.query = get_pool_memory(PM_FNAME);
96    rx.bsr = new_bsr();
97
98    i = find_arg_with_value(ua, "where");
99    if (i >= 0) {
100       rx.where = ua->argv[i];
101    }
102
103    i = find_arg_with_value(ua, "strip_prefix");
104    if (i >= 0) {
105       strip_prefix = ua->argv[i];
106    }
107
108    i = find_arg_with_value(ua, "add_prefix");
109    if (i >= 0) {
110       add_prefix = ua->argv[i];
111    }
112
113    i = find_arg_with_value(ua, "add_suffix");
114    if (i >= 0) {
115       add_suffix = ua->argv[i];
116    }
117
118    i = find_arg_with_value(ua, "regexwhere");
119    if (i >= 0) {
120       rx.RegexWhere = ua->argv[i];
121    }
122
123    if (strip_prefix || add_suffix || add_prefix) {
124       int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
125       regexp = (char *)bmalloc(len * sizeof(char));
126
127       bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
128       rx.RegexWhere = regexp;
129    }
130
131    /* TODO: add acl for regexwhere ? */
132
133    if (rx.RegexWhere) {
134       if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere)) {
135          ua->error_msg(_("\"RegexWhere\" specification not authorized.\n"));
136          goto bail_out;
137       }
138    }
139
140    if (rx.where) {
141       if (!acl_access_ok(ua, Where_ACL, rx.where)) {
142          ua->error_msg(_("\"where\" specification not authorized.\n"));
143          goto bail_out;
144       }
145    }
146
147    if (!open_client_db(ua)) {
148       goto bail_out;
149    }
150
151    /* Ensure there is at least one Restore Job */
152    LockRes();
153    foreach_res(job, R_JOB) {
154       if (job->JobType == JT_RESTORE) {
155          if (!rx.restore_job) {
156             rx.restore_job = job;
157          }
158          rx.restore_jobs++;
159       }
160    }
161    UnlockRes();
162    if (!rx.restore_jobs) {
163       ua->error_msg(_(
164          "No Restore Job Resource found in bacula-dir.conf.\n"
165          "You must create at least one before running this command.\n"));
166       goto bail_out;
167    }
168
169    /*
170     * Request user to select JobIds or files by various different methods
171     *  last 20 jobs, where File saved, most recent backup, ...
172     *  In the end, a list of files are pumped into
173     *  add_findex()
174     */
175    switch (user_select_jobids_or_files(ua, &rx)) {
176    case 0:                            /* error */
177       goto bail_out;
178    case 1:                            /* selected by jobid */
179       get_and_display_basejobs(ua, &rx);
180       if (!build_directory_tree(ua, &rx)) {
181          ua->send_msg(_("Restore not done.\n"));
182          goto bail_out;
183       }
184       break;
185    case 2:                            /* selected by filename, no tree needed */
186       break;
187    }
188
189    if (rx.bsr->JobId) {
190       char ed1[50];
191       if (!complete_bsr(ua, rx.bsr)) {   /* find Vol, SessId, SessTime from JobIds */
192          ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
193          goto bail_out;
194       }
195       if (!(rx.selected_files = write_bsr_file(ua, rx))) {
196          ua->warning_msg(_("No files selected to be restored.\n"));
197          goto bail_out;
198       }
199       display_bsr_info(ua, rx);          /* display vols needed, etc */
200
201       if (rx.selected_files==1) {
202          ua->info_msg(_("\n1 file selected to be restored.\n\n"));
203       } else {
204          ua->info_msg(_("\n%s files selected to be restored.\n\n"), 
205             edit_uint64_with_commas(rx.selected_files, ed1));
206       }
207    } else {
208       ua->warning_msg(_("No files selected to be restored.\n"));
209       goto bail_out;
210    }
211
212    if (rx.restore_jobs == 1) {
213       job = rx.restore_job;
214    } else {
215       job = select_restore_job_resource(ua);
216    }
217    if (!job) {
218       goto bail_out;
219    }
220
221    get_client_name(ua, &rx);
222    if (!rx.ClientName) {
223       ua->error_msg(_("No Client resource found!\n"));
224       goto bail_out;
225    }
226    get_restore_client_name(ua, rx);
227
228    escaped_bsr_name = escape_filename(jcr->RestoreBootstrap);
229
230    /* Build run command */
231    if (rx.RegexWhere) {
232       escaped_where_name = escape_filename(rx.RegexWhere);
233       Mmsg(ua->cmd,
234           "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
235           " bootstrap=\"%s\" regexwhere=\"%s\" files=%u catalog=\"%s\"",
236           job->name(), rx.ClientName, rx.RestoreClientName, 
237           rx.store?rx.store->name():"",
238           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
239           escaped_where_name ? escaped_where_name : rx.RegexWhere,
240           rx.selected_files, ua->catalog->name());
241
242    } else if (rx.where) {
243       escaped_where_name = escape_filename(rx.where);
244       Mmsg(ua->cmd,
245           "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
246           " bootstrap=\"%s\" where=\"%s\" files=%u catalog=\"%s\"",
247           job->name(), rx.ClientName, rx.RestoreClientName,
248           rx.store?rx.store->name():"",
249           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
250           escaped_where_name ? escaped_where_name : rx.where,
251           rx.selected_files, ua->catalog->name());
252
253    } else {
254       Mmsg(ua->cmd,
255           "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
256           " bootstrap=\"%s\" files=%u catalog=\"%s\"",
257           job->name(), rx.ClientName, rx.RestoreClientName,
258           rx.store?rx.store->name():"",
259           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
260           rx.selected_files, ua->catalog->name());
261    }
262
263    if (escaped_bsr_name != NULL) {
264       bfree(escaped_bsr_name);
265    }
266
267    if (escaped_where_name != NULL) {
268       bfree(escaped_where_name);
269    }
270    
271    if (regexp) {
272       bfree(regexp);
273    }
274
275    if (find_arg(ua, NT_("yes")) > 0) {
276       pm_strcat(ua->cmd, " yes");    /* pass it on to the run command */
277    }
278    Dmsg1(200, "Submitting: %s\n", ua->cmd);
279    parse_ua_args(ua);
280    run_cmd(ua, ua->cmd);
281    free_rx(&rx);
282    return 1;
283
284 bail_out:
285    if (escaped_bsr_name != NULL) {
286       bfree(escaped_bsr_name);
287    }
288
289    if (escaped_where_name != NULL) {
290       bfree(escaped_where_name);
291    }
292
293    if (regexp) {
294       bfree(regexp);
295    }
296
297    free_rx(&rx);
298    return 0;
299
300 }
301
302 /* 
303  * Fill the rx->BaseJobIds and display the list
304  */
305 static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx)
306 {
307    db_list_ctx jobids;
308
309    if (!db_get_used_base_jobids(ua->jcr, ua->db, rx->JobIds, &jobids)) {
310       ua->warning_msg("%s", db_strerror(ua->db));
311    }
312    
313    if (jobids.count) {
314       POOL_MEM q;
315       Mmsg(q, uar_print_jobs, jobids.list);
316       ua->send_msg(_("The restore will use the following job(s) as Base\n"));
317       db_list_sql_query(ua->jcr, ua->db, q.c_str(), prtit, ua, 1, HORZ_LIST);
318    }
319    pm_strcpy(rx->BaseJobIds, jobids.list);
320 }
321
322 static void free_rx(RESTORE_CTX *rx)
323 {
324    free_bsr(rx->bsr);
325    rx->bsr = NULL;
326    free_and_null_pool_memory(rx->JobIds);
327    free_and_null_pool_memory(rx->BaseJobIds);
328    free_and_null_pool_memory(rx->fname);
329    free_and_null_pool_memory(rx->path);
330    free_and_null_pool_memory(rx->query);
331    free_name_list(&rx->name_list);
332 }
333
334 static bool has_value(UAContext *ua, int i)
335 {
336    if (!ua->argv[i]) {
337       ua->error_msg(_("Missing value for keyword: %s\n"), ua->argk[i]);
338       return false;
339    }
340    return true;
341 }
342
343 /*
344  * This gets the client name from which the backup was made
345  */
346 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
347 {
348    /* If no client name specified yet, get it now */
349    if (!rx->ClientName[0]) {
350       CLIENT_DBR cr;
351       /* try command line argument */
352       int i = find_arg_with_value(ua, NT_("client"));
353       if (i < 0) {
354          i = find_arg_with_value(ua, NT_("backupclient"));
355       }
356       if (i >= 0) {
357          if (!has_value(ua, i)) {
358             return 0;
359          }
360          bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
361          return 1;
362       }
363       memset(&cr, 0, sizeof(cr));
364       if (!get_client_dbr(ua, &cr)) {
365          return 0;
366       }
367       bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
368    }
369    return 1;
370 }
371
372 /*
373  * This is where we pick up a client name to restore to.
374  */
375 static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx)
376 {
377    /* Start with same name as backup client */
378    bstrncpy(rx.RestoreClientName, rx.ClientName, sizeof(rx.RestoreClientName));    
379
380    /* try command line argument */
381    int i = find_arg_with_value(ua, NT_("restoreclient"));
382    if (i >= 0) {
383       if (!has_value(ua, i)) {
384          return 0;
385       }
386       bstrncpy(rx.RestoreClientName, ua->argv[i], sizeof(rx.RestoreClientName));
387       return 1;
388    }
389    return 1;
390 }
391
392
393
394 /*
395  * The first step in the restore process is for the user to
396  *  select a list of JobIds from which he will subsequently
397  *  select which files are to be restored.
398  *
399  *  Returns:  2  if filename list made
400  *            1  if jobid list made
401  *            0  on error
402  */
403 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
404 {
405    char *p;
406    char date[MAX_TIME_LENGTH];
407    bool have_date = false;
408    /* Include current second if using current time */
409    utime_t now = time(NULL) + 1;
410    JobId_t JobId;
411    JOB_DBR jr = { (JobId_t)-1 };
412    bool done = false;
413    int i, j;
414    const char *list[] = {
415       _("List last 20 Jobs run"),
416       _("List Jobs where a given File is saved"),
417       _("Enter list of comma separated JobIds to select"),
418       _("Enter SQL list command"),
419       _("Select the most recent backup for a client"),
420       _("Select backup for a client before a specified time"),
421       _("Enter a list of files to restore"),
422       _("Enter a list of files to restore before a specified time"),
423       _("Find the JobIds of the most recent backup for a client"),
424       _("Find the JobIds for a backup for a client before a specified time"),
425       _("Enter a list of directories to restore for found JobIds"),
426       _("Select full restore to a specified Job date"),
427       _("Cancel"),
428       NULL };
429
430    const char *kw[] = {
431        /* These keywords are handled in a for loop */
432       "jobid",       /* 0 */
433       "current",     /* 1 */
434       "before",      /* 2 */
435       "file",        /* 3 */
436       "directory",   /* 4 */
437       "select",      /* 5 */
438       "pool",        /* 6 */
439       "all",         /* 7 */
440
441       /* The keyword below are handled by individual arg lookups */
442       "client",       /* 8 */
443       "storage",      /* 9 */
444       "fileset",      /* 10 */
445       "where",        /* 11 */
446       "yes",          /* 12 */
447       "bootstrap",    /* 13 */
448       "done",         /* 14 */
449       "strip_prefix", /* 15 */
450       "add_prefix",   /* 16 */
451       "add_suffix",   /* 17 */
452       "regexwhere",   /* 18 */
453       "restoreclient", /* 19 */
454       "copies",        /* 20 */
455       NULL
456    };
457
458    rx->JobIds[0] = 0;
459
460    for (i=1; i<ua->argc; i++) {       /* loop through arguments */
461       bool found_kw = false;
462       for (j=0; kw[j]; j++) {         /* loop through keywords */
463          if (strcasecmp(kw[j], ua->argk[i]) == 0) {
464             found_kw = true;
465             break;
466          }
467       }
468       if (!found_kw) {
469          ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]);
470          return 0;
471       }
472       /* Found keyword in kw[] list, process it */
473       switch (j) {
474       case 0:                            /* jobid */
475          if (!has_value(ua, i)) {
476             return 0;
477          }
478          if (*rx->JobIds != 0) {
479             pm_strcat(rx->JobIds, ",");
480          }
481          pm_strcat(rx->JobIds, ua->argv[i]);
482          done = true;
483          break;
484       case 1:                            /* current */
485          /*
486           * Note, we add one second here just to include any job
487           *  that may have finished within the current second,
488           *  which happens a lot in scripting small jobs.
489           */
490          bstrutime(date, sizeof(date), now);
491          have_date = true;
492          break;
493       case 2:                            /* before */
494          if (have_date || !has_value(ua, i)) {
495             return 0;
496          }
497          if (str_to_utime(ua->argv[i]) == 0) {
498             ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
499             return 0;
500          }
501          bstrncpy(date, ua->argv[i], sizeof(date));
502          have_date = true;
503          break;
504       case 3:                            /* file */
505       case 4:                            /* dir */
506          if (!has_value(ua, i)) {
507             return 0;
508          }
509          if (!have_date) {
510             bstrutime(date, sizeof(date), now);
511          }
512          if (!get_client_name(ua, rx)) {
513             return 0;
514          }
515          pm_strcpy(ua->cmd, ua->argv[i]);
516          insert_one_file_or_dir(ua, rx, date, j==4);
517          return 2;
518       case 5:                            /* select */
519          if (!have_date) {
520             bstrutime(date, sizeof(date), now);
521          }
522          if (!select_backups_before_date(ua, rx, date)) {
523             return 0;
524          }
525          done = true;
526          break;
527       case 6:                            /* pool specified */
528          if (!has_value(ua, i)) {
529             return 0;
530          }
531          rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
532          if (!rx->pool) {
533             ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
534             return 0;
535          }
536          if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
537             rx->pool = NULL;
538             ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
539             return 0;
540          }
541          break;
542       case 7:                         /* all specified */
543          rx->all = true;
544          break;
545       /*
546        * All keywords 7 or greater are ignored or handled by a select prompt
547        */
548       default:
549          break;
550       }
551    }
552
553    if (!done) {
554       ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
555                   "to be restored. You will be presented several methods\n"
556                   "of specifying the JobIds. Then you will be allowed to\n"
557                   "select which files from those JobIds are to be restored.\n\n"));
558    }
559
560    /* If choice not already made above, prompt */
561    for ( ; !done; ) {
562       char *fname;
563       int len;
564       bool gui_save;
565       db_list_ctx jobids;
566
567       start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
568       for (int i=0; list[i]; i++) {
569          add_prompt(ua, list[i]);
570       }
571       done = true;
572       switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
573       case -1:                        /* error or cancel */
574          return 0;
575       case 0:                         /* list last 20 Jobs run */
576          if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
577             ua->error_msg(_("SQL query not authorized.\n"));
578             return 0;
579          }
580          gui_save = ua->jcr->gui;
581          ua->jcr->gui = true;
582          db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
583          ua->jcr->gui = gui_save;
584          done = false;
585          break;
586       case 1:                         /* list where a file is saved */
587          if (!get_client_name(ua, rx)) {
588             return 0;
589          }
590          if (!get_cmd(ua, _("Enter Filename (no path):"))) {
591             return 0;
592          }
593          len = strlen(ua->cmd);
594          fname = (char *)malloc(len * 2 + 1);
595          db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len);
596          Mmsg(rx->query, uar_file[db_type], rx->ClientName, fname);
597          free(fname);
598          gui_save = ua->jcr->gui;
599          ua->jcr->gui = true;
600          db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
601          ua->jcr->gui = gui_save;
602          done = false;
603          break;
604       case 2:                         /* enter a list of JobIds */
605          if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
606             return 0;
607          }
608          pm_strcpy(rx->JobIds, ua->cmd);
609          break;
610       case 3:                         /* Enter an SQL list command */
611          if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
612             ua->error_msg(_("SQL query not authorized.\n"));
613             return 0;
614          }
615          if (!get_cmd(ua, _("Enter SQL list command: "))) {
616             return 0;
617          }
618          gui_save = ua->jcr->gui;
619          ua->jcr->gui = true;
620          db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
621          ua->jcr->gui = gui_save;
622          done = false;
623          break;
624       case 4:                         /* Select the most recent backups */
625          if (!have_date) {
626             bstrutime(date, sizeof(date), now);
627          }
628          if (!select_backups_before_date(ua, rx, date)) {
629             return 0;
630          }
631          break;
632       case 5:                         /* select backup at specified time */
633          if (!have_date) {
634             if (!get_date(ua, date, sizeof(date))) {
635                return 0;
636             }
637          }
638          if (!select_backups_before_date(ua, rx, date)) {
639             return 0;
640          }
641          break;
642       case 6:                         /* Enter files */
643          if (!have_date) {
644             bstrutime(date, sizeof(date), now);
645          }
646          if (!get_client_name(ua, rx)) {
647             return 0;
648          }
649          ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
650                         "containing a list of file names with paths, and terminate\n"
651                         "them with a blank line.\n"));
652          for ( ;; ) {
653             if (!get_cmd(ua, _("Enter full filename: "))) {
654                return 0;
655             }
656             len = strlen(ua->cmd);
657             if (len == 0) {
658                break;
659             }
660             insert_one_file_or_dir(ua, rx, date, false);
661          }
662          return 2;
663        case 7:                        /* enter files backed up before specified time */
664          if (!have_date) {
665             if (!get_date(ua, date, sizeof(date))) {
666                return 0;
667             }
668          }
669          if (!get_client_name(ua, rx)) {
670             return 0;
671          }
672          ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
673                         "containing a list of file names with paths, and terminate\n"
674                         "them with a blank line.\n"));
675          for ( ;; ) {
676             if (!get_cmd(ua, _("Enter full filename: "))) {
677                return 0;
678             }
679             len = strlen(ua->cmd);
680             if (len == 0) {
681                break;
682             }
683             insert_one_file_or_dir(ua, rx, date, false);
684          }
685          return 2;
686
687       case 8:                         /* Find JobIds for current backup */
688          if (!have_date) {
689             bstrutime(date, sizeof(date), now);
690          }
691          if (!select_backups_before_date(ua, rx, date)) {
692             return 0;
693          }
694          done = false;
695          break;
696
697       case 9:                         /* Find JobIds for give date */
698          if (!have_date) {
699             if (!get_date(ua, date, sizeof(date))) {
700                return 0;
701             }
702          }
703          if (!select_backups_before_date(ua, rx, date)) {
704             return 0;
705          }
706          done = false;
707          break;
708
709       case 10:                        /* Enter directories */
710          if (*rx->JobIds != 0) {
711             ua->send_msg(_("You have already selected the following JobIds: %s\n"),
712                rx->JobIds);
713          } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
714             if (*rx->JobIds != 0 && *ua->cmd) {
715                pm_strcat(rx->JobIds, ",");
716             }
717             pm_strcat(rx->JobIds, ua->cmd);
718          }
719          if (*rx->JobIds == 0 || *rx->JobIds == '.') {
720             return 0;                 /* nothing entered, return */
721          }
722          if (!have_date) {
723             bstrutime(date, sizeof(date), now);
724          }
725          if (!get_client_name(ua, rx)) {
726             return 0;
727          }
728          ua->send_msg(_("Enter full directory names or start the name\n"
729                         "with a < to indicate it is a filename containing a list\n"
730                         "of directories and terminate them with a blank line.\n"));
731          for ( ;; ) {
732             if (!get_cmd(ua, _("Enter directory name: "))) {
733                return 0;
734             }
735             len = strlen(ua->cmd);
736             if (len == 0) {
737                break;
738             }
739             /* Add trailing slash to end of directory names */
740             if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
741                strcat(ua->cmd, "/");
742             }
743             insert_one_file_or_dir(ua, rx, date, true);
744          }
745          return 2;
746
747       case 11:                        /* Choose a jobid and select jobs */
748          if (!get_cmd(ua, _("Enter JobId to get the state to restore: ")) ||
749              !is_an_integer(ua->cmd)) 
750          {
751             return 0;
752          }
753
754          memset(&jr, 0, sizeof(JOB_DBR));
755          jr.JobId = str_to_int64(ua->cmd);
756          if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
757             ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
758                           ua->cmd, db_strerror(ua->db));
759             return 0;
760          }
761          ua->send_msg(_("Selecting jobs to build the Full state at %s\n"),
762                       jr.cStartTime);
763          jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
764          if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) {
765             return 0;
766          }
767          pm_strcpy(rx->JobIds, jobids.list);
768          Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds);
769          break;
770       case 12:                        /* Cancel or quit */
771          return 0;
772       }
773    }
774
775    memset(&jr, 0, sizeof(JOB_DBR));
776    POOLMEM *JobIds = get_pool_memory(PM_FNAME);
777    *JobIds = 0;
778    rx->TotalFiles = 0;
779    /*        
780     * Find total number of files to be restored, and filter the JobId
781     *  list to contain only ones permitted by the ACL conditions.
782     */
783    for (p=rx->JobIds; ; ) {
784       char ed1[50];
785       int stat = get_next_jobid_from_list(&p, &JobId);
786       if (stat < 0) {
787          ua->error_msg(_("Invalid JobId in list.\n"));
788          free_pool_memory(JobIds);
789          return 0;
790       }
791       if (stat == 0) {
792          break;
793       }
794       if (jr.JobId == JobId) {
795          continue;                    /* duplicate of last JobId */
796       }
797       memset(&jr, 0, sizeof(JOB_DBR));
798       jr.JobId = JobId;
799       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
800          ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
801             edit_int64(JobId, ed1), db_strerror(ua->db));
802          free_pool_memory(JobIds);
803          return 0;
804       }
805       if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
806          ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"),
807             edit_int64(JobId, ed1), jr.Name);
808          continue;
809       }
810       if (*JobIds != 0) {
811          pm_strcat(JobIds, ",");
812       }
813       pm_strcat(JobIds, edit_int64(JobId, ed1));
814       rx->TotalFiles += jr.JobFiles;
815    }
816    free_pool_memory(rx->JobIds);
817    rx->JobIds = JobIds;               /* Set ACL filtered list */
818    if (*rx->JobIds == 0) {
819       ua->warning_msg(_("No Jobs selected.\n"));
820       return 0;
821    }
822
823    if (strchr(rx->JobIds,',')) {
824       ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds);
825    } else {
826       ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds);
827    }
828    return 1;
829 }
830
831 /*
832  * Get date from user
833  */
834 static int get_date(UAContext *ua, char *date, int date_len)
835 {
836    ua->send_msg(_("The restored files will the most current backup\n"
837                   "BEFORE the date you specify below.\n\n"));
838    for ( ;; ) {
839       if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
840          return 0;
841       }
842       if (str_to_utime(ua->cmd) != 0) {
843          break;
844       }
845       ua->error_msg(_("Improper date format.\n"));
846    }
847    bstrncpy(date, ua->cmd, date_len);
848    return 1;
849 }
850
851 /*
852  * Insert a single file, or read a list of files from a file
853  */
854 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
855 {
856    FILE *ffd;
857    char file[5000];
858    char *p = ua->cmd;
859    int line = 0;
860
861    switch (*p) {
862    case '<':
863       p++;
864       if ((ffd = fopen(p, "rb")) == NULL) {
865          berrno be;
866          ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
867             p, be.bstrerror());
868          break;
869       }
870       while (fgets(file, sizeof(file), ffd)) {
871          line++;
872          if (dir) {
873             if (!insert_dir_into_findex_list(ua, rx, file, date)) {
874                ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
875             }
876          } else {
877             if (!insert_file_into_findex_list(ua, rx, file, date)) {
878                ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
879             }
880          }
881       }
882       fclose(ffd);
883       break;
884    case '?':
885       p++;
886       insert_table_into_findex_list(ua, rx, p);
887       break;
888    default:
889       if (dir) {
890          insert_dir_into_findex_list(ua, rx, ua->cmd, date);
891       } else {
892          insert_file_into_findex_list(ua, rx, ua->cmd, date);
893       }
894       break;
895    }
896 }
897
898 /*
899  * For a given file (path+filename), split into path and file, then
900  *   lookup the most recent backup in the catalog to get the JobId
901  *   and FileIndex, then insert them into the findex list.
902  */
903 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
904                                         char *date)
905 {
906    strip_trailing_newline(file);
907    split_path_and_filename(ua, rx, file);
908    if (*rx->JobIds == 0) {
909       Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, 
910            rx->ClientName);
911    } else {
912       Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
913            rx->path, rx->fname, rx->ClientName);
914    }
915    rx->found = false;
916    /* Find and insert jobid and File Index */
917    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
918       ua->error_msg(_("Query failed: %s. ERR=%s\n"),
919          rx->query, db_strerror(ua->db));
920    }
921    if (!rx->found) {
922       ua->error_msg(_("No database record found for: %s\n"), file);
923 //    ua->error_msg("Query=%s\n", rx->query);
924       return true;
925    }
926    return true;
927 }
928
929 /*
930  * For a given path lookup the most recent backup in the catalog
931  * to get the JobId and FileIndexes of all files in that directory.
932  */
933 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
934                                         char *date)
935 {  
936    strip_trailing_junk(dir);
937    if (*rx->JobIds == 0) {
938       ua->error_msg(_("No JobId specified cannot continue.\n"));
939       return false;
940    } else {
941       Mmsg(rx->query, uar_jobid_fileindex_from_dir[db_type], rx->JobIds, dir, rx->ClientName);
942    }
943    rx->found = false;
944    /* Find and insert jobid and File Index */
945    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
946       ua->error_msg(_("Query failed: %s. ERR=%s\n"),
947          rx->query, db_strerror(ua->db));
948    }
949    if (!rx->found) {
950       ua->error_msg(_("No database record found for: %s\n"), dir);
951       return true;
952    }
953    return true;
954 }
955
956 /*
957  * Get the JobId and FileIndexes of all files in the specified table
958  */
959 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
960 {
961    strip_trailing_junk(table);
962    Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
963
964    rx->found = false;
965    /* Find and insert jobid and File Index */
966    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
967       ua->error_msg(_("Query failed: %s. ERR=%s\n"),
968          rx->query, db_strerror(ua->db));
969    }
970    if (!rx->found) {
971       ua->error_msg(_("No table found: %s\n"), table);
972       return true;
973    }
974    return true;
975 }
976
977 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *name)
978 {
979    char *p, *f;
980
981    /* Find path without the filename.
982     * I.e. everything after the last / is a "filename".
983     * OK, maybe it is a directory name, but we treat it like
984     * a filename. If we don't find a / then the whole name
985     * must be a path name (e.g. c:).
986     */
987    for (p=f=name; *p; p++) {
988       if (IsPathSeparator(*p)) {
989          f = p;                       /* set pos of last slash */
990       }
991    }
992    if (IsPathSeparator(*f)) {         /* did we find a slash? */
993       f++;                            /* yes, point to filename */
994    } else {                           /* no, whole thing must be path name */
995       f = p;
996    }
997
998    /* If filename doesn't exist (i.e. root directory), we
999     * simply create a blank name consisting of a single
1000     * space. This makes handling zero length filenames
1001     * easier.
1002     */
1003    rx->fnl = p - f;
1004    if (rx->fnl > 0) {
1005       rx->fname = check_pool_memory_size(rx->fname, 2*(rx->fnl)+1);
1006       db_escape_string(ua->jcr, ua->db, rx->fname, f, rx->fnl);
1007    } else {
1008       rx->fname[0] = 0;
1009       rx->fnl = 0;
1010    }
1011
1012    rx->pnl = f - name;
1013    if (rx->pnl > 0) {
1014       rx->path = check_pool_memory_size(rx->path, 2*(rx->pnl)+1);
1015       db_escape_string(ua->jcr, ua->db, rx->path, name, rx->pnl);
1016    } else {
1017       rx->path[0] = 0;
1018       rx->pnl = 0;
1019    }
1020
1021    Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
1022 }
1023
1024 static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
1025 {
1026    if (find_arg(ua, NT_("all")) >= 0) {  /* if user enters all on command line */
1027       return true;                       /* select everything */
1028    }
1029    ua->send_msg(_("\n\nFor one or more of the JobIds selected, no files were found,\n"
1030                  "so file selection is not possible.\n"
1031                  "Most likely your retention policy pruned the files.\n"));
1032    if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
1033       if (ua->pint32_val == 1)
1034          return true;
1035       while (get_cmd(ua, _("\nRegexp matching files to restore? (empty to abort): "))) {
1036          if (ua->cmd[0] == '\0') {
1037             break;
1038          } else {
1039             regex_t *fileregex_re = NULL;
1040             int rc;
1041             char errmsg[500] = "";
1042
1043             fileregex_re = (regex_t *)bmalloc(sizeof(regex_t));
1044             rc = regcomp(fileregex_re, ua->cmd, REG_EXTENDED|REG_NOSUB);
1045             if (rc != 0) {
1046                regerror(rc, fileregex_re, errmsg, sizeof(errmsg));
1047             }
1048             regfree(fileregex_re);
1049             free(fileregex_re);
1050             if (*errmsg) {
1051                ua->send_msg(_("Regex compile error: %s\n"), errmsg);
1052             } else {
1053                rx->bsr->fileregex = bstrdup(ua->cmd);
1054                return true;
1055             }
1056          }
1057       }
1058    }
1059    return false;
1060 }
1061
1062 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
1063 {
1064    TREE_CTX tree;
1065    JobId_t JobId, last_JobId;
1066    char *p;
1067    bool OK = true;
1068    char ed1[50];
1069
1070    memset(&tree, 0, sizeof(TREE_CTX));
1071    /*
1072     * Build the directory tree containing JobIds user selected
1073     */
1074    tree.root = new_tree(rx->TotalFiles);
1075    tree.ua = ua;
1076    tree.all = rx->all;
1077    last_JobId = 0;
1078    /*
1079     * For display purposes, the same JobId, with different volumes may
1080     * appear more than once, however, we only insert it once.
1081     */
1082    p = rx->JobIds;
1083    tree.FileEstimate = 0;
1084    if (get_next_jobid_from_list(&p, &JobId) > 0) {
1085       /* Use first JobId as estimate of the number of files to restore */
1086       Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
1087       if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1088          ua->error_msg("%s\n", db_strerror(ua->db));
1089       }
1090       if (rx->found) {
1091          /* Add about 25% more than this job for over estimate */
1092          tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
1093          tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
1094       }
1095    }
1096
1097    ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ...  "),
1098                 rx->JobIds);
1099
1100 #define new_get_file_list
1101 #ifdef new_get_file_list
1102    if (!db_get_file_list(ua->jcr, ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) {
1103       ua->error_msg("%s", db_strerror(ua->db));
1104    }
1105    if (*rx->BaseJobIds) {
1106       pm_strcat(rx->JobIds, ",");
1107       pm_strcat(rx->JobIds, rx->BaseJobIds);
1108    }
1109 #else
1110    for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1111       char ed1[50];
1112
1113       if (JobId == last_JobId) {
1114          continue;                    /* eliminate duplicate JobIds */
1115       }
1116       last_JobId = JobId;
1117       /*
1118        * Find files for this JobId and insert them in the tree
1119        */
1120       Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
1121       if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
1122          ua->error_msg("%s", db_strerror(ua->db));
1123       }
1124    }
1125 #endif
1126    /*
1127     * Look at the first JobId on the list (presumably the oldest) and
1128     *  if it is marked purged, don't do the manual selection because
1129     *  the Job was pruned, so the tree is incomplete.
1130     */
1131    if (tree.FileCount != 0) {
1132       /* Find out if any Job is purged */
1133       Mmsg(rx->query, "SELECT SUM(PurgedFiles) FROM Job WHERE JobId IN (%s)", rx->JobIds);
1134       if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1135          ua->error_msg("%s\n", db_strerror(ua->db));
1136       }
1137       /* rx->JobId is the PurgedFiles flag */
1138       if (rx->found && rx->JobId > 0) {
1139          tree.FileCount = 0;           /* set count to zero, no tree selection */
1140       }
1141    }
1142    if (tree.FileCount == 0) {
1143       OK = ask_for_fileregex(ua, rx);
1144       if (OK) {
1145          last_JobId = 0;
1146          for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1147              if (JobId == last_JobId) {
1148                 continue;                    /* eliminate duplicate JobIds */
1149              }
1150              add_findex_all(rx->bsr, JobId);
1151          }
1152       }
1153    } else {
1154       char ec1[50];
1155       if (tree.all) {
1156          ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"),
1157                       edit_uint64_with_commas(tree.FileCount, ec1));
1158       } else {
1159          ua->info_msg(_("\n%s files inserted into the tree.\n"),
1160                       edit_uint64_with_commas(tree.FileCount, ec1));
1161       }
1162
1163       if (find_arg(ua, NT_("done")) < 0) {
1164          /* Let the user interact in selecting which files to restore */
1165          OK = user_select_files_from_tree(&tree);
1166       }
1167
1168       /*
1169        * Walk down through the tree finding all files marked to be
1170        *  extracted making a bootstrap file.
1171        */
1172       if (OK) {
1173          for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
1174             Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
1175             if (node->extract || node->extract_dir) {
1176                Dmsg3(400, "JobId=%lld type=%d FI=%d\n", (uint64_t)node->JobId, node->type, node->FileIndex);
1177                add_findex(rx->bsr, node->JobId, node->FileIndex);
1178                if (node->extract && node->type != TN_NEWDIR) {
1179                   rx->selected_files++;  /* count only saved files */
1180                }
1181             }
1182          }
1183       }
1184    }
1185
1186    free_tree(tree.root);              /* free the directory tree */
1187    return OK;
1188 }
1189
1190
1191 /*
1192  * This routine is used to get the current backup or a backup
1193  *   before the specified date.
1194  */
1195 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1196 {
1197    bool ok = false;
1198    FILESET_DBR fsr;
1199    CLIENT_DBR cr;
1200    char fileset_name[MAX_NAME_LENGTH];
1201    char ed1[50], ed2[50];
1202    char pool_select[MAX_NAME_LENGTH];
1203    int i;
1204
1205    /* Create temp tables */
1206    db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1207    db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1208    if (!db_sql_query(ua->db, uar_create_temp[db_type], NULL, NULL)) {
1209       ua->error_msg("%s\n", db_strerror(ua->db));
1210    }
1211    if (!db_sql_query(ua->db, uar_create_temp1[db_type], NULL, NULL)) {
1212       ua->error_msg("%s\n", db_strerror(ua->db));
1213    }
1214    /*
1215     * Select Client from the Catalog
1216     */
1217    memset(&cr, 0, sizeof(cr));
1218    if (!get_client_dbr(ua, &cr)) {
1219       goto bail_out;
1220    }
1221    bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1222
1223    /*
1224     * Get FileSet
1225     */
1226    memset(&fsr, 0, sizeof(fsr));
1227    i = find_arg_with_value(ua, "FileSet");
1228    if (i >= 0) {
1229       bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1230       if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1231          ua->error_msg(_("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1232             db_strerror(ua->db));
1233          i = -1;
1234       }
1235    }
1236    if (i < 0) {                       /* fileset not found */
1237       edit_int64(cr.ClientId, ed1);
1238       Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1239       start_prompt(ua, _("The defined FileSet resources are:\n"));
1240       if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1241          ua->error_msg("%s\n", db_strerror(ua->db));
1242       }
1243       if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1244                  fileset_name, sizeof(fileset_name)) < 0) {
1245          ua->error_msg(_("No FileSet found for client \"%s\".\n"), cr.Name);
1246          goto bail_out;
1247       }
1248
1249       bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1250       if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1251          ua->warning_msg(_("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1252          ua->send_msg(_("This probably means you modified the FileSet.\n"
1253                      "Continuing anyway.\n"));
1254       }
1255    }
1256
1257    /* If Pool specified, add PoolId specification */
1258    pool_select[0] = 0;
1259    if (rx->pool) {
1260       POOL_DBR pr;
1261       memset(&pr, 0, sizeof(pr));
1262       bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1263       if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1264          bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", 
1265             edit_int64(pr.PoolId, ed1));
1266       } else {
1267          ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1268       }
1269    }
1270
1271    /* Find JobId of last Full backup for this client, fileset */
1272    edit_int64(cr.ClientId, ed1);
1273    Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1274          pool_select);
1275    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1276       ua->error_msg("%s\n", db_strerror(ua->db));
1277       goto bail_out;
1278    }
1279
1280    /* Find all Volumes used by that JobId */
1281    if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1282       ua->error_msg("%s\n", db_strerror(ua->db));
1283       goto bail_out;
1284    }
1285
1286    /* Note, this is needed because I don't seem to get the callback
1287     * from the call just above.
1288     */
1289    rx->JobTDate = 0;
1290    if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1291       ua->warning_msg("%s\n", db_strerror(ua->db));
1292    }
1293    if (rx->JobTDate == 0) {
1294       ua->error_msg(_("No Full backup before %s found.\n"), date);
1295       goto bail_out;
1296    }
1297
1298    /* Now find most recent Differental Job after Full save, if any */
1299    Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1300         edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1301    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1302       ua->warning_msg("%s\n", db_strerror(ua->db));
1303    }
1304    /* Now update JobTDate to look into Differental, if any */
1305    rx->JobTDate = 0;
1306    if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1307       ua->warning_msg("%s\n", db_strerror(ua->db));
1308    }
1309    if (rx->JobTDate == 0) {
1310       ua->error_msg(_("No Full backup before %s found.\n"), date);
1311       goto bail_out;
1312    }
1313
1314    /* Now find all Incremental Jobs after Full/dif save */
1315    Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1316         edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1317    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1318       ua->warning_msg("%s\n", db_strerror(ua->db));
1319    }
1320
1321    /* Get the JobIds from that list */
1322    rx->last_jobid[0] = rx->JobIds[0] = 0;
1323
1324    if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1325       ua->warning_msg("%s\n", db_strerror(ua->db));
1326    }
1327
1328    if (rx->JobIds[0] != 0) {
1329       if (find_arg(ua, NT_("copies")) > 0) {
1330          /* Display a list of all copies */
1331          db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, 
1332                                 prtit, ua, HORZ_LIST);
1333       }
1334       /* Display a list of Jobs selected for this restore */
1335       db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1,HORZ_LIST);
1336       ok = true;
1337
1338    } else {
1339       ua->warning_msg(_("No jobs found.\n"));
1340    }
1341
1342 bail_out:
1343    db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1344    db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1345    return ok;
1346 }
1347
1348 static int restore_count_handler(void *ctx, int num_fields, char **row)
1349 {
1350    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1351    rx->JobId = str_to_int64(row[0]);
1352    rx->found = true;
1353    return 0;
1354 }
1355
1356 /*
1357  * Callback handler to get JobId and FileIndex for files
1358  *   can insert more than one depending on the caller.
1359  */
1360 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1361 {
1362    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1363
1364    Dmsg2(200, "JobId=%s FileIndex=%s\n", row[0], row[1]);
1365    rx->JobId = str_to_int64(row[0]);
1366    add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1367    rx->found = true;
1368    rx->selected_files++;
1369    return 0;
1370 }
1371
1372 /*
1373  * Callback handler make list of JobIds
1374  */
1375 static int jobid_handler(void *ctx, int num_fields, char **row)
1376 {
1377    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1378
1379    if (strcmp(rx->last_jobid, row[0]) == 0) {
1380       return 0;                       /* duplicate id */
1381    }
1382    bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1383    if (rx->JobIds[0] != 0) {
1384       pm_strcat(rx->JobIds, ",");
1385    }
1386    pm_strcat(rx->JobIds, row[0]);
1387    return 0;
1388 }
1389
1390
1391 /*
1392  * Callback handler to pickup last Full backup JobTDate
1393  */
1394 static int last_full_handler(void *ctx, int num_fields, char **row)
1395 {
1396    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1397
1398    rx->JobTDate = str_to_int64(row[1]);
1399    return 0;
1400 }
1401
1402 /*
1403  * Callback handler build FileSet name prompt list
1404  */
1405 static int fileset_handler(void *ctx, int num_fields, char **row)
1406 {
1407    /* row[0] = FileSet (name) */
1408    if (row[0]) {
1409       add_prompt((UAContext *)ctx, row[0]);
1410    }
1411    return 0;
1412 }
1413
1414 /*
1415  * Free names in the list
1416  */
1417 static void free_name_list(NAME_LIST *name_list)
1418 {
1419    for (int i=0; i < name_list->num_ids; i++) {
1420       free(name_list->name[i]);
1421    }
1422    bfree_and_null(name_list->name);
1423    name_list->max_ids = 0;
1424    name_list->num_ids = 0;
1425 }
1426
1427 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType) 
1428 {
1429    STORE *store;
1430
1431    if (rx.store) {
1432       Dmsg1(200, "Already have store=%s\n", rx.store->name());
1433       return;
1434    }
1435    /*
1436     * Try looking up Storage by name
1437     */
1438    LockRes();
1439    foreach_res(store, R_STORAGE) {
1440       if (strcmp(Storage, store->name()) == 0) {
1441          if (acl_access_ok(ua, Storage_ACL, store->name())) {
1442             rx.store = store;
1443          }
1444          break;
1445       }
1446    }
1447    UnlockRes();
1448
1449    if (rx.store) {
1450       /* Check if an explicit storage resource is given */
1451       store = NULL;
1452       int i = find_arg_with_value(ua, "storage");
1453       if (i > 0) {
1454          store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1455          if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1456             store = NULL;
1457          }
1458       }
1459       if (store && (store != rx.store)) {
1460          ua->info_msg(_("Warning default storage overridden by \"%s\" on command line.\n"),
1461             store->name());
1462          rx.store = store;
1463          Dmsg1(200, "Set store=%s\n", rx.store->name());
1464       }
1465       return;
1466    }
1467
1468    /* If no storage resource, try to find one from MediaType */
1469    if (!rx.store) {
1470       LockRes();
1471       foreach_res(store, R_STORAGE) {
1472          if (strcmp(MediaType, store->media_type) == 0) {
1473             if (acl_access_ok(ua, Storage_ACL, store->name())) {
1474                rx.store = store;
1475                Dmsg1(200, "Set store=%s\n", rx.store->name());
1476                ua->warning_msg(_("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1477                   Storage, store->name(), MediaType);
1478             }
1479             UnlockRes();
1480             return;
1481          }
1482       }
1483       UnlockRes();
1484       ua->warning_msg(_("\nUnable to find Storage resource for\n"
1485          "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1486    }
1487
1488    /* Take command line arg, or ask user if none */
1489    rx.store = get_storage_resource(ua, false /* don't use default */);
1490    if (rx.store) {
1491       Dmsg1(200, "Set store=%s\n", rx.store->name());
1492    }
1493
1494 }