]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/filed/job.c
Clear delta sequence when not using in plugin
[bacula/bacula] / bacula / src / filed / job.c
index 4054b29f30438db510d97222b153ef92ee13d4de..e81f608342c3203ca1079fa4f0d21b4e3cdb9e32 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2010 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
 
    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 two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
    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 General Public License
+   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.
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
@@ -30,8 +30,6 @@
  *
  *    Kern Sibbald, October MM
  *
  *
  *    Kern Sibbald, October MM
  *
- *   Version $Id$
- *
  */
 
 #include "bacula.h"
  */
 
 #include "bacula.h"
@@ -44,7 +42,7 @@ static pthread_mutex_t vss_mutex = PTHREAD_MUTEX_INITIALIZER;
 static int enable_vss = 0;
 #endif
 
 static int enable_vss = 0;
 #endif
 
-/*
+/**
  * As Windows saves ACLs as part of the standard backup stream
  * we just pretend here that is has implicit acl support.
  */
  * As Windows saves ACLs as part of the standard backup stream
  * we just pretend here that is has implicit acl support.
  */
@@ -90,8 +88,13 @@ static int runscript_cmd(JCR *jcr);
 static int runbefore_cmd(JCR *jcr);
 static int runafter_cmd(JCR *jcr);
 static int runbeforenow_cmd(JCR *jcr);
 static int runbefore_cmd(JCR *jcr);
 static int runafter_cmd(JCR *jcr);
 static int runbeforenow_cmd(JCR *jcr);
-static void set_options(findFOPTS *fo, const char *opts);
+static int restore_object_cmd(JCR *jcr);
+static int set_options(findFOPTS *fo, const char *opts);
 static void set_storage_auth_key(JCR *jcr, char *key);
 static void set_storage_auth_key(JCR *jcr, char *key);
+static int sm_dump_cmd(JCR *jcr);
+#ifdef DEVELOPER
+static int exit_cmd(JCR *jcr);
+#endif
 
 /* Exported functions */
 
 
 /* Exported functions */
 
@@ -101,7 +104,7 @@ struct s_cmds {
    int monitoraccess; /* specify if monitors have access to this function */
 };
 
    int monitoraccess; /* specify if monitors have access to this function */
 };
 
-/*
+/**
  * The following are the recognized commands from the Director.
  */
 static struct s_cmds cmds[] = {
  * The following are the recognized commands from the Director.
  */
 static struct s_cmds cmds[] = {
@@ -113,7 +116,7 @@ static struct s_cmds cmds[] = {
    {"fileset",      fileset_cmd,   0},
    {"JobId=",       job_cmd,       0},
    {"level = ",     level_cmd,     0},
    {"fileset",      fileset_cmd,   0},
    {"JobId=",       job_cmd,       0},
    {"level = ",     level_cmd,     0},
-   {"restore",      restore_cmd,   0},
+   {"restore ",     restore_cmd,   0},
    {"endrestore",   end_restore_cmd, 0},
    {"session",      session_cmd,   0},
    {"status",       status_cmd,    1},
    {"endrestore",   end_restore_cmd, 0},
    {"session",      session_cmd,   0},
    {"status",       status_cmd,    1},
@@ -126,6 +129,11 @@ static struct s_cmds cmds[] = {
    {"RunAfterJob",  runafter_cmd,  0},
    {"Run",          runscript_cmd, 0},
    {"accurate",     accurate_cmd,  0},
    {"RunAfterJob",  runafter_cmd,  0},
    {"Run",          runscript_cmd, 0},
    {"accurate",     accurate_cmd,  0},
+   {"restoreobject", restore_object_cmd, 0},
+   {"sm_dump",      sm_dump_cmd, 0},
+#ifdef DEVELOPER
+   {"exit",         exit_cmd, 0},
+#endif
    {NULL,       NULL}                  /* list terminator */
 };
 
    {NULL,       NULL}                  /* list terminator */
 };
 
@@ -137,6 +145,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 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 endrestoreobjectcmd[] = "restoreobject end\n";
 static char verifycmd[]   = "verify level=%30s";
 static char estimatecmd[] = "estimate listing=%d";
 static char runbefore[]   = "RunBeforeJob %s";
 static char verifycmd[]   = "verify level=%30s";
 static char estimatecmd[] = "estimate listing=%d";
 static char runbefore[]   = "RunBeforeJob %s";
@@ -167,6 +177,7 @@ static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
 static char OKRunAfter[]  = "2000 OK RunAfter\n";
 static char OKRunScript[] = "2000 OK RunScript\n";
 static char BADcmd[]      = "2902 Bad %s\n";
 static char OKRunAfter[]  = "2000 OK RunAfter\n";
 static char OKRunScript[] = "2000 OK RunScript\n";
 static char BADcmd[]      = "2902 Bad %s\n";
+static char OKRestoreObject[] = "2000 OK ObjectRestored\n";
 
 
 /* Responses received from Storage Daemon */
 
 
 /* Responses received from Storage Daemon */
@@ -187,7 +198,7 @@ static char read_open[]    = "read open session = %s %ld %ld %ld %ld %ld %ld\n";
 static char read_data[]    = "read data %d\n";
 static char read_close[]   = "read close session %d\n";
 
 static char read_data[]    = "read data %d\n";
 static char read_close[]   = "read close session %d\n";
 
-/*
+/**
  * Accept requests from a Director
  *
  * NOTE! We are running as a separate thread
  * Accept requests from a Director
  *
  * NOTE! We are running as a separate thread
@@ -227,10 +238,12 @@ void *handle_client_request(void *dirp)
    JCR *jcr;
    BSOCK *dir = (BSOCK *)dirp;
    const char jobname[12] = "*Director*";
    JCR *jcr;
    BSOCK *dir = (BSOCK *)dirp;
    const char jobname[12] = "*Director*";
+// saveCWD save_cwd;
 
    jcr = new_jcr(sizeof(JCR), filed_free_jcr); /* create JCR */
    jcr->dir_bsock = dir;
    jcr->ff = init_find_files();
 
    jcr = new_jcr(sizeof(JCR), filed_free_jcr); /* create JCR */
    jcr->dir_bsock = dir;
    jcr->ff = init_find_files();
+// save_cwd.save(jcr);
    jcr->start_time = time(NULL);
    jcr->RunScripts = New(alist(10, not_owned_by_alist));
    jcr->last_fname = get_pool_memory(PM_FNAME);
    jcr->start_time = time(NULL);
    jcr->RunScripts = New(alist(10, not_owned_by_alist));
    jcr->last_fname = get_pool_memory(PM_FNAME);
@@ -312,6 +325,7 @@ void *handle_client_request(void *dirp)
    dir->signal(BNET_TERMINATE);
 
    free_plugins(jcr);                 /* release instantiated plugins */
    dir->signal(BNET_TERMINATE);
 
    free_plugins(jcr);                 /* release instantiated plugins */
+   free_and_null_pool_memory(jcr->job_metadata);
 
    /* Clean up fileset */
    FF_PKT *ff = jcr->ff;
 
    /* Clean up fileset */
    FF_PKT *ff = jcr->ff;
@@ -342,13 +356,13 @@ void *handle_client_request(void *dirp)
             fo->base.destroy();
             fo->fstype.destroy();
             fo->drivetype.destroy();
             fo->base.destroy();
             fo->fstype.destroy();
             fo->drivetype.destroy();
-            if (fo->ignoredir != NULL) {
-               free(fo->ignoredir);
-            }
          }
          incexe->opts_list.destroy();
          incexe->name_list.destroy();
          incexe->plugin_list.destroy();
          }
          incexe->opts_list.destroy();
          incexe->name_list.destroy();
          incexe->plugin_list.destroy();
