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