]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/ua_restore.c
Fix bug #2212 where restore jobid=nn file=xxx restores the files twice
[bacula/bacula] / bacula / src / dird / ua_restore.c
index 1dc1a4046083c947aabd6f83710b9039e5eba6b6..85979b8d62a76da3b431400f8ea1a9507f3c3a55 100644 (file)
@@ -1,20 +1,22 @@
 /*
-   Bacula® - The Network Backup Solution
+   Bacula(R) - The Network Backup Solution
 
-   Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2017 Kern Sibbald
 
-   The main author of Bacula is Kern Sibbald, with contributions from many
-   others, a complete list can be found in the file AUTHORS.
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
 
    You may use this file and others of this release according to the
    license defined in the LICENSE file, which includes the Affero General
    Public License, v3.0 ("AGPLv3") and some additional permissions and
    terms pursuant to its AGPLv3 Section 7.
 
-   Bacula® is a registered trademark of Kern Sibbald.
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
- *
  *   Bacula Director -- User Agent Database restore Command
  *      Creates a bootstrap file for restoring files and
  *      starts the restore job.
@@ -42,7 +44,6 @@ static int fileset_handler(void *ctx, int num_fields, char **row);
 static void free_name_list(NAME_LIST *name_list);
 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
-static void free_rx(RESTORE_CTX *rx);
 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *fname);
 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
@@ -54,9 +55,35 @@ static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
 static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx);
 static bool get_date(UAContext *ua, char *date, int date_len);
 static int restore_count_handler(void *ctx, int num_fields, char **row);
-static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
 static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx);
 
+void new_rx(RESTORE_CTX *rx)
+{
+   RBSR *bsr = NULL;
+   memset(rx, 0, sizeof(*rx));
+   rx->path = get_pool_memory(PM_FNAME);
+   rx->path[0] = 0;
+
+   rx->fname = get_pool_memory(PM_FNAME);
+   rx->fname[0] = 0;
+
+   rx->JobIds = get_pool_memory(PM_FNAME);
+   rx->JobIds[0] = 0;
+
+   rx->component_fname = get_pool_memory(PM_FNAME);
+   rx->component_fname[0] = 0;
+
+   rx->BaseJobIds = get_pool_memory(PM_FNAME);
+   rx->BaseJobIds[0] = 0;
+
+   rx->query = get_pool_memory(PM_FNAME);
+   rx->query[0] = 0;
+
+   rx->bsr_list = New(rblist(bsr, &bsr->link));
+   rx->hardlinks_in_mem = true;
+}
+
+
 /*
  *   Restore files
  *
@@ -73,33 +100,19 @@ int restore_cmd(UAContext *ua, const char *cmd)
    char *strip_prefix, *add_prefix, *add_suffix, *regexp;
    strip_prefix = add_prefix = add_suffix = regexp = NULL;
 
-   memset(&rx, 0, sizeof(rx));
-   rx.path = get_pool_memory(PM_FNAME);
-   rx.path[0] = 0;
-
-   rx.fname = get_pool_memory(PM_FNAME);
-   rx.fname[0] = 0;
-
-   rx.JobIds = get_pool_memory(PM_FNAME);
-   rx.JobIds[0] = 0;
-
-   rx.component_fname = get_pool_memory(PM_FNAME);
-   rx.component_fname[0] = 0;
-
-   rx.BaseJobIds = get_pool_memory(PM_FNAME);
-   rx.BaseJobIds[0] = 0;
-
-   rx.query = get_pool_memory(PM_FNAME);
-   rx.query[0] = 0;
-
-   rx.bsr = new_bsr();
-   rx.hardlinks_in_mem = true;
-
+   new_rx(&rx);                 /* Initialize RESTORE_CTX */
+   
    if (!open_new_client_db(ua)) {
       goto bail_out;
    }
 
    for (i = 0; i < ua->argc ; i++) {
+      if (strcasecmp(ua->argk[i], "fdcalled") == 0) {
+         rx.fdcalled = true;
+
+      } else if (strcasecmp(ua->argk[i], "noautoparent") == 0) {
+         rx.no_auto_parent = true;
+      }
       if (!ua->argv[i]) {
          continue;           /* skip if no value given */
       }
@@ -112,6 +125,9 @@ int restore_cmd(UAContext *ua, const char *cmd)
       } else if (strcasecmp(ua->argk[i], "where") == 0) {
          rx.where = ua->argv[i];
 
+      } else if (strcasecmp(ua->argk[i], "when") == 0) {
+         rx.when = ua->argv[i];
+
       } else if (strcasecmp(ua->argk[i], "replace") == 0) {
          rx.replace = ua->argv[i];
 
@@ -132,7 +148,7 @@ int restore_cmd(UAContext *ua, const char *cmd)
              strcasecmp(ua->argv[i], "false")) {
             rx.hardlinks_in_mem = false;
          }