+         if (incexe->ignoredir) {
+            free(incexe->ignoredir);
+         }
       }
       fileset->include_list.destroy();
 
       }
       fileset->include_list.destroy();
 
@@ -371,6 +385,9 @@ void *handle_client_request(void *dirp)
          incexe->opts_list.destroy();
          incexe->name_list.destroy();
          incexe->plugin_list.destroy();
          incexe->opts_list.destroy();
          incexe->name_list.destroy();
          incexe->plugin_list.destroy();
+         if (incexe->ignoredir) {
+            free(incexe->ignoredir);
+         }
       }
       fileset->exclude_list.destroy();
       free(fileset);
       }
       fileset->exclude_list.destroy();
       free(fileset);
@@ -378,15 +395,36 @@ void *handle_client_request(void *dirp)
    ff->fileset = NULL;
    Dmsg0(100, "Calling term_find_files\n");
    term_find_files(jcr->ff);
    ff->fileset = NULL;
    Dmsg0(100, "Calling term_find_files\n");
    term_find_files(jcr->ff);
+// save_cwd.restore(jcr);
+// save_cwd.release();
    jcr->ff = NULL;
    Dmsg0(100, "Done with term_find_files\n");
    free_jcr(jcr);                     /* destroy JCR record */
    Dmsg0(100, "Done with free_jcr\n");
    Dsm_check(1);
    jcr->ff = NULL;
    Dmsg0(100, "Done with term_find_files\n");
    free_jcr(jcr);                     /* destroy JCR record */
    Dmsg0(100, "Done with free_jcr\n");
    Dsm_check(1);
+   garbage_collect_memory_pool();
    return NULL;
 }
 
    return NULL;
 }
 
-/*
+static int sm_dump_cmd(JCR *jcr)
+{
+   close_memory_pool();
+   sm_dump(false, true);
+   jcr->dir_bsock->fsend("2000 sm_dump OK\n");
+   return 1;
+}
+
+#ifdef DEVELOPER
+static int exit_cmd(JCR *jcr)
+{
+   jcr->dir_bsock->fsend("2000 exit OK\n");
+   terminate_filed(0);
+   return 0;
+}
+#endif
+
+
+/**
  * Hello from Director he must identify himself and provide his
  *  password.
  */
  * Hello from Director he must identify himself and provide his
  *  password.
  */
@@ -401,7 +439,7 @@ static int hello_cmd(JCR *jcr)
    return 1;
 }
 
    return 1;
 }
 
-/*
+/**
  * Cancel a Job
  */
 static int cancel_cmd(JCR *jcr)
  * Cancel a Job
  */
 static int cancel_cmd(JCR *jcr)
@@ -414,13 +452,13 @@ static int cancel_cmd(JCR *jcr)
       if (!(cjcr=get_jcr_by_full_name(Job))) {
          dir->fsend(_("2901 Job %s not found.\n"), Job);
       } else {
       if (!(cjcr=get_jcr_by_full_name(Job))) {
          dir->fsend(_("2901 Job %s not found.\n"), Job);
       } else {
+         generate_plugin_event(cjcr, bEventCancelCommand, NULL);
+         set_jcr_job_status(cjcr, JS_Canceled);
          if (cjcr->store_bsock) {
             cjcr->store_bsock->set_timed_out();
             cjcr->store_bsock->set_terminated();
          if (cjcr->store_bsock) {
             cjcr->store_bsock->set_timed_out();
             cjcr->store_bsock->set_terminated();
-            pthread_kill(cjcr->my_thread_id, TIMEOUT_SIGNAL);
+            cjcr->my_thread_send_signal(TIMEOUT_SIGNAL);
          }
          }
-         generate_plugin_event(cjcr, bEventCancelCommand, NULL);
-         set_jcr_job_status(cjcr, JS_Canceled);
          free_jcr(cjcr);
          dir->fsend(_("2001 Job %s marked to be canceled.\n"), Job);
       }
          free_jcr(cjcr);
          dir->fsend(_("2001 Job %s marked to be canceled.\n"), Job);
       }
@@ -432,7 +470,7 @@ static int cancel_cmd(JCR *jcr)
 }
 
 
 }
 
 
-/*
+/**
  * Set debug level as requested by the Director
  *
  */
  * Set debug level as requested by the Director
  *
  */
@@ -471,7 +509,7 @@ static int estimate_cmd(JCR *jcr)
    return 1;
 }
 
    return 1;
 }
 
-/*
+/**
  * Get JobId and Storage Daemon Authorization key from Director
  */
 static int job_cmd(JCR *jcr)
  * Get JobId and Storage Daemon Authorization key from Director
  */
 static int job_cmd(JCR *jcr)
@@ -609,6 +647,98 @@ static int runscript_cmd(JCR *jcr)
    return dir->fsend(OKRunScript);
 }
 
    return dir->fsend(OKRunScript);
 }
 
+/*
+ * This reads data sent from the Director from the
+ *   RestoreObject table that allows us to get objects
+ *   that were backed up (VSS .xml data) and are needed
+ *   before starting the restore.
+ */
+static int restore_object_cmd(JCR *jcr)
+{
+   BSOCK *dir = jcr->dir_bsock;
+   int32_t FileIndex;
+   restore_object_pkt rop;
+
+   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);
+   }
+
+   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;
+   }
+
+   Dmsg6(100, "Recv object: JobId=%u objlen=%d full_len=%d objinx=%d objtype=%d FI=%d\n",
+         rop.JobId, rop.object_len, rop.object_full_len, 
+         rop.object_index, rop.object_type, FileIndex);
+   /* Read Object name */
+   if (dir->recv() < 0) {
+      goto bail_out;
+   }
+   Dmsg2(100, "Recv Oname object: len=%d Oname=%s\n", dir->msglen, dir->msg);
+   rop.object_name = bstrdup(dir->msg);
+
+   /* Read Object */
+   if (dir->recv() < 0) {
+      goto bail_out;
+   }
+   /* Transfer object from message buffer, and get new message buffer */
+   rop.object = dir->msg;
+   dir->msg = get_pool_memory(PM_MESSAGE);
+
+   /* If object is compressed, uncompress it */
+   if (rop.object_compression == 1) {   /* zlib level 9 */
+      int stat;
+      int out_len = rop.object_full_len + 100;
+      POOLMEM *obj = get_memory(out_len);
+      Dmsg2(100, "Inflating from %d to %d\n", rop.object_len, rop.object_full_len);
+      stat = Zinflate(rop.object, rop.object_len, obj, out_len);
+      Dmsg1(100, "Zinflate stat=%d\n", stat);
+      if (out_len != rop.object_full_len) {
+         Jmsg3(jcr, M_ERROR, 0, ("Decompression failed. Len wanted=%d got=%d. Object=%s\n"),
+            rop.object_full_len, out_len, rop.object_name);
+      }
+      free_pool_memory(rop.object);   /* release compressed object */
+      rop.object = obj;               /* new uncompressed object */
+      rop.object_len = out_len;
+   }
+   Dmsg2(100, "Recv Object: len=%d Object=%s\n", rop.object_len, rop.object);
+   /* Special Job meta data */
+   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);
+   }
+
+   if (rop.object_name) {
+      free(rop.object_name);
+   }
+   if (rop.object) {
+      free_pool_memory(rop.object);
+   }
+
+   Dmsg1(100, "Send: %s", OKRestoreObject);
+   return 1;
+
+bail_out:
+   dir->fsend(_("2909 Bad RestoreObject command.\n"));
+   return 0;
+
+}
+
 
 static bool init_fileset(JCR *jcr)
 {
 
 static bool init_fileset(JCR *jcr)
 {
@@ -631,39 +761,30 @@ static bool init_fileset(JCR *jcr)
    return true;
 }
 
    return true;
 }
 
