[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bacula-devel] Selective restore when files are pruned [patch]


I needed to restore a subset of some old backups.  Restoring the full
backups would need a terabyte of temporary storage, which seemed a bit
wasteful (and inconvenient to get hold of) since the data I was
interested in took less than a gigabyte.

Anyway -- I implemented a simple regex to filter the files to restore.
It works like this:

    Building directory tree for JobId(s) 28644 ...  
    There were no files inserted into the tree, so file selection
    is not possible.Most likely your retention policy pruned the files
    
    Do you want to restore all the files? (yes|no): no
    
    Regexp matching files to restore? (empty to abort): ^/var/log

The patch adds a new keyword to the bootstrap file, FilePattern, which
the storage daemon will apply to all files before deciding whether to
send the file over to the fd.  The fd doesn't need any changes, btw.

This is just a quick hack, and there is some polishing left to do:

   * Only available interactively in the specific case above, but
     could be useful as an alternative/supplement to marking files and
     directories manually.

   * Can not be modified like the other job parameters.

   * Bacula will complain that the number of restored files is
     different from what it expected in the final report.

   * Documentation is not updated.

The patch is against revision 7469, which we are now running in
production (don't tell my boss ;-).  I hope others will find it
useful.

PS. Kern, the GPL paperwork is on its way to Switzerland.

Index: bacula/src/dird/ua_restore.c
===================================================================
--- bacula/src/dird/ua_restore.c	(revision 7469)
+++ bacula/src/dird/ua_restore.c	(working copy)
@@ -987,6 +987,39 @@
    Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
 }
 
+static bool ask_for_filepattern(UAContext *ua, RESTORE_CTX *rx)
+{
+   ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
+                  "is not possible.Most likely your retention policy pruned the files\n"));
+   if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
+      if (ua->pint32_val == 1)
+         return true;
+      while (get_cmd(ua, _("\nRegexp matching files to restore? (empty to abort): "))) {
+         if (ua->cmd[0] == '\0') {
+            break;
+         } else {
+            regex_t *filepattern_re = NULL;
+            int rc;
+            char errmsg[500] = "";
+
+            filepattern_re = (regex_t *)bmalloc(sizeof(regex_t));
+            rc = regcomp(filepattern_re, ua->cmd, REG_EXTENDED|REG_NOSUB);
+            if (rc != 0)
+               regerror(rc, filepattern_re, errmsg, sizeof(errmsg));
+            regfree(filepattern_re);
+            free(filepattern_re);
+            if (*errmsg) {
+               ua->send_msg(_("Regex compile error: %s\n"), errmsg);
+            } else {
+               rx->bsr->filepattern = bstrdup(ua->cmd);
+               return true;
+            }
+         }
+      }
+   }
+   return false;
+}
+
 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
 {
    TREE_CTX tree;
@@ -1048,19 +1081,15 @@
    }
 #endif
    if (tree.FileCount == 0) {
-      ua->send_msg(_("\nThere were no files inserted into the tree, so file selection\n"
-         "is not possible.Most likely your retention policy pruned the files\n"));
-      if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
-         OK = false;
-      } else {
+      OK = ask_for_filepattern(ua, rx);
+      if (OK) {
          last_JobId = 0;
          for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
              if (JobId == last_JobId) {
                 continue;                    /* eliminate duplicate JobIds */
              }
              add_findex_all(rx->bsr, JobId);
-          }
-          OK = true;
+         }
       }
    } else {
       char ec1[50];
Index: bacula/src/dird/bsr.c
===================================================================
--- bacula/src/dird/bsr.c	(revision 7469)
+++ bacula/src/dird/bsr.c	(working copy)
@@ -361,6 +361,9 @@
             }
             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
+            if (bsr->filepattern) {
+               fprintf(fd, "FilePattern=%s\n", bsr->filepattern);
+            }
             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
                fprintf(fd, "Device=\"%s\"\n", device);
             }
@@ -424,6 +427,9 @@
             }
             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
+            if (bsr->filepattern) {
+               fprintf(fd, "FilePattern=%s\n", bsr->filepattern);
+            }
             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
                fprintf(fd, "Device=\"%s\"\n", device);
             }
@@ -480,6 +486,8 @@
                   bsr->VolParams[i].EndFile);
          ua->send_msg("VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
                   bsr->VolParams[i].EndBlock);
+         if (bsr->filepattern)
+            ua->send_msg("FilePattern=%s\n", bsr->filepattern);
          print_findex(ua, bsr->fi);
       }
       print_bsr(ua, bsr->next);
Index: bacula/src/dird/bsr.h
===================================================================
--- bacula/src/dird/bsr.h	(revision 7469)
+++ bacula/src/dird/bsr.h	(working copy)
@@ -62,5 +62,6 @@
    int      VolCount;                 /* Volume parameter count */
    VOL_PARAMS *VolParams;             /* Volume, start/end file/blocks */
    RBSR_FINDEX *fi;                   /* File indexes this JobId */
+   char        *filepattern;          /* Only restore files matching regex */
 };
 
