]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
metaplugin: Add AcceptFile() support.
authorRadosław Korzeniewski <radoslaw@korzeniewski.net>
Mon, 4 Oct 2021 19:57:50 +0000 (21:57 +0200)
committerEric Bollengier <eric@baculasystems.com>
Wed, 6 Sep 2023 07:49:01 +0000 (09:49 +0200)
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.

bacula/src/plugins/fd/pluginlib/metaplugin.cpp
bacula/src/plugins/fd/pluginlib/metaplugin.h
bacula/src/plugins/fd/pluginlib/metaplugin_attributes.cpp
bacula/src/plugins/fd/pluginlib/metaplugin_attributes.h
bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c
regress/scripts/kubernetes-plugin-test-bacula-dir.conf.in
regress/scripts/metaplugin-protocol-tests.sh
regress/scripts/openshift-plugin-test-bacula-dir.conf.in
regress/scripts/rhv-plugin-test-bacula-dir.conf.in
regress/scripts/swift-plugin-test-bacula-dir.conf.in

index 311b36378541ceab6e2ed59620161b7c838985e2..78914b15ff43f46727050b52dad2de84b19526ec 100644 (file)
@@ -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
  *
index d83b9736e4efe303ce9b18deccbef69ea68d481d..3e2c3057a76ed3b0d7a1e630cbf353d6662a4230 100644 (file)
@@ -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);
index 4029614f8197732d32b58955e967f51124dfe603..a5e4579cbd572f642e1167e7bd7c49a3fb7f56da 100644 (file)
@@ -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
index 2fd1872590d280aa2bdacee60ba3ae625793d182..a2fc77038b559410378444061b8d58b552aefddb 100644 (file)
@@ -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
  * @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_
index 386192f03ad956c2b35813d53a538a625b9d9bc8..afbd464bf6fa6d199ff6e70797e1f7055b75036b 100644 (file)
@@ -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;
index 0c32a187733c0dc3fb2a5d496d6863097640d481..a6faeb067a914f2478849fd41dedb94a13462cc5 100644 (file)
@@ -80,6 +80,9 @@ FileSet {
     Plugin = "kubernetes:"
     Plugin = "kubernetes: regress_backup_other_file"
   }
+  Exclude {
+    File = "/exclude/file1"
+  }
 }
 
 Job {
index fa1520b47253c26fa64082af757543e3ba9f6f63..702c4b5c502e6639129f4400ef91503df7da0ec5 100755 (executable)
@@ -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
 
index c80385ca2aa68dc2cc23ea3291900c2d720dd7df..ab190e2452a12578c3bd1e10a4df376b18e45d6b 100644 (file)
@@ -79,6 +79,9 @@ FileSet {
     Plugin = "openshift:"
     Plugin = "openshift: regress_backup_other_file"
   }
+  Exclude {
+    File = "/exclude/file1"
+  }
 }
 
 Job {
index e9f5c21615f82288c37bddfa7a8294bfbc73c19c..1b8c56b3f769b25a7e47d2ff900d51f477e66756 100644 (file)
@@ -116,6 +116,9 @@ FileSet {
     Plugin = "rhv:"
     Plugin = "rhv: regress_backup_other_file"
   }
+  Exclude {
+    File = "/exclude/file1"
+  }
 }
 
 Job {
index c117955be2ae2fb6acac9dc6fbb6fe8654b916be..b8084ad0f5f79c315f21b6acb2d6692542edec23 100644 (file)
@@ -79,6 +79,9 @@ FileSet {
     Plugin = "swift:"
     Plugin = "swift: regress_backup_other_file"
   }
+  Exclude {
+    File = "/exclude/file1"
+  }
 }
 
 Job {