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