/*
Bacula(R) - The Network Backup Solution
- Copyright (C) 2000-2015 Kern Sibbald
- Copyright (C) 2009-2014 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2017 Kern Sibbald
The original author of Bacula is Kern Sibbald, with contributions
from many others, a complete list can be found in the file AUTHORS.
Public License, v3.0 ("AGPLv3") and some additional permissions and
terms pursuant to its AGPLv3 Section 7.
- This notice must be preserved when any source code is
+ This notice must be preserved when any source code is
conveyed and/or propagated.
Bacula(R) is a registered trademark of Kern Sibbald.
#include "lib/htable.h"
#include "bvfs.h"
+#define can_access(x) (true)
+
+/* from libbacfind */
+extern int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI);
+
#define dbglevel DT_BVFS|10
#define dbglevel_sql DT_SQL|15
return 0;
}
-Bvfs::Bvfs(JCR *j, BDB *mdb) {
+Bvfs::Bvfs(JCR *j, BDB *mdb)
+{
jcr = j;
jcr->inc_use_count();
db = mdb; /* need to inc ref count */
tmp = get_pool_memory(PM_NAME);
escaped_list = get_pool_memory(PM_NAME);
*filename = *jobids = *prev_dir = *pattern = 0;
- dir_filenameid = pwd_id = offset = 0;
+ pwd_id = offset = 0;
see_copies = see_all_versions = false;
+ compute_delta = true;
limit = 1000;
attr = new_attr(jcr);
list_entries = result_handler;
user_data = this;
username = NULL;
job_acl = client_acl = pool_acl = fileset_acl = NULL;
+ last_dir_acl = NULL;
+ dir_acl = NULL;
+ use_acl = false;
+ dir_filenameid = 0; /* special FilenameId where Name='' */
}
Bvfs::~Bvfs() {
}
free_attr(attr);
jcr->dec_use_count();
+ if (dir_acl) {
+ delete dir_acl;
+ }
}
char *Bvfs::escape_list(alist *lst)
return escaped_list;
}
-void Bvfs::filter_jobid()
+/* Returns the number of jobids in the result */
+int Bvfs::filter_jobid()
{
POOL_MEM query;
POOL_MEM sub_where;
/* No ACL, no username, no check */
if (!job_acl && !fileset_acl && !client_acl && !pool_acl && !username) {
Dmsg0(dbglevel_sql, "No ACL\n");
- return;
+ /* Just count the number of items in the list */
+ int nb = (*jobids != 0) ? 1 : 0;
+ for (char *p=jobids; *p ; p++) {
+ if (*p == ',') {
+ nb++;
+ }
+ }
+ return nb;
}
if (job_acl) {
Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
db->bdb_sql_query(query.c_str(), db_list_handler, &ctx);
pm_strcpy(jobids, ctx.list);
+ return ctx.count;
}
-void Bvfs::set_jobid(JobId_t id)
+/* Return the number of jobids after the filter */
+int Bvfs::set_jobid(JobId_t id)
{
Mmsg(jobids, "%lld", (uint64_t)id);
- filter_jobid();
+ return filter_jobid();
}
-void Bvfs::set_jobids(char *ids)
+/* Return the number of jobids after the filter */
+int Bvfs::set_jobids(char *ids)
{
pm_strcpy(jobids, ids);
- filter_jobid();
+ return filter_jobid();
}
/*
nb_node = 0;
table_node = New(alist(5, owned_by_alist));
table_node->append(nodes);
- }
+ };
hlink *get_hlink() {
if (++nb_node >= max_node) {
table_node->append(nodes);
}
return nodes + nb_node;
- }
+ };
bool lookup(char *pathid) {
bool ret = cache_ppathid->lookup(pathid) != NULL;
return ret;
- }
+ };
void insert(char *pathid) {
hlink *h = get_hlink();
cache_ppathid->insert(pathid, h);
- }
+ };
~pathid_cache() {
cache_ppathid->destroy();
free(cache_ppathid);
delete table_node;
- }
+ };
private:
pathid_cache(const pathid_cache &); /* prohibit pass by value */
pathid_cache &operator= (const pathid_cache &);/* prohibit class assignment*/
/* Inserting path records for JobId */
Mmsg(mdb->cmd, "INSERT INTO PathVisibility (PathId, JobId) "
"SELECT DISTINCT PathId, JobId "
- "FROM (SELECT PathId, JobId FROM File WHERE JobId = %s "
+ "FROM (SELECT PathId, JobId FROM File WHERE JobId = %s AND FileIndex <> 0 "
"UNION "
"SELECT PathId, BaseFiles.JobId "
"FROM BaseFiles JOIN File AS F USING (FileId) "
"AND h.PPathId NOT IN (SELECT PathId FROM PathVisibility WHERE JobId=%s)",
jobid, jobid, jobid );
- } else if (mdb->bdb_get_type_index() == SQL_TYPE_MYSQL) {
+ } else if (mdb->bdb_get_type_index() == SQL_TYPE_MYSQL) {
Mmsg(mdb->cmd,
"INSERT INTO PathVisibility (PathId, JobId) "
"SELECT a.PathId,%s "
"LEFT JOIN PathVisibility AS b ON (b.JobId=%s and a.PathId = b.PathId) "
"WHERE b.PathId IS NULL", jobid, jobid, jobid);
- } else { /* TODO: Test the MYSQL Query with PostgreSQL */
+ } else {
Mmsg(mdb->cmd,
"INSERT INTO PathVisibility (PathId, JobId) "
"SELECT a.PathId,%s "
} while (ret && mdb->sql_affected_rows() > 0);
Mmsg(mdb->cmd, "UPDATE Job SET HasCache=1 WHERE JobId=%s", jobid);
- ret = mdb->UpdateDB(jcr, mdb->cmd);
+ ret = mdb->UpdateDB(jcr, mdb->cmd, false);
bail_out:
mdb->bdb_end_transaction(jcr);
return ret;
}
-/*
- * Find an store the filename descriptor for empty directories Filename.Name=''
- */
-DBId_t Bvfs::get_dir_filenameid()
-{
- uint32_t id;
- if (dir_filenameid) {
- return dir_filenameid;
- }
- Mmsg(db->cmd, "SELECT FilenameId FROM Filename WHERE Name = ''");
- db_sql_query(db, db->cmd, db_int_handler, &id);
- dir_filenameid = id;
- return dir_filenameid;
-}
-
/* Compute the cache for the bfileview compoment */
void Bvfs::fv_update_cache()
{
db->bdb_unlock();
}
+/*
+ * Find an store the filename descriptor for empty directories Filename.Name=''
+ */
+DBId_t Bvfs::get_dir_filenameid()
+{
+ uint32_t id;
+ if (dir_filenameid) {
+ return dir_filenameid;
+ }
+ Mmsg(db->cmd, "SELECT FilenameId FROM Filename WHERE Name = ''");
+ db_sql_query(db, db->cmd, db_int_handler, &id);
+ dir_filenameid = id;
+ return dir_filenameid;
+}
+
/* Not yet working */
void Bvfs::fv_get_big_files(int64_t pathid, int64_t min_size, int32_t limit)
{
"LIMIT %d ", pathid, jobids, min_size, limit);
}
+
/* Get the current path size and files count */
void Bvfs::fv_get_current_size_and_count(int64_t pathid, int64_t *size, int64_t *count)
{
" WHERE JobId = %s "
" AND PathId = %lld ", count, size, jobids, pathid);
- db->UpdateDB(jcr, db->cmd);
+ db->UpdateDB(jcr, db->cmd, false);
}
void bvfs_update_cache(JCR *jcr, BDB *mdb)
bvfs_update_path_hierarchy_cache(jcr, db, jobids);
}
+
+bool Bvfs::ch_dir(DBId_t pathid)
+{
+ reset_offset();
+
+ pwd_id = pathid;
+ return pwd_id != 0;
+}
+
+
/* Change the current directory, returns true if the path exists */
bool Bvfs::ch_dir(const char *path)
{
+ db->bdb_lock();
pm_strcpy(db->path, path);
db->pnl = strlen(db->path);
- db->bdb_lock();
ch_dir(db->bdb_get_path_record(jcr));
db->bdb_unlock();
return pwd_id != 0;
POOL_MEM query;
- 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 "
+ Mmsg(query,// 1 2 3 4
+"SELECT 'V', File.PathId, File.FilenameId, 0, File.JobId, "
+// 5 6 7
+ "File.LStat, File.FileId, File.Md5, "
+// 8 9
+ "Media.VolumeName, Media.InChanger "
"FROM File, Job, Client, JobMedia, Media "
"WHERE File.FilenameId = %s "
"AND File.PathId=%s "
db->bdb_sql_query(query.c_str(), list_entries, user_data);
}
+/*
+ * Get all file versions for a specified client
+ * TODO: Handle basejobs using different client
+ */
+bool Bvfs::get_delta(FileId_t fileid)
+{
+ Dmsg1(dbglevel, "get_delta(%lld)\n", (uint64_t)fileid);
+ char ed1[50];
+ int32_t num;
+ SQL_ROW row;
+ POOL_MEM q;
+ POOL_MEM query;
+ char *fn = NULL;
+ bool ret = false;
+ db->bdb_lock();
+
+ /* Check if some FileId have DeltaSeq > 0
+ * Foreach of them we need to get the accurate_job list, and compute
+ * what are dependencies
+ */
+ Mmsg(query,
+ "SELECT F.JobId, FN.Name, F.PathId, F.DeltaSeq "
+ "FROM File AS F, Filename AS FN WHERE FileId = %lld "
+ "AND FN.FilenameId = F.FilenameId AND DeltaSeq > 0", fileid);
+
+ if (!db->QueryDB(jcr, query.c_str())) {
+ Dmsg1(dbglevel_sql, "Can't execute query=%s\n", query.c_str());
+ goto bail_out;
+ }
+
+ /* TODO: Use an other DB connection can avoid to copy the result of the
+ * previous query into a temporary buffer
+ */
+ num = db->sql_num_rows();
+ Dmsg2(dbglevel, "Found %d Delta parts q=%s\n",
+ num, query.c_str());
+
+ if (num > 0 && (row = db->sql_fetch_row())) {
+ JOB_DBR jr, jr2;
+ db_list_ctx lst;
+ memset(&jr, 0, sizeof(jr));
+ memset(&jr2, 0, sizeof(jr2));
+
+ fn = bstrdup(row[1]); /* Filename */
+ int64_t jid = str_to_int64(row[0]); /* JobId */
+ int64_t pid = str_to_int64(row[2]); /* PathId */
+
+ /* Need to limit the query to StartTime, Client/Fileset */
+ jr2.JobId = jid;
+ if (!db->bdb_get_job_record(jcr, &jr2)) {
+ Dmsg1(0, "Unable to get job record for jobid %d\n", jid);
+ goto bail_out;
+ }
+
+ jr.JobId = jid;
+ jr.ClientId = jr2.ClientId;
+ jr.FileSetId = jr2.FileSetId;
+ jr.JobLevel = L_INCREMENTAL;
+ jr.StartTime = jr2.StartTime;
+
+ /* Get accurate jobid list */
+ if (!db->bdb_get_accurate_jobids(jcr, &jr, &lst)) {
+ Dmsg1(0, "Unable to get Accurate list for jobid %d\n", jid);
+ goto bail_out;
+ }
+
+ /* Escape filename */
+ db->fnl = strlen(fn);
+ db->esc_name = check_pool_memory_size(db->esc_name, 2*db->fnl+2);
+ db->bdb_escape_string(jcr, db->esc_name, fn, db->fnl);
+
+ edit_int64(pid, ed1); /* pathid */
+
+ int id=db->bdb_get_type_index();
+ Mmsg(query, bvfs_select_delta_version_with_basejob_and_delta[id],
+ lst.list, db->esc_name, ed1,
+ lst.list, db->esc_name, ed1,
+ lst.list, lst.list);
+
+ Mmsg(db->cmd,
+ // 0 1 2 3 4 5 6 7
+ "SELECT 'd', PathId, 0, JobId, LStat, FileId, DeltaSeq, JobTDate"
+ " FROM (%s) AS F1 "
+ "ORDER BY DeltaSeq ASC",
+ query.c_str());
+
+ Dmsg1(dbglevel_sql, "q=%s\n", db->cmd);
+
+ if (!db->bdb_sql_query(db->cmd, list_entries, user_data)) {
+ Dmsg1(dbglevel_sql, "Can't exec q=%s\n", db->cmd);
+ goto bail_out;
+ }
+ }
+ ret = true;
+bail_out:
+ if (fn) {
+ free(fn);
+ }
+ db->bdb_unlock();
+ return ret;
+}
+
/*
* Get all volumes for a specific file
*/
/* can have the same path 2 times */
if (strcmp(row[BVFS_PathId], prev_dir)) {
pm_strcpy(prev_dir, row[BVFS_PathId]);
+ if (strcmp(NPRTB(row[BVFS_FileIndex]), "0") == 0 &&
+ strcmp(NPRTB(row[BVFS_FileId]), "0") != 0)
+ {
+ /* The directory was probably deleted */
+ return 0;
+ }
return list_entries(user_data, fields, row);
}
}
POOL_MEM query;
Mmsg(query,
-"(SELECT PPathId AS PathId, '..' AS Path "
- "FROM PathHierarchy "
- "WHERE PathId = %s "
+"(SELECT PathHierarchy.PPathId AS PathId, '..' AS Path "
+ "FROM PathHierarchy JOIN PathVisibility USING (PathId) "
+ "WHERE PathHierarchy.PathId = %s "
+ "AND PathVisibility.JobId IN (%s) "
"UNION "
"SELECT %s AS PathId, '.' AS Path)",
- edit_uint64(pwd_id, ed1), ed1);
+ edit_uint64(pwd_id, ed1), jobids, ed1);
POOL_MEM query2;
Mmsg(query2,// 1 2 3 4 5 6
-"SELECT 'D', tmp.PathId, 0, tmp.Path, JobId, LStat, FileId "
+"SELECT 'D', tmp.PathId, 0, tmp.Path, JobId, LStat, FileId, FileIndex "
"FROM %s AS tmp LEFT JOIN ( " // get attributes if any
"SELECT File1.PathId AS PathId, File1.JobId AS JobId, "
- "File1.LStat AS LStat, File1.FileId AS FileId FROM File AS File1 "
+ "File1.LStat AS LStat, File1.FileId AS FileId, "
+ "File1.FileIndex AS FileIndex, "
+ "Job1.JobTDate AS JobTDate "
+ "FROM File AS File1 JOIN Job AS Job1 USING (JobId)"
"WHERE File1.FilenameId = %s "
"AND File1.JobId IN (%s)) AS listfile1 "
"ON (tmp.PathId = listfile1.PathId) "
- "ORDER BY tmp.Path, JobId DESC ",
+ "ORDER BY tmp.Path, JobTDate DESC ",
query.c_str(), edit_uint64(dir_filenameid, ed2), jobids);
Dmsg1(dbglevel_sql, "q=%s\n", query2.c_str());
*/
/* Then we get all the dir entries from File ... */
Mmsg(query,
-// 0 1 2 3 4 5 6
-"SELECT 'D', PathId, 0, Path, JobId, LStat, FileId FROM ( "
+// 0 1 2 3 4 5 6
+"SELECT 'D', PathId, 0, Path, JobId, LStat, FileId, FileIndex FROM ( "
"SELECT Path1.PathId AS PathId, Path1.Path AS Path, "
"lower(Path1.Path) AS lpath, "
"listfile1.JobId AS JobId, listfile1.LStat AS LStat, "
- "listfile1.FileId AS FileId "
+ "listfile1.FileId AS FileId, "
+ "listfile1.JobTDate AS JobTDate, "
+ "listfile1.FileIndex AS FileIndex "
"FROM ( "
"SELECT DISTINCT PathHierarchy1.PathId AS PathId "
"FROM PathHierarchy AS PathHierarchy1 "
"LEFT JOIN ( " /* get attributes if any */
"SELECT File1.PathId AS PathId, File1.JobId AS JobId, "
- "File1.LStat AS LStat, File1.FileId AS FileId FROM File AS File1 "
+ "File1.LStat AS LStat, File1.FileId AS FileId, "
+ "File1.FileIndex, Job1.JobTDate AS JobTDate "
+ "FROM File AS File1 JOIN Job AS Job1 USING (JobId) "
"WHERE File1.FilenameId = %s "
"AND File1.JobId IN (%s)) AS listfile1 "
"ON (listpath1.PathId = listfile1.PathId) "
- ") AS A ORDER BY 2,3 DESC LIMIT %d OFFSET %d",
+ ") AS A ORDER BY Path,JobTDate DESC LIMIT %d OFFSET %d",
edit_uint64(pwd_id, ed1),
jobids,
filter.c_str(),
(*hardlink && !is_a_number_list(hardlink))||
(!*hardlink && !*fileid && !*dirid && !*hardlink))
{
+ Dmsg0(dbglevel, "ERROR: One or more of FileId, DirId or HardLink is not given or not a number.\n");
return false;
}
if (!check_temp(output_table)) {
+ Dmsg0(dbglevel, "ERROR: Wrong format for table name (in path field).\n");
return false;
}
init=true;
Mmsg(tmp,"SELECT Job.JobId, JobTDate, FileIndex, FilenameId, "
"PathId, FileId "
- "FROM File JOIN Job USING (JobId) WHERE FileId IN (%s)",
+ "FROM File,Job WHERE Job.JobId=File.Jobid "
+ "AND FileId IN (%s)",
fileid);
pm_strcat(query, tmp.c_str());
}
Mmsg(tmp, "SELECT Path FROM Path WHERE PathId=%lld", id);
if (!db->bdb_sql_query(tmp.c_str(), get_path_handler, (void *)&tmp2)) {
- Dmsg0(dbglevel, "Can't search for path\n");
+ Dmsg3(dbglevel, "ERROR: Path not found %lld q=%s s=%s\n",
+ id, tmp.c_str(), tmp2.c_str());
/* print error */
goto bail_out;
}
if (!strcmp(tmp2.c_str(), "")) { /* path not found */
- Dmsg3(dbglevel, "Path not found %lld q=%s s=%s\n",
+ Dmsg3(dbglevel, "ERROR: Path not found %lld q=%s s=%s\n",
id, tmp.c_str(), tmp2.c_str());
break;
}
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");
+ Dmsg0(dbglevel, "ERROR: hardlink should be two by two\n");
goto bail_out;
}
if (jobid != prev_jobid) { /* new job */
Dmsg1(dbglevel_sql, "query=%s\n", query.c_str());
if (!db->bdb_sql_query(query.c_str(), NULL, NULL)) {
- Dmsg1(dbglevel, "Can't execute query=%s\n", query.c_str());
+ Dmsg1(dbglevel, "ERROR executing query=%s\n", query.c_str());
goto bail_out;
}
/* TODO: handle jobid filter */
Dmsg1(dbglevel_sql, "query=%s\n", query.c_str());
if (!db->bdb_sql_query(query.c_str(), NULL, NULL)) {
- Dmsg1(dbglevel, "Can't execute query=%s\n", query.c_str());
+ Dmsg1(dbglevel, "ERROR executing query=%s\n", query.c_str());
goto bail_out;
}
output_table, output_table);
Dmsg1(dbglevel_sql, "query=%s\n", query.c_str());
if (!db->bdb_sql_query(query.c_str(), NULL, NULL)) {
- Dmsg1(dbglevel, "Can't execute query=%s\n", query.c_str());
+ Dmsg1(dbglevel, "ERROR executing query=%s\n", query.c_str());
goto bail_out;
}
}
result[i++] = str_to_int64(row[1]); /* JobId */
result[i++] = str_to_int64(row[2]); /* FilenameId */
result[i++] = str_to_int64(row[3]); /* PathId */
- }
+ }
i=0;
while (num > 0) {
db->bdb_unlock();
return ret;
}
-
+
void Bvfs::insert_missing_delta(char *output_table, int64_t *res)
{
- char ed1[50], ed2[50];
+ char ed1[50];
db_list_ctx lst;
POOL_MEM query;
JOB_DBR jr, jr2;
Dmsg1(dbglevel_sql, "JobId list after strip is %s\n", lst.list);
- edit_int64(res[2], ed1); /* fnid */
- edit_int64(res[3], ed2); /* pathid */
+ /* Escape filename */
+ db->fnl = strlen((char *)res[2]);
+ db->esc_name = check_pool_memory_size(db->esc_name, 2*db->fnl+2);
+ db->bdb_escape_string(jcr, db->esc_name, (char *)res[2], db->fnl);
+
+ edit_int64(res[3], ed1); /* pathid */
int id=db->bdb_get_type_index();
Mmsg(query, bvfs_select_delta_version_with_basejob_and_delta[id],
- lst.list, ed1, ed2,
- lst.list, ed1, ed2,
+ lst.list, db->esc_name, ed1,
+ lst.list, db->esc_name, ed1,
lst.list, lst.list);
Mmsg(db->cmd, "INSERT INTO %s "