-static findFOPTS *start_options(FF_PKT *ff)
+static void append_file(JCR *jcr, findINCEXE *incexe, 
+                        const char *buf, bool is_file)
 {
 {
-   int state = ff->fileset->state;
-   findINCEXE *incexe = ff->fileset->incexe;
+   if (is_file) {
+      incexe->name_list.append(new_dlistString(buf));
 
 
-   if (state != state_options) {
-      ff->fileset->state = state_options;
-      findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS));
-      memset(fo, 0, sizeof(findFOPTS));
-      fo->regex.init(1, true);
-      fo->regexdir.init(1, true);
-      fo->regexfile.init(1, true);
-      fo->wild.init(1, true);
-      fo->wilddir.init(1, true);
-      fo->wildfile.init(1, true);
-      fo->wildbase.init(1, true);
-      fo->base.init(1, true);
-      fo->fstype.init(1, true);
-      fo->drivetype.init(1, true);
-      incexe->current_opts = fo;
-      incexe->opts_list.append(fo);
-   }
-   return incexe->current_opts;
+   } else if (me->plugin_directory) {
+      generate_plugin_event(jcr, bEventPluginCommand, (void *)buf);
+      incexe->plugin_list.append(new_dlistString(buf));
 
 
+   } else {
+      Jmsg(jcr, M_FATAL, 0, 
+           _("Plugin Directory not defined. Cannot use plugin: \"%s\"\n"),
+           buf);
+   }
 }
 
 }
 
-/*
+/**
  * Add fname to include/exclude fileset list. First check for
  * | and < and if necessary perform command.
  */
  * Add fname to include/exclude fileset list. First check for
  * | and < and if necessary perform command.
  */
-static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *fileset,
-                                bool is_file)
+void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
 {
 {
+   findFILESET *fileset = jcr->ff->fileset;
    char *p;
    BPIPE *bpipe;
    POOLMEM *fn;
    char *p;
    BPIPE *bpipe;
    POOLMEM *fn;
@@ -690,11 +811,7 @@ static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *filese
       free_pool_memory(fn);
       while (fgets(buf, sizeof(buf), bpipe->rfd)) {
          strip_trailing_junk(buf);
       free_pool_memory(fn);
       while (fgets(buf, sizeof(buf), bpipe->rfd)) {
          strip_trailing_junk(buf);
-         if (is_file) {
-            fileset->incexe->name_list.append(new_dlistString(buf));
-         } else {
-            fileset->incexe->plugin_list.append(new_dlistString(buf));
-         }
+         append_file(jcr, fileset->incexe, buf, is_file);
       }
       if ((stat=close_bpipe(bpipe)) != 0) {
          berrno be;
       }
       if ((stat=close_bpipe(bpipe)) != 0) {
          berrno be;
@@ -708,36 +825,200 @@ static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *filese
       p++;                      /* skip over < */
       if ((ffd = fopen(p, "rb")) == NULL) {
          berrno be;
       p++;                      /* skip over < */
       if ((ffd = fopen(p, "rb")) == NULL) {
          berrno be;
-         Jmsg(jcr, M_FATAL, 0, _("Cannot open FileSet input file: %s. ERR=%s\n"),
+         Jmsg(jcr, M_FATAL, 0, 
+              _("Cannot open FileSet input file: %s. ERR=%s\n"),
             p, be.bstrerror());
          return;
       }
       while (fgets(buf, sizeof(buf), ffd)) {
          strip_trailing_junk(buf);
             p, be.bstrerror());
          return;
       }
       while (fgets(buf, sizeof(buf), ffd)) {
          strip_trailing_junk(buf);
-         Dmsg1(100, "%s\n", buf);
-         if (is_file) {
-            fileset->incexe->name_list.append(new_dlistString(buf));
-         } else {
-            fileset->incexe->plugin_list.append(new_dlistString(buf));
-         }
+         append_file(jcr, fileset->incexe, buf, is_file);
       }
       fclose(ffd);
       break;
    default:
       }
       fclose(ffd);
       break;
    default:
-      if (is_file) {
-         fileset->incexe->name_list.append(new_dlistString(fname));
-      } else {
-         if (me->plugin_directory) {
-            fileset->incexe->plugin_list.append(new_dlistString(fname));
-         } else {
-            Jmsg(jcr, M_FATAL, 0, _("Plugin Directory not defined. Cannot use plugin: \"%\"\n"),
-               fname);
-         }
-      }
+      append_file(jcr, fileset->incexe, fname, is_file);
       break;
    }
 }
 
       break;
    }
 }
 
