]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/fd_plugins.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / filed / fd_plugins.c
index 2e4767ccb2fad1821903bc43b8e0a7717c38d700..066541b2c508ea80ba976ad4753de40cef491480 100644 (file)
@@ -1,29 +1,20 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2007-2012 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.
-   This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version three of the GNU Affero General Public
-   License as published by the Free Software Foundation, which is 
-   listed in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU Affero General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA.
-
-   Bacula® is a registered trademark of Kern Sibbald.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2015 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /**
  * Main program to test loading and running Bacula plugins.
@@ -38,7 +29,6 @@ extern CLIENT *me;
 extern DLL_IMP_EXP char *exepath;
 extern DLL_IMP_EXP char *version;
 extern DLL_IMP_EXP char *dist_name;
-extern DLL_IMP_EXP int beef;
 
 const int dbglvl = 150;
 #ifdef HAVE_WIN32
@@ -51,7 +41,7 @@ extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
 
 /* Function pointers to be set here */
-extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
+extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
 extern DLL_IMP_EXP int     (*plugin_bclose)(BFILE *bfd);
 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
@@ -86,7 +76,7 @@ static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
  * These will be plugged into the global pointer structure for
  *  the findlib.
  */
-static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
+static int     my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
 static int     my_plugin_bclose(BFILE *bfd);
 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
@@ -96,7 +86,7 @@ static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
 /* Bacula info */
 static bInfo binfo = {
    sizeof(bInfo),
-   FD_PLUGIN_INTERFACE_VERSION 
+   FD_PLUGIN_INTERFACE_VERSION
 };
 
 /* Bacula entry points */
@@ -122,7 +112,7 @@ static bFuncs bfuncs = {
    baculaAcceptFile
 };
 
-/* 
+/*
  * Bacula private context
  */
 struct bacula_ctx {
@@ -148,7 +138,7 @@ static bool for_this_plugin(Plugin *plugin, char *name, int len)
    }
    if (strcmp("*all*", name) == 0) { /* new v6.0 name for VSS job metadata */
       return true;
-   } 
+   }
    /* Check if this is the correct plugin */
    if (len == plugin->file_len && strncmp(plugin->file, name, len) == 0) {
       return true;
@@ -180,7 +170,7 @@ bool is_plugin_disabled(JCR *jcr)
  * 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)     
+void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
 {
    bpContext *plugin_ctx;
    bEvent event;
@@ -192,10 +182,10 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
    restore_object_pkt *rop;
 
    Dsm_check(999);
-   if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) {
+   if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list) {
       return;                         /* Return if no plugins loaded */
    }
-   
+
    /*
     * Some events are sent to only a particular plugin or must be
     *  called even if the job is canceled
@@ -225,7 +215,7 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
       call_if_canceled = true; /* plugin *must* see this call */
       break;
    case bEventStartRestoreJob:
-      foreach_alist_index(i, plugin, bplugin_list) {
+      foreach_alist_index(i, plugin, b_plugin_list) {
          plugin->restoreFileStarted = false;
          plugin->createFileCalled = false;
       }
@@ -251,19 +241,20 @@ void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
     * Pass event to every plugin (except if name is set). If name
     *   is set, we pass it only to the plugin with that name.
     */
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       if (!for_this_plugin(plugin, name, len)) {
-         Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n", 
+         Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n",
             name, name==NULL?1:0);
          continue;
       }
       /*
-       * Note, at this point do not change 
+       * Note, at this point do not change
        *   jcr->plugin or jcr->plugin_ctx
        */
       Dsm_check(999);
       plugin_ctx = &plugin_ctx_list[i];
       if (is_plugin_disabled(plugin_ctx)) {
+         Dmsg1(50, "Plugin %s disabled\n", plugin->file);
          continue;
       }
       if (eventType == bEventEndRestoreJob) {
@@ -291,7 +282,7 @@ bool plugin_check_file(JCR *jcr, char *fname)
    int i;
 
    Dsm_check(999);
-   if (!bplugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
+   if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
       return false;                      /* Return if no plugins loaded */
    }
 
@@ -300,7 +291,7 @@ bool plugin_check_file(JCR *jcr, char *fname)
    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
 
    /* Pass event to every plugin */
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       jcr->plugin_ctx = &plugin_ctx_list[i];
       jcr->plugin = plugin;
       if (is_plugin_disabled(jcr)) {
@@ -322,7 +313,7 @@ bool plugin_check_file(JCR *jcr, char *fname)
 }
 
 /* Get the first part of the the plugin command
- *  systemstate:/@SYSTEMSTATE/ 
+ *  systemstate:/@SYSTEMSTATE/
  * => ret = 11
  * => can use for_this_plugin(plug, cmd, ret);
  *
@@ -371,14 +362,14 @@ static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
       ff_pkt->flags &= ~FO_DELTA;   /* clean delta sequence number */
       ff_pkt->delta_seq = 0;
    }
