]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/cats/bvfs.c
bvfs: Restore should be ok with MySQL
[bacula/bacula] / bacula / src / cats / bvfs.c
index 8a71ff5412c6476f823f21b064d8da783bc26ace..c3b8bbd8a28ba94d589990bebc64b3f27931a1c7 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2009-2009 Free Software Foundation Europe e.V.
+   Copyright (C) 2009-2010 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation, which is 
    listed in the file LICENSE.
 
@@ -15,7 +15,7 @@
    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
+   You should have received a copy of the GNU Affero General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
@@ -63,7 +63,7 @@ Bvfs::Bvfs(JCR *j, B_DB *mdb) {
    pattern = get_pool_memory(PM_NAME);
    *jobids = *prev_dir = *pattern = 0;
    dir_filenameid = pwd_id = offset = 0;
-   see_copies = see_all_version = false;
+   see_copies = see_all_versions = false;
    limit = 1000;
    attr = new_attr(jcr);
    list_entries = result_handler;
@@ -95,6 +95,9 @@ private:
    hlink *nodes;
    int nb_node;
    int max_node;
+
+   alist *table_node;
+
    htable *cache_ppathid;
 
 public:
@@ -105,14 +108,17 @@ public:
       max_node = NITEMS;
       nodes = (hlink *) malloc(max_node * sizeof (hlink));
       nb_node = 0;
+      table_node = New(alist(5, owned_by_alist));
+      table_node->append(nodes);
    }
 
    hlink *get_hlink() {
-      if (nb_node >= max_node) {
-         max_node *= 2;
-         nodes = (hlink *)brealloc(nodes, sizeof(hlink) * max_node);
+      if (++nb_node >= max_node) {
+         nb_node = 0;
+         nodes = (hlink *)malloc(max_node * sizeof(hlink));
+         table_node->append(nodes);
       }
-      return nodes + nb_node++;
+      return nodes + nb_node;
    }
 
    bool lookup(char *pathid) {
@@ -128,7 +134,7 @@ public:
    ~pathid_cache() {
       cache_ppathid->destroy();
       free(cache_ppathid);
-      free(nodes);
+      delete table_node;
    }
 private:
    pathid_cache(const pathid_cache &); /* prohibit pass by value */
@@ -276,11 +282,14 @@ static void update_path_hierarchy_cache(JCR *jcr,
 
    /* Inserting path records for JobId */
    Mmsg(mdb->cmd, "INSERT INTO PathVisibility (PathId, JobId) "
-                  "SELECT DISTINCT PathId, JobId FROM File WHERE JobId = %s",
-        jobid);
+                   "SELECT DISTINCT PathId, JobId "
+                     "FROM (SELECT PathId, JobId FROM File WHERE JobId = %s "
+                           "UNION "
+                           "SELECT PathId, BaseFiles.JobId FROM BaseFiles JOIN File AS F USING (FileId) "
+                            "WHERE BaseFiles.JobId = %s) AS B",
+        jobid, jobid);
    QUERY_DB(jcr, mdb, mdb->cmd);
 
-
    /* Now we have to do the directory recursion stuff to determine missing
     * visibility We try to avoid recursion, to be as fast as possible We also
     * only work on not allready hierarchised directories...
@@ -321,7 +330,7 @@ static void update_path_hierarchy_cache(JCR *jcr,
       }
       free(result);
    }
-   
+
    Mmsg(mdb->cmd, 
   "INSERT INTO PathVisibility (PathId, JobId)  "
    "SELECT a.PathId,%s "
@@ -412,7 +421,7 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb)
 
    Mmsg(mdb->cmd, 
  "SELECT JobId from Job "
-  "WHERE HashCache = 0 "
+  "WHERE HasCache = 0 "
     "AND Type IN ('B') AND JobStatus IN ('T', 'f', 'A') "
   "ORDER BY JobId");
 
@@ -431,6 +440,7 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb)
    Dmsg1(dbglevel, "Affected row(s) = %d\n", nb);
 
    db_end_transaction(jcr, mdb);
+   db_unlock(mdb);
 }
 
 /*
@@ -475,6 +485,7 @@ bool Bvfs::ch_dir(const char *path)
 
 /* 
  * Get all file versions for a specified client
+ * TODO: Handle basejobs using different client
  */
 void Bvfs::get_all_file_versions(DBId_t pathid, DBId_t fnid, const char *client)
 {
@@ -490,19 +501,21 @@ void Bvfs::get_all_file_versions(DBId_t pathid, DBId_t fnid, const char *client)
 
    POOL_MEM query;
 
-   Mmsg(query,//    1           2          3       4
-"SELECT 'V', File.FileId, File.Md5, File.JobId, File.LStat, "
-//         5                6
+   Mmsg(query,//    1           2              3       
+"SELECT 'V', File.PathId, File.FilenameId,  File.Md5, "
+//         4          5           6
+        "File.JobId, File.LStat, File.FileId, "
+//         7                    8
        "Media.VolumeName, Media.InChanger "
 "FROM File, Job, Client, JobMedia, Media "
 "WHERE File.FilenameId = %s "
   "AND File.PathId=%s "
   "AND File.JobId = Job.JobId "
-  "AND Job.ClientId = Client.ClientId "
   "AND Job.JobId = JobMedia.JobId "
   "AND File.FileIndex >= JobMedia.FirstIndex "
   "AND File.FileIndex <= JobMedia.LastIndex "
   "AND JobMedia.MediaId = Media.MediaId "
+  "AND Job.ClientId = Client.ClientId "
   "AND Client.Name = '%s' "
   "%s ORDER BY FileId LIMIT %d OFFSET %d"
         ,edit_uint64(fnid, ed1), edit_uint64(pathid, ed2), client, q.c_str(),
@@ -666,7 +679,7 @@ bool Bvfs::ls_files()
    if (*pattern) {
       Mmsg(filter, " AND Filename.Name %s '%s' ", SQL_MATCH, pattern);
    }
-
+   /* TODO: Use JobTDate instead of FileId to determine the latest version */
    POOL_MEM query;
    Mmsg(query, //    1              2             3          4
 "SELECT 'F', File.PathId, File.FilenameId, listfiles.Name, File.JobId, "
@@ -697,3 +710,197 @@ bool Bvfs::ls_files()
 
    return nb_record == limit;
 }
+
+
+/* 
+ * Return next Id from comma separated list   
+ *
+ * Returns:
+ *   1 if next Id returned
+ *   0 if no more Ids are in list
+ *  -1 there is an error
+ * TODO: merge with get_next_jobid_from_list() and get_next_dbid_from_list()
+ */
+static int get_next_id_from_list(char **p, int64_t *Id)
+{
+   const int maxlen = 30;
+   char id[maxlen+1];
+   char *q = *p;
+
+   id[0] = 0;
+   for (int i=0; i<maxlen; i++) {
+      if (*q == 0) {
+         break;
+      } else if (*q == ',') {
+         q++;
+         break;
+      }
+      id[i] = *q++;
+      id[i+1] = 0;
+   }
+   if (id[0] == 0) {
+      return 0;
+   } else if (!is_a_number(id)) {
+      return -1;                      /* error */
+   }
+   *p = q;
+   *Id = str_to_int64(id);
+   return 1;
+}
+
+static int get_path_handler(void *ctx, int fields, char **row)
+{
+   POOL_MEM *buf = (POOL_MEM *) ctx;
+   pm_strcpy(*buf, row[0]);
+   return 0;
+}
+
+static bool check_temp(char *output_table)
+{
+   if (output_table[0] == 'b' &&
+       output_table[1] == '2' &&
+       is_an_integer(output_table + 2))
+   {
+      return true;
+   }
+   return false;
+}
+
+bool Bvfs::drop_restore_list(char *output_table)
+{
+   POOL_MEM query;
+   if (check_temp(output_table)) {
+      Mmsg(query, "DROP TABLE %s", output_table);
+      db_sql_query(db, query.c_str(), NULL, NULL);
+      return true;
+   }
+   return false;
+}
+
+bool Bvfs::compute_restore_list(char *fileid, char *dirid, char *hardlink, 
+                                char *output_table)
+{
+   POOL_MEM query;
+   POOL_MEM tmp, tmp2;
+   int64_t id, jobid;
+   bool init=false;
+   bool ret=false;
+   /* check args */
+   if ((*fileid   && !is_a_number_list(fileid))  ||
+       (*dirid    && !is_a_number_list(dirid))   ||
+       (*hardlink && !is_a_number_list(hardlink))||
+       (!*hardlink && !*fileid && !*dirid && !*hardlink))
+   {
+      return false;
+   }
+   if (!check_temp(output_table)) {
+      return false;
+   }
+
+   Mmsg(query, "CREATE TEMPORARY TABLE btemp%s AS ", output_table);
+
+   if (*fileid) {
+      init=true;
+      Mmsg(tmp, "(SELECT JobId, FileIndex, FilenameId, PathId, FileId "
+                   "FROM File WHERE FileId IN (%s))", fileid);
+      pm_strcat(query, tmp.c_str());
+   }
+
+   while (get_next_id_from_list(&dirid, &id) == 1) {
+      Mmsg(tmp, "SELECT Path FROM Path WHERE PathId=%lld", id);
+      
+      if (!db_sql_query(db, tmp.c_str(), get_path_handler, (void *)&tmp2)) {
+         Dmsg0(dbglevel, "Can't search for path\n");
+         /* print error */
+         return false;
+      }
+      if (!strcmp(tmp2.c_str(), "")) { /* path not found */
+         Dmsg3(dbglevel, "Path not found %lld q=%s s=%s\n",
+               id, tmp.c_str(), tmp2.c_str());
+         break;
+      }
+      /* escape % and _ for LIKE search */
+      tmp.check_size((strlen(tmp2.c_str())+1) * 2);
+      char *p = tmp.c_str();
+      for (char *s = tmp2.c_str(); *s ; s++) {
+         if (*s == '%' || *s == '_' || *s == '\\') {
+            *p = '\\'; 
+            p++;
+         }
+         *p = *s; 
+         p++;
+      }
+      *p = '\0';
+      tmp.strcat("%");
+
+      size_t len = strlen(tmp.c_str());
+      tmp2.check_size((len+1) * 2);
+      db_escape_string(jcr, db, tmp2.c_str(), tmp.c_str(), len);
+
+      if (init) {
+         query.strcat(" UNION ");
+      }
+      Mmsg(tmp, "(SELECT File.JobId, File.FileIndex, File.FilenameId, "
+                        "File.PathId, FileId "
+                   "FROM Path JOIN File USING (PathId) "
+                  "WHERE Path.Path LIKE '%s' AND File.JobId IN (%s)) ", 
+           tmp2.c_str(), jobids); 
+      query.strcat(tmp.c_str());
+      init = true;
+   }
+
+   /* expect jobid,fileindex */
+   int64_t prev_jobid=0;
+   while (get_next_id_from_list(&hardlink, &jobid) == 1) {
+      if (get_next_id_from_list(&hardlink, &id) != 1) {
+         Dmsg0(dbglevel, "hardlink should be two by two\n");
+         return false;
+      }
+      if (jobid != prev_jobid) { /* new job */
+         if (prev_jobid == 0) {  /* first jobid */
+            if (init) {
+               query.strcat(" UNION ");
+            }
+         } else {               /* end last job, start new one */
+            tmp.strcat(")) UNION ");
+            query.strcat(tmp.c_str());
+         }
+         Mmsg(tmp, "(SELECT JobId, FileIndex, FilenameId, PathId, FileId "
+                       "FROM File WHERE JobId = %lld " 
+                        "AND FileIndex IN (%lld", jobid, id);
+         prev_jobid = jobid;
+
+      } else {                  /* same job, add new findex */
+         Mmsg(tmp2, ", %lld", id);
+         tmp.strcat(tmp2.c_str());
+      }
+   }
+
+   if (prev_jobid != 0) {       /* end last job */
+      tmp.strcat(")) ");
+      query.strcat(tmp.c_str());
+      init = true;
+   }
+
+   Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
+
+   if (!db_sql_query(db, query.c_str(), NULL, NULL)) {
+      Dmsg0(dbglevel, "Can't execute q\n");
+      goto bail_out;
+   }
+
+   /* TODO: handle basejob and SQLite3 */
+   Mmsg(query, sql_bvfs_select[db_type], output_table, output_table);
+
+   Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
+   if (!db_sql_query(db, query.c_str(), NULL, NULL)) {
+      Dmsg0(dbglevel, "Can't execute q\n");
+      goto bail_out;
+   }
+   ret = true;
+
+bail_out:
+   Mmsg(query, "DROP TABLE btemp%s", output_table);
+   db_sql_query(db, query.c_str(), NULL, NULL);
+   return ret;
+}