+void set_incexe(JCR *jcr, findINCEXE *incexe)
+{
+   findFILESET *fileset = jcr->ff->fileset;
+   fileset->incexe = incexe;
+}
+
+
+/**
+ * Define a new Exclude block in the FileSet
+ */
+findINCEXE *new_exclude(JCR *jcr)
+{
+   findFILESET *fileset = jcr->ff->fileset;
+
+   /* New exclude */
+   fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
+   memset(fileset->incexe, 0, sizeof(findINCEXE));
+   fileset->incexe->opts_list.init(1, true);
+   fileset->incexe->name_list.init();
+   fileset->incexe->plugin_list.init();
+   fileset->exclude_list.append(fileset->incexe);
+   return fileset->incexe;
+}
+
+/**
+ * Define a new Include block in the FileSet
+ */
+findINCEXE *new_include(JCR *jcr)
+{
+   findFILESET *fileset = jcr->ff->fileset;
+
+   /* New include */
+   fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
+   memset(fileset->incexe, 0, sizeof(findINCEXE));
+   fileset->incexe->opts_list.init(1, true);
+   fileset->incexe->name_list.init(); /* for dlist;  was 1,true for alist */
+   fileset->incexe->plugin_list.init();
+   fileset->include_list.append(fileset->incexe);
+   return fileset->incexe;
+}
+
+/**
+ * Define a new preInclude block in the FileSet
+ *   That is the include is prepended to the other
+ *   Includes.  This is used for plugin exclusions.
+ */
+findINCEXE *new_preinclude(JCR *jcr)
+{
+   findFILESET *fileset = jcr->ff->fileset;
+
+   /* New pre-include */
+   fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
+   memset(fileset->incexe, 0, sizeof(findINCEXE));
+   fileset->incexe->opts_list.init(1, true);
+   fileset->incexe->name_list.init(); /* for dlist;  was 1,true for alist */
+   fileset->incexe->plugin_list.init();
+   fileset->include_list.prepend(fileset->incexe);
+   return fileset->incexe;
+}
+
+static findFOPTS *start_options(FF_PKT *ff)
+{
+   int state = ff->fileset->state;
+   findINCEXE *incexe = ff->fileset->incexe;
+
+   if (state != state_options) {
+      ff->fileset->state = state_options;
+      findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS));
+      memset(fo, 0, sizeof(findFOPTS));
+      fo->regex.init(1, true);
+      fo->regexdir.init(1, true);
+      fo->regexfile.init(1, true);
+      fo->wild.init(1, true);
+      fo->wilddir.init(1, true);
+      fo->wildfile.init(1, true);
+      fo->wildbase.init(1, true);
+      fo->base.init(1, true);
+      fo->fstype.init(1, true);
+      fo->drivetype.init(1, true);
+      incexe->current_opts = fo;
+      incexe->opts_list.append(fo);
+   }
+   return incexe->current_opts;
+}
+
+/*
+ * Used by plugins to define a new options block
+ */
+void new_options(JCR *jcr, findINCEXE *incexe)
+{
+   if (!incexe) {
+      incexe = jcr->ff->fileset->incexe;
+   }
+   findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS));
+   memset(fo, 0, sizeof(findFOPTS));
+   fo->regex.init(1, true);
+   fo->regexdir.init(1, true);
+   fo->regexfile.init(1, true);
+   fo->wild.init(1, true);
+   fo->wilddir.init(1, true);
+   fo->wildfile.init(1, true);
+   fo->wildbase.init(1, true);
+   fo->base.init(1, true);
+   fo->fstype.init(1, true);
+   fo->drivetype.init(1, true);
+   incexe->current_opts = fo;
+   incexe->opts_list.prepend(fo);
+   jcr->ff->fileset->state = state_options;
+}
+
+/**
+ * Add a regex to the current fileset
+ */
+int add_regex_to_fileset(JCR *jcr, const char *item, int type)
+{
+   findFOPTS *current_opts = start_options(jcr->ff);
+   regex_t *preg;
+   int rc;
+   char prbuf[500];
+
+   preg = (regex_t *)malloc(sizeof(regex_t));
+   if (current_opts->flags & FO_IGNORECASE) {
+      rc = regcomp(preg, item, REG_EXTENDED|REG_ICASE);
+   } else {
+      rc = regcomp(preg, item, REG_EXTENDED);
+   }
+   if (rc != 0) {
+      regerror(rc, preg, prbuf, sizeof(prbuf));
+      regfree(preg);
+      free(preg);
+      Jmsg(jcr, M_FATAL, 0, _("REGEX %s compile error. ERR=%s\n"), item, prbuf);
+      return state_error;
+   }
+   if (type == ' ') {
+      current_opts->regex.append(preg);
+   } else if (type == 'D') {
+      current_opts->regexdir.append(preg);
+   } else if (type == 'F') {
+      current_opts->regexfile.append(preg);
+   } else {
+      return state_error;
+   }
+   return state_options;
+}
+
+/**
+ * Add a wild card to the current fileset
+ */
+int add_wild_to_fileset(JCR *jcr, const char *item, int type)
+{
+   findFOPTS *current_opts = start_options(jcr->ff);
+
+   if (type == ' ') {
+      current_opts->wild.append(bstrdup(item));
+   } else if (type == 'D') {
+      current_opts->wilddir.append(bstrdup(item));
+   } else if (type == 'F') {
+      current_opts->wildfile.append(bstrdup(item));
+   } else if (type == 'B') {
+      current_opts->wildbase.append(bstrdup(item));
+   } else {
+      return state_error;
+   }
+   return state_options;
+}
+
+
+/**
+ * Add options to the current fileset
+ */
+int add_options_to_fileset(JCR *jcr, const char *item)
+{
+   findFOPTS *current_opts = start_options(jcr->ff);
+
+   set_options(current_opts, item);
+   return state_options;
+}
 
 static void add_fileset(JCR *jcr, const char *item)
 {
 
 static void add_fileset(JCR *jcr, const char *item)
 {
@@ -767,7 +1048,7 @@ static void add_fileset(JCR *jcr, const char *item)
       return;
    }
 
       return;
    }
 
-   /*
+   /**
     * The switch tests the code for validity.
     * The subcode is always good if it is a space, otherwise we must confirm.
     * We set state to state_error first assuming the subcode is invalid,
     * The switch tests the code for validity.
     * The subcode is always good if it is a space, otherwise we must confirm.
     * We set state to state_error first assuming the subcode is invalid,
@@ -779,72 +1060,33 @@ static void add_fileset(JCR *jcr, const char *item)
    }
    switch (code) {
    case 'I':
    }
    switch (code) {
    case 'I':
-      /* New include */
-      fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
-      memset(fileset->incexe, 0, sizeof(findINCEXE));
-      fileset->incexe->opts_list.init(1, true);
-      fileset->incexe->name_list.init(); /* for dlist;  was 1,true for alist */
-      fileset->incexe->plugin_list.init();
-      fileset->include_list.append(fileset->incexe);
+      (void)new_include(jcr);
       break;
    case 'E':
       break;
    case 'E':
-      /* New exclude */
-      fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
-      memset(fileset->incexe, 0, sizeof(findINCEXE));
-      fileset->incexe->opts_list.init(1, true);
-      fileset->incexe->name_list.init();
-      fileset->incexe->plugin_list.init();
-      fileset->exclude_list.append(fileset->incexe);
+      (void)new_exclude(jcr);
       break;
       break;
-   case 'N':
+   case 'N':                             /* null */
       state = state_none;
       break;
       state = state_none;
       break;
-   case 'F':
+   case 'F':                             /* file = */
       /* File item to include or exclude list */
       state = state_include;
       /* File item to include or exclude list */
       state = state_include;
-      add_file_to_fileset(jcr, item, fileset, true);
+      add_file_to_fileset(jcr, item, true);
       break;
       break;
-   case 'P':
+   case 'P':                              /* plugin */
       /* Plugin item to include list */
       state = state_include;
       /* Plugin item to include list */
       state = state_include;
-      add_file_to_fileset(jcr, item, fileset, false);
+      add_file_to_fileset(jcr, item, false);
       break;
       break;
-   case 'R':
-      current_opts = start_options(ff);
-      regex_t *preg;
-      int rc;
-      char prbuf[500];
-      preg = (regex_t *)malloc(sizeof(regex_t));
-      if (current_opts->flags & FO_IGNORECASE) {
-         rc = regcomp(preg, item, REG_EXTENDED|REG_ICASE);
-      } else {
-         rc = regcomp(preg, item, REG_EXTENDED);
-      }
-      if (rc != 0) {
-         regerror(rc, preg, prbuf, sizeof(prbuf));
-         regfree(preg);
-         free(preg);
-         Jmsg(jcr, M_FATAL, 0, _("REGEX %s compile error. ERR=%s\n"), item, prbuf);
-         state = state_error;
-         break;
-      }
-      state = state_options;
-      if (subcode == ' ') {
-         current_opts->regex.append(preg);
-      } else if (subcode == 'D') {
-         current_opts->regexdir.append(preg);
-      } else if (subcode == 'F') {
-         current_opts->regexfile.append(preg);
-      } else {
-         state = state_error;
-      }
+   case 'R':                              /* regex */
+      state = add_regex_to_fileset(jcr, item, subcode);
       break;
    case 'B':
       current_opts = start_options(ff);
       current_opts->base.append(bstrdup(item));
       state = state_options;
       break;
       break;
    case 'B':
       current_opts = start_options(ff);
       current_opts->base.append(bstrdup(item));
       state = state_options;
       break;
