From: Radosław Korzeniewski Date: Mon, 4 Oct 2021 19:57:50 +0000 (+0200) Subject: metaplugin: Add AcceptFile() support. X-Git-Tag: Beta-15.0.0~861 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=67ebf54ead8929379b72f7597b6b1b2b3a5f1e68;p=thirdparty%2Fbacula.git metaplugin: Add AcceptFile() support. It implements a new ACCEPT: command so you can query Bacula Fileset to check if the file to backup should be accepted or skipped during backup. The query operation is similar to accurate check available before. --- diff --git a/bacula/src/plugins/fd/pluginlib/metaplugin.cpp b/bacula/src/plugins/fd/pluginlib/metaplugin.cpp index 311b36378..78914b15f 100644 --- a/bacula/src/plugins/fd/pluginlib/metaplugin.cpp +++ b/bacula/src/plugins/fd/pluginlib/metaplugin.cpp @@ -1683,19 +1683,13 @@ bRC METAPLUGIN::perform_write_xattr(bpContext* ctx, const xacl_pkt* xacl) return bRC_OK; } -/* - * The method works as a dispatcher for expected commands received from backend. - * It handles a three commands associated with file attributes/metadata: - * - FNAME:... - the next file to backup - * - ACL - next data will be acl data, so perform_read_acl() - * - XATTR - next data will be xattr data, so perform_read_xattr() - * and additionally when no more files to backup it handles EOD. +/** + * @brief The method works as a dispatcher for expected commands received from backend. + * It handles a number of commands available at main dispatcher loop. * - * in: - * bpContext - for Bacula debug and jobinfo messages - * out: - * bRC_OK - when plugin read the command, dispatched a work and setup flags - * bRC_Error - on any error during backup + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @return bRC bRC_OK - when plugin read the command, dispatched a work and setup flags + * bRC_Error - on any error during backup */ bRC METAPLUGIN::perform_read_metacommands(bpContext *ctx) { @@ -1741,6 +1735,11 @@ bRC METAPLUGIN::perform_read_metacommands(bpContext *ctx) perform_accurate_check_get(ctx); continue; } + if (scan_parameter_str(cmd, "ACCEPT:", fname)){ + /* got AcceptFile() query */ + perform_accept_file(ctx); + continue; + } if (bstrcmp(cmd.c_str(), "ACL")){ /* got ACL header */ perform_read_acl(ctx); @@ -1944,6 +1943,62 @@ bRC METAPLUGIN::perform_accurate_check_get(bpContext *ctx) return bRC_OK; } +/** + * @brief Perform a check if selected file should be accepted to backup. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @return bRC bRC_OK when success, bRC_Error if not + */ +bRC METAPLUGIN::perform_accept_file(bpContext *ctx) +{ + if (strlen(fname.c_str()) == 0){ + // input variable is not valid + return bRC_Error; + } + + DMSG0(ctx, DDEBUG, "perform_accept_file()\n"); + + POOL_MEM cmd(PM_FNAME); + struct save_pkt sp; + memset(&sp, 0, sizeof(sp)); + + metaplugin::attributes::Status status = metaplugin::attributes::read_attributes_command(ctx, backend.ctx, cmd, &sp); + switch(status) + { + case metaplugin::attributes::Invalid_File_Type: + JMSG2(ctx, M_ERROR, "Invalid file type: %c for %s\n", sp.type, fname.c_str()); + return bRC_Error; + + case metaplugin::attributes::Invalid_Stat_Packet: + JMSG1(ctx, backend.ctx->jmsg_err_level(), "Invalid stat packet: %s\n", cmd.c_str()); + return bRC_Error; + + case metaplugin::attributes::Status_OK: + { + // success we can perform accurate check for stat packet + sp.fname = fname.c_str(); + bRC rc = bfuncs->AcceptFile(ctx, &sp); + + POOL_MEM checkstatus(PM_NAME); + Mmsg(checkstatus, "%s\n", rc == bRC_Skip ? "SKIP" : "OK"); + DMSG1(ctx, DINFO, "perform_accept_file(): %s", checkstatus.c_str()); + + if (!backend.ctx->write_command(ctx, checkstatus)) { + DMSG0(ctx, DERROR, "Cannot send AcceptFile() response to backend\n"); + JMSG0(ctx, backend.ctx->jmsg_err_level(), "Cannot send AcceptFile() response to backend\n"); + return bRC_Error; + } + } + break; + + default: + JMSG2(ctx, M_ERROR, "Invalid accept file protocol: %d for %s\n", status, fname.c_str()); + return bRC_Error; + } + + return bRC_OK; +} + /** * @brief * diff --git a/bacula/src/plugins/fd/pluginlib/metaplugin.h b/bacula/src/plugins/fd/pluginlib/metaplugin.h index d83b9736e..3e2c3057a 100644 --- a/bacula/src/plugins/fd/pluginlib/metaplugin.h +++ b/bacula/src/plugins/fd/pluginlib/metaplugin.h @@ -282,6 +282,7 @@ private: bRC perform_file_index_query(bpContext *ctx); bRC perform_accurate_check(bpContext *ctx); bRC perform_accurate_check_get(bpContext *ctx); + bRC perform_accept_file(bpContext *ctx); // bRC perform_write_metadata_info(bpContext *ctx, struct meta_pkt *mp); metadata_type scan_metadata_type(bpContext *ctx, const POOL_MEM &cmd); const char *prepare_metadata_type(metadata_type type); diff --git a/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.cpp b/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.cpp index 4029614f8..a5e4579cb 100644 --- a/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.cpp +++ b/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.cpp @@ -17,7 +17,7 @@ Bacula(R) is a registered trademark of Kern Sibbald. */ /** - * @file metaplugin_stat.cpp + * @file metaplugin_attributes.cpp * @author Radosław Korzeniewski (radoslaw@korzeniewski.net) * @brief This is a Backend attributes (STAT, TSTAMP) command handling subroutines for metaplugin. * @version 1.0.0 @@ -41,6 +41,16 @@ namespace metaplugin { namespace attributes { + /** + * @brief Scans an input command and handles STAT sttributes. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @param cmd a command buffer to scan and analyze + * @param sp save packet to fill - output + * @return Status Status_OK when data managed + * Status_Handled when data managed and TSTAMP command is not required + * Not_Command when it was not this command + */ Status read_scan_stat_command(bpContext *ctx, POOL_MEM &cmd, struct save_pkt *sp) { char type; @@ -53,10 +63,10 @@ namespace attributes if (strncmp(cmd.c_str(), "STAT:/", 6) == 0) { - DMSG0(ctx, DDEBUG, "read_scan_stat_command():new stat(2)\n"); POOL_MEM param(PM_FNAME); // handle stat(2) for this file scan_parameter_str(cmd, "STAT:", param); + DMSG1(ctx, DDEBUG, "read_scan_stat_command():stat:%s\n", param.c_str()); int rc = stat(param.c_str(), &sp->statp); if (rc < 0) { @@ -65,7 +75,7 @@ namespace attributes return Invalid_Stat_Packet; } // stat is working as expected - DMSG1(ctx, DDEBUG, "read_scan_stat_command():stat: %o\n", sp->statp.st_mode & S_IFMT); + DMSG1(ctx, DDEBUG, "read_scan_stat_command():stat: %o\n", sp->statp.st_mode); switch (sp->statp.st_mode & S_IFMT) { case S_IFDIR: @@ -134,6 +144,15 @@ namespace attributes return Not_Command; } + /** + * @brief Scans an input command and handles TSTAMP sttributes. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @param cmd a command buffer to scan and analyze + * @param sp save packet to fill - output + * @return Status Status_OK when data managed + * Not_Command when it was not this command + */ Status read_scan_tstamp_command(bpContext *ctx, POOL_MEM &cmd, struct save_pkt *sp) { time_t _atime; @@ -158,6 +177,14 @@ namespace attributes return Not_Command; } + /** + * @brief Prepare a STAT command based on selected data. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @param cmd the command buffer - output + * @param rp restore packet to use + * @return Status Status_OK + */ Status make_stat_command(bpContext *ctx, POOL_MEM &cmd, const restore_pkt *rp) { /* STAT:... */ @@ -193,6 +220,14 @@ namespace attributes return Status_OK; } + /** + * @brief Prepares a TSTAMP command based on selected data. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @param cmd the command buffer - output + * @param rp restore packet to use + * @return Status Status_OK + */ Status make_tstamp_command(bpContext *ctx, POOL_MEM &cmd, const restore_pkt *rp) { /* TSTAMP:... */ @@ -201,6 +236,48 @@ namespace attributes return Status_OK; } -} // attributes -} // metaplugin + /** + * @brief Reads a file attributes sequence (STAT, TSTAMP) used at different sections. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @param ptcomm backend communication class - the current context + * @param cmd the buffer for command handled + * @param sp save packet to fill when file attributes handled + * @return Status Status_OK when file attributes commands handled, Status_Error on any error, other depends on enum. + */ + Status read_attributes_command(bpContext *ctx, PTCOMM *ptcomm, POOL_MEM &cmd, struct save_pkt *sp) + { + DMSG0(ctx, DDEBUG, "read_attributes_command()\n"); + + // supported sequence is `STAT` followed by `TSTAMP` + if (ptcomm->read_command(ctx, cmd) < 0) { + // error + return Status_Error; + } + + Status status = read_scan_stat_command(ctx, cmd, sp); + switch(status) + { + case Status_OK: + // go with TSATMP command + if (ptcomm->read_command(ctx, cmd) < 0) { + // error + return Status_Error; + } + status = read_scan_tstamp_command(ctx, cmd, sp); + break; + + case Status_Handled: + status = Status_OK; + break; + + default: + break; + } + + return status; + } + +} // namespace attributes +} // namespace metaplugin diff --git a/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.h b/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.h index 2fd187259..a2fc77038 100644 --- a/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.h +++ b/bacula/src/plugins/fd/pluginlib/metaplugin_attributes.h @@ -17,7 +17,7 @@ Bacula(R) is a registered trademark of Kern Sibbald. */ /** - * @file metaplugin_stat.h + * @file metaplugin_attributes.h * @author Radosław Korzeniewski (radoslaw@korzeniewski.net) * @brief This is a Backend `STAT` command handling subroutines for metaplugin. * @version 1.0.0 @@ -26,10 +26,11 @@ * @copyright Copyright (c) 2021 All rights reserved. IP transferred to Bacula Systems according to agreement. */ -#ifndef _METAPLUGIN_STAT_H_ -#define _METAPLUGIN_STAT_H_ +#ifndef _METAPLUGIN_ATTRIBUTES_H_ +#define _METAPLUGIN_ATTRIBUTES_H_ #include "pluginlib.h" +#include "ptcomm.h" namespace metaplugin @@ -43,13 +44,15 @@ namespace attributes Invalid_File_Type, Status_Handled, Not_Command, + Status_Error, } Status; Status read_scan_stat_command(bpContext *ctx, POOL_MEM &cmd, struct save_pkt *sp); Status make_stat_command(bpContext *ctx, POOL_MEM &cmd, const restore_pkt *rp); Status read_scan_tstamp_command(bpContext *ctx, POOL_MEM &cmd, struct save_pkt *sp); Status make_tstamp_command(bpContext *ctx, POOL_MEM &cmd, const restore_pkt *rp); + Status read_attributes_command(bpContext *ctx, PTCOMM *ptcomm, POOL_MEM &cmd, struct save_pkt *sp); } // attributes } // metaplugin -#endif // _METAPLUGIN_STAT_H_ +#endif // _METAPLUGIN_ATTRIBUTES_H_ diff --git a/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c b/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c index 386192f03..afbd464bf 100644 --- a/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c +++ b/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c @@ -789,6 +789,41 @@ void perform_backup() write_plugin('D', "/* here comes another file line */"); write_plugin('I', "TEST7-Other-End"); signal_eod(); + + // check acceptfile() skip + write_plugin('C', "ACCEPT:/exclude/file1\n"); + write_plugin('C', "STAT:F 1048576 200 200 100640 1\n"); + write_plugin('C', "TSTAMP:1504271937 1504271937 1504271937\n"); + read_plugin(buf); + write_plugin('I', "TEST ACCEPT Response\n"); + write_plugin('I', buf); + if (strncmp(buf, "SKIP", 4) == 0) + { + write_plugin('I', "TEST ACCEPT Response OK (ACCEPT_FILE_OK)\n"); + } + + // check acceptfile() ok + write_plugin('C', "ACCEPT:/etc/passwd\n"); + write_plugin('C', "STAT:F 1048576 200 200 100640 1\n"); + write_plugin('C', "TSTAMP:1504271937 1504271937 1504271937\n"); + read_plugin(buf); + write_plugin('I', "TEST ACCEPT Response\n"); + write_plugin('I', buf); + if (strncmp(buf, "OK", 2) == 0) + { + write_plugin('I', "TEST ACCEPT Response OK (ACCEPT_FILE_OK)\n"); + } + + // check acceptfile() with STAT(2) on file + write_plugin('C', "ACCEPT:/etc/passwd\n"); + write_plugin('C', "STAT:/etc/passwd\n"); + read_plugin(buf); + write_plugin('I', "TEST ACCEPT Response\n"); + write_plugin('I', buf); + if (strncmp(buf, "OK", 2) == 0) + { + write_plugin('I', "TEST ACCEPT Response OK (ACCEPT_FILE_OK)\n"); + } } bool seen = false; diff --git a/regress/scripts/kubernetes-plugin-test-bacula-dir.conf.in b/regress/scripts/kubernetes-plugin-test-bacula-dir.conf.in index 0c32a1877..a6faeb067 100644 --- a/regress/scripts/kubernetes-plugin-test-bacula-dir.conf.in +++ b/regress/scripts/kubernetes-plugin-test-bacula-dir.conf.in @@ -80,6 +80,9 @@ FileSet { Plugin = "kubernetes:" Plugin = "kubernetes: regress_backup_other_file" } + Exclude { + File = "/exclude/file1" + } } Job { diff --git a/regress/scripts/metaplugin-protocol-tests.sh b/regress/scripts/metaplugin-protocol-tests.sh index fa1520b47..702c4b5c5 100755 --- a/regress/scripts/metaplugin-protocol-tests.sh +++ b/regress/scripts/metaplugin-protocol-tests.sh @@ -461,10 +461,11 @@ BFILE3=$(grep "$Plugin/bucket" ${cwd}/tmp/log5.out | grep -c lockfile) BFILE4=$(grep "$Plugin/bucket" ${cwd}/tmp/log5.out | grep -c file.xattr) BFILE5=$(grep "$Plugin/bucket" ${cwd}/tmp/log5.out | grep -c vmsnap.iso) BFILE6=$(grep "$Plugin/bucket" ${cwd}/tmp/log5.out | grep -c vm222-other-file.iso) +BACCEPT=$(grep -c "ACCEPT_FILE_OK" ${cwd}/tmp/log5.out) BEND=$(grep -w -c "TESTEND" ${cwd}/tmp/log5.out) -if [ "x$RET" != "xT" ] || [ "$BFILE1" -ne 2 ] || [ "$BFILE2" -ne 2 ] || [ "$BFILE3" -ne 2 ] || [ "$BFILE4" -ne 2 ] || [ "$BFILE5" -ne 2 ] || [ "$BFILE6" -ne 1 ] || [ "$BEND" -ne 2 ] +if [ "x$RET" != "xT" ] || [ "$BFILE1" -ne 2 ] || [ "$BFILE2" -ne 2 ] || [ "$BFILE3" -ne 2 ] || [ "$BFILE4" -ne 2 ] || [ "$BFILE5" -ne 2 ] || [ "$BFILE6" -ne 1 ] || [ "$BEND" -ne 2 ] || [ "$BACCEPT" -ne 3 ] then - echo "log5" "$RET" "$BFILE1" "$BFILE2" "$BFILE3" "$BFILE4" "$BFILE5" "$BFILE6" "$BEND" + echo "log5" "$RET" "$BFILE1" "$BFILE2" "$BFILE3" "$BFILE4" "$BFILE5" "$BFILE6" "$BACCEPT" "$BEND" bstat=$((bstat+8)) fi diff --git a/regress/scripts/openshift-plugin-test-bacula-dir.conf.in b/regress/scripts/openshift-plugin-test-bacula-dir.conf.in index c80385ca2..ab190e245 100644 --- a/regress/scripts/openshift-plugin-test-bacula-dir.conf.in +++ b/regress/scripts/openshift-plugin-test-bacula-dir.conf.in @@ -79,6 +79,9 @@ FileSet { Plugin = "openshift:" Plugin = "openshift: regress_backup_other_file" } + Exclude { + File = "/exclude/file1" + } } Job { diff --git a/regress/scripts/rhv-plugin-test-bacula-dir.conf.in b/regress/scripts/rhv-plugin-test-bacula-dir.conf.in index e9f5c2161..1b8c56b3f 100644 --- a/regress/scripts/rhv-plugin-test-bacula-dir.conf.in +++ b/regress/scripts/rhv-plugin-test-bacula-dir.conf.in @@ -116,6 +116,9 @@ FileSet { Plugin = "rhv:" Plugin = "rhv: regress_backup_other_file" } + Exclude { + File = "/exclude/file1" + } } Job { diff --git a/regress/scripts/swift-plugin-test-bacula-dir.conf.in b/regress/scripts/swift-plugin-test-bacula-dir.conf.in index c117955be..b8084ad0f 100644 --- a/regress/scripts/swift-plugin-test-bacula-dir.conf.in +++ b/regress/scripts/swift-plugin-test-bacula-dir.conf.in @@ -79,6 +79,9 @@ FileSet { Plugin = "swift:" Plugin = "swift: regress_backup_other_file" } + Exclude { + File = "/exclude/file1" + } } Job {