-      }
+     }
    }
 
    if (strip_prefix || add_suffix || add_prefix) {
@@ -197,9 +213,9 @@ int restore_cmd(UAContext *ua, const char *cmd)
       break;
    }
 
-   if (rx.bsr->JobId) {
+   if (rx.bsr_list->size() > 0) {
       char ed1[50];
-      if (!complete_bsr(ua, rx.bsr)) {   /* find Vol, SessId, SessTime from JobIds */
+      if (!complete_bsr(ua, rx.bsr_list)) {   /* find Vol, SessId, SessTime from JobIds */
          ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
          goto bail_out;
       }
@@ -207,6 +223,8 @@ int restore_cmd(UAContext *ua, const char *cmd)
          ua->warning_msg(_("No files selected to be restored.\n"));
          goto bail_out;
       }
+
+      ua->send_msg(_("Bootstrap records written to %s\n"), ua->jcr->RestoreBootstrap);
       display_bsr_info(ua, rx);          /* display vols needed, etc */
 
       if (rx.selected_files==1) {
@@ -230,7 +248,7 @@ int restore_cmd(UAContext *ua, const char *cmd)
    }
 
    get_client_name(ua, &rx);
-   if (!rx.ClientName) {
+   if (!rx.ClientName[0]) {
       ua->error_msg(_("No Client resource found!\n"));
       goto bail_out;
    }
@@ -270,6 +288,15 @@ int restore_cmd(UAContext *ua, const char *cmd)
       pm_strcat(ua->cmd, buf);
    }
 
+   if (rx.fdcalled) {
+      pm_strcat(ua->cmd, " fdcalled=yes");
+   }
+
+   if (rx.when) {
+      Mmsg(buf, " when=\"%s\"", rx.when);
+      pm_strcat(ua->cmd, buf);
+   }
+
    if (rx.comment) {
       Mmsg(buf, " comment=\"%s\"", rx.comment);
       pm_strcat(ua->cmd, buf);
@@ -292,14 +319,21 @@ int restore_cmd(UAContext *ua, const char *cmd)
    }
    Dmsg1(200, "Submitting: %s\n", ua->cmd);
    /*
-    * Transfer jobids, to jcr to
+    * Transfer jobids, component stuff to jcr to
     *  pass to run_cmd().  Note, these are fields and
     *  other things that are not passed on the command
     *  line.
     */
    /* ***FIXME*** pass jobids on command line */
+   if (jcr->JobIds) {
+      free_pool_memory(jcr->JobIds);
+   }
    jcr->JobIds = rx.JobIds;
    rx.JobIds = NULL;
+   jcr->component_fname = rx.component_fname;
+   rx.component_fname = NULL;
+   jcr->component_fd = rx.component_fd;
+   rx.component_fd = NULL;
    parse_ua_args(ua);
    run_cmd(ua, ua->cmd);
    free_rx(&rx);
@@ -319,6 +353,12 @@ bail_out:
       bfree(regexp);
    }
 
+   /* Free the plugin config if needed, we don't want to re-use
+    * this part of the next try
+    */
+   free_plugin_config_items(jcr->plugin_config);
+   jcr->plugin_config = NULL;
+
    free_rx(&rx);
    garbage_collect_memory();       /* release unused memory */
    return 0;
@@ -345,15 +385,27 @@ static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx)
    pm_strcpy(rx->BaseJobIds, jobids.list);
 }
 
-static void free_rx(RESTORE_CTX *rx)
+void free_rx(RESTORE_CTX *rx)
 {
-   free_bsr(rx->bsr);
-   rx->bsr = NULL;
+   free_bsr(rx->bsr_list);
+   rx->bsr_list = NULL;
    free_and_null_pool_memory(rx->JobIds);
    free_and_null_pool_memory(rx->BaseJobIds);
    free_and_null_pool_memory(rx->fname);
    free_and_null_pool_memory(rx->path);
    free_and_null_pool_memory(rx->query);
+   if (rx->fileregex) {
+      free(rx->fileregex);
+      rx->fileregex = NULL;
+   }
+   if (rx->component_fd) {
+      fclose(rx->component_fd);
+      rx->component_fd = NULL;
+   }
+   if (rx->component_fname) {
+      unlink(rx->component_fname);
+   }
+   free_and_null_pool_memory(rx->component_fname);
    free_name_list(&rx->name_list);
 }
 
@@ -388,7 +440,8 @@ static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
          return 1;
       }
       memset(&cr, 0, sizeof(cr));
-      if (!get_client_dbr(ua, &cr)) {
+      /* We want the name of the client where the backup was made */
+      if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
          return 0;
       }
       bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