-   case 'X':
+   case 'X':                             /* Filetype or Drive type */
       current_opts = start_options(ff);
       state = state_options;
       if (subcode == ' ') {
       current_opts = start_options(ff);
       state = state_options;
       if (subcode == ' ') {
@@ -855,39 +1097,24 @@ static void add_fileset(JCR *jcr, const char *item)
          state = state_error;
       }
       break;
          state = state_error;
       }
       break;
-   case 'W':
-      current_opts = start_options(ff);
-      state = state_options;
-      if (subcode == ' ') {
-         current_opts->wild.append(bstrdup(item));
-      } else if (subcode == 'D') {
-         current_opts->wilddir.append(bstrdup(item));
-      } else if (subcode == 'F') {
-         current_opts->wildfile.append(bstrdup(item));
-      } else if (subcode == 'B') {
-         current_opts->wildbase.append(bstrdup(item));
-      } else {
-         state = state_error;
-      }
+   case 'W':                               /* wild cards */
+      state = add_wild_to_fileset(jcr, item, subcode);
       break;
       break;
-   case 'O':
-      current_opts = start_options(ff);
-      set_options(current_opts, item);
-      state = state_options;
+   case 'O':                                /* Options */
+      state = add_options_to_fileset(jcr, item);
       break;
       break;
-   case 'Z':
-      current_opts = start_options(ff);
-      current_opts->ignoredir = bstrdup(item);
-      state = state_options;
+   case 'Z':                                /* ignore dir */
+      state = state_include;
+      fileset->incexe->ignoredir = bstrdup(item);
       break;
    case 'D':
       current_opts = start_options(ff);
       break;
    case 'D':
       current_opts = start_options(ff);
-//    current_opts->reader = bstrdup(item);
+//    current_opts->reader = bstrdup(item); /* deprecated */
       state = state_options;
       break;
    case 'T':
       current_opts = start_options(ff);
       state = state_options;
       break;
    case 'T':
       current_opts = start_options(ff);
-//    current_opts->writer = bstrdup(item);
+//    current_opts->writer = bstrdup(item); /* deprecated */
       state = state_options;
       break;
    default:
       state = state_options;
       break;
    default:
@@ -941,9 +1168,9 @@ static bool term_fileset(JCR *jcr)
          for (k=0; k<fo->drivetype.size(); k++) {
             Dmsg1(400, "XD %s\n", (char *)fo->drivetype.get(k));
          }
          for (k=0; k<fo->drivetype.size(); k++) {
             Dmsg1(400, "XD %s\n", (char *)fo->drivetype.get(k));
          }
-         if (fo->ignoredir) {
-            Dmsg1(400, "Z %s\n", fo->ignoredir);
-         }
+      }
+      if (incexe->ignoredir) {
+         Dmsg1(400, "Z %s\n", incexe->ignoredir);
       }
       dlistString *node;
       foreach_dlist(node, &incexe->name_list) {
       }
       dlistString *node;
       foreach_dlist(node, &incexe->name_list) {
@@ -1002,12 +1229,12 @@ static bool term_fileset(JCR *jcr)
 }
 
 
 }
 
 
-/*
+/**
  * As an optimization, we should do this during
  *  "compile" time in filed/job.c, and keep only a bit mask
  *  and the Verify options.
  */
  * As an optimization, we should do this during
  *  "compile" time in filed/job.c, and keep only a bit mask
  *  and the Verify options.
  */
-static void set_options(findFOPTS *fo, const char *opts)
+static int set_options(findFOPTS *fo, const char *opts)
 {
    int j;
    const char *p;
 {
    int j;
    const char *p;
@@ -1049,6 +1276,7 @@ static void set_options(findFOPTS *fo, const char *opts)
          break;
       case 'R':                 /* Resource forks and Finder Info */
          fo->flags |= FO_HFSPLUS;
          break;
       case 'R':                 /* Resource forks and Finder Info */
          fo->flags |= FO_HFSPLUS;
+         break;
       case 'r':                 /* read fifo */
          fo->flags |= FO_READFIFO;
          break;
       case 'r':                 /* read fifo */
          fo->flags |= FO_READFIFO;
          break;
@@ -1112,6 +1340,16 @@ static void set_options(findFOPTS *fo, const char *opts)
          }
          fo->AccurateOpts[j] = 0;
          break;
          }
          fo->AccurateOpts[j] = 0;
          break;
+      case 'J':                  /* Basejob options */
+         /* Copy BaseJob Options */
+         for (j=0; *p && *p != ':'; p++) {
+            fo->BaseJobOpts[j] = *p;
+            if (j < (int)sizeof(fo->BaseJobOpts) - 1) {
+               j++;
+            }
+         }
+         fo->BaseJobOpts[j] = 0;
+         break;
       case 'P':                  /* strip path */
          /* Get integer */
          p++;                    /* skip P */
       case 'P':                  /* strip path */
          /* Get integer */
          p++;                    /* skip P */
@@ -1153,15 +1391,17 @@ static void set_options(findFOPTS *fo, const char *opts)
          break;
       }
    }
          break;
       }
    }
+   return state_options;
 }
 
 
 }
 
 
-/*
+/**
  * Director is passing his Fileset
  */
 static int fileset_cmd(JCR *jcr)
 {
    BSOCK *dir = jcr->dir_bsock;
  * Director is passing his Fileset
  */
 static int fileset_cmd(JCR *jcr)
 {
    BSOCK *dir = jcr->dir_bsock;
+   int rtnstat;
 
 #if defined(WIN32_VSS)
    int vss = 0;
 
 #if defined(WIN32_VSS)
    int vss = 0;
@@ -1181,7 +1421,9 @@ static int fileset_cmd(JCR *jcr)
    if (!term_fileset(jcr)) {
       return 0;
    }
    if (!term_fileset(jcr)) {
       return 0;
    }
-   return dir->fsend(OKinc);
+   rtnstat = dir->fsend(OKinc);
+   generate_plugin_event(jcr, bEventEndFileSet);
+   return rtnstat;
 }
 
 static void free_bootstrap(JCR *jcr)
 }
 
 static void free_bootstrap(JCR *jcr)
@@ -1197,9 +1439,11 @@ static void free_bootstrap(JCR *jcr)
 static pthread_mutex_t bsr_mutex = PTHREAD_MUTEX_INITIALIZER;
 static uint32_t bsr_uniq = 0;
 
 static pthread_mutex_t bsr_mutex = PTHREAD_MUTEX_INITIALIZER;
 static uint32_t bsr_uniq = 0;
 
-/* 
+/**
  * The Director sends us the bootstrap file, which
  *   we will in turn pass to the SD.
  * The Director sends us the bootstrap file, which
  *   we will in turn pass to the SD.
+ *   Deprecated.  The bsr is now sent directly from the
+ *   Director to the SD.
  */
 static int bootstrap_cmd(JCR *jcr)
 {
  */
 static int bootstrap_cmd(JCR *jcr)
 {
@@ -1244,7 +1488,7 @@ static int bootstrap_cmd(JCR *jcr)
 }
 
 
 }
 
 
-/*
+/**
  * Get backup level from Director
  *
  */
  * Get backup level from Director
  *
  */