-   
+
    if (sp->flags & FO_OFFSETS) {
       ff_pkt->flags |= FO_OFFSETS;
    } else {
       ff_pkt->flags &= ~FO_OFFSETS;
    }
    /* Sparse code doesn't work with plugins
-    * that use FIFO or STDOUT/IN to communicate 
+    * that use FIFO or STDOUT/IN to communicate
     */
    if (sp->flags & FO_SPARSE) {
       ff_pkt->flags |= FO_SPARSE;
@@ -399,6 +390,7 @@ bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
 {
    Plugin *plugin;
    bRC ret = bRC_Error;
+   bool found=false;
    char *cmd = ff_pkt->plugin;
    int len;
    int i=0;
@@ -418,7 +410,7 @@ bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
    sp->delta_seq = ff_pkt->delta_seq;
    sp->accurate_found = ff_pkt->accurate_found;
 
-   if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
+   if (!b_plugin_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 */
    }
@@ -428,12 +420,14 @@ bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
    }
 
    /* Note, we stop the loop on the first plugin that matches the name */
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
       if (!for_this_plugin(plugin, cmd, len)) {
          continue;
       }
 
+      found=true;
+
       Dsm_check(999);
       if (is_plugin_disabled(&plugin_ctx_list[i])) {
          goto bail_out;
@@ -441,10 +435,10 @@ bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
 
       jcr->plugin_ctx = &plugin_ctx_list[i];
       jcr->plugin = plugin;
-      
-      ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], 
+
+      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;
@@ -463,11 +457,14 @@ bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
       goto bail_out;
    } /* end foreach loop */
 bail_out:
+   if (!found) {
+      Jmsg1(jcr, M_FATAL, 0, "Options plugin \"%s\" not found.\n", cmd);
+   }
    Dsm_check(999);
    return ret;
 }
 
-/**  
+/**
  * Sequence of calls for a backup:
  * 1. plugin_save() here is called with ff_pkt
  * 2. we find the plugin requested on the command string
@@ -494,7 +491,7 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    POOL_MEM link(PM_FNAME);
 
    Dsm_check(999);
-   if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
+   if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
       return 1;                            /* Return if no plugins loaded */
    }
@@ -508,12 +505,12 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    }
 
    /* Note, we stop the loop on the first plugin that matches the name */
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
       if (!for_this_plugin(plugin, cmd, len)) {
          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.
@@ -531,7 +528,7 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          goto bail_out;
       }
       /* Loop getting filenames to backup then saving them */
-      while (!jcr->is_job_canceled()) { 
+      while (!jcr->is_job_canceled()) {
          memset(&sp, 0, sizeof(sp));
          sp.pkt_size = sizeof(sp);
          sp.pkt_end = sizeof(sp);
@@ -554,7 +551,7 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          jcr->plugin_sp = &sp;
          ff_pkt = jcr->ff;
          /*
-          * Copy fname and link because save_file() zaps them.  This 
+          * Copy fname and link because save_file() zaps them.  This
           *  avoids zaping the plugin's strings.
           */
          ff_pkt->type = sp.type;
@@ -590,7 +587,7 @@ int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          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);
-         }   
+         }
          /* Call Bacula core code to backup the plugin's file */
          save_file(jcr, ff_pkt, true);
          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
