]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/ua_restore.c
- Add index file to JobId field of File records for PostgreSQL.
[bacula/bacula] / bacula / src / dird / ua_restore.c
index 6027674d0fb613b19456e492e660820f9607b50f..5065c39f160dd0543a8da9874157ebc5132311da 100644 (file)
@@ -38,7 +38,7 @@
 
 
 /* Imported functions */
-extern int run_cmd(UAContext *ua, char *cmd);
+extern int run_cmd(UAContext *ua, const char *cmd);
 extern void print_bsr(UAContext *ua, RBSR *bsr);
 
 /* Imported variables */
@@ -48,6 +48,7 @@ extern char *uar_create_temp1,         *uar_last_full,   *uar_full;
 extern char *uar_inc,           *uar_list_temp,   *uar_sel_jobid_temp;
 extern char *uar_sel_all_temp1,  *uar_sel_fileset, *uar_mediatype;
 extern char *uar_jobid_fileindex, *uar_dif,       *uar_sel_all_temp;
+extern char *uar_count_files;
 
 
 struct NAME_LIST {
@@ -74,12 +75,13 @@ struct RESTORE_CTX {
    uint32_t selected_files;
    char *where;
    RBSR *bsr;
-   POOLMEM *fname;
-   POOLMEM *path;
+   POOLMEM *fname;                   /* filename only */
+   POOLMEM *path;                    /* path only */
    POOLMEM *query;
-   int fnl;
-   int pnl;
+   int fnl;                          /* filename length */
+   int pnl;                          /* path length */
    bool found;
+   bool all;                         /* mark all as default */
    NAME_LIST name_list;
 };
 
@@ -107,12 +109,13 @@ static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *fi
 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
 static int get_date(UAContext *ua, char *date, int date_len);
+static int count_handler(void *ctx, int num_fields, char **row);
 
 /*
  *   Restore files
  *
  */
-int restore_cmd(UAContext *ua, char *cmd)
+int restore_cmd(UAContext *ua, const char *cmd)
 {
    RESTORE_CTX rx;                   /* restore context */
    JOB *job;
@@ -176,7 +179,9 @@ int restore_cmd(UAContext *ua, char *cmd)
          bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
         goto bail_out;
       }
-      write_bsr_file(ua, rx.bsr);
+      if (!write_bsr_file(ua, rx.bsr)) {
+        goto bail_out;
+      }
       bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
          rx.selected_files==1?"":"s");
    } else {
@@ -201,25 +206,24 @@ int restore_cmd(UAContext *ua, char *cmd)
 
    /* Build run command */
    if (rx.where) {
-      Mmsg(&ua->cmd, 
+      Mmsg(ua->cmd, 
           "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
           " where=\"%s\" files=%d",
           job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
          working_directory, rx.where, rx.selected_files);
    } else {
-      Mmsg(&ua->cmd, 
-          "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
+      Mmsg(ua->cmd, 
+          "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
+          " files=%d",
           job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
-         working_directory);
+         working_directory, rx.selected_files);
    }
    if (find_arg(ua, _("yes")) > 0) {
-      pm_strcat(&ua->cmd, " yes");    /* pass it on to the run command */
+      pm_strcat(ua->cmd, " yes");    /* pass it on to the run command */
    }
    Dmsg1(400, "Submitting: %s\n", ua->cmd);
    parse_ua_args(ua);
    run_cmd(ua, ua->cmd);
-
-   bsendmsg(ua, _("Restore command done.\n"));
    free_rx(&rx);
    return 1;
 
@@ -286,7 +290,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
    JOB_DBR jr;
    bool done = false;
    int i, j;
-   char *list[] = { 
+   const char *list[] = { 
       "List last 20 Jobs run",
       "List Jobs where a given File is saved",
       "Enter list of comma separated JobIds to select",
@@ -298,18 +302,20 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       "Cancel",
       NULL };
 
-   char *kw[] = {
+   const char *kw[] = {
       "jobid",     /* 0 */
       "current",   /* 1 */
       "before",    /* 2 */
       "file",      /* 3 */
       "select",    /* 4 */
       "pool",      /* 5 */
-      "client",    /* 6 */
-      "storage",   /* 7 */
-      "where",     /* 8 */
-      "all",       /* 9 */
-      "yes",       /* 10 */
+      "all",       /* 6 */
+      "client",    /* 7 */
+      "storage",   /* 8 */
+      "fileset",   /* 9 */
+      "where",     /* 10 */
+      "yes",       /* 11 */
+      "done",      /* 12 */
       NULL
    };
 
@@ -331,9 +337,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       switch (j) {
       case 0:                           /* jobid */
         if (*rx->JobIds != 0) {
-            pm_strcat(&rx->JobIds, ",");
+            pm_strcat(rx->JobIds, ",");
         }
-        pm_strcat(&rx->JobIds, ua->argv[i]);
+        pm_strcat(rx->JobIds, ua->argv[i]);
         done = true;
         break;
       case 1:                           /* current */
@@ -355,7 +361,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
         if (!get_client_name(ua, rx)) {
            return 0;
         }
-        pm_strcpy(&ua->cmd, ua->argv[i]);
+        pm_strcpy(ua->cmd, ua->argv[i]);
         insert_one_file(ua, rx, date);
         if (rx->name_list.num_ids) {
            /* Check MediaType and select storage that corresponds */
@@ -384,8 +390,11 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
            return 0;
         }
         break;
+      case 6:                        /* all specified */
+        rx->all = true;
+        break;
       /*     
-       * All keywords 6 or greater are ignored or handled by a select prompt
+       * All keywords 7 or greater are ignored or handled by a select prompt
        */
       default:
         break;
@@ -406,6 +415,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
    for ( ; !done; ) {
       char *fname;
       int len;
+      bool gui_save;
 
       start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
       for (int i=0; list[i]; i++) {
@@ -416,32 +426,41 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       case -1:                       /* error */
         return 0;
       case 0:                        /* list last 20 Jobs run */
+        gui_save = ua->jcr->gui;
+        ua->jcr->gui = true;
         db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
+        ua->jcr->gui = gui_save;
         done = false;
         break;
       case 1:                        /* list where a file is saved */
-         if (!get_cmd(ua, _("Enter Filename"))) {
+         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, fname);
         free(fname);
+        gui_save = ua->jcr->gui;
+        ua->jcr->gui = true;
         db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
+        ua->jcr->gui = gui_save;
         done = false;
         break;
       case 2:                        /* enter a list of JobIds */
          if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
            return 0;
         }
-        pm_strcpy(&rx->JobIds, ua->cmd);
+        pm_strcpy(rx->JobIds, ua->cmd);
         break;
       case 3:                        /* Enter an SQL list command */
          if (!get_cmd(ua, _("Enter SQL list command: "))) {
            return 0;
         }
+        gui_save = ua->jcr->gui;
+        ua->jcr->gui = true;
         db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
+        ua->jcr->gui = gui_save;
         done = false;
         break;
       case 4:                        /* Select the most recent backups */
@@ -463,11 +482,11 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
         if (!get_client_name(ua, rx)) {
            return 0;
         }
-         bsendmsg(ua, _("Enter file names, or < to enter a filename\n"      
-                        "containg a list of file names, and terminate\n"
+         bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"      
+                        "containg a list of file names with paths, and terminate\n"
                         "them with a blank line.\n"));
         for ( ;; ) {
-            if (!get_cmd(ua, _("Enter filename: "))) {
+            if (!get_cmd(ua, _("Enter full filename: "))) {
               return 0;
            }
            len = strlen(ua->cmd);
@@ -488,11 +507,11 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
         if (!get_client_name(ua, rx)) {
            return 0;
         }
-         bsendmsg(ua, _("Enter file names, or < to enter a filename\n"      
-                        "containg a list of file names, and terminate\n"
+         bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n"      
+                        "containg a list of file names with paths, and terminate\n"
                         "them with a blank line.\n"));
         for ( ;; ) {
-            if (!get_cmd(ua, _("Enter filename: "))) {
+            if (!get_cmd(ua, _("Enter full filename: "))) {
               return 0;
            }
            len = strlen(ua->cmd);
@@ -613,7 +632,7 @@ static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *fi
 {
    strip_trailing_junk(file);
    split_path_and_filename(rx, file);
-   Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
+   Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName);
    rx->found = false;
    /* Find and insert jobid and File Index */
    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
@@ -628,7 +647,7 @@ static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *fi
    /*
     * Find the MediaTypes for this JobId and add to the name_list
     */
-   Mmsg(&rx->query, uar_mediatype, rx->JobId);
+   Mmsg(rx->query, uar_mediatype, rx->JobId);
    if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
       bsendmsg(ua, "%s", db_strerror(ua->db));
       return 0;
@@ -692,7 +711,6 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
    TREE_CTX tree;
    JobId_t JobId, last_JobId;
    char *p;
-   char *nofname = "";
    bool OK = true;
 
    memset(&tree, 0, sizeof(TREE_CTX));
@@ -700,44 +718,60 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
     * Build the directory tree containing JobIds user selected
     */
    tree.root = new_tree(rx->TotalFiles);
-   tree.root->fname = nofname;
    tree.ua = ua;
+   tree.all = rx->all;
    last_JobId = 0;
    /*
     * For display purposes, the same JobId, with different volumes may
     * appear more than once, however, we only insert it once.
     */
    int items = 0;
+   p = rx->JobIds;
+   tree.FileEstimate = 0;
+   if (get_next_jobid_from_list(&p, &JobId) > 0) {
+      /* Use first JobId as estimate of the number of files to restore */
+      Mmsg(rx->query, uar_count_files, JobId);
+      if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
+         bsendmsg(ua, "%s\n", db_strerror(ua->db));
+      }
+      if (rx->found) {
+        /* Add about 25% more than this job for over estimate */
+        tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
+        tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
+      }
+   }
    for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
 
       if (JobId == last_JobId) {            
         continue;                    /* eliminate duplicate JobIds */
       }
       last_JobId = JobId;
-      bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
+      bsendmsg(ua, _("\nBuilding directory tree for JobId %u ...  "), JobId);
       items++;
       /*
        * Find files for this JobId and insert them in the tree
        */
-      Mmsg(&rx->query, uar_sel_files, JobId);
+      Mmsg(rx->query, uar_sel_files, JobId);
       if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
          bsendmsg(ua, "%s", db_strerror(ua->db));
       }
       /*
        * Find the MediaTypes for this JobId and add to the name_list
        */
-      Mmsg(&rx->query, uar_mediatype, JobId);
+      Mmsg(rx->query, uar_mediatype, JobId);
       if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
          bsendmsg(ua, "%s", db_strerror(ua->db));
       }
    }
-   bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n", 
-      items, items==1?"":"s");
+   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":"");
 
    /* Check MediaType and select storage that corresponds */
    get_storage_from_mediatype(ua, &rx->name_list, rx);
 
-   if (find_arg(ua, _("all")) < 0) {
+   if (find_arg(ua, _("done")) < 0) {
       /* Let the user interact in selecting which files to restore */
       OK = user_select_files_from_tree(&tree);
    }
@@ -752,7 +786,9 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
         if (node->extract || node->extract_dir) {
             Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
            add_findex(rx->bsr, node->JobId, node->FileIndex);
-           rx->selected_files++;
+           if (node->extract) {
+              rx->selected_files++;  /* count only saved files */
+           }
         }
       }
    }
@@ -809,7 +845,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date
       }
    }
    if (i < 0) {                      /* fileset not found */
-      Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
+      Mmsg(rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
       start_prompt(ua, _("The defined FileSet resources are:\n"));
       if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
          bsendmsg(ua, "%s\n", db_strerror(ua->db));
@@ -841,7 +877,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date
    }
 
    /* Find JobId of last Full backup for this client, fileset */
-   Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
+   Mmsg(rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet,
         pool_select);
    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
       bsendmsg(ua, "%s\n", db_strerror(ua->db));
@@ -866,7 +902,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date
    }
 
    /* Now find most recent Differental Job after Full save, if any */
-   Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
+   Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
        cr.ClientId, fsr.FileSet, pool_select);
    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
       bsendmsg(ua, "%s\n", db_strerror(ua->db));
@@ -882,7 +918,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date
    }
 
    /* Now find all Incremental Jobs after Full/dif save */