@@ -483,6 +536,10 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       "comment",       /* 21 */
       "restorejob",    /* 22 */
       "replace",       /* 23 */
+      "xxxxxxxxx",     /* 24 */
+      "fdcalled",      /* 25 */
+      "when",          /* 26 */
+      "noautoparent",  /* 27 */
       NULL
    };
 
@@ -794,7 +851,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
          ua->send_msg(_("Selecting jobs to build the Full state at %s\n"),
                       jr.cStartTime);
          jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
-         if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) {
+         if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &jobids)) {
             return 0;
          }
          pm_strcpy(rx->JobIds, jobids.list);
@@ -846,8 +903,8 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       pm_strcat(JobIds, edit_int64(JobId, ed1));
       rx->TotalFiles += jr.JobFiles;
    }
-   free_pool_memory(rx->JobIds);
-   rx->JobIds = JobIds;               /* Set ACL filtered list */
+   pm_strcpy(rx->JobIds, JobIds);     /* Set ACL filtered list */
+   free_pool_memory(JobIds);
    if (*rx->JobIds == 0) {
       ua->warning_msg(_("No Jobs selected.\n"));
       return 0;
@@ -894,7 +951,7 @@ static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, b
    switch (*p) {
    case '<':
       p++;
-      if ((ffd = fopen(p, "rb")) == NULL) {
+      if ((ffd = bfopen(p, "rb")) == NULL) {
          berrno be;
          ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
             p, be.bstrerror());
@@ -944,6 +1001,13 @@ static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *f
    } else {
       Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
            rx->path, rx->fname, rx->ClientName);
+      /*
+       * Note: we have just edited the JobIds into the query, so
+       *  we need to clear JobIds, or they will be added
+       *  back into JobIds with the query below, and then
+       *  restored twice.  Fixes bug #2212.
+       */
+      rx->JobIds[0] = 0;
    }
    rx->found = false;
    /* Find and insert jobid and File Index */
@@ -989,7 +1053,7 @@ static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *di
 /*
  * Get the JobId and FileIndexes of all files in the specified table
  */
-static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
+bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
 {
    strip_trailing_junk(table);
    Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
@@ -1054,14 +1118,41 @@ static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *name)
    Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
 }
 
+static bool can_restore_all_files(UAContext *ua)
+{
+   alist *lst;
+   if (ua->cons) {
+      lst = ua->cons->ACL_lists[Directory_ACL];
+      /* ACL not defined, or the first entry is not *all* */
+      /* TODO: See if we search for *all* in all the list */
+      if (!lst || strcasecmp((char*)lst->get(0), "*all*") != 0) {
+         return false;
+      }
+      if (!lst || strcasecmp((char *)lst->get(0), "*all*") != 0) {
+         return false;
+      }
+   }
+   return true;
+}
+
 static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
 {
-   if (find_arg(ua, NT_("all")) >= 0) {  /* if user enters all on command line */
+   bool can_restore=can_restore_all_files(ua);
+
+   if (can_restore && find_arg(ua, NT_("all")) >= 0) {  /* if user enters all on command line */
       return true;                       /* select everything */
    }
+
    ua->send_msg(_("\n\nFor one or more of the JobIds selected, no files were found,\n"
                  "so file selection is not possible.\n"
                  "Most likely your retention policy pruned the files.\n"));
+
+   if (!can_restore) {
+      ua->error_msg(_("\nThe current Console has UserId or Directory restrictions. "
+                      "The full restore is not allowed.\n"));
+      return false;
+   }
+
    if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
       if (ua->pint32_val == 1)
          return true;
@@ -1083,7 +1174,7 @@ static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
             if (*errmsg) {
                ua->send_msg(_("Regex compile error: %s\n"), errmsg);
             } else {
-               rx->bsr->fileregex = bstrdup(ua->cmd);
+               rx->fileregex = bstrdup(ua->cmd);
                return true;
             }
          }
@@ -1106,9 +1197,46 @@ static void add_delta_list_findex(RESTORE_CTX *rx, struct delta_list *lst)
    if (lst->next) {
       add_delta_list_findex(rx, lst->next);
    }
-   add_findex(rx->bsr, lst->JobId, lst->FileIndex);
+   add_findex(rx->bsr_list, lst->JobId, lst->FileIndex);
 }
 
