Skip to content
Permalink
Browse files

dird: redesign GetNdmpEnvironmentString() API

Fixes #1056: NDMP restore on 16.2.5 and above does
             not fill NDMP environment correctly

Previously one overload of the function
GetNdmpEnvironmentString() wanted a JobDbRecord*
and expected jr->VolSessionId and jr->VolSessionTime
to contain the values for the volume from which the
restore happens. These had to be filled manually
before calling GetNdmlEnvironmentString() which had
not been done since 16.2.5 resulting in #1056.

This patch now redesigns the API for all overloads
of GetNdmpEnviromentString() to make it harder
to misuse.

We also add a new struct VolumeSessionInfo to wrap
a pair of VolumeSessionId and VolumeSessionTime.
These two numbers are only meaningful together, so
they now have their own container.
  • Loading branch information...
arogge committed Mar 14, 2019
1 parent a3affbe commit a2014835a91e61b1d784319ca76ca7377895843a
@@ -35,6 +35,8 @@
#ifndef BAREOS_CATS_CATS_H_
#define BAREOS_CATS_CATS_H_ 1

#include "lib/volume_session_info.h"

/* import automatically generated SQL_QUERY_ENUM */
#include "bdb_query_enum_class.h"

@@ -821,12 +823,15 @@ class BareosDb
int GetNdmpLevelMapping(JobControlRecord* jcr,
JobDbRecord* jr,
char* filesystem);
bool GetNdmpEnvironmentString(JobControlRecord* jcr,
JobDbRecord* jr,
bool GetNdmpEnvironmentString(const VolumeSessionInfo vsi,
const int32_t FileIndex,
DB_RESULT_HANDLER* ResultHandler,
void* ctx);
bool GetNdmpEnvironmentString(const JobId_t JobId,
DB_RESULT_HANDLER* ResultHandler,
void* ctx);
bool GetNdmpEnvironmentString(JobControlRecord* jcr,
JobId_t JobId,
bool GetNdmpEnvironmentString(const JobId_t JobId,
const int32_t FileIndex,
DB_RESULT_HANDLER* ResultHandler,
void* ctx);
bool PrepareMediaSqlQuery(JobControlRecord* jcr,
@@ -39,6 +39,7 @@
#include "cats.h"
#include "sql.h"
#include "lib/edit.h"
#include "lib/volume_session_info.h"

/* -----------------------------------------------------------------------
*
@@ -1711,32 +1712,46 @@ int BareosDb::GetNdmpLevelMapping(JobControlRecord* jcr,


/**
* Fetch the NDMP Job Environment Strings for NDMP_NATIVE Backups
* Fetch the NDMP Job Environment Strings based on JobId only
*
* Returns false: on failure
* true: on success
*/
bool BareosDb::GetNdmpEnvironmentString(JobControlRecord* jcr,
JobId_t JobId,
bool BareosDb::GetNdmpEnvironmentString(const JobId_t JobId,
DB_RESULT_HANDLER* ResultHandler,
void* ctx)
{
PoolMem query(PM_FNAME);
char ed1[50];
db_int64_ctx lctx;
bool retval = false;

/*
* Lookup all environment settings belonging to this JobId.
*/
Mmsg(query,
"SELECT EnvName, EnvValue FROM NDMPJobEnvironment "
"WHERE JobId='%s' ",
edit_uint64(JobId, ed1));

retval = SqlQueryWithHandler(query.c_str(), ResultHandler, ctx);
ASSERT(JobId > 0)
/* ***FIXME*** better use std::string */
char query[100];
Bsnprintf(
query, 99,
"SELECT EnvName, EnvValue FROM NDMPJobEnvironment WHERE JobId=%lld ",
JobId);
bool status = SqlQueryWithHandler(query, ResultHandler, ctx);
return status && SqlNumRows() > 0; // no rows means no environment was found
}

return retval;
/**
* Fetch the NDMP Job Environment Strings based on JobId and FileIndex
*
* Returns false: on failure
* true: on success
*/
bool BareosDb::GetNdmpEnvironmentString(const JobId_t JobId,
const int32_t FileIndex,
DB_RESULT_HANDLER* ResultHandler,
void* ctx)
{
ASSERT(JobId > 0)
/* ***FIXME*** better use std::string */
char query[150];
Bsnprintf(query, 149,
"SELECT EnvName, EnvValue FROM NDMPJobEnvironment "
"WHERE JobId=%lld AND FileIndex=%lld ",
JobId, FileIndex);
bool status = SqlQueryWithHandler(query, ResultHandler, ctx);
return status && SqlNumRows() > 0; // no rows means no environment was found
}


@@ -1746,48 +1761,29 @@ bool BareosDb::GetNdmpEnvironmentString(JobControlRecord* jcr,
* Returns false: on failure
* true: on success
*/
bool BareosDb::GetNdmpEnvironmentString(JobControlRecord* jcr,
JobDbRecord* jr,
bool BareosDb::GetNdmpEnvironmentString(const VolumeSessionInfo vsi,
const int32_t FileIndex,
DB_RESULT_HANDLER* ResultHandler,
void* ctx)
{
PoolMem query(PM_MESSAGE);
char ed1[50], ed2[50];
char query[150];
db_int64_ctx lctx;
JobId_t JobId;
bool retval = false;

lctx.count = 0;
lctx.value = 0;

/*
* Lookup the JobId
*/
Mmsg(query,
"SELECT JobId FROM Job "
"WHERE VolSessionId = '%s' "
"AND VolSessionTime = '%s'",
edit_uint64(jr->VolSessionId, ed1),
edit_uint64(jr->VolSessionTime, ed2));
if (!SqlQueryWithHandler(query.c_str(), db_int64_handler, &lctx)) {
goto bail_out;
/* Lookup the JobId and pass it to the JobId-based version */
Bsnprintf(query, 149,
"SELECT JobId FROM Job WHERE VolSessionId = %lld "
"AND VolSessionTime = %lld ",
vsi.id, vsi.time);
if (SqlQueryWithHandler(query, db_int64_handler, &lctx)) {
if (lctx.count == 1) {
return GetNdmpEnvironmentString(lctx.value, FileIndex, ResultHandler,
ctx);
}
}

JobId = (JobId_t)lctx.value;

/*
* Lookup all environment settings belonging to this JobId and FileIndex.
*/
Mmsg(query,
"SELECT EnvName, EnvValue FROM NDMPJobEnvironment "
"WHERE JobId='%s' "
"AND FileIndex='%s'",
edit_uint64(JobId, ed1), edit_uint64(jr->FileIndex, ed2));

retval = SqlQueryWithHandler(query.c_str(), ResultHandler, ctx);

bail_out:
return retval;
Dmsg3(100,
"Got %d JobIds for VolSessionTime=%lld VolSessionId=%lld instead of 1",
lctx.count, vsi.time, vsi.id);
return false;
}

/**
@@ -36,6 +36,7 @@
#include "dird/storage.h"

#include "lib/parse_bsr.h"
#include "lib/volume_session_info.h"

#if HAVE_NDMP
#include "dird/ndmp_dma_generic.h"
@@ -159,6 +160,7 @@ static inline int set_files_to_restore(JobControlRecord* jcr,
*/
static inline bool fill_restore_environment(JobControlRecord* jcr,
int32_t current_fi,
VolumeSessionInfo current_session,
struct ndm_job_param* job)
{
int i;
@@ -202,8 +204,8 @@ static inline bool fill_restore_environment(JobControlRecord* jcr,
/*
* Lookup the environment stack saved during the backup so we can restore it.
*/
if (!jcr->db->GetNdmpEnvironmentString(jcr, &jcr->jr, NdmpEnvHandler,
&job->env_tab)) {
if (!jcr->db->GetNdmpEnvironmentString(current_session, current_fi,
NdmpEnvHandler, &job->env_tab)) {
/*
* Fallback code try to build a environment stack that is good enough to
* restore this NDMP backup. This is used when the data is not available in
@@ -540,14 +542,13 @@ static inline bool DoNdmpRestoreBootstrap(JobControlRecord* jcr)
bool next_fi = true;
int first_fi = jcr->bsr->FileIndex->findex;
int last_fi = jcr->bsr->FileIndex->findex2;
uint32_t current_sessionid = jcr->bsr->sessid->sessid;
uint32_t current_sessiontime = jcr->bsr->sesstime->sesstime;
VolumeSessionInfo current_session{jcr->bsr->sessid->sessid,
jcr->bsr->sesstime->sesstime};
cnt = 0;

for (bsr = jcr->bsr; bsr; bsr = bsr->next) {
if (current_sessionid != bsr->sessid->sessid) {
current_sessionid = bsr->sessid->sessid;
current_sessiontime = bsr->sesstime->sesstime;
if (current_session.id != bsr->sessid->sessid) {
current_session = {bsr->sessid->sessid, bsr->sesstime->sesstime};
first_run = true;
next_sessid = true;
}
@@ -568,7 +569,7 @@ static inline bool DoNdmpRestoreBootstrap(JobControlRecord* jcr)
}
}
Dmsg4(20, "sessionid:sesstime : first_fi/last_fi : %d:%d %d/%d \n",
current_sessionid, current_sessiontime, first_fi, last_fi);
current_session.id, current_session.time, first_fi, last_fi);
}

if (next_sessid | next_fi) {
@@ -580,8 +581,8 @@ static inline bool DoNdmpRestoreBootstrap(JobControlRecord* jcr)
Jmsg(
jcr, M_INFO, 0,
_("Run restore for sesstime %s (%d), sessionid %d, fileindex %d\n"),
bstrftime(dt, sizeof(dt), current_sessiontime, NULL),
current_sessiontime, current_sessionid, current_fi);
bstrftime(dt, sizeof(dt), current_session.time, NULL),
current_session.time, current_session.id, current_fi);


/*
@@ -630,7 +631,7 @@ static inline bool DoNdmpRestoreBootstrap(JobControlRecord* jcr)

memcpy(&ndmp_sess.control_acb->job, &ndmp_job,
sizeof(struct ndm_job_param));
if (!fill_restore_environment(jcr, current_fi,
if (!fill_restore_environment(jcr, current_fi, current_session,
&ndmp_sess.control_acb->job)) {
Jmsg(jcr, M_ERROR, 0, _("ERROR in fill_restore_environment\n"));
goto cleanup_ndmp;
@@ -73,11 +73,13 @@ static inline bool fill_restore_environment_ndmp_native(
* We use the first jobid to get the environment string
*/

JobId_t JobId = str_to_int32(jcr->JobIds);
// TODO: Check if JobId is Zero as this indicates error
JobId_t JobId = (JobId_t)str_to_int32(jcr->JobIds);
if (JobId <= 0) {
Jmsg(jcr, M_FATAL, 0, "Impossible JobId: %d", JobId);
return false;
}

if (jcr->db->GetNdmpEnvironmentString(jcr, JobId, NdmpEnvHandler,
&job->env_tab)) {
if (jcr->db->GetNdmpEnvironmentString(JobId, NdmpEnvHandler, &job->env_tab)) {
/*
* extract ndmp_filesystem from environment
*/
@@ -0,0 +1,35 @@
/*
* BAREOS® - Backup Archiving REcovery Open Sourced
*
* Copyright (C) 2019-2019 Bareos GmbH & Co. KG
*
* This program is Free Software; you can redistribute it and/or modify
* it under the terms of version three of the GNU Affero General Public License
* as published by the Free Software Foundation and included in the file
* LICENSE.
* 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 Affero General Public License for more
* details. 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.
*
*/

/*
* provides a struct to wrap a pair of VolumeSessionId and VolumeSessionTime
*/

#ifndef BAREOS_LIB_VOLUME_SESSION_INFO_H_
#define BAREOS_LIB_VOLUME_SESSION_INFO_H_ 1

struct VolumeSessionInfo {
uint32_t id;
uint32_t time;

/* explicit constructor disables default construction */
VolumeSessionInfo(uint32_t t_id, uint32_t t_time) : id(t_id), time(t_time) {}
};

#endif /** BAREOS_LIB_VOLUME_SESSION_INFO_H_ */

0 comments on commit a201483

Please sign in to comment.
You can’t perform that action at this time.