]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/ua_restore.c
Fix out of order volumes during restore.
[bacula/bacula] / bacula / src / dird / ua_restore.c
index 59947a36d52d5b60a40602ee46d7dcf9b5870b26..37a88125742e2432273b701b0ae67101ae7abcfb 100644 (file)
  *
  *   Version $Id$
  */
-
 /*
    Copyright (C) 2002-2005 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public
-   License along with this program; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
+
 #include "bacula.h"
 #include "dird.h"
 
@@ -51,48 +46,10 @@ extern char *uar_count_files,     *uar_jobids_fileindex;
 extern char *uar_jobid_fileindex_from_dir;
 
 
-struct NAME_LIST {
-   char **name;                       /* list of names */
-   int num_ids;                       /* ids stored */
-   int max_ids;                       /* size of array */
-   int num_del;                       /* number deleted */
-   int tot_ids;                       /* total to process */
-};
-
-
-/* Main structure for obtaining JobIds or Files to be restored */
-struct RESTORE_CTX {
-   utime_t JobTDate;
-   uint32_t TotalFiles;
-   uint32_t JobId;
-   char ClientName[MAX_NAME_LENGTH];
-   char last_jobid[10];
-   POOLMEM *JobIds;                   /* User entered string of JobIds */
-   STORE  *store;
-   JOB *restore_job;
-   POOL *pool;
-   int restore_jobs;
-   uint32_t selected_files;
-   char *where;
-   RBSR *bsr;
-   POOLMEM *fname;                    /* filename only */
-   POOLMEM *path;                     /* path only */
-   POOLMEM *query;
-   int fnl;                           /* filename length */
-   int pnl;                           /* path length */
-   bool found;
-   bool all;                          /* mark all as default */
-   NAME_LIST name_list;
-};
-
-
-#define MAX_ID_LIST_LEN 1000000
-
 
 /* Forward referenced functions */
 static int last_full_handler(void *ctx, int num_fields, char **row);
 static int jobid_handler(void *ctx, int num_fields, char **row);
-static int get_next_jobid_from_list(char **p, uint32_t *JobId);
 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
 static int fileset_handler(void *ctx, int num_fields, char **row);
 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
@@ -122,6 +79,7 @@ int restore_cmd(UAContext *ua, const char *cmd)
    RESTORE_CTX rx;                    /* restore context */
    JOB *job;
    int i;
+   POOLMEM *fname;
 
    memset(&rx, 0, sizeof(rx));
    rx.path = get_pool_memory(PM_FNAME);
@@ -177,16 +135,25 @@ int restore_cmd(UAContext *ua, const char *cmd)
    }
 
    if (rx.bsr->JobId) {
+      uint32_t selected_files;
       if (!complete_bsr(ua, rx.bsr)) {   /* find Vol, SessId, SessTime from JobIds */
          bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
          goto bail_out;
       }
-      if (!(rx.selected_files = write_bsr_file(ua, rx.bsr))) {
+      if (!(selected_files = write_bsr_file(ua, rx))) {
          bsendmsg(ua, _("No files selected to be restored.\n"));
          goto bail_out;
       }
-      bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
-         rx.selected_files==1?"":"s");
+      /* If no count of files, use bsr generated value (often wrong) */
+      if (rx.selected_files == 0) {
+         rx.selected_files = selected_files;
+      }
+      if (rx.selected_files==1) {
+         bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
+      }
+      else {
+         bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
+      }
    } else {
       bsendmsg(ua, _("No files selected to be restored.\n"));
       goto bail_out;
@@ -208,20 +175,23 @@ int restore_cmd(UAContext *ua, const char *cmd)
    }
 
    /* Build run command */
+   fname = get_pool_memory(PM_MESSAGE);
+   make_unique_restore_filename(ua, &fname);
    if (rx.where) {
       Mmsg(ua->cmd,
-          "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
+          "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
           " where=\"%s\" files=%d catalog=\"%s\"",
           job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
-          working_directory, rx.where, rx.selected_files, ua->catalog->hdr.name);
+          fname, rx.where, rx.selected_files, ua->catalog->hdr.name);
    } else {
       Mmsg(ua->cmd,
-          "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
+          "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
           " files=%d catalog=\"%s\"",
           job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
-          working_directory, rx.selected_files, ua->catalog->hdr.name);
+          fname, rx.selected_files, ua->catalog->hdr.name);
    }