@@ -616,7 +613,7 @@ bail_out:
 }
 
 
-/**  
+/**
  * 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
@@ -639,7 +636,7 @@ int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    ATTR attr;
 
    Dsm_check(999);
-   if (!bplugin_list || !jcr->plugin_ctx_list) {
+   if (!b_plugin_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 */
    }
@@ -653,12 +650,12 @@ int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
    }
 
    /* Note, we stop the loop on the first plugin that matches the name */
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
       if (!for_this_plugin(plugin, cmd, len)) {
          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.
@@ -676,7 +673,7 @@ int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
          goto bail_out;
       }
       /* Loop getting filenames to backup then saving them */
-      while (!jcr->is_job_canceled()) { 
+      while (!jcr->is_job_canceled()) {
          Dsm_check(999);
          memset(&sp, 0, sizeof(sp));
          sp.pkt_size = sizeof(sp);
@@ -778,7 +775,7 @@ bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
    if (jcr->is_job_canceled()) {
       return false;
    }
-  
+
    if (start) {
       index++;                  /* JobFiles not incremented yet */
    }
@@ -819,7 +816,7 @@ bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
  * Returns: true  if start of stream
  *          false if end of steam
  */
-bool plugin_name_stream(JCR *jcr, char *name)    
+bool plugin_name_stream(JCR *jcr, char *name)
 {
    char *p = name;
    char *cmd;
@@ -844,7 +841,7 @@ bool plugin_name_stream(JCR *jcr, char *name)
       cmd = p;
    } else {
       /*
-       * End of plugin data, notify plugin, then clear flags   
+       * End of plugin data, notify plugin, then clear flags
        */
       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
       if (jcr->plugin && jcr->plugin->restoreFileStarted) {
@@ -862,7 +859,7 @@ bool plugin_name_stream(JCR *jcr, char *name)
    if (!plugin_ctx_list) {
       goto bail_out;
    }
-      
+
    /*
     * After this point, we are dealing with a restore start
     */
@@ -871,10 +868,10 @@ bool plugin_name_stream(JCR *jcr, char *name)
    }
 
    /*
-    * Search for correct plugin as specified on the command 
+    * Search for correct plugin as specified on the command
     */
    Dsm_check(999);
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       bEvent event;
       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
       if (!for_this_plugin(plugin, cmd, len)) {
@@ -888,8 +885,8 @@ bool plugin_name_stream(JCR *jcr, char *name)
          goto bail_out;
       }
       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
-      event.eventType = bEventRestoreCommand;     
-      if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
+      event.eventType = bEventRestoreCommand;
+      if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
             &event, cmd) != bRC_OK) {
          Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
          goto bail_out;
@@ -939,7 +936,7 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
    int rc;
 
    Dsm_check(999);
-   if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
+   if (!plugin || !plugin_ctx || jcr->is_job_canceled()) {
       return CF_ERROR;
    }
 
@@ -960,7 +957,7 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
    rp.RegexWhere = jcr->RegexWhere;
    rp.replace = jcr->replace;
    rp.create_status = CF_ERROR;
-   Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n", 
+   Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
          rp.stream, rp.type, rp.LinkFI, rp.ofname);
    if (rp.attrEx) {
       Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
@@ -992,6 +989,9 @@ int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
       return CF_CORE;           /* Let Bacula core handle the file creation */
    }
 
+   /* Use the bfile for plugin */
+   set_cmd_plugin(bfd, jcr);
+
    /* Created link or directory? */
    if (rp.create_status == CF_CREATED) {
       return rp.create_status;        /* yes, no need to bopen */
@@ -1055,7 +1055,7 @@ bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
    rp.create_status = CF_ERROR;
 
    plug_func(plugin)->setFileAttributes(jcr->plugin_ctx, &rp);
-   
+
    if (rp.create_status == CF_CORE) {
       set_attributes(jcr, attr, ofd);
    } else {
@@ -1069,6 +1069,206 @@ bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
    return true;
 }
 
+/*
+ * The Plugin ACL data backup. We are using a new Plugin callback:
+ *    handleXACLdata() for that. The new callback get a pointer to
+ *    struct xacl_pkt as a main argument which consist of the following
+ *    data:
+ *       xacl.func - could be the one of BACL_BACKUP, BACL_RESTORE,
+ *                   BXATTR_BACKUP, BXATTR_RESTORE
+ *       xacl.count - the length of data at the content buffer
+ *       xacl.content - the buffer itself
+ *    The buffer (xacl.content) is supplied by Bacula during restore and has to
+ *    be supplied by a Plugin during backup.
+ *    The new callback should return bRC_OK on success and bRC_Error on
+ *    any error.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file save packet
+ *    data is a pointer to variable returned
+ * out:
+ *    data - the pointer to data buffer returned from plugin
+ *    0 - Success, no more data to save
+ *    > 0 - Success and the number of bytes returned in **data buffer
+ *    -1 - Error, no acls data to backup
+ */
+int plugin_backup_acl(JCR *jcr, FF_PKT *ff_pkt, char **data)
+{
+   struct xacl_pkt xacl;
+   Plugin *plugin = (Plugin *)jcr->plugin;
+   bRC rc;
+
+   Dmsg0(dbglvl, "plugin_backup_acl\n");
+
+   /* check of input variables */
+   if (!plugin || !jcr->plugin_ctx || !data) {
+      return 0;
+   }
+
+   /* prepare the xacl packet */
+   memset(&xacl, 0, sizeof(xacl));
+   xacl.pkt_size = sizeof(xacl);
+   xacl.pkt_end = sizeof(xacl);
+   xacl.func = BACL_BACKUP;
+
+   rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
+
+   /* check out status */
+   if (rc != bRC_OK){
+      Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
+      return -1;
+   }
+   if (xacl.count > 0){
+      /* we have something to save, so prepare return data */
+      *data = xacl.content;
+      return xacl.count;
+   }
+
+   return 0;
+}
+
+/*
+ * Called here when Bacula got ACL stream to restore but not every stream but
+ *    a specific one: STREAM_XACL_PLUGIN_ACL which means a plugin has to
+ *    be called.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    data - content to restore
+ *    length - the length of the content to restore
+ * out:
+ *    true - when successful
+ *    false - on any Error
+ */
+bool plugin_restore_acl(JCR *jcr, char *data, uint32_t length)
+{
+   struct xacl_pkt xacl;
+   Plugin *plugin = (Plugin *)jcr->plugin;
+   bRC rc;
+
+   Dmsg0(dbglvl, "plugin_restore_acl\n");
+
+   /* check of input variables */
+   if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
+      return true;
+   }
+
+   /* prepare the xacl packet */
+   memset(&xacl, 0, sizeof(xacl));
+   xacl.pkt_size = sizeof(xacl);
+   xacl.pkt_end = sizeof(xacl);
+   xacl.func = BACL_RESTORE;
+   xacl.content = data;
+   xacl.count = length;
+
+   rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
+
+   /* check out status */
+   if (rc != bRC_OK){
+      Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
+      return false;
+   }
+
+   return true;
+}
+
+/*
+ * The Plugin XATTR data backup. We are using a new Plugin callback:
+ *    handleXACLdata() for that. Check plugin_backup_acl for new callback
+ *    description.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    ff_pkt - file save packet
+ *    data is a pointer to variable returned
+ * out:
+ *    data - the pointer to data buffer returned from plugin
+ *    0 - Success, no more data to save
+ *    >0 - Success and the number of bytes returned in **data buffer
+ *    <0 - Error
+ */
+int plugin_backup_xattr(JCR *jcr, FF_PKT *ff_pkt, char **data)
+{
+
+   struct xacl_pkt xacl;
+   Plugin *plugin = (Plugin *)jcr->plugin;
+   bRC rc;
+
+   Dmsg0(dbglvl, "plugin_backup_xattr\n");
+
+   /* check of input variables */
+   if (!plugin || !jcr->plugin_ctx || !data) {
+      return 0;
+   }
+
+   /* prepare the xacl packet */
+   memset(&xacl, 0, sizeof(xacl));
+   xacl.pkt_size = sizeof(xacl);
+   xacl.pkt_end = sizeof(xacl);
+   xacl.func = BXATTR_BACKUP;
+
+   rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
+
+   /* check out status */
+   if (rc != bRC_OK){
+      Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
+      return -1;
+   }
+   if (xacl.count > 0){
+      /* we have something to save, so prepare return data */
+      *data = xacl.content;
+      return xacl.count;
+   }
+
+   return 0;
+}
+
+/*
+ * Called here when Bacula got XATTR stream to restore but not every stream but
+ *    a specific one: STREAM_XACL_PLUGIN_XATTR which means a plugin has to
+ *    be called.
+ *
+ * in:
+ *    jcr - Job Control Record
+ *    data - content to restore
+ *    length - the length of the content to restore
+ * out:
+ *    true - when successful
+ *    false - on any Error
+ */
+bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length)
+{
+   struct xacl_pkt xacl;
+   Plugin *plugin = (Plugin *)jcr->plugin;
+   bRC rc;
+
+   Dmsg0(dbglvl, "plugin_restore_xattr\n");
+
+   /* check of input variables */
+   if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
+      return true;
+   }
+
+   /* prepare the xacl packet */
+   memset(&xacl, 0, sizeof(xacl));
+   xacl.pkt_size = sizeof(xacl);
+   xacl.pkt_end = sizeof(xacl);
+   xacl.func = BXATTR_RESTORE;
+   xacl.content = data;
+   xacl.count = length;
+
+   rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
+
+   /* check out status */
+   if (rc != bRC_OK){
+      Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
+      return false;
+   }
+
+   return true;
+}
+
 /*
  * Print to file the plugin info.
  */