@@ -1256,6 +1500,8 @@ static int level_cmd(JCR *jcr)
 
    level = get_memory(dir->msglen+1);
    Dmsg1(100, "level_cmd: %s", dir->msg);
 
    level = get_memory(dir->msglen+1);
    Dmsg1(100, "level_cmd: %s", dir->msg);
+
+   /* keep compatibility with older directors */
    if (strstr(dir->msg, "accurate")) {
       jcr->accurate = true;
    }
    if (strstr(dir->msg, "accurate")) {
       jcr->accurate = true;
    }
@@ -1284,7 +1530,7 @@ static int level_cmd(JCR *jcr)
       buf = get_memory(dir->msglen+1);
       utime_t since_time, adj;
       btime_t his_time, bt_start, rt=0, bt_adj=0;
       buf = get_memory(dir->msglen+1);
       utime_t since_time, adj;
       btime_t his_time, bt_start, rt=0, bt_adj=0;
-      if (jcr->get_JobLevel() == L_NONE) {
+      if (jcr->getJobLevel() == L_NONE) {
          jcr->set_JobLevel(L_SINCE);     /* if no other job level set, do it now */
       }
       if (sscanf(dir->msg, "level = since_utime %s mtime_only=%d",
          jcr->set_JobLevel(L_SINCE);     /* if no other job level set, do it now */
       }
       if (sscanf(dir->msg, "level = since_utime %s mtime_only=%d",
@@ -1347,7 +1593,7 @@ static int level_cmd(JCR *jcr)
    if (buf) {
       free_memory(buf);
    }
    if (buf) {
       free_memory(buf);
    }
-   generate_plugin_event(jcr, bEventLevel, (void *)jcr->get_JobLevel());
+   generate_plugin_event(jcr, bEventLevel, (void *)jcr->getJobLevel());
    return dir->fsend(OKlevel);
 
 bail_out:
    return dir->fsend(OKlevel);
 
 bail_out:
@@ -1360,8 +1606,9 @@ bail_out:
    return 0;
 }
 
    return 0;
 }
 
-/*
+/**
  * Get session parameters from Director -- this is for a Restore command
  * Get session parameters from Director -- this is for a Restore command
+ *   This is deprecated. It is now passed via the bsr.
  */
 static int session_cmd(JCR *jcr)
 {
  */
 static int session_cmd(JCR *jcr)
 {
@@ -1387,7 +1634,8 @@ static void set_storage_auth_key(JCR *jcr, char *key)
       return;
    }
 
       return;
    }
 
-   /* We can be contacting multiple storage daemons.
+   /**
+    * We can be contacting multiple storage daemons.
     * So, make sure that any old jcr->store_bsock is cleaned up. 
     */
    if (jcr->store_bsock) {
     * So, make sure that any old jcr->store_bsock is cleaned up. 
     */
    if (jcr->store_bsock) {
@@ -1395,11 +1643,13 @@ static void set_storage_auth_key(JCR *jcr, char *key)
       jcr->store_bsock = NULL;
    }
 
       jcr->store_bsock = NULL;
    }
 
-   /* We can be contacting multiple storage daemons.
+   /**
+    * We can be contacting multiple storage daemons.
     *   So, make sure that any old jcr->sd_auth_key is cleaned up. 
     */
    if (jcr->sd_auth_key) {
     *   So, make sure that any old jcr->sd_auth_key is cleaned up. 
     */
    if (jcr->sd_auth_key) {
-      /* If we already have a Authorization key, director can do multi
+      /*
+       * If we already have a Authorization key, director can do multi
        * storage restore
        */
       Dmsg0(5, "set multi_restore=true\n");
        * storage restore
        */
       Dmsg0(5, "set multi_restore=true\n");
@@ -1408,9 +1658,10 @@ static void set_storage_auth_key(JCR *jcr, char *key)
    }
 
    jcr->sd_auth_key = bstrdup(key);
    }
 
    jcr->sd_auth_key = bstrdup(key);
+   Dmsg0(5, "set sd auth key\n");
 }
 
 }
 
-/*
+/**
  * Get address of storage daemon from Director
  *
  */
  * Get address of storage daemon from Director
  *
  */
@@ -1426,11 +1677,9 @@ static int storage_cmd(JCR *jcr)
    Dmsg1(100, "StorageCmd: %s", dir->msg);
    sd_auth_key.check_size(dir->msglen);
    if (sscanf(dir->msg, storaddr, &jcr->stored_addr, &stored_port, 
    Dmsg1(100, "StorageCmd: %s", dir->msg);
    sd_auth_key.check_size(dir->msglen);
    if (sscanf(dir->msg, storaddr, &jcr->stored_addr, &stored_port, 
-              &enable_ssl, sd_auth_key.c_str()) != 4)
-   {
+              &enable_ssl, sd_auth_key.c_str()) != 4) {
       if (sscanf(dir->msg, storaddr_v1, &jcr->stored_addr,
       if (sscanf(dir->msg, storaddr_v1, &jcr->stored_addr,
-                 &stored_port, &enable_ssl) != 3)
-      {
+                 &stored_port, &enable_ssl) != 3) {
          pm_strcpy(jcr->errmsg, dir->msg);
          Jmsg(jcr, M_FATAL, 0, _("Bad storage command: %s"), jcr->errmsg);
          goto bail_out;
          pm_strcpy(jcr->errmsg, dir->msg);
          Jmsg(jcr, M_FATAL, 0, _("Bad storage command: %s"), jcr->errmsg);
          goto bail_out;
@@ -1479,7 +1728,7 @@ bail_out:
 }
 
 
 }
 
 
-/*
+/**
  * Do a backup.
  */
 static int backup_cmd(JCR *jcr)
  * Do a backup.
  */
 static int backup_cmd(JCR *jcr)
@@ -1500,7 +1749,7 @@ static int backup_cmd(JCR *jcr)
    }
 #endif
 
    }
 #endif
 
-   /*
+   /**
     * Validate some options given to the backup make sense for the compiled in
     * options of this filed.
     */
     * Validate some options given to the backup make sense for the compiled in
     * options of this filed.
     */
@@ -1526,12 +1775,12 @@ static int backup_cmd(JCR *jcr)
    dir->fsend(OKbackup);
    Dmsg1(110, "filed>dird: %s", dir->msg);
 
    dir->fsend(OKbackup);
    Dmsg1(110, "filed>dird: %s", dir->msg);
 
-   /*
+   /**
     * Send Append Open Session to Storage daemon
     */
    sd->fsend(append_open);
    Dmsg1(110, ">stored: %s", sd->msg);
     * Send Append Open Session to Storage daemon
     */
    sd->fsend(append_open);
    Dmsg1(110, ">stored: %s", sd->msg);
-   /*
+   /**
     * Expect to receive back the Ticket number
     */
    if (bget_msg(sd) >= 0) {
     * Expect to receive back the Ticket number
     */
    if (bget_msg(sd) >= 0) {
@@ -1546,13 +1795,13 @@ static int backup_cmd(JCR *jcr)
       goto cleanup;
    }
 
       goto cleanup;
    }
 
-   /*
+   /**
     * Send Append data command to Storage daemon
     */
    sd->fsend(append_data, jcr->Ticket);
    Dmsg1(110, ">stored: %s", sd->msg);
 
     * Send Append data command to Storage daemon
     */
    sd->fsend(append_data, jcr->Ticket);
    Dmsg1(110, ">stored: %s", sd->msg);
 