-   if (find_arg(ua, _("yes")) > 0) {
+   free_pool_memory(fname);
+   if (find_arg(ua, N_("yes")) > 0) {
       pm_strcat(ua->cmd, " yes");    /* pass it on to the run command */
    }
    Dmsg1(400, "Submitting: %s\n", ua->cmd);
@@ -265,7 +235,7 @@ static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
    if (!rx->ClientName[0]) {
       CLIENT_DBR cr;
       /* try command line argument */
-      int i = find_arg_with_value(ua, _("client"));
+      int i = find_arg_with_value(ua, N_("client"));
       if (i >= 0) {
          bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
          return 1;
@@ -298,18 +268,18 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
    bool done = false;
    int i, j;
    const char *list[] = {
-      "List last 20 Jobs run",
-      "List Jobs where a given File is saved",
-      "Enter list of comma separated JobIds to select",
-      "Enter SQL list command",
-      "Select the most recent backup for a client",
-      "Select backup for a client before a specified time",
-      "Enter a list of files to restore",
-      "Enter a list of files to restore before a specified time",
-      "Find the JobIds of the most recent backup for a client",
-      "Find the JobIds for a backup for a client before a specified time",
-      "Enter a list of directories to restore for given JobIds",
-      "Cancel",
+      _("List last 20 Jobs run"),
+      _("List Jobs where a given File is saved"),
+      _("Enter list of comma separated JobIds to select"),
+      _("Enter SQL list command"),
+      _("Select the most recent backup for a client"),
+      _("Select backup for a client before a specified time"),
+      _("Enter a list of files to restore"),
+      _("Enter a list of files to restore before a specified time"),
+      _("Find the JobIds of the most recent backup for a client"),
+      _("Find the JobIds for a backup for a client before a specified time"),
+      _("Enter a list of directories to restore for found JobIds"),
+      _("Cancel"),
       NULL };
 
    const char *kw[] = {
@@ -329,7 +299,8 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       "fileset",   /* 10 */
       "where",     /* 11 */
       "yes",       /* 12 */
-      "done",      /* 13 */
+      "bootstrap", /* 13 */
+      "done",      /* 14 */
       NULL
    };
 
@@ -448,13 +419,16 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
          done = false;
          break;
       case 1:                         /* list where a file is saved */
+         if (!get_client_name(ua, rx)) {
+            return 0;
+         }
          if (!get_cmd(ua, _("Enter Filename (no path):"))) {
             return 0;
          }
          len = strlen(ua->cmd);
          fname = (char *)malloc(len * 2 + 1);
          db_escape_string(fname, ua->cmd, len);
-         Mmsg(rx->query, uar_file, fname);
+         Mmsg(rx->query, uar_file, rx->ClientName, fname);
          free(fname);
          gui_save = ua->jcr->gui;
          ua->jcr->gui = true;
@@ -576,9 +550,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
          if (!get_client_name(ua, rx)) {
             return 0;
          }
-         bsendmsg(ua, _("Enter directory names with a trailing /, or < to enter a filename\n"
-                        "containg a list of directories and terminate\n"
-                        "them with a blank line.\n"));
+         bsendmsg(ua, _("Enter full directory names or start the name\n"
+                        "with a < to indicate it is a filename containg a list\n"
+                        "of directories and terminate them with a blank line.\n"));
          for ( ;; ) {
             if (!get_cmd(ua, _("Enter directory name: "))) {
                return 0;
@@ -587,7 +561,8 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
             if (len == 0) {
                break;
             }
-            if (ua->cmd[len-1] != '/') {
+            /* Add trailing slash to end of directory names */
+            if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
                strcat(ua->cmd, "/");
             }
             insert_one_file_or_dir(ua, rx, date, true);
@@ -607,8 +582,12 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       bsendmsg(ua, _("No Jobs selected.\n"));
       return 0;
    }
-   bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
-      strchr(rx->JobIds,',')?"s":"",rx->JobIds);
+   if (strchr(rx->JobIds,',')) {
+      bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
+   }
+   else {
+      bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
+   }
 
    memset(&jr, 0, sizeof(JOB_DBR));
 
@@ -894,9 +873,9 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
       }
    }
    if (tree.FileCount == 0) {
-      bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
-         "is not possible.\nMost likely your retention policy pruned the files\n");
-      if (!get_yesno(ua, _("Do you want to restore all the files? (yes|no): "))) {
+      bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
+         "is not possible.Most likely your retention policy pruned the files\n"));
+      if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
          OK = false;
       } else {
          last_JobId = 0;
@@ -910,14 +889,31 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
       }
    } else {
       char ec1[50];
-      bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
-         items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
-         tree.all?" and marked for extraction":"");
+      if (items==1) {
+         if (tree.all) {
+            bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
+              edit_uint64_with_commas(tree.FileCount, ec1));
+         }
+         else {
+            bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
+              edit_uint64_with_commas(tree.FileCount, ec1));
+         }
+      }
+      else {
+         if (tree.all) {
+            bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
+              items, edit_uint64_with_commas(tree.FileCount, ec1));
+         }
+         else {
+            bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
+              items, edit_uint64_with_commas(tree.FileCount, ec1));
+         }
+      }
 
       /* Check MediaType and select storage that corresponds */
       get_storage_from_mediatype(ua, &rx->name_list, rx);
 
-      if (find_arg(ua, _("done")) < 0) {
+      if (find_arg(ua, N_("done")) < 0) {
          /* Let the user interact in selecting which files to restore */
          OK = user_select_files_from_tree(&tree);
       }
@@ -1097,7 +1093,7 @@ bail_out:
 
 
 /* Return next JobId from comma separated list */
-static int get_next_jobid_from_list(char **p, uint32_t *JobId)
+int get_next_jobid_from_list(char **p, JobId_t *JobId)
 {
    char jobid[30];
    char *q = *p;