@@ -1101,14 +1301,14 @@ void load_fd_plugins(const char *plugin_dir)
       return;
    }
 
-   bplugin_list = New(alist(10, not_owned_by_alist));
+   b_plugin_list = New(alist(10, not_owned_by_alist));
    Dsm_check(999);
    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
                      is_plugin_compatible)) {
       /* Either none found, or some error */
-      if (bplugin_list->size() == 0) {
-         delete bplugin_list;
-         bplugin_list = NULL;
+      if (b_plugin_list->size() == 0) {
+         delete b_plugin_list;
+         b_plugin_list = NULL;
          Dmsg0(dbglvl, "No plugins loaded\n");
          return;
       }
@@ -1122,11 +1322,11 @@ void load_fd_plugins(const char *plugin_dir)
    plugin_blseek = my_plugin_blseek;
    Dsm_check(999);
 
-   /* 
+   /*
     * Verify that the plugin is acceptable, and print information
     *  about it.
     */
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
    }
@@ -1144,7 +1344,7 @@ static bool is_plugin_compatible(Plugin *plugin)
    pInfo *info = (pInfo *)plugin->pinfo;
    Dmsg0(dbglvl, "is_plugin_compatible called\n");
    Dsm_check(999);
-   if (debug_level >= 50) {
+   if (chk_dbglvl(50)) {
       dump_fd_plugin(plugin, stdin);
    }
    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
@@ -1164,6 +1364,7 @@ static bool is_plugin_compatible(Plugin *plugin)
    }
    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
        strcmp(info->plugin_license, "AGPLv3") != 0 &&
+       strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
            plugin->file, info->plugin_license);
       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