-   /*
+   /**
     * Expect to get OK data
     */
    Dmsg1(110, "<stored: %s", sd->msg);
     * Expect to get OK data
     */
    Dmsg1(110, "<stored: %s", sd->msg);
@@ -1566,49 +1815,48 @@ static int backup_cmd(JCR *jcr)
 #if defined(WIN32_VSS)
    /* START VSS ON WIN32 */
    if (jcr->VSS) {      
 #if defined(WIN32_VSS)
    /* START VSS ON WIN32 */
    if (jcr->VSS) {      
-      if (g_pVSSClient->InitializeForBackup()) {   
+      if (g_pVSSClient->InitializeForBackup(jcr)) {   
+        generate_plugin_event(jcr, bEventVssBackupAddComponents);
         /* tell vss which drives to snapshot */   
         char szWinDriveLetters[27];   
         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)) {               
         /* tell vss which drives to snapshot */   
         char szWinDriveLetters[27];   
         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)) {               
-               Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshots failed.\n"));
-               jcr->JobErrors++;
+               berrno be;
+               Jmsg(jcr, M_FATAL, 0, _("Generate VSS snapshots failed. ERR=%s\n"), be.bstrerror());
             } else {
                /* tell user if snapshot creation of a specific drive failed */
                int i;
                for (i=0; i < (int)strlen(szWinDriveLetters); i++) {
                   if (islower(szWinDriveLetters[i])) {
             } else {
                /* tell user if snapshot creation of a specific drive failed */
                int i;
                for (i=0; i < (int)strlen(szWinDriveLetters); i++) {
                   if (islower(szWinDriveLetters[i])) {
-                     Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshot of drive \"%c:\\\" failed. VSS support is disabled on this drive.\n"), szWinDriveLetters[i]);
-                     jcr->JobErrors++;
+                     Jmsg(jcr, M_FATAL, 0, _("Generate VSS snapshot of drive \"%c:\\\" failed.\n"), szWinDriveLetters[i]);
                   }
                }
                /* inform user about writer states */
                   }
                }
                /* inform user about writer states */
-               for (i=0; i < (int)g_pVSSClient->GetWriterCount(); i++)                
+               for (i=0; i < (int)g_pVSSClient->GetWriterCount(); i++) {               
                   if (g_pVSSClient->GetWriterState(i) < 1) {
                   if (g_pVSSClient->GetWriterState(i) < 1) {
-                     Jmsg(jcr, M_WARNING, 0, _("VSS Writer (PrepareForBackup): %s\n"), g_pVSSClient->GetWriterInfo(i));                    
-                     jcr->JobErrors++;
+                     Jmsg(jcr, M_INFO, 0, _("VSS Writer (PrepareForBackup): %s\n"), g_pVSSClient->GetWriterInfo(i));                    
                   }                            
                   }                            
+               }
             }
         } else {
             }
         } else {
-            Jmsg(jcr, M_INFO, 0, _("No drive letters found for generating VSS snapshots.\n"));
+            Jmsg(jcr, M_FATAL, 0, _("No drive letters found for generating VSS snapshots.\n"));
         }
       } else {
          berrno be;
         }
       } else {
          berrno be;
-         Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled. ERR=%s\n"), be.bstrerror());
+         Jmsg(jcr, M_FATAL, 0, _("VSS was not initialized properly. ERR=%s\n"), be.bstrerror());
       } 
       run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS");
    }
 #endif
 
       } 
       run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS");
    }
 #endif
 
-   /*
+   /**
     * Send Files to Storage daemon
     */
    Dmsg1(110, "begin blast ff=%p\n", (FF_PKT *)jcr->ff);
    if (!blast_data_to_storage_daemon(jcr, NULL)) {
       set_jcr_job_status(jcr, JS_ErrorTerminated);
       bnet_suppress_error_messages(sd, 1);
     * Send Files to Storage daemon
     */
    Dmsg1(110, "begin blast ff=%p\n", (FF_PKT *)jcr->ff);
    if (!blast_data_to_storage_daemon(jcr, NULL)) {
       set_jcr_job_status(jcr, JS_ErrorTerminated);
       bnet_suppress_error_messages(sd, 1);
-      bget_msg(sd);                   /* Read final response from append_data */
       Dmsg0(110, "Error in blast_data.\n");
    } else {
       set_jcr_job_status(jcr, JS_Terminated);
       Dmsg0(110, "Error in blast_data.\n");
    } else {
       set_jcr_job_status(jcr, JS_Terminated);
@@ -1617,7 +1865,7 @@ static int backup_cmd(JCR *jcr)
          bnet_suppress_error_messages(sd, 1);
          goto cleanup;                /* bail out now */
       }
          bnet_suppress_error_messages(sd, 1);
          goto cleanup;                /* bail out now */
       }
-      /*
+      /**
        * Expect to get response to append_data from Storage daemon
        */
       if (!response(jcr, sd, OK_append, "Append Data")) {
        * Expect to get response to append_data from Storage daemon
        */
       if (!response(jcr, sd, OK_append, "Append Data")) {
@@ -1625,7 +1873,7 @@ static int backup_cmd(JCR *jcr)
          goto cleanup;
       }
 
          goto cleanup;
       }
 
-      /*
+      /**
        * Send Append End Data to Storage daemon
        */
       sd->fsend(append_end, jcr->Ticket);
        * Send Append End Data to Storage daemon
        */
       sd->fsend(append_end, jcr->Ticket);
@@ -1635,7 +1883,7 @@ static int backup_cmd(JCR *jcr)
          goto cleanup;
       }
 
          goto cleanup;
       }
 
-      /*
+      /**
        * Send Append Close to Storage daemon
        */
       sd->fsend(append_close, jcr->Ticket);
        * Send Append Close to Storage daemon
        */
       sd->fsend(append_close, jcr->Ticket);
@@ -1657,21 +1905,9 @@ static int backup_cmd(JCR *jcr)
 
 cleanup:
 #if defined(WIN32_VSS)
 
 cleanup:
 #if defined(WIN32_VSS)
-   /* STOP VSS ON WIN32 */
-   /* tell vss to close the backup session */
    if (jcr->VSS) {
    if (jcr->VSS) {
-      if (g_pVSSClient->CloseBackup()) {             
-         /* inform user about writer states */
-         for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) {
-            int msg_type = M_INFO;
-            if (g_pVSSClient->GetWriterState(i) < 1) {
-               msg_type = M_WARNING;
-               jcr->JobErrors++;
-            }
-            Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i));
-         }
-      }
       Win32ConvCleanupCache();
       Win32ConvCleanupCache();
+      g_pVSSClient->DestroyWriterInfo();
       V(vss_mutex);
    }
 #endif
       V(vss_mutex);
    }
 #endif
@@ -1680,7 +1916,7 @@ cleanup:
    return 0;                          /* return and stop command loop */
 }
 
    return 0;                          /* return and stop command loop */
 }
 
-/*
+/**
  * Do a Verify for Director
  *
  */
  * Do a Verify for Director
  *
  */
@@ -1714,12 +1950,12 @@ static int verify_cmd(JCR *jcr)
    dir->fsend(OKverify);
 
    generate_daemon_event(jcr, "JobStart");
    dir->fsend(OKverify);
 
    generate_daemon_event(jcr, "JobStart");
