From: Eric Bollengier Date: Wed, 7 Sep 2022 14:44:09 +0000 (+0200) Subject: Add FileEvents features X-Git-Tag: Beta-15.0.0~449 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0f4cbc940af7e4ed623ed4294b8dc2939253eba4;p=thirdparty%2Fbacula.git Add FileEvents features --- diff --git a/bacula/src/cats/bdb.h b/bacula/src/cats/bdb.h index e640d60fb..4de12cdd0 100644 --- a/bacula/src/cats/bdb.h +++ b/bacula/src/cats/bdb.h @@ -216,6 +216,7 @@ public: bool bdb_create_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap); int bdb_create_file_record(JCR *jcr, ATTR_DBR *ar); bool bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar); + bool bdb_create_fileevent_record(JCR *jcr, FILEEVENT_DBR *event); /* sql_get.c */ char *bdb_get_jobids(const char *jobids, POOLMEM **ret, bool append); @@ -277,6 +278,7 @@ public: void bdb_list_fileevents_for_job(JCR *jcr, uint32_t jobid, char etype, DB_LIST_HANDLER sendit, void *ctx, e_list_type type); void bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); void bdb_list_jobmedia_records(JCR *jcr, JobId_t JobId, char *volume, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); + void bdb_list_fileevents_records(JCR *jcr, FILEEVENT_DBR *event, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); void bdb_list_filemedia_records(JCR *jcr, JobId_t JobId, uint32_t FileIndex, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); void bdb_list_joblog_records(JCR *jcr, JobId_t JobId, const char *pattern, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); int bdb_list_sql_query(JCR *jcr, const char *title, const char *query, DB_LIST_HANDLER *sendit, void *ctx, int verbose, e_list_type type); diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index 6de2842a9..aa13dca09 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -713,6 +713,35 @@ public: bool check(); /* check if valid */ }; +class FILEEVENT_DBR: public SMARTALLOC +{ +public: + DBId_t FileIndex; // FileIndex of the File + DBId_t JobId; // JobId of the file + DBId_t SourceJobId; // Verify/Restore JobId + char Type; // antivirus, malware, lost file + int Severity; // level of severity (0 OK, 100 Important) + char Description[MAX_NAME_LENGTH]; + char Source[MAX_NAME_LENGTH]; + FILEEVENT_DBR(): FileIndex(0), JobId(0), Type(0), + Severity(0) + { + *Description = *Source = 0; + }; + ~FILEEVENT_DBR() {}; + + bool unpack(uint32_t stream, const char *data, int data_len) { + SourceJobId = JobId = FileIndex = 0; + if (scan_string(data, "%c %d %127s %127s 0", &Type, &Severity, Source, Description) == 4) { + unbash_spaces(Source); + unbash_spaces(Description); + return true; + } + Dmsg1(50, "Got incorrect stream for FileEvent %s\n", data); + return false; + }; +}; + /* Call back context for getting a 32/64 bit value from the database */ class db_int64_ctx { public: diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index 2243eabfb..68675d7a7 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -164,7 +164,8 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr); bdb_disable_batch_insert(disable) #define db_create_snapshot_record(jcr, mdb, sr) \ mdb->bdb_create_snapshot_record(jcr, sr) - +#define db_create_fileevent_record(jcr, mdb, ev) \ + mdb->bdb_create_fileevent_record(jcr, ev) /* sql_delete.c */ #define db_delete_pool_record(jcr, mdb, pool_dbr) \ @@ -296,6 +297,8 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr); mdb->bdb_list_jobmedia_records(jcr, JobId, volume, sendit, ctx, type) #define db_list_filemedia_records(jcr, mdb, JobId, FI, sendit, ctx, type) \ mdb->bdb_list_filemedia_records(jcr, JobId, FI, sendit, ctx, type) +#define db_list_fileevents_records(jcr, mdb, event, sendit, ctx, type) \ + mdb->bdb_list_fileevents_records(jcr, event, sendit, ctx, type) #define db_list_joblog_records(jcr, mdb, JobId, pattern, sendit, ctx, type) \ mdb->bdb_list_joblog_records(jcr, JobId, pattern, sendit, ctx, type) #define db_list_sql_query(jcr, mdb, title, query, sendit, ctx, verbose, type) \ diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index 3a4caaa87..48a8d76d6 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -1460,4 +1460,21 @@ bool BDB::bdb_create_tag_record(JCR *jcr, TAG_DBR *tag) return ret; } +bool BDB::bdb_create_fileevent_record(JCR *jcr, FILEEVENT_DBR *event) +{ + char esc1[MAX_ESCAPE_NAME_LENGTH]; + char esc2[MAX_ESCAPE_NAME_LENGTH]; + bool ret=false; + + bdb_lock(); + bdb_escape_string(jcr, esc1, event->Description, strlen(event->Description)); + bdb_escape_string(jcr, esc2, event->Source, strlen(event->Source)); + Mmsg(cmd, "INSERT INTO FileEvents (SourceJobId, JobId, FileIndex, Type, Description, Severity, Source) " + " VALUES (%lu, %lu, %ld, '%c', '%s', %d, '%s')", + event->SourceJobId, event->JobId, event->FileIndex, event->Type, esc1, event->Severity, esc2); + ret = bdb_sql_query(cmd, NULL, (void *)NULL); + bdb_unlock(); + return ret; +} + #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */ diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index d0ac2cf77..561a239ad 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -645,6 +645,53 @@ void BDB::bdb_list_filemedia_records(JCR *jcr, uint32_t JobId, uint32_t FileInde bdb_unlock(); } +/* List FileEvents records for a given job/file */ +void BDB::bdb_list_fileevents_records(JCR *jcr, FILEEVENT_DBR *rec, + DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) +{ + POOL_MEM tmp, filter; + char ed1[50]; + + Mmsg(filter, "FileEvents.JobId=%s ", edit_int64(rec->JobId, ed1)); + + if (rec->FileIndex > 0) { + Mmsg(tmp, "AND FileEvents.FileIndex=%s ", edit_int64(rec->FileIndex, ed1)); + pm_strcat(filter, tmp.c_str()); + } + + if (B_ISALPHA(rec->Type)) { + Mmsg(tmp, "AND FileEvents.Type='%c' ", rec->Type); + pm_strcat(filter, tmp.c_str()); + } + + if (rec->Severity > 0) { + Mmsg(tmp, "AND FileEvents.Severity >= %d ", rec->Severity); + pm_strcat(filter, tmp.c_str()); + } + + bdb_lock(); + const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false); + const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)) : ""; + + if (type == VERT_LIST || type == JSON_LIST) { + Mmsg(cmd, "SELECT JobId,FileIndex,Path,Filename,Source,Severity,Type,Description " + "FROM FileEvents JOIN File USING (Jobid, FileIndex) JOIN Path USING (PathId) %s WHERE " + "%s %s ORDER BY JobId, FileIndex ASC", join, filter.c_str(), where); + } else { + Mmsg(cmd, "SELECT JobId,Path,Filename,Severity,Type,Description " + "FROM FileEvents JOIN File USING (Jobid, FileIndex) JOIN Path USING (PathId) %s WHERE " + "%s %s ORDER BY JobId, FileIndex ASC", join, filter.c_str(), where); + } + if (!QueryDB(jcr, cmd)) { + bdb_unlock(); + return; + } + + list_result(jcr, this, "fileevents", sendit, ctx, type); + + sql_free_result(); + bdb_unlock(); +} void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index 1a38b2336..d3b14d710 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -49,6 +49,11 @@ static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s" " VolFirstWritten=%lld VolType=%u VolParts=%d VolCloudParts=%d" " LastPartBytes=%lld Enabled=%d Recycle=%d\n"; +static char FileEvent_add[] = "%c %d %127s %127s 0"; +/* Full format when coming from the Verify Job */ +static char FileEvent_fd_add[] = "CatReq JobId=%ld FileEvent %lu %lu %lu %c %d %127s %127s 0"; +static char FileEvent_fd_add1[] = "CatReq JobId=%ld FileEvent %lu"; + static char Create_jobmedia[] = "CatReq JobId=%ld CreateJobMedia\n"; static char Create_filemedia[] = "CatReq JobId=%ld CreateFileMedia\n"; @@ -121,6 +126,55 @@ static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr return stat; } +static bool catreq_fileevent(JCR *jcr, DBId_t FileIndex, const char *p) +{ + FILEEVENT_DBR event; + bool ok=false; + uint32_t t1, t2, t3, t4; + event.FileIndex = FileIndex; + + /* If the string is going through the attribute flow, we don't have the CatReq header */ + if (scan_string(p, FileEvent_add, &event.Type, &event.Severity, event.Source, event.Description) == 4) + { + event.JobId = jcr->JobId; // Might need to look for previous jobid for example + event.FileIndex = FileIndex; + ok = true; + + /* We get the VolSessionTime, VolSessionId and FileIndex in the protocol, but we keep it for later */ + } else if (scan_string(p, FileEvent_fd_add, &t1, &t2, &t3, &t4, + &event.Type, &event.Severity, event.Source, event.Description) == 8) + { + event.JobId = jcr->previous_jr.JobId; + ok = true; + } + if (ok) { + event.SourceJobId = jcr->JobId; + unbash_spaces(event.Source); + unbash_spaces(event.Description); + if (!db_create_fileevent_record(jcr, jcr->db, &event)) { + Jmsg(jcr, M_WARNING, 0, _("Catalog error creating FileEvent record. %s"), + db_strerror(jcr->db)); + } + return true; + + } else { + Dmsg1(50, "Error while decoding FileEvent %s\n", p); + } + return false; +} + +/* Can be Snapshot or FileEvent requests from the FD */ +void fd_catreq(JCR *jcr, BSOCK *bs) +{ + DBId_t FileIndex=0, xjobid; + if (scan_string(bs->msg, FileEvent_fd_add1, &xjobid, &FileIndex) == 2) { + catreq_fileevent(jcr, FileIndex, bs->msg); + + } else { + snapshot_catreq(jcr, bs); + } +} + void catalog_request(JCR *jcr, BSOCK *bs) { MEDIA_DBR mr, sdmr; @@ -697,6 +751,8 @@ static void update_attribute(JCR *jcr, char *msg, int32_t msglen) if (!db_create_restore_object_record(jcr, jcr->db, &ro)) { Jmsg1(jcr, M_FATAL, 0, _("Restore object create error. %s"), db_strerror(jcr->db)); } + } else if (Stream == STREAM_FILEEVENT) { + catreq_fileevent(jcr, FileIndex, p); } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) { fname = p; diff --git a/bacula/src/dird/getmsg.c b/bacula/src/dird/getmsg.c index a10c7443d..b831689ca 100644 --- a/bacula/src/dird/getmsg.c +++ b/bacula/src/dird/getmsg.c @@ -57,7 +57,6 @@ static char Device_update[] = "DevUpd JobId=%127s " "DevWriteBytes=%d\n"; #endif - static char OK_msg[] = "1000 OK\n"; @@ -268,9 +267,9 @@ int bget_dirmsg(JCR *jcr, BSOCK *bs, BSOCK_CLIENT_TYPE role) catalog_request(jcr, bs); continue; } - /* Only the Snapshot commands are authorized for the FD */ + /* Only the Snapshot and FileEvent commands are authorized for the FD */ if (role==BSOCK_TYPE_FD && bs->msg[0] == 'C') { - snapshot_catreq(jcr, bs); + fd_catreq(jcr, bs); continue; } if (role==BSOCK_TYPE_SD && bs->msg[0] == 'U') { /* SD sending attributes */ diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index 468e5e4e9..9987333b7 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -80,6 +80,7 @@ bool split_bsr_loop(JCR *jcr, bootstrap_info &info); /* catreq.c */ +extern void fd_catreq(JCR *jcr, BSOCK *bs); extern void catalog_request(JCR *jcr, BSOCK *bs); extern void catalog_update(JCR *jcr, BSOCK *bs); extern bool despool_attributes_from_file(JCR *jcr, const char *file); diff --git a/bacula/src/dird/ua_output.c b/bacula/src/dird/ua_output.c index 10eff1f5f..183415de6 100644 --- a/bacula/src/dird/ua_output.c +++ b/bacula/src/dird/ua_output.c @@ -337,6 +337,7 @@ bail_out: * list objects [type=objecttype job_id=id clientname=n,status=S] - list plugin objects * list pluginrestoreconf jobid=x,y,z [id=k] * list filemedia jobid=x fileindex=z + * list fileevents jobid=x fileindex=z * list metadata type=[email|attachment] tenant=xxx owner=xxx jobid= client= * from= * to= cc= tags= @@ -624,6 +625,27 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) } return 1; + } else if (strcasecmp(ua->argk[i], NT_("fileevents")) == 0) { + FILEEVENT_DBR event; + for (j=i+1; jargc; j++) { + if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) { + event.JobId = str_to_int64(ua->argv[j]); + + } else if (strcasecmp(ua->argk[j], NT_("fileindex")) == 0 && ua->argv[j]) { + event.FileIndex = str_to_int64(ua->argv[j]); + + } else if (strcasecmp(ua->argk[j], NT_("severity")) == 0 && ua->argv[j]) { + event.Severity = str_to_int64(ua->argv[j]); + + } else if (strcasecmp(ua->argk[j], NT_("Type")) == 0 && ua->argv[j]) { + event.Type = ua->argv[j][0]; + } + } + if (event.JobId) { + db_list_fileevents_records(ua->jcr, ua->db, &event, prtit, ua, llist); + } + return 1; + /* List JOBLOG */ } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) { bool done = false; diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index 755d3c0fb..27f1f07b0 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -308,6 +308,41 @@ bail_out: return stat; } + +/* + * Send the fileevent_pkt to the SD for the record + * Returns: true if OK + * false if error + */ +bool fileevent_save(JCR *jcr, fileevent_pkt *fevent) +{ + bool stat = false; + BSOCK *sd = jcr->store_bsock; + + Dmsg1(50, "Sending STREAM_FILEEVENT %d\n", jcr->JobFiles); + stat = sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_FILEEVENT); + if (!stat) { + goto bail_out; + } + + bash_spaces(fevent->Description); + bash_spaces(fevent->Source); + stat = sd->fsend("%c %d %s %s 0", fevent->Type, fevent->Severity, fevent->Source, fevent->Description); + if (!stat) { + goto bail_out; + } + sd->signal(BNET_EOD); /* indicate end of attributes data */ + +bail_out: + if (!stat) { + if (!jcr->is_canceled() && !jcr->is_incomplete()) { + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), + sd->bstrerror()); + } + } + return stat; +} + /** * Called here by find() for each file included. * This is a callback. The original is find_files() above. @@ -644,6 +679,17 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) goto bail_out; } + /* + * Handle FileEvent that might have been generated during the backup + * of the file. + */ + struct fileevent_pkt *fevent; + foreach_alist(fevent, jcr->fileevents) { + if (!fileevent_save(jcr, fevent)) { + break; + } + } + good_rtn: rtnstat = 1; diff --git a/bacula/src/filed/fd_plugins.c b/bacula/src/filed/fd_plugins.c index a229f728f..d9d36cf0d 100644 --- a/bacula/src/filed/fd_plugins.c +++ b/bacula/src/filed/fd_plugins.c @@ -77,6 +77,7 @@ static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp); static bRC baculaAccurateAttribs(bpContext *ctx, accurate_attribs_pkt *att); static void plugin_register_verify_data(bpContext *ctx); static bRC baculaAddPlugin(bpContext *ctx, const char *file); +static bRC baculaAddFileEvent(bpContext *ctx, struct fileevent_pkt *event); /* * These will be plugged into the global pointer structure for @@ -117,7 +118,8 @@ static bFuncs bfuncs = { baculaCheckChanges, baculaAcceptFile, baculaAccurateAttribs, - baculaAddPlugin + baculaAddPlugin, + baculaAddFileEvent }; /* @@ -2312,6 +2314,30 @@ static bRC baculaAddInclude(bpContext *ctx, const char *file) return bRC_OK; } +/** + * Let the plugin create FileEvent to report problems on files + */ +static bRC baculaAddFileEvent(bpContext *ctx, struct fileevent_pkt *event) +{ + JCR *jcr; + bacula_ctx *bctx; + struct fileevent_pkt *e; + Dsm_check(999); + if (!is_ctx_good(ctx, jcr, bctx)) { + return bRC_Error; + } + // Sanity check + if (!event) { + return bRC_Error; + } + e = (struct fileevent_pkt*) malloc(sizeof(struct fileevent_pkt)); + memcpy(e, event, sizeof(struct fileevent_pkt)); + jcr->fileevents->append(e); + + Dsm_check(999); + return bRC_OK; +} + /** * Let the plugin define plugin to be included * from the main backup. diff --git a/bacula/src/filed/fd_plugins.h b/bacula/src/filed/fd_plugins.h index 3b788046e..b43d1f92e 100644 --- a/bacula/src/filed/fd_plugins.h +++ b/bacula/src/filed/fd_plugins.h @@ -554,7 +554,7 @@ typedef struct s_baculaFuncs { bRC (*AcceptFile)(bpContext *ctx, struct save_pkt *sp); /* Need fname and statp */ bRC (*getAccurateAttribs)(bpContext *ctx, accurate_attribs_pkt *att); bRC (*AddPlugin)(bpContext *ctx, const char *file); - + bRC (*AddFileEvent)(bpContext *ctx, struct fileevent_pkt *ev); } bFuncs; @@ -573,7 +573,7 @@ typedef enum { #define FD_PLUGIN_MAGIC "*FDPluginData*" -#define FD_PLUGIN_INTERFACE_VERSION ( 22 ) +#define FD_PLUGIN_INTERFACE_VERSION ( 23 + BEEF ) typedef struct s_pluginInfo { uint32_t size; @@ -628,6 +628,16 @@ typedef struct s_pluginFuncs { #define plug_func(plugin) ((pFuncs *)(plugin->pfuncs)) #define plug_info(plugin) ((pInfo *)(plugin->pinfo)) +#define FILEEVENT_TYPE_ANTIVIRUS 'a' + +struct fileevent_pkt { + int32_t FileIndex; + char Type; + int Severity; + char Description[MAX_NAME_LENGTH]; + char Source[MAX_NAME_LENGTH]; +}; + #ifdef __cplusplus } #endif diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 8ad7a6dba..f318cf6d5 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -264,6 +264,7 @@ JCR *new_fd_jcr() jcr->ff = init_find_files(); jcr->start_time = time(NULL); jcr->RunScripts = New(alist(10, not_owned_by_alist)); + jcr->fileevents = New(alist(5, owned_by_alist)); jcr->last_fname = get_pool_memory(PM_FNAME); jcr->last_fname[0] = 0; jcr->client_name = get_memory(strlen(my_name) + 1); @@ -3576,6 +3577,7 @@ void filed_free_jcr(JCR *jcr) bdelete_and_null(jcr->plugin_options_list); free_plugins(jcr); /* release instantiated plugins */ + delete jcr->fileevents; free_runscripts(jcr->RunScripts); delete jcr->RunScripts; free_path_list(jcr); diff --git a/bacula/src/filed/restore.c b/bacula/src/filed/restore.c index 67ec493ff..ed14c2fd7 100644 --- a/bacula/src/filed/restore.c +++ b/bacula/src/filed/restore.c @@ -1059,6 +1059,7 @@ void do_restore(JCR *jcr) plugin_name_stream(jcr, bmsg->rbuf); break; + case STREAM_FILEEVENT: case STREAM_RESTORE_OBJECT: break; /* these are sent by Director */ diff --git a/bacula/src/filed/verify_vol.c b/bacula/src/filed/verify_vol.c index 497e30c43..5adf8d5dc 100644 --- a/bacula/src/filed/verify_vol.c +++ b/bacula/src/filed/verify_vol.c @@ -248,6 +248,31 @@ bool v_ctx::close_previous_stream() return rtn; } +static bool fileevent_dir_save(JCR *jcr, fileevent_pkt *fevent) +{ + bool stat = false; + BSOCK *dir = jcr->dir_bsock; + + bash_spaces(fevent->Description); + bash_spaces(fevent->Source); + /* We keep some space for SessionTime and SessionId */ + stat = dir->fsend("CatReq JobId=%lu FileEvent %lu 0 0 %c %d %s %s 0", + jcr->JobId, fevent->FileIndex, fevent->Type, fevent->Severity, + fevent->Source, fevent->Description); + if (!stat) { + goto bail_out; + } + +bail_out: + if (!stat) { + if (!jcr->is_canceled() && !jcr->is_incomplete()) { + Jmsg1(jcr, M_FATAL, 0, _("Network send error to DIR. ERR=%s\n"), + dir->bstrerror()); + } + } + return stat; +} + /* * Verify attributes or data of the requested files on the Volume * @@ -446,6 +471,9 @@ void do_verify_volume(JCR *jcr) jcr->unlock(); break; + case STREAM_FILEEVENT: + break; // information only + case STREAM_PLUGIN_META_BLOB: case STREAM_PLUGIN_META_CATALOG: //TODO Add some metadata verification when it is possible @@ -619,6 +647,17 @@ void do_verify_volume(JCR *jcr) if (!accurate_finish(jcr)) { goto bail_out; } + /* + * Handle FileEvent that might have been generated during the verification + * of the file. + */ + struct fileevent_pkt *fevent; + foreach_alist(fevent, jcr->fileevents) { + if (!fileevent_dir_save(jcr, fevent)) { + break; + } + } + jcr->setJobStatus(JS_Terminated); goto ok_out; diff --git a/bacula/src/findlib/bfile.c b/bacula/src/findlib/bfile.c index 4c3df3fc1..2477be038 100644 --- a/bacula/src/findlib/bfile.c +++ b/bacula/src/findlib/bfile.c @@ -218,6 +218,8 @@ const char *stream_to_ascii(int stream) return _("Plugin Metadata Blob"); case STREAM_PLUGIN_META_CATALOG: return _("Plugin Metadata Catalog"); + case STREAM_FILEEVENT: + return _("FileEvent"); default: sprintf(buf, "%d", stream); return (const char *)buf; diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 83d85d31b..4e657d6f6 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -517,6 +517,7 @@ public: VSSClient *pVSSClient; /* VSS handler */ alist *plugin_verify; /* Registered plugins that need a copy of the data in verify job */ alist *plugin_options_list; /* list of the options to use in a job */ + alist *fileevents; /* list of the current file events to record and send to the DIR */ #endif /* FILE_DAEMON */ diff --git a/bacula/src/plugins/fd/test-plugin-fd.c b/bacula/src/plugins/fd/test-plugin-fd.c index bbeb8000a..54e4ece9c 100644 --- a/bacula/src/plugins/fd/test-plugin-fd.c +++ b/bacula/src/plugins/fd/test-plugin-fd.c @@ -942,6 +942,16 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) p_ctx->fd = NULL; } io->status = 0; + { + struct fileevent_pkt event; + bstrncpy(event.Description, "A good description", sizeof(event.Description)); + bstrncpy(event.Source, "A very good source", sizeof(event.Source)); + event.Type = 'c'; + event.Severity = 100; + bfuncs->AddFileEvent(ctx, &event); + bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "Adding FileEvent\n"); + } + break; case IO_SEEK: diff --git a/bacula/src/stored/append.c b/bacula/src/stored/append.c index 4c9c76348..4ec9555ae 100644 --- a/bacula/src/stored/append.c +++ b/bacula/src/stored/append.c @@ -440,7 +440,8 @@ fi_checked: /* Send attributes and digest to Director for Catalog */ bool send_attrs_to_dir(JCR *jcr, DEV_RECORD *rec) { - if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES || + if (rec->maskedStream == STREAM_FILEEVENT || + rec->maskedStream == STREAM_UNIX_ATTRIBUTES || rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX || rec->maskedStream == STREAM_RESTORE_OBJECT || rec->maskedStream == STREAM_PLUGIN_OBJECT || @@ -452,7 +453,7 @@ bool send_attrs_to_dir(JCR *jcr, DEV_RECORD *rec) if (are_attributes_spooled(jcr)) { dir->set_spooling(); } - Dmsg1(850, "Send attributes to dir. FI=%d\n", rec->FileIndex); + Dmsg2(850, "Send attributes to dir. FI=%d Stream=%s\n", rec->FileIndex, stream_to_ascii(rec->Stream)); if (!dir_update_file_attributes(jcr->dcr, rec)) { Jmsg(jcr, M_FATAL, 0, _("Error updating file attributes. ERR=%s\n"), dir->bstrerror()); diff --git a/bacula/src/stored/bextract.c b/bacula/src/stored/bextract.c index 4032cfa34..8bed0750f 100644 --- a/bacula/src/stored/bextract.c +++ b/bacula/src/stored/bextract.c @@ -729,6 +729,9 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) // TODO landonf: Investigate crypto support in the storage daemon break; + case STREAM_FILEEVENT: // Nothing to do in particular + break; + case STREAM_PROGRAM_NAMES: case STREAM_PROGRAM_DATA: if (!prog_name_msg) { diff --git a/bacula/src/stored/bscan.c b/bacula/src/stored/bscan.c index 97be7bdef..4a2b6c0f5 100644 --- a/bacula/src/stored/bscan.c +++ b/bacula/src/stored/bscan.c @@ -433,6 +433,7 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) DEV_BLOCK *block = dcr->block; POOL_MEM sql_buffer; db_int64_ctx jmr_count; + FILEEVENT_DBR fevent; char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; @@ -971,6 +972,29 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) } break; + case STREAM_FILEEVENT: + if (verbose > 1) { + Pmsg0(000, _("Got FileEvent Stream record.\n")); + } + if (!fevent.unpack(rec->Stream, rec->data, rec->data_len)) { + Emsg0(M_ERROR, 0, _("Unable to decode FileEvent.\n")); + break; + } + mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); + if (!mjcr) { + Pmsg2(000, _("Could not find SessId=%d SessTime=%d for File record.\n"), + rec->VolSessionId, rec->VolSessionTime); + break; + } + fevent.SourceJobId = fevent.JobId = mjcr->JobId; + fevent.FileIndex = mjcr->JobFiles; + if (!db_create_fileevent_record(mjcr, mjcr->db, &fevent)) { + Jmsg2(mjcr, M_ERROR, 0, _("Failed to insert FileEvent record for file: %d. err: %s\n"), + fevent.FileIndex, db_strerror(db)); + } + mjcr->dec_use_count(); /* Decrease reference counter increased by get_jcr_by_session call */ + break; + case STREAM_UNIX_ACCESS_ACL: /* Deprecated Standard ACL attributes on UNIX */ case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX */ case STREAM_HFSPLUS_ATTRIBUTES: diff --git a/bacula/src/stored/record_util.c b/bacula/src/stored/record_util.c index 51513b2c0..bd24d896b 100644 --- a/bacula/src/stored/record_util.c +++ b/bacula/src/stored/record_util.c @@ -161,7 +161,8 @@ const char *stream_to_ascii(char *buf, int stream, int fi) return "contADATA-BLOCK-HEADER"; case STREAM_ADATA_RECORD_HEADER: return "contADATA-RECORD-HEADER"; - + case STREAM_FILEEVENT: + return _("FileEvent"); default: sprintf(buf, "%d", -stream); return buf; @@ -239,6 +240,8 @@ const char *stream_to_ascii(char *buf, int stream, int fi) return "ADATA-BLOCK-HEADER"; case STREAM_ADATA_RECORD_HEADER: return "ADATA-RECORD-HEADER"; + case STREAM_FILEEVENT: + return _("FileEvent"); default: sprintf(buf, "%d", stream); return buf; diff --git a/bacula/src/streams.h b/bacula/src/streams.h index 9b356b6b0..dc155f582 100644 --- a/bacula/src/streams.h +++ b/bacula/src/streams.h @@ -90,6 +90,7 @@ #define STREAM_PLUGIN_NAME 26 /* Plugin "file" string */ #define STREAM_PLUGIN_DATA 27 /* Plugin specific data */ #define STREAM_RESTORE_OBJECT 28 /* Plugin restore object */ + /* * Non-gzip compressed streams. Those streams can handle arbitrary * compression algorithm data as an additional header is stored @@ -105,6 +106,7 @@ #define STREAM_PLUGIN_META_BLOB 35 /* Plugin metadata (blob) for file being backed up */ #define STREAM_PLUGIN_META_CATALOG 36 /* Plugin metadata (to be stored in catalog) for file being backed up */ #define STREAM_UNIX_ATTRIBUTE_UPDATE 37 /* File's updated metadata */ +#define STREAM_FILEEVENT 38 /* FileEvent associated with the current object */ #define STREAM_ADATA_BLOCK_HEADER 200 /* Adata block header */ #define STREAM_ADATA_RECORD_HEADER 201 /* Adata record header */