+/*
+ * This is a list of all the files (components) that the
+ *  user has requested for restore. It is requested by
+ *  the plugin (for now hard coded only for VSS).
+ *  In the future, this will be requested by a RestoreObject
+ *  and the plugin name will be sent to the FD.
+ */
+static bool write_component_file(UAContext *ua, RESTORE_CTX *rx, char *fname)
+{
+   int fd;
+   if (!rx->component_fd) {
+      Mmsg(rx->component_fname, "%s/%s.restore.sel.XXXXXX", working_directory, my_name);
+      fd = mkstemp(rx->component_fname);
+      if (fd < 0) {
+         berrno be;
+         ua->error_msg(_("Unable to create component file %s. ERR=%s\n"),
+            rx->component_fname, be.bstrerror());
+         return false;
+      }
+      rx->component_fd = fdopen(fd, "w+");
+      if (!rx->component_fd) {
+         berrno be;
+         ua->error_msg(_("Unable to fdopen component file %s. ERR=%s\n"),
+            rx->component_fname, be.bstrerror());
+         return false;
+      }
+   }
+   fprintf(rx->component_fd, "%s\n", fname);
+   if (ferror(rx->component_fd)) {
+      ua->error_msg(_("Error writing component file.\n"));
+     fclose(rx->component_fd);
+     unlink(rx->component_fname);
+     rx->component_fd = NULL;
+     return false;
+   }
+   return true;
+}
 
 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
 {
@@ -1126,7 +1254,9 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
    tree.ua = ua;
    tree.all = rx->all;
    tree.hardlinks_in_mem = rx->hardlinks_in_mem;
+   tree.no_auto_parent = rx->no_auto_parent;
    last_JobId = 0;
+   tree.last_dir_acl = NULL;
    /*
     * For display purposes, the same JobId, with different volumes may
     * appear more than once, however, we only insert it once.
@@ -1210,7 +1340,7 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
              if (JobId == last_JobId) {
                 continue;                    /* eliminate duplicate JobIds */
              }
-             add_findex_all(rx->bsr, JobId);
+             add_findex_all(rx->bsr_list, JobId, rx->fileregex);
          }
       }
    } else {
@@ -1233,13 +1363,26 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
        *  extracted making a bootstrap file.
        */
       if (OK) {
+         char cwd[2000];
          for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
             Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
             if (node->extract || node->extract_dir) {
                Dmsg3(400, "JobId=%lld type=%d FI=%d\n", (uint64_t)node->JobId, node->type, node->FileIndex);
                /* TODO: optimize bsr insertion when jobid are non sorted */
                add_delta_list_findex(rx, node->delta_list);
-               add_findex(rx->bsr, node->JobId, node->FileIndex);
+               add_findex(rx->bsr_list, node->JobId, node->FileIndex);
+               /*
+                * Special VSS plugin code to return selected
+                *   components. For the moment, it is hard coded
+                *   for the VSS plugin.
+                */
+               if (fnmatch(":component_info_*", node->fname, 0) == 0) {
+                  tree_getpath(node, cwd, sizeof(cwd));
+                  if (!write_component_file(ua, rx, cwd)) {
+                     OK = false;
+                     break;
+                  }
+               }
                if (node->extract && node->type != TN_NEWDIR) {
                   rx->selected_files++;  /* count only saved files */
                }
@@ -1247,7 +1390,11 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
          }
       }
    }
-
+   if (tree.uid_acl) {
+      delete tree.uid_acl;
+      delete tree.gid_acl;
+      delete tree.dir_acl;
+   }
    free_tree(tree.root);              /* free the directory tree */
    return OK;
 }
@@ -1268,8 +1415,8 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat
    int i;
 
    /* Create temp tables */
-   db_sql_query(ua->db, uar_del_temp, NULL, NULL);
-   db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
+  db_sql_query(ua->db, uar_del_temp, NULL, NULL);
+  db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
    if (!db_sql_query(ua->db, uar_create_temp[db_get_type_index(ua->db)], NULL, NULL)) {
       ua->error_msg("%s\n", db_strerror(ua->db));
    }
@@ -1280,7 +1427,7 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat
     * Select Client from the Catalog
     */
    memset(&cr, 0, sizeof(cr));
-   if (!get_client_dbr(ua, &cr)) {
+   if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
       goto bail_out;
    }
    bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
@@ -1409,8 +1556,8 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat
    }
 
 bail_out:
-   db_sql_query(ua->db, uar_del_temp, NULL, NULL);
-   db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
+  db_sql_query(ua->db, uar_del_temp, NULL, NULL);
+  db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
    return ok;
 }
 
@@ -1446,7 +1593,7 @@ static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
       rx->JobId = JobId;
    }
 
-   add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
+   add_findex(rx->bsr_list, rx->JobId, str_to_int64(row[1]));
    rx->found = true;
    rx->selected_files++;
    return 0;
@@ -1563,7 +1710,7 @@ void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *
             if (acl_access_ok(ua, Storage_ACL, store->name())) {
                rx.store = store;
                Dmsg1(200, "Set store=%s\n", rx.store->name());
-               if (Storage == NULL) {
+               if (Storage == NULL || Storage[0] == 0) {
                   ua->warning_msg(_("Using Storage \"%s\" from MediaType \"%s\".\n"),
                      store->name(), MediaType);
                } else {