-   Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
+   Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
        cr.ClientId, fsr.FileSet, pool_select);
    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
       bsendmsg(ua, "%s\n", db_strerror(ua->db));
@@ -934,6 +970,14 @@ static int get_next_jobid_from_list(char **p, uint32_t *JobId)
    return 1;
 }
 
+static int count_handler(void *ctx, int num_fields, char **row)
+{
+   RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
+   rx->JobId = atoi(row[0]);
+   rx->found = true;
+   return 0;
+}
+
 /*
  * Callback handler to get JobId and FileIndex for files
  */
@@ -958,9 +1002,9 @@ static int jobid_handler(void *ctx, int num_fields, char **row)
    }
    bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
    if (rx->JobIds[0] != 0) {
-      pm_strcat(&rx->JobIds, ",");
+      pm_strcat(rx->JobIds, ",");
    }
-   pm_strcat(&rx->JobIds, row[0]);
+   pm_strcat(rx->JobIds, row[0]);
    return 0;
 }
 
@@ -1074,14 +1118,33 @@ static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, REST
    LockRes();
    foreach_res(store, R_STORAGE) {
       if (strcmp(name_list->name[0], store->media_type) == 0) {
-        rx->store = store;
-        UnlockRes();
-        return;
+        if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
+           rx->store = store;
+        }
+        break;
       }
    }
    UnlockRes();
 
-   /* Try asking user */
+   if (rx->store) {
+      /* Check if an explicit storage resource is given */
+      store = NULL;
+      int i = find_arg_with_value(ua, "storage");        
+      if (i > 0) {
+        store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
+        if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
+           store = NULL;
+        }
+      }
+      if (store && (store != rx->store)) {
+         bsendmsg(ua, _("Warning default storage overridden by %s on command line.\n"),
+           store->hdr.name);
+        rx->store = store;
+      }
+      return;
+   }
+
+   /* Take command line arg, or ask user if none */
    rx->store = get_storage_resource(ua, false /* don't use default */);
 
    if (!rx->store) {