@@ -1176,7 +1377,7 @@ static bool is_plugin_compatible(Plugin *plugin)
            plugin->file, sizeof(pInfo), info->size);
       return false;
    }
-      
+
    Dsm_check(999);
    return true;
 }
@@ -1184,7 +1385,7 @@ static bool is_plugin_compatible(Plugin *plugin)
 
 /**
  * Create a new instance of each plugin for this Job
- *   Note, bplugin_list can exist but jcr->plugin_ctx_list can
+ *   Note, b_plugin_list can exist but jcr->plugin_ctx_list can
  *   be NULL if no plugins were loaded.
  */
 void new_plugins(JCR *jcr)
@@ -1193,7 +1394,7 @@ void new_plugins(JCR *jcr)
    int i;
 
    Dsm_check(999);
-   if (!bplugin_list) {
+   if (!b_plugin_list) {
       Dmsg0(dbglvl, "plugin list is NULL\n");
       return;
    }
@@ -1201,7 +1402,7 @@ void new_plugins(JCR *jcr)
       return;
    }
 
-   int num = bplugin_list->size();
+   int num = b_plugin_list->size();
 
    if (num == 0) {
       Dmsg0(dbglvl, "No plugins loaded\n");
@@ -1212,7 +1413,7 @@ void new_plugins(JCR *jcr)
 
    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
-   foreach_alist_index(i, plugin, bplugin_list) {
+   foreach_alist_index(i, plugin, b_plugin_list) {
       Dsm_check(999);
       /* Start a new instance of each plugin */
       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
@@ -1221,6 +1422,7 @@ void new_plugins(JCR *jcr)
       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
       plugin_ctx_list[i].pContext = NULL;
       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
+         Dmsg1(000, "Plugin %s will be disabled\n", plugin->file);
          b_ctx->disabled = true;
       }
    }
@@ -1239,14 +1441,14 @@ void free_plugins(JCR *jcr)
    Plugin *plugin;
    int i;
 
-   if (!bplugin_list || !jcr->plugin_ctx_list) {
+   if (!b_plugin_list || !jcr->plugin_ctx_list) {
       return;                         /* no plugins, nothing to do */
    }
 
    Dsm_check(999);
    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
-   foreach_alist_index(i, plugin, bplugin_list) {   
+   foreach_alist_index(i, plugin, b_plugin_list) {
       /* Free the plugin instance */
       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
@@ -1257,7 +1459,7 @@ void free_plugins(JCR *jcr)
    jcr->plugin_ctx_list = NULL;
 }
 
-static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
+static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
 {
    JCR *jcr = bfd->jcr;
    Plugin *plugin = (Plugin *)jcr->plugin;
@@ -1278,6 +1480,7 @@ static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode
    io.mode = mode;
    io.win32 = false;
    io.lerror = 0;
+   io.status = -1;
    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
    bfd->berrno = io.io_errno;
    if (io.win32) {
@@ -1309,6 +1512,7 @@ static int my_plugin_bclose(BFILE *bfd)
    io.buf = NULL;
    io.win32 = false;
    io.lerror = 0;
+   io.status = -1;
    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
    bfd->berrno = io.io_errno;
    if (io.win32) {
@@ -1341,6 +1545,7 @@ static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
    io.win32 = false;
    io.offset = 0;
    io.lerror = 0;
+   io.status = -1;
    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
    bfd->offset = io.offset;
    bfd->berrno = io.io_errno;
@@ -1373,6 +1578,7 @@ static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
    io.buf = (char *)buf;
    io.win32 = false;
    io.lerror = 0;
+   io.status = -1;
    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
    bfd->berrno = io.io_errno;
    if (io.win32) {
@@ -1445,8 +1651,11 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
    case bVarDistName:
       *(char **)value = dist_name;
       break;
-   case bVarBEEF:
-      *((int *)value) = beef;
+   case bVarPrevJobName:
+      break;
+   case bVarPrefixLinks:
+      break;
+   case bVarxxx:
       break;
    default:
       break;
@@ -1498,22 +1707,26 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
       *((int *)value) = (int)jcr->accurate;
       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
       break;
+   case bVarInteractiveSession:
+      *(int *)value = (int)jcr->interactive_session;
+      break;
+   case bVarFileIndex:
+      *(int *)value = (int)jcr->JobFiles;
+      break;
    case bVarFileSeen:
       break;                 /* a write only variable, ignore read request */
    case bVarVssObject:
 #ifdef HAVE_WIN32
-      if (g_pVSSClient) {
-         *(void **)value = g_pVSSClient->GetVssObject();
+      if (jcr->pVSSClient) {
+         *(void **)value = jcr->pVSSClient->GetVssObject();
          break;
        }
 #endif
        return bRC_Error;
    case bVarVssDllHandle:
 #ifdef HAVE_WIN32
-      if (g_pVSSClient) {
-         *(void **)value = g_pVSSClient->GetVssDllHandle();
-         break;
-       }
+      *(void **)value = vsslib;
+      break;
 #endif
        return bRC_Error;
    case bVarWhere:
@@ -1525,12 +1738,16 @@ static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
    case bVarPrefixLinks:
       *(int *)value = (int)jcr->prefix_links;
       break;
+   case bVarReplace:
+      *((int*)value) = jcr->replace;
+      Dmsg1(dbglvl, "Bacula: return replace=%c\n", jcr->replace);
+      break;
    case bVarFDName:             /* get warning with g++ if we missed one */
    case bVarWorkingDir:
    case bVarExePath:
    case bVarVersion:
    case bVarDistName:
-   case bVarBEEF:
+   case bVarxxx:
       break;
    }
    Dsm_check(999);
@@ -1549,12 +1766,15 @@ static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
    if (!jcr) {
       return bRC_Error;
    }
-// Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
+// Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
    switch (var) {
    case bVarFileSeen:
       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
          return bRC_Error;
-      } 
+      }
+      break;
+   case bVarInteractiveSession:
+      jcr->interactive_session = (((intptr_t) value) == 1);
       break;
    default:
       break;
@@ -1680,7 +1900,7 @@ static bRC baculaAddExclude(bpContext *ctx, const char *file)
       return bRC_Error;
    }
 
-   if (!bctx->exclude) {  
+   if (!bctx->exclude) {
       bctx->exclude = new_exclude(jcr);
    }
 
@@ -1831,7 +2051,7 @@ static bRC baculaNewPreInclude(bpContext *ctx)
    return bRC_OK;
 }
 
-/* 
+/*
  * Check if a file have to be backuped using Accurate code
  */
 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
@@ -1848,10 +2068,10 @@ static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
    if (!sp) {
       goto bail_out;
    }
-   
+
    ff_pkt = jcr->ff;
    /*
-    * Copy fname and link because save_file() zaps them.  This 
+    * Copy fname and link because save_file() zaps them.  This
     *  avoids zaping the plugin's strings.
     */
    ff_pkt->type = sp->type;
@@ -1871,7 +2091,7 @@ static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
    }
 
    /* check_changes() can update delta sequence number, return it to the
-    * plugin 
+    * plugin
     */
    sp->delta_seq = ff_pkt->delta_seq;
    sp->accurate_found = ff_pkt->accurate_found;
@@ -1882,7 +2102,7 @@ bail_out:
    return ret;
 }
 
-/* 
+/*
  * Check if a file would be saved using current Include/Exclude code
  */
 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
@@ -1902,7 +2122,7 @@ static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
    if (!sp) {
       goto bail_out;
    }
-   
+
    ff_pkt = jcr->ff;
 
    /* Probably not needed, but keep a copy */
@@ -1927,7 +2147,7 @@ bail_out:
 
 #ifdef TEST_PROGRAM
 
-int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
+int     (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
 int     (*plugin_bclose)(JCR *jcr) = NULL;
 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
@@ -1951,7 +2171,7 @@ int main(int argc, char *argv[])
    JCR *jcr2 = &mjcr2;
 
    strcpy(my_name, "test-fd");
-    
+
    getcwd(plugin_dir, sizeof(plugin_dir)-1);
    load_fd_plugins(plugin_dir);