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