Index: bacula/src/stored/read_record.c
===================================================================
--- bacula/src/stored/read_record.c	(revision 7469)
+++ bacula/src/stored/read_record.c	(working copy)
@@ -43,6 +43,11 @@
 
 #include "bacula.h"
 #include "stored.h"
+#ifndef HAVE_REGEX_H
+#include "lib/bregex.h"
+#else
+#include <regex.h>
+#endif
 
 /* Forward referenced functions */
 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
@@ -67,11 +72,29 @@
    bool done = false;
    SESSION_LABEL sessrec;
    dlist *recs;                         /* linked list of rec packets open */
+   ATTR *attr = NULL;
+   int32_t skip_fileindex = 0;
+   regex_t *filepattern_re = NULL;
 
    recs = New(dlist(rec, &rec->link));
    position_to_first_file(jcr, dcr);
    jcr->mount_next_volume = false;
+   if (jcr->bsr->filepattern) {
+      int rc;
+      char prbuf[500];
 
+      attr = new_attr(jcr);
+      filepattern_re = (regex_t *)bmalloc(sizeof(regex_t));
+      rc = regcomp(filepattern_re, jcr->bsr->filepattern,
+                   REG_EXTENDED|REG_NOSUB);
+      if (rc != 0) {
+         regerror(rc, filepattern_re, prbuf, sizeof(prbuf));
+         Jmsg(jcr, M_FATAL, 0, _("REGEX '%s' compile error. ERR=%s\n"),
+              jcr->bsr->filepattern, prbuf);
+         ok = false;
+      }
+   }
+
    for ( ; ok && !done; ) {
       if (job_canceled(jcr)) {
          ok = false;
@@ -273,6 +296,25 @@
                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
             break;                    /* read second part of record */
          }
+
+         if (filepattern_re && (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
+                                rec->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
+            if (unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
+               if (regexec(filepattern_re, attr->fname, 0, NULL, 0) == 0) {
+                  Dmsg2(dbglvl, "Matched pattern, fname=%s FI=%d\n",
+                        attr->fname, rec->FileIndex);
+               } else {
+                  Dmsg2(dbglvl, "Didn't match, skipping fname=%s FI=%d\n",
+                        attr->fname, rec->FileIndex);
+                  skip_fileindex = rec->FileIndex;
+               }
+            }
+         }
+         if (rec->FileIndex == skip_fileindex) {
+            Dmsg1(dbglvl, "Skipped FI=%d\n", rec->FileIndex);
+            continue;
+         }
+
          Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
                rec_state_to_str(rec), block->BlockNumber,
                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
@@ -304,6 +346,13 @@
       free_record(rec);
    }
    delete recs;
+   if (attr)
+      free_attr(attr);
+   if (filepattern_re) {
+      regfree(filepattern_re);
+      free(filepattern_re);
+   }
+
    print_block_read_errors(jcr, block);
    return ok;
 }
Index: bacula/src/stored/parse_bsr.c
===================================================================
--- bacula/src/stored/parse_bsr.c	(revision 7469)
+++ bacula/src/stored/parse_bsr.c	(working copy)
@@ -57,6 +57,7 @@
 static BSR *store_exclude(LEX *lc, BSR *bsr);
 static BSR *store_stream(LEX *lc, BSR *bsr);
 static BSR *store_slot(LEX *lc, BSR *bsr);
+static BSR *store_filepattern(LEX *lc, BSR *bsr);
 static bool is_fast_rejection_ok(BSR *bsr);
 static bool is_positioning_ok(BSR *bsr);
 
@@ -87,8 +88,8 @@
    {"stream",   store_stream},
    {"slot",     store_slot},
    {"device",   store_device},
+   {"filepattern", store_filepattern},
    {NULL, NULL}
-
 };
 
 /*
@@ -445,7 +446,20 @@
    return bsr;
 }
 
+static BSR *store_filepattern(LEX *lc, BSR *bsr)
+{
+   int token;
+ 
+   token = lex_get_token(lc, T_STRING);
+   if (token == T_ERROR) {
+      return NULL;
+   }
 
+   if (bsr->filepattern) bfree(bsr->filepattern);
+   bsr->filepattern = bstrdup(lc->str);
+   return bsr;
+}
+
 static BSR *store_jobtype(LEX *lc, BSR *bsr)
 {
    /* *****FIXME****** */
@@ -817,6 +831,7 @@
    free_bsr_item((BSR *)bsr->FileIndex);
    free_bsr_item((BSR *)bsr->JobType);
    free_bsr_item((BSR *)bsr->JobLevel);
+   if (bsr->filepattern) bfree(bsr->filepattern);
    free_bsr(bsr->next);
    free(bsr);
 }
Index: bacula/src/stored/bsr.h
===================================================================
--- bacula/src/stored/bsr.h	(revision 7469)
+++ bacula/src/stored/bsr.h	(working copy)
@@ -157,6 +157,7 @@
    BSR_JOBTYPE  *JobType;
    BSR_JOBLEVEL *JobLevel;
    BSR_STREAM   *stream;
+   char         *filepattern;
 };
 
 
-- 
Kjetil T. Homme
Linpro AS
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Bacula-devel mailing list
Bacula-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/bacula-devel


This mailing list archive is a service of Copilotco.