From 3d909a18d08c23b3c6e65bff08670632ee24347a Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Wed, 4 Jan 2012 14:08:30 +0100 Subject: [PATCH] Add new features in plugin - Options {} plugin - PreInclude event - Estimate interface - RestoreObject for configuration --- bacula/src/filed/authenticate.c | 4 +- bacula/src/filed/backup.c | 64 +++++-- bacula/src/filed/estimate.c | 3 +- bacula/src/filed/fd_plugins.c | 320 ++++++++++++++++++++++++++++---- bacula/src/filed/fd_plugins.h | 32 ++-- bacula/src/filed/job.c | 94 ++++++---- bacula/src/filed/restore.c | 13 +- bacula/src/filed/verify.c | 1 + bacula/src/filetypes.h | 5 + 9 files changed, 431 insertions(+), 105 deletions(-) diff --git a/bacula/src/filed/authenticate.c b/bacula/src/filed/authenticate.c index 2b32541df9..e9fc51988d 100644 --- a/bacula/src/filed/authenticate.c +++ b/bacula/src/filed/authenticate.c @@ -42,8 +42,10 @@ const int dbglvl = 50; * 1 10Mar08 * 2 13Mar09 - added the ability to restore from multiple storages * 3 03Sep10 - added the restore object command for vss plugin 4.0 + * 4 25Nov10 - added bandwidth command 5.1 + * 5 24Nov11 - added new restore object command format (pluginname) 6.0 */ -static char OK_hello[] = "2000 OK Hello 3\n"; +static char OK_hello[] = "2000 OK Hello 5\n"; static char Dir_sorry[] = "2999 Authentication failed.\n"; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index c4c782a30b..c43660b264 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -330,6 +330,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) { bool do_read = false; bool plugin_started = false; + bool do_plugin_set = false; int stat, data_stream; int rtnstat = 0; DIGEST *digest = NULL; @@ -337,6 +338,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) int digest_stream = STREAM_NONE; SIGNATURE *sig = NULL; bool has_file_data = false; + struct save_pkt sp; /* use by option plugin */ // TODO landonf: Allow the user to specify the digest algorithm #ifdef HAVE_SHA2 crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA256; @@ -369,6 +371,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_RESTORE_FIRST: Dmsg1(100, "FT_RESTORE_FIRST saving: %s\n", ff_pkt->fname); break; + case FT_PLUGIN_CONFIG: + Dmsg1(100, "FT_PLUGIN_CONFIG saving: %s\n", ff_pkt->fname); + break; case FT_DIRBEGIN: jcr->num_files_examined--; /* correct file count */ return 1; /* not used */ @@ -523,7 +528,32 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) if (ff_pkt->flags & FO_PORTABLE) { set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */ } + if (ff_pkt->cmd_plugin) { + do_plugin_set = true; + + /* option and cmd plugin are not compatible together */ + } else if (ff_pkt->opt_plugin) { + + /* ask the option plugin what to do with this file */ + switch (plugin_option_handle_file(jcr, ff_pkt, &sp)) { + case bRC_OK: + Dmsg2(10, "Option plugin %s will be used to backup %s\n", + ff_pkt->plugin, ff_pkt->fname); + do_plugin_set = true; + break; + case bRC_Skip: + Dmsg2(10, "Option plugin %s decided to skip %s\n", + ff_pkt->plugin, ff_pkt->fname); + goto good_rtn; + default: + Dmsg2(10, "Option plugin %s decided to let bacula handle %s\n", + ff_pkt->plugin, ff_pkt->fname); + break; + } + } + + if (do_plugin_set) { /* Tell bfile that it needs to call plugin */ if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) { goto bail_out; @@ -537,7 +567,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) goto bail_out; } /** Meta data only for restore object */ - if (ff_pkt->type == FT_RESTORE_FIRST) { + if (IS_FT_OBJECT(ff_pkt->type)) { goto good_rtn; } @@ -662,7 +692,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) } /** - * Save ACLs when requested and available for anything not being a symlink and not being a plugin. + * Save ACLs when requested and available for anything not being a symlink + * and not being a plugin. */ if (have_acl) { if (ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK && !ff_pkt->cmd_plugin) { @@ -671,8 +702,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) goto bail_out; case bacl_exit_error: /** - * Non-fatal errors, count them and when the number is under ACL_REPORT_ERR_MAX_PER_JOB - * print the error message set by the lower level routine in jcr->errmsg. + * Non-fatal errors, count them and when the number is under + * ACL_REPORT_ERR_MAX_PER_JOB print the error message set by the + * lower level routine in jcr->errmsg. */ if (jcr->acl_data->nr_errors < ACL_REPORT_ERR_MAX_PER_JOB) { Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); @@ -686,7 +718,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) } /** - * Save Extended Attributes when requested and available for all files not being a plugin. + * Save Extended Attributes when requested and available for all files not + * being a plugin. */ if (have_xattr) { if (ff_pkt->flags & FO_XATTR && !ff_pkt->cmd_plugin) { @@ -695,8 +728,9 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) goto bail_out; case bxattr_exit_error: /** - * Non-fatal errors, count them and when the number is under XATTR_REPORT_ERR_MAX_PER_JOB - * print the error message set by the lower level routine in jcr->errmsg. + * Non-fatal errors, count them and when the number is under + * XATTR_REPORT_ERR_MAX_PER_JOB print the error message set by the + * lower level routine in jcr->errmsg. */ if (jcr->xattr_data->nr_errors < XATTR_REPORT_ERR_MAX_PER_JOB) { Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg); @@ -795,12 +829,18 @@ good_rtn: rtnstat = jcr->is_canceled() ? 0 : 1; /* good return if not canceled */ bail_out: - if (jcr->is_incomplete()) { + if (jcr->is_incomplete() || jcr->is_canceled()) { rtnstat = 0; } - if (ff_pkt->cmd_plugin && plugin_started) { + if (plugin_started) { send_plugin_name(jcr, sd, false); /* signal end of plugin data */ } + if (ff_pkt->opt_plugin) { + jcr->plugin_sp = NULL; /* sp is local to this function */ + jcr->plugin_ctx = NULL; + jcr->plugin = NULL; + jcr->opt_plugin = false; + } if (digest) { crypto_digest_free(digest); } @@ -1237,7 +1277,7 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) encode_stat(attribs, &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, data_stream); /** Now possibly extend the attributes */ - if (ff_pkt->type == FT_RESTORE_FIRST) { + if (IS_FT_OBJECT(ff_pkt->type)) { attr_stream = STREAM_RESTORE_OBJECT; } else { attribsEx = attribsExBuf; @@ -1315,6 +1355,7 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; + case FT_PLUGIN_CONFIG: case FT_RESTORE_FIRST: comp_len = ff_pkt->object_len; ff_pkt->object_compression = 0; @@ -1322,7 +1363,8 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) /* Big object, compress it */ comp_len = ff_pkt->object_len + 1000; POOLMEM *comp_obj = get_memory(comp_len); - stat = Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len); + /* *** FIXME *** check Zdeflate error */ + Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len); if (comp_len < ff_pkt->object_len) { ff_pkt->object = comp_obj; ff_pkt->object_compression = 1; /* zlib level 9 compression */ diff --git a/bacula/src/filed/estimate.c b/bacula/src/filed/estimate.c index 3f9779444b..c48a6f1a0b 100644 --- a/bacula/src/filed/estimate.c +++ b/bacula/src/filed/estimate.c @@ -55,8 +55,7 @@ int make_estimate(JCR *jcr) set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file); } - stat = find_files(jcr, (FF_PKT *)jcr->ff, tally_file, NULL); - + stat = find_files(jcr, (FF_PKT *)jcr->ff, tally_file, plugin_estimate); accurate_free(jcr); return stat; } diff --git a/bacula/src/filed/fd_plugins.c b/bacula/src/filed/fd_plugins.c index 978b10b99c..22649d6375 100644 --- a/bacula/src/filed/fd_plugins.c +++ b/bacula/src/filed/fd_plugins.c @@ -76,6 +76,7 @@ static bRC baculaAddRegex(bpContext *ctx, const char *item, int type); static bRC baculaAddWild(bpContext *ctx, const char *item, int type); static bRC baculaNewOptions(bpContext *ctx); static bRC baculaNewInclude(bpContext *ctx); +static bRC baculaNewPreInclude(bpContext *ctx); static bool is_plugin_compatible(Plugin *plugin); static bool get_plugin_name(JCR *jcr, char *cmd, int *ret); static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp); @@ -116,6 +117,7 @@ static bFuncs bfuncs = { baculaAddWild, baculaNewOptions, baculaNewInclude, + baculaNewPreInclude, baculaCheckChanges }; @@ -137,6 +139,9 @@ static bool is_plugin_disabled(bpContext *plugin_ctx) return true; } b_ctx = (bacula_ctx *)plugin_ctx->bContext; + if (!b_ctx) { + return true; + } return b_ctx->disabled; } @@ -146,8 +151,8 @@ static bool is_plugin_disabled(JCR *jcr) } /** - * Create a plugin event - * When receiving bEventCancelCommand, this function is called by an other thread. + * Create a plugin event When receiving bEventCancelCommand, this function is + * called by an other thread. */ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { @@ -158,6 +163,7 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) char *name = NULL; int len = 0; bool call_if_canceled = false; + restore_object_pkt *rop; bRC rc; if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) { @@ -170,25 +176,40 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) */ switch(eventType) { case bEventPluginCommand: + case bEventOptionPlugin: name = (char *)value; if (!get_plugin_name(jcr, name, &len)) { return; } + break; + case bEventRestoreObject: + /* After all RestoreObject, we have it one more time with value=NULL */ + if (value) { + /* Some RestoreObjects may not have a plugin name */ + rop = (restore_object_pkt *)value; + if (*rop->plugin_name) { + name = rop->plugin_name; + get_plugin_name(jcr, name, &len); + } + } + break; case bEventEndBackupJob: case bEventEndVerifyJob: call_if_canceled = true; break; case bEventStartRestoreJob: - if (jcr->plugin) { - jcr->plugin->restoreFileStarted = false; - jcr->plugin->createFileCalled = false; + foreach_alist(plugin, bplugin_list) { + plugin->restoreFileStarted = false; + plugin->createFileCalled = false; } break; case bEventEndRestoreJob: call_if_canceled = true; if (jcr->plugin && jcr->plugin->restoreFileStarted) { plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx); + } + if (jcr->plugin) { jcr->plugin->restoreFileStarted = false; jcr->plugin->createFileCalled = false; } @@ -216,7 +237,7 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) continue; } plugin_ctx = &plugin_ctx_list[i++]; - if (!plugin_ctx || is_plugin_disabled(plugin_ctx)) { + if (is_plugin_disabled(plugin_ctx)) { continue; } rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value); @@ -300,6 +321,89 @@ static bool get_plugin_name(JCR *jcr, char *cmd, int *ret) return true; } + +static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp) +{ + ff_pkt->delta_seq = sp->delta_seq; + if (sp->flags & FO_DELTA) { + ff_pkt->flags |= FO_DELTA; + ff_pkt->delta_seq++; /* make new delta sequence number */ + } else { + ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */ + ff_pkt->delta_seq = 0; + } + + if (sp->flags & FO_OFFSETS) { + ff_pkt->flags |= FO_OFFSETS; + } + if (sp->flags & FO_PORTABLE_DATA) { + ff_pkt->flags |= FO_PORTABLE_DATA; + } + ff_pkt->flags |= FO_PLUGIN; /* data from plugin */ +} + +/* Ask to a Option Plugin what to do with the current file */ +bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp) +{ + Plugin *plugin; + bRC ret = bRC_Error; + char *cmd = ff_pkt->plugin; + int len; + int i=0; + bEvent event; + event.eventType = bEventHandleBackupFile; + + bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list; + memset(sp, 0, sizeof(struct save_pkt)); + sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt); + sp->portable = true; + sp->cmd = cmd; + sp->link = ff_pkt->link; + sp->cmd = ff_pkt->plugin; + sp->statp = ff_pkt->statp; + sp->fname = ff_pkt->fname; + sp->delta_seq = ff_pkt->delta_seq; + + if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) { + Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd); + goto bail_out; /* Return if no plugins loaded */ + } + + if (!get_plugin_name(jcr, cmd, &len)) { + goto bail_out; + } + + /* Note, we stop the loop on the first plugin that matches the name */ + foreach_alist(plugin, bplugin_list) { + Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len); + if (!for_this_plug(plugin, cmd, len)) { + i++; + continue; + } + + if (is_plugin_disabled(&plugin_ctx_list[i])) { + goto bail_out; + } + + ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], + &event, sp); + + /* TODO: would be better to set this in save_file() */ + if (ret == bRC_OK) { + jcr->opt_plugin = true; + jcr->plugin = plugin; + jcr->plugin_sp = sp; /* Unset sp in save_file */ + jcr->plugin_ctx = &plugin_ctx_list[i++]; + + update_ff_pkt(ff_pkt, sp); + } + + goto bail_out; + } /* end foreach loop */ +bail_out: + return ret; +} + /** * Sequence of calls for a backup: * 1. plugin_save() here is called with ff_pkt @@ -388,7 +492,7 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level) * avoids zaping the plugin's strings. */ ff_pkt->type = sp.type; - if (sp.type == FT_RESTORE_FIRST) { + if (IS_FT_OBJECT(sp.type)) { if (!sp.object_name) { Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"), cmd); @@ -408,23 +512,11 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level) } pm_strcpy(fname, sp.fname); pm_strcpy(link, sp.link); + + ff_pkt->fname = fname.c_str(); ff_pkt->link = link.c_str(); - ff_pkt->delta_seq = sp.delta_seq; - if (sp.flags & FO_DELTA) { - ff_pkt->flags |= FO_DELTA; - ff_pkt->delta_seq++; /* make new delta sequence number */ - } else { - ff_pkt->flags &= ~FO_DELTA; /* clean delta sequence number */ - ff_pkt->delta_seq = 0; - } - if (sp.flags & FO_OFFSETS) { - ff_pkt->flags |= FO_OFFSETS; - } - if (sp.flags & FO_PORTABLE_DATA) { - ff_pkt->flags |= FO_PORTABLE_DATA; - } - ff_pkt->flags |= FO_PLUGIN; /* data from plugin */ + update_ff_pkt(ff_pkt, &sp); } memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp)); @@ -454,6 +546,134 @@ bail_out: return 1; } + +/** + * Sequence of calls for a estimate: + * 1. plugin_estimate() here is called with ff_pkt + * 2. we find the plugin requested on the command string + * 3. we generate a bEventEstimateCommand event to the specified plugin + * and pass it the command string. + * 4. we make a startPluginBackup call to the plugin, which gives + * us the data we need in save_pkt + * + */ +int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level) +{ + Plugin *plugin; + int i = 0; + int len; + char *cmd = ff_pkt->top_fname; + struct save_pkt sp; + bEvent event; + POOL_MEM fname(PM_FNAME); + POOL_MEM link(PM_FNAME); + ATTR attr; + + if (!bplugin_list || !jcr->plugin_ctx_list) { + Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd); + return 1; /* Return if no plugins loaded */ + } + + jcr->cmd_plugin = true; + bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list; + event.eventType = bEventEstimateCommand; + + if (!get_plugin_name(jcr, cmd, &len)) { + goto bail_out; + } + + /* Note, we stop the loop on the first plugin that matches the name */ + foreach_alist(plugin, bplugin_list) { + Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len); + if (!for_this_plug(plugin, cmd, len)) { + i++; + continue; + } + /* + * We put the current plugin pointer, and the plugin context + * into the jcr, because during save_file(), the plugin + * will be called many times and these values are needed. + */ + jcr->plugin_ctx = &plugin_ctx_list[i]; + jcr->plugin = plugin; + if (is_plugin_disabled(jcr)) { + goto bail_out; + } + + Dmsg1(dbglvl, "Command plugin = %s\n", cmd); + /* Send the backup command to the right plugin*/ + if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) { + goto bail_out; + } + /* Loop getting filenames to backup then saving them */ + while (!jcr->is_job_canceled()) { + memset(&sp, 0, sizeof(sp)); + sp.pkt_size = sizeof(sp); + sp.pkt_end = sizeof(sp); + sp.portable = true; + sp.flags = 0; + sp.cmd = cmd; + Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks, + &sp); + /* Get the file save parameters. I.e. the stat pkt ... */ + if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) { + goto bail_out; + } + if (sp.type == 0) { + Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"), + cmd); + goto bail_out; + } + + if (!IS_FT_OBJECT(sp.type)) { + if (!sp.fname) { + Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"), + cmd); + goto bail_out; + } + + jcr->num_files_examined++; + jcr->JobFiles++; /* increment number of files seen */ + + if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) { + if (sp.statp.st_size > 0) { + jcr->JobBytes += sp.statp.st_size; + } + } + + if (jcr->listing) { + memcpy(&attr.statp, &sp.statp, sizeof(struct stat)); + attr.type = sp.type; + attr.ofname = (POOLMEM *)sp.fname; + attr.olname = (POOLMEM *)sp.link; + print_ls_output(jcr, &attr); + } + } + + Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname); + if (sp.object) { + Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object); + } + bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx); + if (rc == bRC_More || rc == bRC_OK) { + accurate_mark_file_as_seen(jcr, sp.fname); + } + if (rc == bRC_More) { + continue; + } + goto bail_out; + } /* end while loop */ + goto bail_out; + } /* end loop over all plugins */ + Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd); + +bail_out: + jcr->cmd_plugin = false; + jcr->plugin = NULL; + jcr->plugin_ctx = NULL; + return 1; +} + /** * Send plugin name start/end record to SD */ @@ -511,7 +731,7 @@ bool plugin_name_stream(JCR *jcr, char *name) { char *p = name; char *cmd; - bool start, portable; + bool start; Plugin *plugin; int len; int i = 0; @@ -525,7 +745,7 @@ bool plugin_name_stream(JCR *jcr, char *name) /* Start of plugin data */ skip_nonspaces(&p); /* skip start/end flag */ skip_spaces(&p); - portable = *p == '1'; +// portable = *p == '1'; skip_nonspaces(&p); /* skip portable flag */ skip_spaces(&p); cmd = p; @@ -536,6 +756,8 @@ bool plugin_name_stream(JCR *jcr, char *name) Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx); if (jcr->plugin && jcr->plugin->restoreFileStarted) { plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx); + } + if (jcr->plugin) { jcr->plugin->restoreFileStarted = false; jcr->plugin->createFileCalled = false; } @@ -569,19 +791,20 @@ bool plugin_name_stream(JCR *jcr, char *name) if (is_plugin_disabled(jcr)) { goto bail_out; } - Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd); + Dmsg1(000, "Restore Command plugin = %s\n", cmd); event.eventType = bEventRestoreCommand; if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) { goto bail_out; } - if (plugin->restoreFileStarted) - { - Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile\n"); + if (plugin->restoreFileStarted) { + Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd); + plugin->restoreFileStarted = false; goto bail_out; } - plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd); - plugin->restoreFileStarted = true; + if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) { + plugin->restoreFileStarted = true; + } goto bail_out; } Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd); @@ -634,9 +857,9 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) if (rp.attrEx) { Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx); } - if (!plugin->restoreFileStarted || plugin->createFileCalled) - { + if (!plugin->restoreFileStarted || plugin->createFileCalled) { Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n"); + plugin->createFileCalled = false; return CF_ERROR; } rc = plug_func(plugin)->createFile(plugin_ctx, &rp); @@ -645,6 +868,9 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) rc, attr->ofname); return CF_ERROR; } + if (rp.create_status == CF_CORE) { + return CF_CORE; /* Let Bacula core handle the file creation */ + } if (rp.create_status == CF_SKIP) { return CF_SKIP; } @@ -790,9 +1016,12 @@ static bool is_plugin_compatible(Plugin *plugin) return false; } if (info->size != sizeof(pInfo)) { - Jmsg(NULL, M_ERROR, 0, _("Plugin size mismatch.\n")); + Jmsg(NULL, M_ERROR, 0, + _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"), + plugin->file, sizeof(pInfo), info->size); return false; } + return true; } @@ -811,7 +1040,7 @@ void new_plugins(JCR *jcr) Dmsg0(dbglvl, "plugin list is NULL\n"); return; } - if (jcr->is_job_canceled() || jcr->JobId == 0) { + if (jcr->is_job_canceled()) { return; } @@ -1270,9 +1499,12 @@ static bRC baculaAddInclude(bpContext *ctx, const char *file) if (!file) { return bRC_Error; } - if (!bctx->include) { - bctx->include = new_preinclude(jcr); - new_options(jcr, bctx->include); + /* Not right time to add include */ + if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) { + return bRC_Error; + } + if (!bctx->include) { + bctx->include = jcr->ff->fileset->incexe; } set_incexe(jcr, bctx->include); add_file_to_fileset(jcr, file, true); @@ -1347,6 +1579,20 @@ static bRC baculaNewInclude(bpContext *ctx) return bRC_OK; } +static bRC baculaNewPreInclude(bpContext *ctx) +{ + JCR *jcr; + bacula_ctx *bctx; + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + + bctx->include = new_preinclude(jcr); + new_options(jcr, bctx->include); + set_incexe(jcr, bctx->include); + + return bRC_OK; +} /* * Check if a file have to be backuped using Accurate code diff --git a/bacula/src/filed/fd_plugins.h b/bacula/src/filed/fd_plugins.h index f5c80678dd..0d00eb335f 100644 --- a/bacula/src/filed/fd_plugins.h +++ b/bacula/src/filed/fd_plugins.h @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2007-2010 Free Software Foundation Europe e.V. + Copyright (C) 2007-2011 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. @@ -63,6 +63,7 @@ #endif #endif +#include "../src/version.h" #include "bc_types.h" #include "lib/plugins.h" #include @@ -79,6 +80,7 @@ struct restore_object_pkt { int32_t pkt_size; /* size of this packet */ char *object_name; /* Object name */ char *object; /* restore object data to save */ + char *plugin_name; /* Plugin name */ int32_t object_type; /* FT_xx for this file */ int32_t object_len; /* restore object length */ int32_t object_full_len; /* restore object uncompressed length */ @@ -198,23 +200,26 @@ typedef enum { bEventEndVerifyJob = 8, bEventBackupCommand = 9, bEventRestoreCommand = 10, - bEventLevel = 11, - bEventSince = 12, - bEventCancelCommand = 13, /* Executed by another thread */ - bEventVssBackupAddComponents = 14, /* Just before bEventVssPrepareSnapshot */ - bEventVssRestoreLoadComponentMetadata = 15, - bEventVssRestoreSetComponentsSelected = 16, - bEventRestoreObject = 17, - bEventEndFileSet = 18, - bEventPluginCommand = 19, /* Sent during FileSet creation */ - bEventVssBeforeCloseRestore = 20, + bEventEstimateCommand = 11, + bEventLevel = 12, + bEventSince = 13, + bEventCancelCommand = 14, /* Executed by another thread */ + bEventVssBackupAddComponents = 15, /* Just before bEventVssPrepareSnapshot */ + bEventVssRestoreLoadComponentMetadata = 16, + bEventVssRestoreSetComponentsSelected = 17, + bEventRestoreObject = 18, + bEventEndFileSet = 19, + bEventPluginCommand = 20, /* Sent during FileSet creation */ + bEventVssBeforeCloseRestore = 21, /* Add drives to VSS snapshot * argument: char[27] drivelist * You need to add them without duplicates, * see fd_common.h add_drive() copy_drives() to get help */ - bEventVssPrepareSnapshot = 21 + bEventVssPrepareSnapshot = 22, + bEventOptionPlugin = 23, + bEventHandleBackupFile = 24 /* Used with Options Plugin */ } bEventType; typedef struct s_bEvent { @@ -239,7 +244,9 @@ bool plugin_name_stream(JCR *jcr, char *name); int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace); bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd); int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level); +int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level); bool plugin_check_file(JCR *jcr, char *fname); +bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp); #endif #ifdef __cplusplus @@ -270,6 +277,7 @@ typedef struct s_baculaFuncs { bRC (*AddWild)(bpContext *ctx, const char *item, int type); bRC (*NewOptions)(bpContext *ctx); bRC (*NewInclude)(bpContext *ctx); + bRC (*NewPreInclude)(bpContext *ctx); bRC (*checkChanges)(bpContext *ctx, struct save_pkt *sp); } bFuncs; diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 44fdb315e9..e4086dc4fe 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -146,7 +146,8 @@ static char sessioncmd[] = "session %127s %ld %ld %ld %ld %ld %ld\n"; static char restorecmd[] = "restore replace=%c prelinks=%d where=%s\n"; static char restorecmd1[] = "restore replace=%c prelinks=%d where=\n"; static char restorecmdR[] = "restore replace=%c prelinks=%d regexwhere=%s\n"; -static char restoreobjcmd[] = "restoreobject JobId=%u %d,%d,%d,%d,%d,%d\n"; +static char restoreobjcmd[] = "restoreobject JobId=%u %d,%d,%d,%d,%d,%d,%s"; +static char restoreobjcmd1[] = "restoreobject JobId=%u %d,%d,%d,%d,%d,%d\n"; static char endrestoreobjectcmd[] = "restoreobject end\n"; static char verifycmd[] = "verify level=%30s"; static char estimatecmd[] = "estimate listing=%d"; @@ -338,6 +339,9 @@ void *handle_client_request(void *dirp) findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i); for (j=0; jopts_list.size(); j++) { findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j); + if (fo->plugin) { + free(fo->plugin); + } for (k=0; kregex.size(); k++) { regfree((regex_t *)fo->regex.get(k)); } @@ -694,24 +698,38 @@ static int restore_object_cmd(JCR *jcr) memset(&rop, 0, sizeof(rop)); rop.pkt_size = sizeof(rop); rop.pkt_end = sizeof(rop); + Dmsg1(100, "Enter restoreobject_cmd: %s", dir->msg); if (strcmp(dir->msg, endrestoreobjectcmd) == 0) { generate_plugin_event(jcr, bEventRestoreObject, NULL); return dir->fsend(OKRestoreObject); } + rop.plugin_name = (char *) malloc (dir->msglen); + *rop.plugin_name = 0; + if (sscanf(dir->msg, restoreobjcmd, &rop.JobId, &rop.object_len, &rop.object_full_len, &rop.object_index, - &rop.object_type, &rop.object_compression, &FileIndex) != 7) { - Dmsg0(5, "Bad restore object command\n"); - pm_strcpy(jcr->errmsg, dir->msg); - Jmsg1(jcr, M_FATAL, 0, _("Bad RestoreObject command: %s\n"), jcr->errmsg); - goto bail_out; + &rop.object_type, &rop.object_compression, &FileIndex, + rop.plugin_name) != 8) { + + /* Old version, no plugin_name */ + if (sscanf(dir->msg, restoreobjcmd1, &rop.JobId, &rop.object_len, + &rop.object_full_len, &rop.object_index, + &rop.object_type, &rop.object_compression, &FileIndex) != 7) { + Dmsg0(5, "Bad restore object command\n"); + pm_strcpy(jcr->errmsg, dir->msg); + Jmsg1(jcr, M_FATAL, 0, _("Bad RestoreObject command: %s\n"), jcr->errmsg); + goto bail_out; + } } - Dmsg6(100, "Recv object: JobId=%u objlen=%d full_len=%d objinx=%d objtype=%d FI=%d\n", + unbash_spaces(rop.plugin_name); + + Dmsg7(100, "Recv object: JobId=%u objlen=%d full_len=%d objinx=%d objtype=%d " + "FI=%d plugin_name=%s\n", rop.JobId, rop.object_len, rop.object_full_len, - rop.object_index, rop.object_type, FileIndex); + rop.object_index, rop.object_type, FileIndex, rop.plugin_name); /* Read Object name */ if (dir->recv() < 0) { goto bail_out; @@ -744,16 +762,15 @@ static int restore_object_cmd(JCR *jcr) rop.object_len = out_len; } Dmsg2(100, "Recv Object: len=%d Object=%s\n", rop.object_len, rop.object); - /* Special Job meta data */ + /* we still need to do this to detect a vss restore */ if (strcmp(rop.object_name, "job_metadata.xml") == 0) { Dmsg0(100, "got job metadata\n"); - free_and_null_pool_memory(jcr->job_metadata); - jcr->job_metadata = rop.object; - rop.object = NULL; - } else { - /* pass to plugin */ - generate_plugin_event(jcr, bEventRestoreObject, (void *)&rop); + //free_and_null_pool_memory(jcr->job_metadata); + jcr->job_metadata = rop.object; /* this is like a boolean in the restore case */ + // rop.object = NULL; /* but not this */ } + + generate_plugin_event(jcr, bEventRestoreObject, (void *)&rop); if (rop.object_name) { free(rop.object_name); @@ -761,6 +778,9 @@ static int restore_object_cmd(JCR *jcr) if (rop.object) { free_pool_memory(rop.object); } + if (rop.plugin_name) { + free(rop.plugin_name); + } Dmsg1(100, "Send: %s", OKRestoreObject); return 1; @@ -1149,6 +1169,11 @@ static void add_fileset(JCR *jcr, const char *item) // current_opts->writer = bstrdup(item); /* deprecated */ state = state_options; break; + case 'G': /* Plugin command for this Option block */ + current_opts = start_options(ff); + current_opts->plugin = bstrdup(item); + state = state_options; + break; default: Jmsg(jcr, M_FATAL, 0, _("Invalid FileSet command: %s\n"), item); state = state_error; @@ -1875,8 +1900,7 @@ static int backup_cmd(JCR *jcr) if (get_win32_driveletters(jcr->ff, szWinDriveLetters)) { Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=\"%s\", Drive(s)=\"%s\"\n"), g_pVSSClient->GetDriverName(), szWinDriveLetters); if (!g_pVSSClient->CreateSnapshots(szWinDriveLetters)) { - berrno be; - Jmsg(jcr, M_FATAL, 0, _("Generate VSS snapshots failed. ERR=%s\n"), be.bstrerror()); + Jmsg(jcr, M_FATAL, 0, _("CreateSGenerate VSS snapshots failed.\n")); } else { /* tell user if snapshot creation of a specific drive failed */ int i; @@ -1896,8 +1920,7 @@ static int backup_cmd(JCR *jcr) Jmsg(jcr, M_FATAL, 0, _("No drive letters found for generating VSS snapshots.\n")); } } else { - berrno be; - Jmsg(jcr, M_FATAL, 0, _("VSS was not initialized properly. ERR=%s\n"), be.bstrerror()); + Jmsg(jcr, M_FATAL, 0, _("VSS was not initialized properly.\n")); } run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS"); } @@ -2046,6 +2069,7 @@ static int verify_cmd(JCR *jcr) return 0; /* return and terminate command loop */ } +#if 0 #ifdef WIN32_VSS static bool vss_restore_init_callback(JCR *jcr, int init_type) { @@ -2062,6 +2086,7 @@ static bool vss_restore_init_callback(JCR *jcr, int init_type) } } #endif +#endif /** * Do a Restore for Director @@ -2087,6 +2112,7 @@ static int restore_cmd(JCR *jcr) * data to restore */ enable_vss = jcr->job_metadata != NULL; + jcr->job_metadata = NULL; Dmsg2(50, "g_pVSSClient = %p, enable_vss = %d\n", g_pVSSClient, enable_vss); // capture state here, if client is backed up by multiple directors @@ -2121,6 +2147,11 @@ static int restore_cmd(JCR *jcr) Dmsg2(150, "Got replace %c, where=%s\n", replace, args); unbash_spaces(args); + /* Keep track of newly created directories to apply them correct attributes */ + if (replace == REPLACE_NEVER) { + jcr->keep_path_list = true; + } + if (use_regexwhere) { jcr->where_bregexp = get_bregexps(args); if (!jcr->where_bregexp) { @@ -2160,27 +2191,11 @@ static int restore_cmd(JCR *jcr) #if defined(WIN32_VSS) /* START VSS ON WIN32 */ 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++) { - if (g_pVSSClient->GetWriterState(i) < 1) { - Jmsg(jcr, M_INFO, 0, _("VSS Writer (PreRestore): %s\n"), g_pVSSClient->GetWriterInfo(i)); - //jcr->JobErrors++; - } - } - } 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); -*/ + if (!g_pVSSClient->InitializeForRestore(jcr)) { berrno be; Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled. ERR=%s\n"), be.bstrerror()); } - free_and_null_pool_memory(jcr->job_metadata); + //free_and_null_pool_memory(jcr->job_metadata); run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS"); } #endif @@ -2209,10 +2224,13 @@ static int restore_cmd(JCR *jcr) /* tell vss to close the restore session */ Dmsg0(100, "About to call CloseRestore\n"); if (jcr->VSS) { +#if 0 generate_plugin_event(jcr, bEventVssBeforeCloseRestore); +#endif Dmsg0(100, "Really about to call CloseRestore\n"); if (g_pVSSClient->CloseRestore()) { Dmsg0(100, "CloseRestore success\n"); +#if 0 /* inform user about writer states */ for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) { int msg_type = M_INFO; @@ -2222,6 +2240,7 @@ static int restore_cmd(JCR *jcr) } Jmsg(jcr, msg_type, 0, _("VSS Writer (RestoreComplete): %s\n"), g_pVSSClient->GetWriterInfo(i)); } +#endif } else Dmsg1(100, "CloseRestore fail - %08x\n", errno); @@ -2330,6 +2349,7 @@ static void filed_free_jcr(JCR *jcr) } free_runscripts(jcr->RunScripts); delete jcr->RunScripts; + free_path_list(jcr); if (jcr->JobId != 0) write_state_file(me->working_directory, "bacula-fd", get_first_port_host_order(me->FDaddrs)); diff --git a/bacula/src/filed/restore.c b/bacula/src/filed/restore.c index 4310c3be21..a8b9486d15 100644 --- a/bacula/src/filed/restore.c +++ b/bacula/src/filed/restore.c @@ -340,7 +340,7 @@ void do_restore(JCR *jcr) * Restore objects should be ignored here -- they are * returned at the beginning of the restore. */ - if (rctx.type == FT_RESTORE_FIRST) { + if (IS_FT_OBJECT(rctx.type)) { continue; } @@ -374,9 +374,13 @@ void do_restore(JCR *jcr) */ jcr->num_files_examined++; rctx.extract = false; + stat = CF_CORE; /* By default, let Bacula's core handle it */ + if (jcr->plugin) { stat = plugin_create_file(jcr, attr, &rctx.bfd, jcr->replace); - } else { + } + + if (stat == CF_CORE) { stat = create_file(jcr, attr, &rctx.bfd, jcr->replace); } jcr->lock(); @@ -387,8 +391,7 @@ void do_restore(JCR *jcr) switch (stat) { case CF_ERROR: case CF_SKIP: - pm_strcpy(jcr->last_fname, attr->ofname); - jcr->last_type = attr->type; + jcr->JobFiles++; break; case CF_EXTRACT: /* @@ -415,7 +418,7 @@ void do_restore(JCR *jcr) } /* - * Count the resource forks not as regular files being restored. + * Do not count the resource forks as regular files being restored. */ if (rsrc_len == 0) { jcr->JobFiles++; diff --git a/bacula/src/filed/verify.c b/bacula/src/filed/verify.c index 8992d7dd0b..8219ab3d5f 100644 --- a/bacula/src/filed/verify.c +++ b/bacula/src/filed/verify.c @@ -156,6 +156,7 @@ static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_NOFSCHG: Jmsg(jcr, M_SKIPPED, 1, _(" File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname); return 1; + case FT_PLUGIN_CONFIG: case FT_RESTORE_FIRST: return 1; /* silently skip */ case FT_NOOPEN: { diff --git a/bacula/src/filetypes.h b/bacula/src/filetypes.h index d7bf3b7a33..ea79966221 100644 --- a/bacula/src/filetypes.h +++ b/bacula/src/filetypes.h @@ -76,8 +76,13 @@ #define FT_BASE 24 /* Duplicate base file entry */ #define FT_RESTORE_FIRST 25 /* Restore this "object" first */ #define FT_JUNCTION 26 /* Win32 Junction point */ +#define FT_PLUGIN_CONFIG 27 /* Object for Plugin configuration */ +#define FT_PLUGIN_CONFIG_FILLED 28 /* Object for Plugin configuration filled by Director */ /* Definitions for upper part of type word (see above). */ #define AR_DATA_STREAM (1<<16) /* Data stream id present */ +/* Quick way to know if a Filetype is about a plugin "Object" */ +#define IS_FT_OBJECT(x) (((x) == FT_RESTORE_FIRST) || ((x) == FT_PLUGIN_CONFIG_FILLED) || ((x) == FT_PLUGIN_CONFIG)) + #endif /* __BFILETYPES_H */ -- 2.39.5