From b0abc65e10d2bdf4eb7970202430b46464809ca7 Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Sun, 2 May 2010 15:24:01 +0200 Subject: [PATCH] Fix RestoreObject for PostgreSQL --- bacula/src/cats/cats.h | 5 ++ bacula/src/cats/make_postgresql_tables.in | 2 +- bacula/src/cats/mysql.c | 32 ++++++++++++ bacula/src/cats/postgresql.c | 62 ++++++++++++++++++++++- bacula/src/cats/protos.h | 4 ++ bacula/src/cats/sql_create.c | 7 +-- bacula/src/cats/sqlite.c | 57 +++++++++++++++++++++ bacula/src/dird/fd_cmds.c | 34 +++++++++---- bacula/src/filed/job.c | 6 +++ 9 files changed, 190 insertions(+), 19 deletions(-) diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index 79581201d6..a33650ba69 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -149,6 +149,7 @@ struct B_DB { POOLMEM *path; /* Path only */ POOLMEM *esc_name; /* Escaped file name */ POOLMEM *esc_path; /* Escaped path name */ + POOLMEM *esc_obj; /* Escaped restore object */ int fnl; /* file name length */ int pnl; /* path name length */ }; @@ -270,6 +271,7 @@ struct B_DB { POOLMEM *path; /* Path only */ POOLMEM *esc_name; /* Escaped file name */ POOLMEM *esc_path; /* Escaped path name */ + POOLMEM *esc_obj; /* Escaped restore object */ int fnl; /* file name length */ int pnl; /* path name length */ }; @@ -374,6 +376,7 @@ struct B_DB { POOLMEM *path; /* Path only */ POOLMEM *esc_name; /* Escaped file name */ POOLMEM *esc_path; /* Escaped path name */ + POOLMEM *esc_obj; /* Escaped restore object */ int fnl; /* file name length */ int pnl; /* path name length */ }; @@ -478,6 +481,7 @@ struct B_DB { POOLMEM *path; /* Path only */ POOLMEM *esc_name; /* Escaped file name */ POOLMEM *esc_path; /* Escaped path name */ + unsigned char *esc_obj; /* Escaped restore object */ int fnl; /* file name length */ int pnl; /* path name length */ }; @@ -715,6 +719,7 @@ struct B_DB { POOLMEM *path; /* Path only */ POOLMEM *esc_name; /* Escaped file name */ POOLMEM *esc_path; /* Escaped path name */ + POOLMEM *esc_obj; /* Escaped restore object */ int fnl; /* file name length */ int pnl; /* path name length */ }; diff --git a/bacula/src/cats/make_postgresql_tables.in b/bacula/src/cats/make_postgresql_tables.in index 04e217f913..fd831d3774 100644 --- a/bacula/src/cats/make_postgresql_tables.in +++ b/bacula/src/cats/make_postgresql_tables.in @@ -78,7 +78,7 @@ CREATE INDEX file_jobid_idx on File (JobId); CREATE TABLE RestoreObject ( RestoreObjectId SERIAL NOT NULL, ObjectName TEXT NOT NULL, - RestoreObject TEXT NOT NULL, + RestoreObject BYTEA NOT NULL, PluginName TEXT NOT NULL, ObjectLength INTEGER DEFAULT 0, ObjectFullLength INTEGER DEFAULT 0, diff --git a/bacula/src/cats/mysql.c b/bacula/src/cats/mysql.c index 9e34826b3a..555bbf9cc0 100644 --- a/bacula/src/cats/mysql.c +++ b/bacula/src/cats/mysql.c @@ -125,6 +125,7 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char mdb->path = get_pool_memory(PM_FNAME); mdb->esc_name = get_pool_memory(PM_FNAME); mdb->esc_path = get_pool_memory(PM_FNAME); + mdb->esc_obj = get_pool_memory(PM_FNAME); mdb->allow_transactions = mult_db_connections; db_list->append(mdb); /* put db in list */ Dmsg3(100, "initdb ref=%d connected=%d db=%p\n", mdb->ref_count, @@ -253,6 +254,7 @@ db_close_database(JCR *jcr, B_DB *mdb) free_pool_memory(mdb->path); free_pool_memory(mdb->esc_name); free_pool_memory(mdb->esc_path); + free_pool_memory(mdb->esc_obj); if (mdb->db_name) { free(mdb->db_name); } @@ -315,6 +317,36 @@ int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index) return 1; } +/* + * Escape binary object so that MySQL is happy + * Memory is stored in B_DB struct, no need to free it + */ +char * +db_escape_object(JCR *jcr, B_DB *db, char *old, int len) +{ + mdb->esc_obj = check_pool_memory_size(mdb->esc_obj, len*2+1); + mysql_real_escape_string(mdb->db, mdb->esc_obj, old, len); + return mdb->esc_obj; +} + +/* + * Unescape binary object so that MySQL is happy + */ +void +db_unescape_object(JCR *jcr, B_DB *db, + char *from, int32_t expected_len, + POOLMEM **dest, int32_t *dest_len) +{ + if (!from) { + *dest[0] = 0; + *dest_len = 0; + return; + } + *dest = check_pool_memory_size(*dest, expected_len+1); + *dest_len = expected_len; + memcpy(*dest, from, expected_len); + (*dest)[expected_len]=0; +} /* * Escape strings so that MySQL is happy diff --git a/bacula/src/cats/postgresql.c b/bacula/src/cats/postgresql.c index f6919995cc..a86f4e39e1 100644 --- a/bacula/src/cats/postgresql.c +++ b/bacula/src/cats/postgresql.c @@ -126,7 +126,7 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char mdb->fname = get_pool_memory(PM_FNAME); mdb->path = get_pool_memory(PM_FNAME); mdb->esc_name = get_pool_memory(PM_FNAME); - mdb->esc_path = get_pool_memory(PM_FNAME); + mdb->esc_path = get_pool_memory(PM_FNAME); mdb->allow_transactions = mult_db_connections; db_list->append(mdb); /* put db in list */ V(mutex); @@ -290,6 +290,9 @@ db_close_database(JCR *jcr, B_DB *mdb) if (mdb->db_socket) { free(mdb->db_socket); } + if (mdb->esc_obj) { + PQfreemem(mdb->esc_obj); + } free(mdb); if (db_list->size() == 0) { delete db_list; @@ -327,6 +330,61 @@ int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index) return 1; } +/* + * Escape binary so that PostgreSQL is happy + * + */ +char * +db_escape_object(JCR *jcr, B_DB *mdb, char *old, int len) +{ + size_t new_len; + if (mdb->esc_obj) { + PQfreemem(mdb->esc_obj); + } + + mdb->esc_obj = PQescapeByteaConn(mdb->db, (unsigned const char *)old, + len, &new_len); + + if (!mdb->esc_obj) { + Jmsg(jcr, M_FATAL, 0, _("PQescapeByteaConn returned NULL.\n")); + } + + return (char *)mdb->esc_obj; +} + +/* + * Unescape binary object so that PostgreSQL is happy + * + */ +void +db_unescape_object(JCR *jcr, B_DB *mdb, + char *from, int32_t expected_len, + POOLMEM **dest, int32_t *dest_len) +{ + size_t new_len; + unsigned char *obj; + + if (!from) { + *dest[0] = 0; + *dest_len = 0; + return; + } + + obj = PQunescapeBytea((unsigned const char *)from, &new_len); + + if (!obj) { + Jmsg(jcr, M_FATAL, 0, _("PQunescapeByteaConn returned NULL.\n")); + } + + *dest_len = new_len; + *dest = check_pool_memory_size(*dest, new_len+1); + memcpy(*dest, obj, new_len); + (*dest)[new_len]=0; + + PQfreemem(obj); + + Dmsg1(000, "obj size: %d\n", *dest_len); +} /* * Escape strings so that PostgreSQL is happy @@ -653,7 +711,7 @@ static int my_postgresql_currval(B_DB *mdb, const char *table_name) bail_out: PQclear(result); - + return id; } diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index 35b8d84953..5e0610776b 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -51,6 +51,10 @@ int db_open_database(JCR *jcr, B_DB *db); void db_close_database(JCR *jcr, B_DB *db); bool db_open_batch_connexion(JCR *jcr, B_DB *mdb); void db_escape_string(JCR *jcr, B_DB *db, char *snew, char *old, int len); +char *db_escape_object(JCR *jcr, B_DB *db, char *old, int len); +void db_unescape_object(JCR *jcr, B_DB *db, + char *from, int32_t expected_len, + POOLMEM **dest, int32_t *len); char *db_strerror(B_DB *mdb); int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index); bool db_sql_query(B_DB *mdb, const char *cmd, DB_RESULT_HANDLER *result_handler, void *ctx); diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index 1367056d04..b13e991b1c 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -1229,7 +1229,6 @@ bool db_create_restore_object_record(JCR *jcr, B_DB *mdb, ROBJECT_DBR *ro) { bool stat; int plug_name_len; - POOLMEM *esc_obj = get_pool_memory(PM_MESSAGE); POOLMEM *esc_plug_name = get_pool_memory(PM_MESSAGE); db_lock(mdb); @@ -1241,8 +1240,7 @@ bool db_create_restore_object_record(JCR *jcr, B_DB *mdb, ROBJECT_DBR *ro) mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1); db_escape_string(jcr, mdb, mdb->esc_name, ro->object_name, mdb->fnl); - esc_obj = check_pool_memory_size(esc_obj, ro->object_len*2+1); - db_escape_string(jcr, mdb, esc_obj, ro->object, ro->object_len); + db_escape_object(jcr, mdb, ro->object, ro->object_len); plug_name_len = strlen(ro->plugin_name); esc_plug_name = check_pool_memory_size(esc_plug_name, plug_name_len*2+1); @@ -1253,7 +1251,7 @@ bool db_create_restore_object_record(JCR *jcr, B_DB *mdb, ROBJECT_DBR *ro) "ObjectLength,ObjectFullLength,ObjectIndex,ObjectType," "ObjectCompression,FileIndex,JobId) " "VALUES ('%s','%s','%s',%d,%d,%d,%d,%d,%d,%u)", - mdb->esc_name, esc_plug_name, esc_obj, + mdb->esc_name, esc_plug_name, mdb->esc_obj, ro->object_len, ro->object_full_len, ro->object_index, FT_RESTORE_FIRST, ro->object_compression, ro->FileIndex, ro->JobId); @@ -1267,7 +1265,6 @@ bool db_create_restore_object_record(JCR *jcr, B_DB *mdb, ROBJECT_DBR *ro) stat = true; } db_unlock(mdb); - free_pool_memory(esc_obj); free_pool_memory(esc_plug_name); return stat; } diff --git a/bacula/src/cats/sqlite.c b/bacula/src/cats/sqlite.c index 0a8619a309..c90195a35c 100644 --- a/bacula/src/cats/sqlite.c +++ b/bacula/src/cats/sqlite.c @@ -131,6 +131,7 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char mdb->path = get_pool_memory(PM_FNAME); mdb->esc_name = get_pool_memory(PM_FNAME); mdb->esc_path = get_pool_memory(PM_FNAME); + mdb->esc_obj = get_pool_memory(PM_FNAME); mdb->allow_transactions = mult_db_connections; db_list->append(mdb); V(mutex); @@ -258,6 +259,7 @@ db_close_database(JCR *jcr, B_DB *mdb) free_pool_memory(mdb->path); free_pool_memory(mdb->esc_name); free_pool_memory(mdb->esc_path); + free_pool_memory(mdb->esc_obj); if (mdb->db_name) { free(mdb->db_name); } @@ -299,6 +301,61 @@ int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index) return 1; } +/* + * Escape binary object so that SQLite is happy + * Memory is stored in B_DB struct, no need to free it + * + * TODO: this should be implemented (escape \0) + */ +char * +db_escape_object(JCR *jcr, B_DB *db, char *old, int len) +{ + char *n, *o; + + n = mdb->esc_obj = check_pool_memory_size(mdb->esc_obj, len*2+1); + o = old; + while (len--) { + switch (*o) { + case '\'': + *n++ = '\''; + *n++ = '\''; + o++; + break; + case 0: + *n++ = '\\'; + *n++ = 0; + o++; + break; + default: + *n++ = *o++; + break; + } + } + *n = 0; + return mdb->esc_obj; +} + +/* + * Unescape binary object so that SQLIte is happy + * Memory is stored in B_DB struct, no need to free it + * + * TODO: need to be implemented (escape \0) + */ +void +db_unescape_object(JCR *jcr, B_DB *db, + char *from, int32_t expected_len, + POOLMEM **dest, int32_t *dest_len) +{ + if (!from) { + *dest[0] = 0; + *dest_len = 0; + return; + } + *dest = check_pool_memory_size(*dest, expected_len+1); + *dest_len = expected_len; + memcpy(*dest, from, expected_len); + (*dest)[expected_len]=0; +} /* * Escape strings so that SQLite is happy diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index 56085ccd29..56f2f8fb9c 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -672,7 +672,6 @@ static int restore_object_handler(void *ctx, int num_fields, char **row) { JCR *jcr = (JCR *)ctx; BSOCK *fd; - POOLMEM *msg_save; fd = jcr->file_bsock; if (jcr->is_job_canceled()) { @@ -683,17 +682,27 @@ static int restore_object_handler(void *ctx, int num_fields, char **row) Dmsg1(000, "Send obj hdr=%s", fd->msg); - msg_save = fd->msg; - fd->msg = row[7] ? row[7] : (char *)""; - fd->msglen = strlen(fd->msg); + fd->msglen = pm_strcpy(fd->msg, row[7]); fd->send(); /* send Object name */ + Dmsg1(000, "Send obj: %s\n", fd->msg); - fd->msg = row[8] ? row[8] : (char *)""; /* object */ - fd->msglen = str_to_uint64(row[1]); /* object length */ +// fd->msglen = str_to_uint64(row[1]); /* object length */ +// Dmsg1(000, "obj size: %lld\n", (uint64_t)fd->msglen); + + /* object */ + db_unescape_object(jcr, jcr->db, + row[8], /* Object */ + str_to_uint64(row[1]), /* Object length */ + &fd->msg, &fd->msglen); fd->send(); /* send object */ -// Dmsg1(000, "Send obj: %s\n", fd->msg); - fd->msg = msg_save; + + if (debug_level) { + for (int i=0; i < fd->msglen; i++) + if (!fd->msg[i]) + fd->msg[i] = ' '; + Dmsg1(000, "Send obj: %s\n", fd->msg); + } return 0; } @@ -707,9 +716,12 @@ bool send_restore_objects(JCR *jcr) if (!jcr->JobIds || !jcr->JobIds[0]) { return true; } - Mmsg(query, "SELECT JobId,ObjectLength,ObjectFullLength,ObjectIndex,ObjectType," - "ObjectCompression,FileIndex,ObjectName,RestoreObject FROM RestoreObject " - "WHERE JobId IN (%s) ORDER BY ObjectIndex ASC", jcr->JobIds); + Mmsg(query, "SELECT JobId,ObjectLength,ObjectFullLength,ObjectIndex," + "ObjectType,ObjectCompression,FileIndex,ObjectName," + "RestoreObject " + "FROM RestoreObject " + "WHERE JobId IN (%s) " + "ORDER BY ObjectIndex ASC", jcr->JobIds); /* restore_object_handler is called for each file found */ db_sql_query(jcr->db, query.c_str(), restore_object_handler, (void *)jcr); diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index f1421ceafd..8cac931472 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -2083,6 +2083,7 @@ static int restore_cmd(JCR *jcr) if (jcr->VSS) { if (g_pVSSClient->InitializeForRestore(jcr, vss_restore_init_callback, (WCHAR *)jcr->job_metadata)) { + /* inform user about writer states */ int i; for (i=0; i < (int)g_pVSSClient->GetWriterCount(); i++) { @@ -2097,6 +2098,11 @@ static int restore_cmd(JCR *jcr) } } } else { +/* + int fd = open("C:\\eric.xml", O_CREAT | O_WRONLY | O_TRUNC, 0777); + write(fd, (WCHAR *)jcr->job_metadata, wcslen((WCHAR *)jcr->job_metadata) * sizeof(WCHAR)); + close(fd); +*/ berrno be; Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled. ERR=%s\n"), be.bstrerror()); } -- 2.39.5