From ec38a8eb3cc7dbea4222a26ac13ef9d12796d707 Mon Sep 17 00:00:00 2001 From: Marco van Wieringen Date: Mon, 24 Feb 2014 20:56:20 +0100 Subject: [PATCH] Fix some bugs in the NDMP implementation. Up until now we were not fully RFC compliant. You need to restore the full NDMP environment returned during a NDMP backup when performing a NDMP restore. We more or less re-created the environment when doing a restore but were missing some important elements. We now save the full environment in the database and use that to restore the whole environment when doing a restore. --- src/cats/cats.h | 2 +- src/cats/grant_ingres_privileges.in | 1 + src/cats/grant_postgresql_privileges.in | 1 + src/cats/make_ingres_tables.in | 9 + src/cats/make_mysql_tables.in | 8 + src/cats/make_postgresql_tables.in | 8 + src/cats/make_sqlite3_tables.in | 8 + src/cats/protos.h | 2 + src/cats/sql_create.c | 33 ++- src/cats/sql_get.c | 86 +++++-- src/dird/ndmp_dma.c | 288 +++++++++++++++++++++--- src/dird/restore.c | 64 ++++-- src/dird/ua_label.c | 14 +- src/dird/ua_purge.c | 4 + src/ndmp/ndma_comm_dispatch.c | 18 ++ src/ndmp/ndma_comm_session.c | 8 +- src/ndmp/ndma_cops_backreco.c | 2 - src/ndmp/ndma_listmgmt.c | 64 +++++- src/ndmp/ndmagents.h | 1 + src/ndmp/ndmjob_job.c | 6 + src/ndmp/ndmp3_translate.c | 9 +- src/ndmp/ndmp4_translate.c | 20 +- src/ndmp/ndmp9.x | 3 + src/stored/dir_cmd.c | 18 ++ src/stored/ndmp_tape.c | 54 ++++- 25 files changed, 624 insertions(+), 107 deletions(-) diff --git a/src/cats/cats.h b/src/cats/cats.h index cd6d006905a..b61879932c9 100644 --- a/src/cats/cats.h +++ b/src/cats/cats.h @@ -452,7 +452,7 @@ typedef int (DB_RESULT_HANDLER)(void *, int, char **); #define db_unlock(mdb) mdb->_db_unlock(__FILE__, __LINE__) /* Current database version number for all drivers */ -#define BDB_VERSION 2001 +#define BDB_VERSION 2002 class B_DB: public SMARTALLOC { protected: diff --git a/src/cats/grant_ingres_privileges.in b/src/cats/grant_ingres_privileges.in index ac3ae702b5d..0135180ba19 100755 --- a/src/cats/grant_ingres_privileges.in +++ b/src/cats/grant_ingres_privileges.in @@ -41,6 +41,7 @@ GRANT ALL ON TABLE PathVisibility TO ${db_user}; GRANT ALL ON TABLE Version TO ${db_user}; GRANT ALL ON TABLE Status TO ${db_user}; GRANT ALL ON TABLE NDMPLevelMap TO ${db_user}; +GRANT ALL ON TABLE NDMPJobEnvironment TO ${db_user}; -- for sequences ON those tables GRANT NEXT ON SEQUENCE Filename_Seq TO ${db_user}; diff --git a/src/cats/grant_postgresql_privileges.in b/src/cats/grant_postgresql_privileges.in index b48ba729083..2aeaf01a2f6 100644 --- a/src/cats/grant_postgresql_privileges.in +++ b/src/cats/grant_postgresql_privileges.in @@ -43,6 +43,7 @@ GRANT ALL ON PathVisibility TO ${db_user}; GRANT ALL ON RestoreObject TO ${db_user}; GRANT ALL ON Quota TO ${db_user}; GRANT ALL ON NDMPLevelMap TO ${db_user}; +GRANT ALL ON NDMPJobEnvironment TO ${db_user}; -- For sequences ON those tables GRANT SELECT, UPDATE ON filename_filenameid_seq TO ${db_user}; diff --git a/src/cats/make_ingres_tables.in b/src/cats/make_ingres_tables.in index 1e22f9247c1..4f0ebeab0f2 100755 --- a/src/cats/make_ingres_tables.in +++ b/src/cats/make_ingres_tables.in @@ -465,6 +465,15 @@ CREATE TABLE NDMPLevelMap DumpLevel INTEGER DEFAULT 0, CONSTRAINT NDMPLevelMap_pkey PRIMARY KEY (ClientId, FilesetId, FileSystem) ); + +CREATE TABLE NDMPJobEnvironment +( + JobId INTEGER NOT NULL, + FileIndex INTEGER NOT NULL, + EnvName VARBYTE(256) NOT NULL, + EnvValue VARBYTE(256) NOT NULL, + CONSTRAINT NDMPJobEnvironment_pkey PRIMARY KEY (JobId, FileIndex, EnvName) +); \g INSERT INTO Status (JobStatus,JobStatusLong,Severity) VALUES diff --git a/src/cats/make_mysql_tables.in b/src/cats/make_mysql_tables.in index 80e9aec9ef2..3e490d03886 100644 --- a/src/cats/make_mysql_tables.in +++ b/src/cats/make_mysql_tables.in @@ -407,6 +407,14 @@ CREATE TABLE NDMPLevelMap ( CONSTRAINT NDMPLevelMap_pkey PRIMARY KEY (ClientId, FilesetId, FileSystem(256)) ); +CREATE TABLE NDMPJobEnvironment ( + JobId INTEGER UNSIGNED NOT NULL REFERENCES Job, + FileIndex INTEGER UNSIGNED NOT NULL, + EnvName TINYBLOB NOT NULL, + EnvValue TINYBLOB NOT NULL, + CONSTRAINT NDMPJobEnvironment_pkey PRIMARY KEY (JobId, FileIndex, EnvName) +); + INSERT INTO Status (JobStatus,JobStatusLong,Severity) VALUES ('C', 'Created, not yet running', 15), ('R', 'Running', 15), diff --git a/src/cats/make_postgresql_tables.in b/src/cats/make_postgresql_tables.in index 6b79a5bd937..299f828188d 100644 --- a/src/cats/make_postgresql_tables.in +++ b/src/cats/make_postgresql_tables.in @@ -409,6 +409,14 @@ CREATE TABLE NDMPLevelMap ( CONSTRAINT NDMPLevelMap_pkey PRIMARY KEY (ClientId, FilesetId, FileSystem) ); +CREATE TABLE NDMPJobEnvironment ( + JobId INTEGER NOT NULL, + FileIndex INTEGER NOT NULL, + EnvName TEXT NOT NULL, + EnvValue TEXT NOT NULL, + CONSTRAINT NDMPJobEnvironment_pkey PRIMARY KEY (JobId, FileIndex, EnvName) +); + INSERT INTO Status (JobStatus,JobStatusLong,Severity) VALUES ('C', 'Created, not yet running',15); INSERT INTO Status (JobStatus,JobStatusLong,Severity) VALUES diff --git a/src/cats/make_sqlite3_tables.in b/src/cats/make_sqlite3_tables.in index ac8bb84b4a6..a30592cbdc6 100644 --- a/src/cats/make_sqlite3_tables.in +++ b/src/cats/make_sqlite3_tables.in @@ -393,6 +393,14 @@ CREATE TABLE NDMPLevelMap ( CONSTRAINT NDMPLevelMap_pkey PRIMARY KEY (ClientId, FilesetId, FileSystem) ); +CREATE TABLE NDMPJobEnvironment ( + JobId integer NOT NULL, + FileIndex INTEGER UNSIGNED NOT NULL, + EnvName TEXT NOT NULL, + EnvValue TEXT NOT NULL, + CONSTRAINT NDMPJobEnvironment_pkey PRIMARY KEY (JobId, FileIndex, EnvName) +); + INSERT INTO Status (JobStatus,JobStatusLong,Severity) VALUES ('C', 'Created, not yet running',15); INSERT INTO Status (JobStatus,JobStatusLong,Severity) VALUES diff --git a/src/cats/protos.h b/src/cats/protos.h index ec08c829ea4..a96939e9591 100644 --- a/src/cats/protos.h +++ b/src/cats/protos.h @@ -62,6 +62,7 @@ bool db_commit_base_file_attributes_record(JCR *jcr, B_DB *mdb); bool db_create_base_file_list(JCR *jcr, B_DB *mdb, char *jobids); bool db_create_quota_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr); bool db_create_ndmp_level_mapping(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *filesystem); +bool db_create_ndmp_environment_string(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *name, char *value); /* sql_delete.c */ bool db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr); @@ -105,6 +106,7 @@ bool db_get_quota_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr); bool db_get_quota_jobbytes(JCR *jcr, B_DB *mdb, JOB_DBR *jr, utime_t JobRetention); bool db_get_quota_jobbytes_nofailed(JCR *jcr, B_DB *mdb, JOB_DBR *jr, utime_t JobRetention); int db_get_ndmp_level_mapping(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *filesystem); +bool db_get_ndmp_environment_string(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_RESULT_HANDLER *result_handler, void *ctx); /* sql_list.c */ void db_list_pool_records(JCR *jcr, B_DB *db, POOL_DBR *pr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type); diff --git a/src/cats/sql_create.c b/src/cats/sql_create.c index 8cc81c6d38d..fe0851334d6 100644 --- a/src/cats/sql_create.c +++ b/src/cats/sql_create.c @@ -63,7 +63,7 @@ bool db_create_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) struct tm tm; int len; utime_t JobTDate; - char ed1[30],ed2[30]; + char ed1[30], ed2[30]; char esc_job[MAX_ESCAPE_NAME_LENGTH]; char esc_name[MAX_ESCAPE_NAME_LENGTH]; @@ -1337,4 +1337,35 @@ bool db_create_ndmp_level_mapping(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *filesy db_unlock(mdb); return retval; } + +/** + * Create a NDMP Job Environment String + * Returns: false on failure + * true on success + */ +bool db_create_ndmp_environment_string(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *name, char *value) +{ + bool retval = false; + char ed1[50], ed2[50]; + char esc_name[MAX_ESCAPE_NAME_LENGTH]; + char esc_value[MAX_ESCAPE_NAME_LENGTH]; + + db_lock(mdb); + + mdb->db_escape_string(jcr, esc_name, name, strlen(name)); + mdb->db_escape_string(jcr, esc_value, value, strlen(value)); + Mmsg(mdb->cmd, "INSERT INTO NDMPJobEnvironment (JobId, FileIndex, EnvName, EnvValue)" + " VALUES ('%s', '%s', '%s', '%s')", + edit_int64(jr->JobId, ed1), edit_uint64(jr->FileIndex, ed2), esc_name, esc_value); + if (!INSERT_DB(jcr, mdb, mdb->cmd)) { + Mmsg2(&mdb->errmsg, _("Create DB NDMP Job Environment record %s failed. ERR=%s\n"), + mdb->cmd, sql_strerror(mdb)); + Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg); + } else { + retval = true; + } + + db_unlock(mdb); + return retval; +} #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */ diff --git a/src/cats/sql_get.c b/src/cats/sql_get.c index a84a01f1e77..50ddd6c235c 100644 --- a/src/cats/sql_get.c +++ b/src/cats/sql_get.c @@ -1127,28 +1127,31 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, bool use_md5, bool use_delta, DB_RESULT_HANDLER *result_handler, void *ctx) { + POOL_MEM query(PM_FNAME); + POOL_MEM query2(PM_FNAME); + if (!*jobids) { db_lock(mdb); Mmsg(mdb->errmsg, _("ERR=JobIds are empty\n")); db_unlock(mdb); return false; } - POOL_MEM buf(PM_MESSAGE); - POOL_MEM buf2(PM_MESSAGE); + if (use_delta) { - Mmsg(buf2, select_recent_version_with_basejob_and_delta[db_get_type_index(mdb)], + Mmsg(query2, select_recent_version_with_basejob_and_delta[db_get_type_index(mdb)], jobids, jobids, jobids, jobids); } else { - Mmsg(buf2, select_recent_version_with_basejob[db_get_type_index(mdb)], + Mmsg(query2, select_recent_version_with_basejob[db_get_type_index(mdb)], jobids, jobids, jobids, jobids); } - /* bsr code is optimized for JobId sorted, with Delta, we need to get + /* + * BSR code is optimized for JobId sorted, with Delta, we need to get * them ordered by date. JobTDate and JobId can be mixed if using Copy * or Migration */ - Mmsg(buf, + Mmsg(query, "SELECT Path.Path, Filename.Name, T1.FileIndex, T1.JobId, LStat, DeltaSeq, MD5 " "FROM ( %s ) AS T1 " "JOIN Filename ON (Filename.FilenameId = T1.FilenameId) " @@ -1156,15 +1159,15 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, "WHERE FileIndex > 0 " "ORDER BY T1.JobTDate, FileIndex ASC",/* Return sorted by JobTDate */ /* FileIndex for restore code */ - buf2.c_str()); + query2.c_str()); if (!use_md5) { - strip_md5(buf.c_str()); + strip_md5(query.c_str()); } - Dmsg1(100, "q=%s\n", buf.c_str()); + Dmsg1(100, "q=%s\n", query.c_str()); - return db_big_sql_query(mdb, buf.c_str(), result_handler, ctx); + return db_big_sql_query(mdb, query.c_str(), result_handler, ctx); } /** @@ -1173,13 +1176,14 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, bool db_get_used_base_jobids(JCR *jcr, B_DB *mdb, POOLMEM *jobids, db_list_ctx *result) { - POOL_MEM buf; - Mmsg(buf, + POOL_MEM query(PM_FNAME); + + Mmsg(query, "SELECT DISTINCT BaseJobId " " FROM Job JOIN BaseFiles USING (JobId) " " WHERE Job.HasBase = 1 " " AND Job.JobId IN (%s) ", jobids); - return db_sql_query(mdb, buf.c_str(), db_list_handler, result); + return db_sql_query(mdb, query.c_str(), db_list_handler, result); } /** @@ -1277,17 +1281,17 @@ bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, bool db_get_base_file_list(JCR *jcr, B_DB *mdb, bool use_md5, DB_RESULT_HANDLER *result_handler, void *ctx) { - POOL_MEM buf(PM_MESSAGE); + POOL_MEM query(PM_FNAME); - Mmsg(buf, + Mmsg(query, "SELECT Path, Name, FileIndex, JobId, LStat, 0 As DeltaSeq, MD5 " "FROM new_basefile%lld ORDER BY JobId, FileIndex ASC", (uint64_t) jcr->JobId); if (!use_md5) { - strip_md5(buf.c_str()); + strip_md5(query.c_str()); } - return db_big_sql_query(mdb, buf.c_str(), result_handler, ctx); + return db_big_sql_query(mdb, query.c_str(), result_handler, ctx); } bool db_get_base_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr, JobId_t *jobid) @@ -1299,6 +1303,7 @@ bool db_get_base_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr, JobId_t *jobid) char esc[MAX_ESCAPE_NAME_LENGTH]; bool retval = false; // char clientid[50], filesetid[50]; + *jobid = 0; lctx.count = 0; lctx.value = 0; @@ -1414,6 +1419,7 @@ bool db_get_quota_jobbytes(JCR *jcr, B_DB *mdb, JOB_DBR *jr, utime_t JobRetentio /** * This function returns the sum of all the Clients JobBytes of non failed jobs. + * * Returns false: on failure * true: on success */ @@ -1562,4 +1568,50 @@ int db_get_ndmp_level_mapping(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *filesystem db_unlock(mdb); return dumplevel; } + +/** + * Fetch the NDMP Job Environment Strings + * + * Returns false: on failure + * true: on success + */ +bool db_get_ndmp_environment_string(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_RESULT_HANDLER *result_handler, void *ctx) +{ + POOL_MEM query(PM_FNAME); + char ed1[50], ed2[50]; + 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 (!db_sql_query(mdb, query.c_str(), db_int64_handler, &lctx)) { + goto bail_out; + } + + 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 = db_sql_query(mdb, query.c_str(), result_handler, ctx); + +bail_out: + return retval; +} #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */ diff --git a/src/dird/ndmp_dma.c b/src/dird/ndmp_dma.c index 4ec51bd06c8..ba9754fa1cb 100644 --- a/src/dird/ndmp_dma.c +++ b/src/dird/ndmp_dma.c @@ -151,6 +151,40 @@ enum { NDMP_ENV_VALUE_YES }; +struct ndmp_backup_format_option { + char *format; + bool uses_file_history; + bool uses_level; + bool restore_prefix_relative; + bool needs_namelist; +}; + +static ndmp_backup_format_option ndmp_backup_format_options[] = { + { (char *)"dump", true, true, true, true }, + { (char *)"tar", true, false, true, true }, + { (char *)"smtape", false, false, false, false }, + { (char *)"zfs", false, true, false, true }, + { NULL, false, false, false } +}; + +static ndmp_backup_format_option *lookup_backup_format_options(const char *backup_format) +{ + int i = 0; + + while (ndmp_backup_format_options[i].format) { + if (bstrcasecmp(backup_format, ndmp_backup_format_options[i].format)) { + break; + } + i++; + } + + if (ndmp_backup_format_options[i].format) { + return &ndmp_backup_format_options[i]; + } + + return (ndmp_backup_format_option *)NULL; +} + /* * Lightweight version of Bareos tree functions for holding the NDMP * filehandle index database. See lib/tree.[ch] for the full version. @@ -386,7 +420,7 @@ static inline void parse_meta_tag(struct ndm_env_table *env_tab, *p = '\0'; pv.name = meta_tag; pv.value = p + 1; - ndma_store_env_list(env_tab, &pv); + ndma_update_env_list(env_tab, &pv); /* * Restore the '=' @@ -408,13 +442,28 @@ static inline bool fill_backup_environment(JCR *jcr, ndmp9_pval pv; POOL_MEM pattern; POOL_MEM tape_device; + ndmp_backup_format_option *nbf_options; /* - * We want to receive file history info from the NDMP backup. + * See if we know this backup format and get it options. */ - pv.name = ndmp_env_keywords[NDMP_ENV_KW_HIST]; - pv.value = ndmp_env_values[NDMP_ENV_VALUE_YES]; - ndma_store_env_list(&job->env_tab, &pv); + nbf_options = lookup_backup_format_options(job->bu_type); + + if (!nbf_options || nbf_options->uses_file_history) { + /* + * We want to receive file history info from the NDMP backup. + */ + pv.name = ndmp_env_keywords[NDMP_ENV_KW_HIST]; + pv.value = ndmp_env_values[NDMP_ENV_VALUE_YES]; + ndma_store_env_list(&job->env_tab, &pv); + } else { + /* + * We don't want to receive file history info from the NDMP backup. + */ + pv.name = ndmp_env_keywords[NDMP_ENV_KW_HIST]; + pv.value = ndmp_env_values[NDMP_ENV_VALUE_NO]; + ndma_store_env_list(&job->env_tab, &pv); + } /* * Tell the data agent what type of backup to make. @@ -424,10 +473,9 @@ static inline bool fill_backup_environment(JCR *jcr, ndma_store_env_list(&job->env_tab, &pv); /* - * See if we are doing a dump backup type and set any specific keywords - * for that backup type. + * See if we are doing a backup type that uses dumplevels. */ - if (bstrcasecmp(job->bu_type, "dump")) { + if (nbf_options && nbf_options->uses_level) { char text_level[50]; /* @@ -537,29 +585,73 @@ static inline bool fill_backup_environment(JCR *jcr, } /* - * Add a filename to the files we want to restore. Whe need - * to create both the original path and the destination patch - * of the file to restore in a ndmp9_name structure. + * Add a filename to the files we want to restore. + * + * The RFC says this: + * + * original_path - The original path name of the data to be recovered, + * relative to the backup root. If original_path is the null + * string, the server shall recover all data contained in the + * backup image. + * + * destination_path, name, other_name + * - Together, these identify the absolute path name to which + * data are to be recovered. + * + * If name is the null string: + * - destination_path identifies the name to which the data + * identified by original_path are to be recovered. + * - other_name must be the null string. + * + * If name is not the null string: + * - destination_path, when concatenated with the server- + * specific path name delimiter and name, identifies the + * name to which the data identified by original_path are + * to be recovered. + * + * If other_name is not the null string: + * - destination_path, when concatenated with the server- + * specific path name delimiter and other_name, + * identifies the alternate name-space name of the data + * to be recovered. The definition of such alternate + * name-space is server-specific. + * + * Neither name nor other_name may contain a path name delimiter. + * + * Under no circumstance may destination_path be the null string. + * + * If intermediate directories that lead to the path name to + * recover do not exist, the server should create them. */ static inline void add_to_namelist(struct ndm_job_param *job, char *filename, - char *restore_prefix) + char *restore_prefix, + char *name, + char *other_name, + int64_t node) { ndmp9_name nl; POOL_MEM destination_path; + memset(&nl, 0, sizeof(ndmp9_name)); + /* * See if the filename is an absolute pathname. */ - if (*filename == '/') { + if (*filename == '\0') { + pm_strcpy(destination_path, restore_prefix); + } else if (*filename == '/') { Mmsg(destination_path, "%s%s", restore_prefix, filename); } else { Mmsg(destination_path, "%s/%s", restore_prefix, filename); } - memset(&nl, 0, sizeof(ndmp9_name)); nl.original_path = filename; nl.destination_path = destination_path.c_str(); + nl.name = name; + nl.other_name = other_name; + nl.node = node; + ndma_store_nlist(&job->nlist_tab, &nl); } @@ -595,6 +687,25 @@ static inline char *lookup_fileindex(JCR *jcr, int32_t FileIndex) return NULL; } +/* + * Database handler that handles the returned environment data for a given JobId. + */ +static int ndmp_env_handler(void *ctx, int num_fields, char **row) +{ + struct ndm_env_table *envtab; + ndmp9_pval pv; + + if (row[0] && row[1]) { + envtab = (struct ndm_env_table *)ctx; + pv.name = row[0]; + pv.value = row[1]; + + ndma_store_env_list(envtab, &pv); + } + + return 0; +} + /* * Fill the NDMP restore environment table with the data for the data agent to act on. */ @@ -607,16 +718,17 @@ static inline bool fill_restore_environment(JCR *jcr, ndmp9_pval pv; FILESETRES *fileset; char *restore_pathname, - *restore_prefix; + *original_pathname, + *restore_prefix, + *level; POOL_MEM tape_device; POOL_MEM destination_path; + ndmp_backup_format_option *nbf_options; /* - * Tell the data agent what type of restore stream to expect. + * See if we know this backup format and get it options. */ - pv.name = ndmp_env_keywords[NDMP_ENV_KW_TYPE]; - pv.value = job->bu_type; - ndma_store_env_list(&job->env_tab, &pv); + nbf_options = lookup_backup_format_options(job->bu_type); /* * Lookup the current fileindex and map it to an actual pathname. @@ -631,14 +743,67 @@ static inline bool fill_restore_environment(JCR *jcr, */ if (!bstrncmp(restore_pathname, "/@NDMP/", 7)) { return false; + } else { + /* + * Skip over the /@NDMP prefix. + */ + original_pathname = restore_pathname + 6; } /* * See if there is a level embedded in the pathname. */ - bp = strrchr(restore_pathname, '%'); + bp = strrchr(original_pathname, '%'); if (bp) { - *bp = '\0'; + *bp++ = '\0'; + level = bp; + } else { + level = NULL; + } + + /* + * Lookup the environment stack saved during the backup so we can restore it. + */ + if (!db_get_ndmp_environment_string(jcr, jcr->db, &jcr->jr, + ndmp_env_handler, &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 + * the database when its either expired or when an old NDMP backup is restored + * where the whole environment was not saved. + */ + + if (!nbf_options || nbf_options->uses_file_history) { + /* + * We asked during the NDMP backup to receive file history info. + */ + pv.name = ndmp_env_keywords[NDMP_ENV_KW_HIST]; + pv.value = ndmp_env_values[NDMP_ENV_VALUE_YES]; + ndma_store_env_list(&job->env_tab, &pv); + } + + /* + * Tell the data agent what type of restore stream to expect. + */ + pv.name = ndmp_env_keywords[NDMP_ENV_KW_TYPE]; + pv.value = job->bu_type; + ndma_store_env_list(&job->env_tab, &pv); + + /* + * Tell the data agent that this is a NDMP backup which uses a level indicator. + */ + if (level) { + pv.name = ndmp_env_keywords[NDMP_ENV_KW_LEVEL]; + pv.value = level; + ndma_store_env_list(&job->env_tab, &pv); + } + + /* + * Tell the data engine what was backuped. + */ + pv.name = ndmp_env_keywords[NDMP_ENV_KW_FILESYSTEM]; + pv.value = original_pathname; + ndma_store_env_list(&job->env_tab, &pv); } /* @@ -657,9 +822,9 @@ static inline bool fill_restore_environment(JCR *jcr, item = (char *)ie->name_list.get(j); /* - * See if the restore path matches. + * See if the original path matches. */ - if (bstrcasecmp(item, restore_pathname)) { + if (bstrcasecmp(item, original_pathname)) { int k, l; FOPTS *fo; @@ -694,26 +859,51 @@ static inline bool fill_restore_environment(JCR *jcr, /* * Tell the data engine where to restore. */ - if (strlen(restore_prefix) == 1 && *restore_prefix == '/') { - pm_strcpy(destination_path, restore_pathname + 6); + if (nbf_options && nbf_options->restore_prefix_relative) { + switch (*restore_prefix) { + case '^': + /* + * Use the restore_prefix as an absolute restore prefix. + * We skip the leading ^ that is the trigger for absolute restores. + */ + pm_strcpy(destination_path, restore_prefix + 1); + break; + default: + /* + * Use the restore_prefix as an relative restore prefix. + */ + if (strlen(restore_prefix) == 1 && *restore_prefix == '/') { + pm_strcpy(destination_path, original_pathname); + } else { + pm_strcpy(destination_path, restore_prefix); + pm_strcat(destination_path, original_pathname); + } + } } else { - pm_strcpy(destination_path, restore_prefix); - pm_strcat(destination_path, restore_pathname + 6); + if (strlen(restore_prefix) == 1 && *restore_prefix == '/') { + /* + * Use the original pathname as restore prefix. + */ + pm_strcpy(destination_path, original_pathname); + } else { + /* + * Use the restore_prefix as an absolute restore prefix. + */ + pm_strcpy(destination_path, restore_prefix); + } } + pv.name = ndmp_env_keywords[NDMP_ENV_KW_PREFIX]; pv.value = destination_path.c_str(); ndma_store_env_list(&job->env_tab, &pv); - /* - * The smtape NDMP backup type doesn't need a namelist to restore the - * data as it restores all data from the stream anyhow. - */ - if (!bstrcasecmp(job->bu_type, "smtape")) { + if (!nbf_options || nbf_options->needs_namelist) { /* * FIXME: For now we say we want to restore everything later on it would * be nice to only restore parts of the whole backup. */ - add_to_namelist(job, (char *)"/", destination_path.c_str()); + add_to_namelist(job, (char *)"", destination_path.c_str(), + (char *)"", (char *)"", NDMP_INVALID_U_QUAD); } /* @@ -895,6 +1085,13 @@ static inline bool extract_post_backup_stats(JCR *jcr, { bool retval = true; struct ndmmedia *me; + ndmp_backup_format_option *nbf_options; + struct ndm_env_entry *ndm_ee; + + /* + * See if we know this backup format and get it options. + */ + nbf_options = lookup_backup_format_options(sess->control_acb->job.bu_type); /* * See if an error was raised during the backup session. @@ -921,10 +1118,23 @@ static inline bool extract_post_backup_stats(JCR *jcr, */ jcr->JobBytes += sess->control_acb->job.bytes_written; + /* + * After a successfull backup we need to store all NDMP ENV variables + * for doing a successfull restore operation. + */ + ndm_ee = sess->control_acb->job.result_env_tab.head; + while (ndm_ee) { + if (!db_create_ndmp_environment_string(jcr, jcr->db, &jcr->jr, + ndm_ee->pval.name, ndm_ee->pval.value)) { + break; + } + ndm_ee = ndm_ee->next; + } + /* * If this was a NDMP backup with backup type dump save the last used dump level. */ - if (bstrcasecmp(sess->control_acb->job.bu_type, "dump")) { + if (nbf_options && nbf_options->uses_level) { db_update_ndmp_level_mapping(jcr, jcr->db, &jcr->jr, filesystem, sess->control_acb->job.bu_level); } @@ -1352,6 +1562,7 @@ bool do_ndmp_backup(JCR *jcr) /* * See if there were any errors during the backup. */ + jcr->jr.FileIndex = cnt + 1; if (!extract_post_backup_stats(jcr, item, &ndmp_sess)) { goto cleanup; } @@ -1761,6 +1972,15 @@ static inline bool do_ndmp_restore_bootstrap(JCR *jcr) /* * Copy the actual job to perform. */ + jcr->jr.FileIndex = current_fi; + if (bsr->sessid && bsr->sesstime) { + jcr->jr.VolSessionId = bsr->sessid->sessid; + jcr->jr.VolSessionTime = bsr->sesstime->sesstime; + } else { + Jmsg(jcr, M_FATAL, 0, _("Wrong BSR missing sessid and/or sesstime\n")); + goto cleanup_ndmp; + } + memcpy(&ndmp_sess.control_acb->job, &ndmp_job, sizeof(struct ndm_job_param)); if (!fill_restore_environment(jcr, current_fi, diff --git a/src/dird/restore.c b/src/dird/restore.c index f06366389e6..ce861fed632 100644 --- a/src/dird/restore.c +++ b/src/dird/restore.c @@ -531,7 +531,37 @@ void generate_restore_summary(JCR *jcr, int msg_type, const char *term_msg) jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg)); jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg)); - Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n" + switch (jcr->getJobProtocol()) { + case PT_NDMP: + Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n" +" Build OS: %s %s %s\n" +" JobId: %d\n" +" Job: %s\n" +" Restore Client: %s\n" +" Start time: %s\n" +" End time: %s\n" +" Files Expected: %s\n" +" Files Restored: %s\n" +" Bytes Restored: %s\n" +" Rate: %.1f KB/s\n" +" SD termination status: %s\n" +" Termination: %s\n\n"), + BAREOS, my_name, VERSION, LSMDATE, + HOST_OS, DISTNAME, DISTVER, + jcr->jr.JobId, + jcr->jr.Job, + jcr->res.client->name(), + sdt, + edt, + edit_uint64_with_commas((uint64_t)jcr->ExpectedFiles, ec1), + edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec2), + edit_uint64_with_commas(jcr->jr.JobBytes, ec3), + (float)kbps, + sd_term_msg, + term_msg); + break; + default: + Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n" " Build OS: %s %s %s\n" " JobId: %d\n" " Job: %s\n" @@ -546,19 +576,21 @@ void generate_restore_summary(JCR *jcr, int msg_type, const char *term_msg) " FD termination status: %s\n" " SD termination status: %s\n" " Termination: %s\n\n"), - BAREOS, my_name, VERSION, LSMDATE, - HOST_OS, DISTNAME, DISTVER, - jcr->jr.JobId, - jcr->jr.Job, - jcr->res.client->name(), - sdt, - edt, - edit_uint64_with_commas((uint64_t)jcr->ExpectedFiles, ec1), - edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec2), - edit_uint64_with_commas(jcr->jr.JobBytes, ec3), - (float)kbps, - jcr->JobErrors, - fd_term_msg, - sd_term_msg, - term_msg); + BAREOS, my_name, VERSION, LSMDATE, + HOST_OS, DISTNAME, DISTVER, + jcr->jr.JobId, + jcr->jr.Job, + jcr->res.client->name(), + sdt, + edt, + edit_uint64_with_commas((uint64_t)jcr->ExpectedFiles, ec1), + edit_uint64_with_commas((uint64_t)jcr->jr.JobFiles, ec2), + edit_uint64_with_commas(jcr->jr.JobBytes, ec3), + (float)kbps, + jcr->JobErrors, + fd_term_msg, + sd_term_msg, + term_msg); + break; + } } diff --git a/src/dird/ua_label.c b/src/dird/ua_label.c index e793809a383..941efdc5489 100644 --- a/src/dird/ua_label.c +++ b/src/dird/ua_label.c @@ -128,8 +128,18 @@ static int do_label(UAContext *ua, const char *cmd, bool relabel) case APT_NDMPV2: case APT_NDMPV3: case APT_NDMPV4: - ua->warning_msg(_("Storage has non-native protocol.\n")); - return 1; + /* + * See if the user selected a NDMP storage device but its + * handled by a native Bareos storage daemon e.g. we have + * a paired_storage pointer. + */ + if (store.store->paired_storage) { + store.store = store.store->paired_storage; + } else { + ua->warning_msg(_("Storage has non-native protocol.\n")); + return 1; + } + break; default: break; } diff --git a/src/dird/ua_purge.c b/src/dird/ua_purge.c index 06509846361..9c09f35f3a1 100644 --- a/src/dird/ua_purge.c +++ b/src/dird/ua_purge.c @@ -479,6 +479,10 @@ void purge_jobs_from_catalog(UAContext *ua, char *jobs) db_sql_query(ua->db, query.c_str()); Dmsg1(050, "Delete PathVisibility sql=%s\n", query.c_str()); + Mmsg(query, "DELETE FROM NDMPJobEnvironment WHERE JobId IN (%s)", jobs); + db_sql_query(ua->db, query.c_str()); + Dmsg1(050, "Delete NDMPJobEnvironment sql=%s\n", query.c_str()); + upgrade_copies(ua, jobs); /* Now remove the Job record itself */ diff --git a/src/ndmp/ndma_comm_dispatch.c b/src/ndmp/ndma_comm_dispatch.c index 82d253896c4..88bfad79e79 100644 --- a/src/ndmp/ndma_comm_dispatch.c +++ b/src/ndmp/ndma_comm_dispatch.c @@ -2951,6 +2951,7 @@ ndmp2_sxa_log_log (struct ndm_session *sess, { char prefix[32]; char * tag; + char * bp; int lev; xa->reply.flags |= NDMNMB_FLAG_NO_SEND; @@ -2961,6 +2962,11 @@ ndmp2_sxa_log_log (struct ndm_session *sess, sprintf (prefix, "%cLM%s", ref_conn->chan.name[1], tag); + bp = strrchr(request->entry, '\n'); + if (bp) { + *bp = '\0'; + } + ndmalogf (sess, prefix, lev, "LOG_LOG: '%s'", request->entry); NDMS_ENDWITH @@ -2976,6 +2982,7 @@ ndmp2_sxa_log_debug (struct ndm_session *sess, { char prefix[32]; char * tag; + char * bp; int lev; xa->reply.flags |= NDMNMB_FLAG_NO_SEND; @@ -2986,6 +2993,11 @@ ndmp2_sxa_log_debug (struct ndm_session *sess, sprintf (prefix, "%cLM%s", ref_conn->chan.name[1], tag); + bp = strrchr(request->message, '\n'); + if (bp) { + *bp = '\0'; + } + ndmalogf (sess, prefix, lev, "LOG_DEBUG: '%s'", request->message); NDMS_ENDWITH @@ -3006,6 +3018,7 @@ ndmp_sxa_log_message (struct ndm_session *sess, { char prefix[32]; char * tag; + char * bp; int lev; xa->reply.flags |= NDMNMB_FLAG_NO_SEND; @@ -3040,6 +3053,11 @@ ndmp_sxa_log_message (struct ndm_session *sess, sprintf (prefix, "%cLM%s", ref_conn->chan.name[1], tag); + bp = strrchr(request->entry, '\n'); + if (bp) { + *bp = '\0'; + } + ndmalogf (sess, prefix, lev, "LOG_MESSAGE: '%s'", request->entry); NDMS_ENDWITH diff --git a/src/ndmp/ndma_comm_session.c b/src/ndmp/ndma_comm_session.c index 477dd60f8f1..a2a77aa0667 100644 --- a/src/ndmp/ndma_comm_session.c +++ b/src/ndmp/ndma_comm_session.c @@ -60,12 +60,12 @@ ndma_client_session (struct ndm_session *sess, struct ndm_job_param *job, int sw sess->robot_agent_enabled = 1; /* - * Old behavious enable session snooping + * Old behaviour enable session snooping */ sess->conn_snooping = 1; /* - * Old behavious enable media info dumping. + * Old behaviour enable media info dumping. */ sess->dump_media_info = 1; @@ -114,12 +114,12 @@ ndma_server_session (struct ndm_session *sess, int control_sock) sess->robot_agent_enabled = 1; /* - * Old behavious enable session snooping + * Old behaviour enable session snooping */ sess->conn_snooping = 1; /* - * Old behavious enable media info dumping. + * Old behaviour enable media info dumping. */ sess->dump_media_info = 1; diff --git a/src/ndmp/ndma_cops_backreco.c b/src/ndmp/ndma_cops_backreco.c index 592ac92a223..fbd30c924d8 100644 --- a/src/ndmp/ndma_cops_backreco.c +++ b/src/ndmp/ndma_cops_backreco.c @@ -554,8 +554,6 @@ ndmca_monitor_recover (struct ndm_session *sess) ndmalogf (sess, 0, 2, "Operation done, cleaning up"); - ndmca_monitor_get_post_backup_env (sess); - return 0; } diff --git a/src/ndmp/ndma_listmgmt.c b/src/ndmp/ndma_listmgmt.c index ff1978ce482..9419667eb91 100644 --- a/src/ndmp/ndma_listmgmt.c +++ b/src/ndmp/ndma_listmgmt.c @@ -121,6 +121,27 @@ ndma_store_env_list (struct ndm_env_table *envtab, ndmp9_pval *pv) return entry; } +/* + * Update an entry in an environment list table. + * If it doesn't exist add a new entry. + * Return entry if caller want to modify it. + */ +struct ndm_env_entry * +ndma_update_env_list (struct ndm_env_table *envtab, ndmp9_pval *pv) +{ + struct ndm_env_entry * entry; + + for (entry = envtab->head; entry; entry = entry->next) { + if (strcmp(entry->pval.name, pv->name) == 0) { + NDMOS_API_FREE (entry->pval.value); + entry->pval.value = NDMOS_API_STRDUP (pv->value); + return entry; + } + } + + return ndma_store_env_list (envtab, pv); +} + /* * Destroy an environment list table including any * enumerate buffers allocated for it. @@ -208,21 +229,27 @@ ndma_store_nlist (struct ndm_nlist_table *nlist, ndmp9_name *nl) entry = NDMOS_API_MALLOC (sizeof(struct ndm_nlist_entry)); if (!entry) - return NULL; + goto bail_out; + + NDMOS_MACRO_ZEROFILL (entry); entry->name.original_path = NDMOS_API_STRDUP (nl->original_path); - if (!entry->name.original_path) { - NDMOS_API_FREE (entry); - return NULL; - } + if (!entry->name.original_path) + goto bail_out; entry->name.destination_path = NDMOS_API_STRDUP (nl->destination_path); - if (!entry->name.destination_path) { - NDMOS_API_FREE (entry->name.original_path); - NDMOS_API_FREE (entry); - return NULL; - } + if (!entry->name.destination_path) + goto bail_out; + + entry->name.name = NDMOS_API_STRDUP (nl->name); + if (!entry->name.name) + goto bail_out; + + entry->name.other_name = NDMOS_API_STRDUP (nl->other_name); + if (!entry->name.other_name) + goto bail_out; + entry->name.node = nl->node; entry->name.fh_info = nl->fh_info; entry->result_err = NDMP9_UNDEFINED_ERR; entry->result_count = 0; @@ -239,6 +266,23 @@ ndma_store_nlist (struct ndm_nlist_table *nlist, ndmp9_name *nl) nlist->n_nlist++; return entry; + +bail_out: + if (entry->name.other_name) + NDMOS_API_FREE (entry->name.other_name); + + if (entry->name.name) + NDMOS_API_FREE (entry->name.name); + + if (entry->name.destination_path) + NDMOS_API_FREE (entry->name.destination_path); + + if (entry->name.original_path) + NDMOS_API_FREE (entry->name.original_path); + + NDMOS_API_FREE (entry); + + return NULL; } /* diff --git a/src/ndmp/ndmagents.h b/src/ndmp/ndmagents.h index b8843a1968d..41787111090 100644 --- a/src/ndmp/ndmagents.h +++ b/src/ndmp/ndmagents.h @@ -157,6 +157,7 @@ struct ndm_env_table { /* ndma_listmgt.c */ extern ndmp9_pval * ndma_enumerate_env_list (struct ndm_env_table *envtab); extern struct ndm_env_entry * ndma_store_env_list (struct ndm_env_table *envtab, ndmp9_pval *pv); +extern struct ndm_env_entry * ndma_update_env_list (struct ndm_env_table *envtab, ndmp9_pval *pv); extern void ndma_destroy_env_list (struct ndm_env_table *envtab); #ifndef NDM_MAX_NLIST diff --git a/src/ndmp/ndmjob_job.c b/src/ndmp/ndmjob_job.c index a168dbb4e70..57f7479f504 100644 --- a/src/ndmp/ndmjob_job.c +++ b/src/ndmp/ndmjob_job.c @@ -318,6 +318,9 @@ args_to_job_recover_nlist (void) nlist[i].original_path = file_arg[i]; nlist[i].destination_path = dest; + nlist[i].name = ""; + nlist[i].other_name = ""; + nlist[i].node = NDMP_INVALID_U_QUAD; } else { len = strlen (file_arg[i]) + prefix_len + 1; @@ -337,6 +340,9 @@ args_to_job_recover_nlist (void) nlist[i].original_path = file_arg[i]; nlist[i].destination_path = dest; + nlist[i].name = ""; + nlist[i].other_name = ""; + nlist[i].node = NDMP_INVALID_U_QUAD; } } diff --git a/src/ndmp/ndmp3_translate.c b/src/ndmp/ndmp3_translate.c index 230c65ca4d3..c1a4be7cf2d 100644 --- a/src/ndmp/ndmp3_translate.c +++ b/src/ndmp/ndmp3_translate.c @@ -2311,7 +2311,8 @@ ndmp_3to9_name ( } name9->destination_path = NDMOS_API_STRDUP(buf); - + name9->other_name = NDMOS_API_STRDUP (name3->other_name); + name9->node = name3->node; if (name3->fh_info != NDMP_INVALID_U_QUAD) { name9->fh_info.valid = NDMP9_VALIDITY_VALID; @@ -2358,8 +2359,8 @@ ndmp_9to3_name ( } } - name3->other_name = NDMOS_API_STRDUP (""); /* Yechy */ - + name3->other_name = NDMOS_API_STRDUP (name9->other_name); + name3->node = name9->node; if (name9->fh_info.valid == NDMP9_VALIDITY_VALID) { name3->fh_info = name9->fh_info.value; @@ -2367,8 +2368,6 @@ ndmp_9to3_name ( name3->fh_info = NDMP_INVALID_U_QUAD; } - name3->node = NDMP_INVALID_U_QUAD; - return 0; } diff --git a/src/ndmp/ndmp4_translate.c b/src/ndmp/ndmp4_translate.c index 980ee0deebf..70b84a46656 100644 --- a/src/ndmp/ndmp4_translate.c +++ b/src/ndmp/ndmp4_translate.c @@ -2299,15 +2299,12 @@ ndmp_4to9_name ( ndmp4_name *name4, ndmp9_name *name9) { - char buf[1024]; - name9->original_path = NDMOS_API_STRDUP(name4->original_path); - strcpy (buf, name4->destination_path); - if (name4->name && *name4->name) { - strcat (buf, "/"); - strcat (buf, name4->name); - } - name9->destination_path = NDMOS_API_STRDUP(buf); + name9->destination_path = NDMOS_API_STRDUP(name4->destination_path); + name9->name = NDMOS_API_STRDUP(name4->name); + name9->other_name = NDMOS_API_STRDUP(name4->other_name); + + name9->node = name4->node; if (name4->fh_info != NDMP_INVALID_U_QUAD) { name9->fh_info.valid = NDMP9_VALIDITY_VALID; name9->fh_info.value = name4->fh_info; @@ -2326,17 +2323,16 @@ ndmp_9to4_name ( { name4->original_path = NDMOS_API_STRDUP(name9->original_path); name4->destination_path = NDMOS_API_STRDUP(name9->destination_path); - name4->name = NDMOS_API_STRDUP(""); - name4->other_name = NDMOS_API_STRDUP(""); + name4->name = NDMOS_API_STRDUP(name9->name); + name4->other_name = NDMOS_API_STRDUP(name9->other_name); + name4->node = name9->node; if (name9->fh_info.valid == NDMP9_VALIDITY_VALID) { name4->fh_info = name9->fh_info.value; } else { name4->fh_info = NDMP_INVALID_U_QUAD; } - name4->node = NDMP_INVALID_U_QUAD; - return 0; } diff --git a/src/ndmp/ndmp9.x b/src/ndmp/ndmp9.x index e1c0f1d83d6..8c4296d0bf8 100644 --- a/src/ndmp/ndmp9.x +++ b/src/ndmp/ndmp9.x @@ -845,6 +845,9 @@ struct ndmp9_name { string original_path<>; /* relative to backup root */ string destination_path<>; /* nt_destination_path<> */ + string name<>; + string other_name<>; + ndmp9_u_quad node; ndmp9_valid_u_quad fh_info; }; diff --git a/src/stored/dir_cmd.c b/src/stored/dir_cmd.c index 2962fb07634..4cf301a3943 100644 --- a/src/stored/dir_cmd.c +++ b/src/stored/dir_cmd.c @@ -504,6 +504,24 @@ static bool cancel_cmd(JCR *cjcr) pthread_cond_broadcast(&wait_device_release); } + /* + * See if the Job has a certain protocol. + * When canceling a NDMP job make sure we call the end_of_ndmp_* functions. + */ + switch (jcr->getJobProtocol()) { + case PT_NDMP: + switch (jcr->getJobType()) { + case JT_BACKUP: + end_of_ndmp_backup(jcr); + break; + case JT_RESTORE: + end_of_ndmp_restore(jcr); + break; + default: + break; + } + } + pthread_cond_signal(&jcr->job_end_wait); /* wake waiting job */ dir->fsend(_("3000 JobId=%ld Job=\"%s\" marked to be %s.\n"), jcr->JobId, jcr->Job, reason); diff --git a/src/stored/ndmp_tape.c b/src/stored/ndmp_tape.c index db0cb80f981..672e0e68e4c 100644 --- a/src/stored/ndmp_tape.c +++ b/src/stored/ndmp_tape.c @@ -92,6 +92,37 @@ struct ndmp_log_cookie { JCR *jcr; }; +struct ndmp_backup_format_option { + char *format; + bool uses_level; +}; + +static ndmp_backup_format_option ndmp_backup_format_options[] = { + { (char *)"dump", true }, + { (char *)"tar", false }, + { (char *)"smtape", false }, + { (char *)"zfs", true }, + { NULL, false } +}; + +static ndmp_backup_format_option *lookup_backup_format_options(const char *backup_format) +{ + int i = 0; + + while (ndmp_backup_format_options[i].format) { + if (bstrcasecmp(backup_format, ndmp_backup_format_options[i].format)) { + break; + } + i++; + } + + if (ndmp_backup_format_options[i].format) { + return &ndmp_backup_format_options[i]; + } + + return (ndmp_backup_format_option *)NULL; +} + /* Static globals */ static bool quit = false; static bool ndmp_initialized = false; @@ -517,6 +548,7 @@ extern "C" ndmp9_error bndmp_tape_open(struct ndm_session *sess, char *filesystem; struct ndmp_session_handle *handle; struct ndm_tape_agent *ta; + ndmp_backup_format_option *nbf_options; /* * The drive_name should be in the form @ @@ -576,6 +608,11 @@ extern "C" ndmp9_error bndmp_tape_open(struct ndm_session *sess, log_cookie->jcr = jcr; } + /* + * See if we know this backup format and get it options. + */ + nbf_options = lookup_backup_format_options(jcr->backup_format); + /* * Depending on the open mode select the right DCR. */ @@ -669,7 +706,7 @@ extern "C" ndmp9_error bndmp_tape_open(struct ndm_session *sess, * Create a virtual file name @NDMP/% or * @NDMP/ and save the attributes to the director. */ - if (bstrcasecmp(jcr->backup_format, "dump")) { + if (nbf_options && nbf_options->uses_level) { Mmsg(virtual_filename, "/@NDMP%s%%%d", filesystem, jcr->DumpLevel); } else { Mmsg(virtual_filename, "/@NDMP%s", filesystem); @@ -1070,7 +1107,12 @@ void end_of_ndmp_backup(JCR *jcr) /* * Release the device -- and send final Vol info to DIR and unlock it. */ - release_device(dcr); + if (jcr->acquired_storage) { + release_device(dcr); + jcr->acquired_storage = false; + } else { + dcr->unreserve_device(); + } jcr->sendJobStatus(); /* update director */ } @@ -1081,7 +1123,13 @@ void end_of_ndmp_restore(JCR *jcr) free_read_context(jcr->rctx); jcr->rctx = NULL; } - release_device(jcr->read_dcr); + + if (jcr->acquired_storage) { + release_device(jcr->read_dcr); + jcr->acquired_storage = false; + } else { + jcr->read_dcr->unreserve_device(); + } } extern "C" void *handle_ndmp_client_request(void *arg)