-   generate_plugin_event(jcr, bEventLevel, (void *)jcr->get_JobLevel());
+   generate_plugin_event(jcr, bEventLevel, (void *)jcr->getJobLevel());
    generate_plugin_event(jcr, bEventStartVerifyJob);
 
    Dmsg1(110, "filed>dird: %s", dir->msg);
 
    generate_plugin_event(jcr, bEventStartVerifyJob);
 
    Dmsg1(110, "filed>dird: %s", dir->msg);
 
-   switch (jcr->get_JobLevel()) {
+   switch (jcr->getJobLevel()) {
    case L_VERIFY_INIT:
    case L_VERIFY_CATALOG:
       do_verify(jcr);
    case L_VERIFY_INIT:
    case L_VERIFY_CATALOG:
       do_verify(jcr);
@@ -1757,7 +1993,24 @@ static int verify_cmd(JCR *jcr)
    return 0;                          /* return and terminate command loop */
 }
 
    return 0;                          /* return and terminate command loop */
 }
 
-/*
+#ifdef WIN32_VSS
+static bool vss_restore_init_callback(JCR *jcr, int init_type)
+{
+   switch (init_type) {
+   case VSS_INIT_RESTORE_AFTER_INIT:
+      generate_plugin_event(jcr, bEventVssRestoreLoadComponentMetadata);
+      return true;
+   case VSS_INIT_RESTORE_AFTER_GATHER:
+      generate_plugin_event(jcr, bEventVssRestoreSetComponentsSelected);
+      return true;
+   default:
+      return false;
+      break;
+   }
+}
+#endif
+
+/**
  * Do a Restore for Director
  *
  */
  * Do a Restore for Director
  *
  */
@@ -1770,10 +2023,28 @@ static int restore_cmd(JCR *jcr)
    int prefix_links;
    char replace;
 
    int prefix_links;
    char replace;
 
-   /*
+   /**
     * Scan WHERE (base directory for restore) from command
     */
     * Scan WHERE (base directory for restore) from command
     */
-   Dmsg0(150, "restore command\n");
+   Dmsg0(100, "restore command\n");
+#if defined(WIN32_VSS)
+
+   /**
+    * No need to enable VSS for restore if we do not have plugin
+    *  data to restore 
+    */
+   enable_vss = 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
+   // and one enables vss and the other does not then enable_vss can change
+   // between here and where its evaluated after the job completes.
+   jcr->VSS = g_pVSSClient && enable_vss;
+   if (jcr->VSS) {
+      /* Run only one at a time */
+      P(vss_mutex);
+   }
+#endif
    /* Pickup where string */
    args = get_memory(dir->msglen+1);
    *args = 0;
    /* Pickup where string */
    args = get_memory(dir->msglen+1);
    *args = 0;
@@ -1826,12 +2097,41 @@ static int restore_cmd(JCR *jcr)
 
    set_jcr_job_status(jcr, JS_Running);
 
 
    set_jcr_job_status(jcr, JS_Running);
 
-   /*
+   /**
     * Do restore of files and data
     */
    start_dir_heartbeat(jcr);
    generate_daemon_event(jcr, "JobStart");
    generate_plugin_event(jcr, bEventStartRestoreJob);
     * Do restore of files and data
     */
    start_dir_heartbeat(jcr);
    generate_daemon_event(jcr, "JobStart");
    generate_plugin_event(jcr, bEventStartRestoreJob);
+
+#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);
+*/
+         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);
+      run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS");
+   }
+#endif
+
    do_restore(jcr);
    stop_dir_heartbeat(jcr);
 
    do_restore(jcr);
    stop_dir_heartbeat(jcr);
 
@@ -1840,17 +2140,42 @@ static int restore_cmd(JCR *jcr)
       bnet_suppress_error_messages(sd, 1);
    }
 
       bnet_suppress_error_messages(sd, 1);
    }
 
-   /*
+   /**
     * Send Close session command to Storage daemon
     */
    sd->fsend(read_close, jcr->Ticket);
     * Send Close session command to Storage daemon
     */
    sd->fsend(read_close, jcr->Ticket);
-   Dmsg1(130, "filed>stored: %s", sd->msg);
+   Dmsg1(100, "filed>stored: %s", sd->msg);
 
    bget_msg(sd);                      /* get OK */
 
    /* Inform Storage daemon that we are done */
    sd->signal(BNET_TERMINATE);
 
 
    bget_msg(sd);                      /* get OK */
 
    /* Inform Storage daemon that we are done */
    sd->signal(BNET_TERMINATE);
 
+#if defined(WIN32_VSS)
+   /* STOP VSS ON WIN32 */
+   /* tell vss to close the restore session */
+   Dmsg0(100, "About to call CloseRestore\n");
+   if (jcr->VSS) {
+      generate_plugin_event(jcr, bEventVssBeforeCloseRestore);
+      Dmsg0(100, "Really about to call CloseRestore\n");
+      if (g_pVSSClient->CloseRestore()) {
+         Dmsg0(100, "CloseRestore success\n");
+         /* inform user about writer states */
+         for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) {
+            int msg_type = M_INFO;
+            if (g_pVSSClient->GetWriterState(i) < 1) {
+               //msg_type = M_WARNING;
+               //jcr->JobErrors++;
+            }
+            Jmsg(jcr, msg_type, 0, _("VSS Writer (RestoreComplete): %s\n"), g_pVSSClient->GetWriterInfo(i));
+         }
+      }
+      else
+         Dmsg1(100, "CloseRestore fail - %08x\n", errno);
+      V(vss_mutex);
+   }
+#endif
+
 bail_out:
    bfree_and_null(jcr->where);
 
 bail_out:
    bfree_and_null(jcr->where);
 
@@ -1858,10 +2183,11 @@ bail_out:
       set_jcr_job_status(jcr, JS_ErrorTerminated);
    }
 
       set_jcr_job_status(jcr, JS_ErrorTerminated);
    }
 
-   Dmsg0(130, "Done in job.c\n");
+   Dmsg0(100, "Done in job.c\n");
 
    int ret;
    if (jcr->multi_restore) {
 
    int ret;
    if (jcr->multi_restore) {
+      Dmsg0(100, OKstoreend);
       dir->fsend(OKstoreend);
       ret = 1;     /* we continue the loop, waiting for next part */
    } else {
       dir->fsend(OKstoreend);
       ret = 1;     /* we continue the loop, waiting for next part */
    } else {
@@ -1869,6 +2195,10 @@ bail_out:
       ret = 0;     /* we stop here */
    }
 
       ret = 0;     /* we stop here */
    }
 
+   if (job_canceled(jcr)) {
+      ret = 0;     /* we stop here */
+   }
+
    return ret;
 }
 
    return ret;
 }
 
@@ -1932,7 +2262,7 @@ static int open_sd_read_session(JCR *jcr)
    return 1;
 }
 
    return 1;
 }
 
-/*
+/**
  * Destroy the Job Control Record and associated
  * resources (sockets).
  */
  * Destroy the Job Control Record and associated
  * resources (sockets).
  */
@@ -1954,7 +2284,7 @@ static void filed_free_jcr(JCR *jcr)
    return;
 }
 
    return;
 }
 
-/*
+/**
  * Get response from Storage daemon to a command we
  * sent. Check that the response is OK.
  *
  * Get response from Storage daemon to a command we
  * sent. Check that the response is OK.
  *