]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Fix #9341 split BSR when a volume cycle is detected
authorAlain Spineux <alain@baculasystems.com>
Thu, 4 Aug 2022 13:06:57 +0000 (15:06 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 14 Sep 2023 11:56:58 +0000 (13:56 +0200)
bacula/src/dird/bsr.c
bacula/src/dird/bsr.h
bacula/src/dird/protos.h
bacula/src/dird/restore.c
bacula/src/dird/vbackup.c

index ac3a8f1a6951a56c84ebb4d2f5d13289157063d9..22e9fd9511d7a37b7d1d4a6df1c35cd97f05c878 100644 (file)
@@ -776,6 +776,8 @@ bool open_bootstrap_file(JCR *jcr, bootstrap_info &info)
    UAContext *ua;
    info.bs = NULL;
    info.ua = NULL;
+   info.split_list = NULL;
+   info.next_split_off = NULL;
 
    if (!jcr->RestoreBootstrap) {
       return false;
@@ -821,4 +823,101 @@ void close_bootstrap_file(bootstrap_info &info)
       free_ua_context(info.ua);
       info.ua = NULL;
    }
+   if (info.split_list) {
+      delete info.split_list;
+      info.split_list = NULL;
+      info.next_split_off = NULL; // no need to free anything
+   }
+}
+
+
+struct bsr_vol_list {
+   hlink link;
+   uint64_t address;
+   char volume[1];
+};
+
+/* Generate a list of split position in the BSR to break any cycle
+ * returns true if a cycle was detected
+ */
+bool split_bsr_loop(JCR *jcr, bootstrap_info &info)
+{
+   UAContext *ua = info.ua;
+   FILE *bs = info.bs;
+   bsr_vol_list *p = NULL;
+   htable volumes(p, &p->link, 100);   // list of volume that have already been used
+
+   POOL_MEM storage(PM_NAME);
+   POOL_MEM volume(PM_NAME), last_volume(PM_NAME);
+
+   uint32_t VolSessionId, VolSessionTime;
+   uint32_t prevVolSessionId = 0, prevVolSessionTime = 0;
+
+   boffset_t start_section_offset; // the offset of the beginning of the section
+   boffset_t start_job_off; // the offset of the first section of the job
+
+   bool first = true;
+   bool after_eof = false;   // used to handle the after EOF inside the loop
+
+   if (info.split_list == NULL) {
+      info.split_list = New(alist(100, owned_by_alist));
+   }
+   while (true) {
+      boffset_t cur_off = ftello(bs); // off of the line
+      after_eof = (bfgets(ua->cmd, bs) == NULL);
+      if (!after_eof) {
+         parse_ua_args(ua);
+         if (ua->argc != 1) {
+            continue; // @ERIC we do the same in check_for_new_storage()
+         }
+      }
+      if (after_eof || strcasecmp(ua->argk[0], "Storage") == 0) {
+         if (first) {
+            first = false;
+         } else {
+            // This was the last part or we have reached the end of the file
+            if (strcmp(last_volume.c_str(), volume.c_str()) != 0) {
+               /* look if the volume has already been used before */
+               bsr_vol_list *item = (bsr_vol_list *)volumes.lookup(volume.c_str());
+               if (item == NULL) {
+                  /* this is the first time we use this volume */
+                  item = (bsr_vol_list *)volumes.hash_malloc(strlen(volume.c_str())+sizeof(bsr_vol_list));
+                  strcpy(item->volume, volume.c_str());
+                  item->address = 0; // unused
+                  volumes.insert(item->volume, item);
+
+               } else {
+                  /* this volume has already been used before */
+                  boffset_t *p = (boffset_t *)malloc(sizeof(boffset_t));
+                  *p = start_job_off;
+                  info.split_list->append(p);
+               }
+            }
+            last_volume.strcpy(volume.c_str());
+            if (prevVolSessionTime != VolSessionTime || prevVolSessionId != VolSessionId) {
+               /* This is a new job */
+               start_job_off = start_section_offset;
+               prevVolSessionId = VolSessionId;
+               prevVolSessionTime = VolSessionTime;
+            }
+            start_section_offset = cur_off;
+         }
+         if (after_eof) {
+            break;
+         }
+         storage.strcpy(ua->argv[0]);
+      }
+      if (strcasecmp(ua->argk[0], "Volume") == 0) {
+         volume.strcpy(ua->argv[0]);
+      }
+      if (strcasecmp(ua->argk[0], "VolSessionId") == 0) {
+         VolSessionId = str_to_uint64(ua->argv[0]);
+      }
+      if (strcasecmp(ua->argk[0], "VolSessionTime") == 0) {
+         VolSessionTime = str_to_uint64(ua->argv[0]);
+      }
+   }
+   fseeko(bs, 0, SEEK_SET);
+   info.next_split_off = (boffset_t *)info.split_list->first();
+   return info.next_split_off != NULL;
 }
index 732dadae7b7ee5de476a3bb971fbfdd6e94049b3..da103e38bc4494701a24db949076d64e3ae63dd4 100644 (file)
@@ -67,6 +67,9 @@ struct bootstrap_info
    FILE *bs;
    UAContext *ua;
    char storage[MAX_NAME_LENGTH+1];
+   alist *split_list; /* when a BSR cannot be restored in once, this is a list */
+                      /* of offset where to split the BSR to send to the SD */
+   boffset_t *next_split_off; /* the next split position */
 };
 bool open_bootstrap_file(JCR *jcr, bootstrap_info &info);
 void close_bootstrap_file(bootstrap_info &info);
index c19e05c5e8dca0ef505d023cbfd7260dfdd619aa..f92d901029aec1df9250ed5cba1d4b1bdf5819ef 100644 (file)
@@ -76,6 +76,7 @@ RBSR_FINDEX *new_findex();
 void make_unique_restore_filename(UAContext *ua, POOLMEM **fname);
 void print_bsr(UAContext *ua, RESTORE_CTX &rx);
 void scan_bsr(JCR *jcr);
+bool split_bsr_loop(JCR *jcr, bootstrap_info &info);
 
 
 /* catreq.c */
index c6c491140b115490ad194c3e1188ddc2854fd0d7..6594cddba3728192b98a09eb7bf20b23a8f2e1c9 100644 (file)
@@ -192,6 +192,16 @@ static bool check_for_new_storage(JCR *jcr, bootstrap_info &info)
    return false;
 }
 
+/* return true if the offset match the next offset where we need to split the BSR */
+static bool split_bsr(JCR *jcr, bootstrap_info &info, boffset_t off)
+{
+   if (info.next_split_off != NULL && *info.next_split_off == off) {
+      info.next_split_off = (boffset_t *)info.split_list->next(); // load next offset
+      return true;
+   }
+   return false;
+}
+
 /**
  * Send bootstrap file to Storage daemon section by section.
  */
@@ -209,8 +219,8 @@ static bool send_bootstrap_file(JCR *jcr, BSOCK *sock,
    }
    sock->fsend(bootstrap);
    pos = ftello(bs);
-   while(bfgets(ua->cmd, bs)) {
-      if (check_for_new_storage(jcr, info)) {
+   while (bfgets(ua->cmd, bs)) {
+      if (check_for_new_storage(jcr, info) || split_bsr(jcr, info, pos)) {
          /* Otherwise, we need to contact another storage daemon.
           * Reset bs to the beginning of the current segment.
           */
@@ -234,10 +244,12 @@ static bool select_rstore(JCR *jcr, bootstrap_info &info)
    STORE *store;
    int i;
 
+   /* Releases the store_bsock between calls to the SD. */
+   free_bsock(jcr->store_bsock);
 
    STORE *rstore = jcr->store_mngr->get_rstore();
    if (!strcmp(rstore->name(), info.storage)) {
-      return true;                 /* same SD nothing to change */
+      return true;                 /* same SD nothing to change in the storage mngr*/
    }
 
    if (!(store = (STORE *)GetResWithName(R_STORAGE,info.storage))) {
@@ -247,12 +259,6 @@ static bool select_rstore(JCR *jcr, bootstrap_info &info)
       return false;
    }
 
-   /*
-    * This releases the store_bsock between calls to the SD.
-    *  I think.
-    */
-   free_bsock(jcr->store_bsock);
-
    /*
     * release current read storage and get a new one
     */
@@ -260,7 +266,7 @@ static bool select_rstore(JCR *jcr, bootstrap_info &info)
    jcr->store_mngr->set_rstore(store, _("Job resource"));
    jcr->setJobStatus(JS_WaitSD);
    /*
-    * Wait for up to 6 hours to increment read stoage counter
+    * Wait for up to 6 hours to increment read storage counter
     */
    for (i=0; i < MAX_TRIES; i++) {
       /* try to get read storage counter incremented */
@@ -302,11 +308,15 @@ bool restore_bootstrap(JCR *jcr)
    POOL_MEM restore_cmd(PM_MESSAGE);
    bool ret = false;
 
-
    /* Open the bootstrap file */
    if (!open_bootstrap_file(jcr, info)) {
       goto bail_out;
    }
+
+   if (split_bsr_loop(jcr, info)) { /* create the split list to break volume cycle */
+      Jmsg(jcr, M_INFO, 0, _("Found a volume cycle in the bootstrap, fixing automatically the reading process\n"));
+   }
+
    /* Read the bootstrap file */
    while (!feof(info.bs)) {
 
@@ -316,8 +326,8 @@ bool restore_bootstrap(JCR *jcr)
 
       /**
        * Open a message channel connection with the Storage
-       * daemon. This is to let him know that our client
-       * will be contacting him for a backup  session.
+       * daemon. This is to let him know that our client will be contacting
+       * him for a backup session. Or the opposite if sdcallsclient is enable
        *
        */
       Dmsg0(10, "Open connection with storage daemon\n");
index 5bea21de42644621be99217d3b86602cf00bf3fb..c66adab45d2b58ab8db25bd83c6528f42c7ad745 100644 (file)
@@ -120,6 +120,7 @@ bool do_vbackup(JCR *jcr)
    sellist     sel;
    db_list_ctx jobids;
    UAContext *ua;
+   bootstrap_info info;
 
    Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->store_mngr->get_rstore_list(), jcr->store_mngr->get_wstore_list());
    Dmsg2(100, "Read store=%s, write store=%s\n",
@@ -308,6 +309,21 @@ _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n")
       return false;
    }
 
+   /* Open the bootstrap file */
+   if (!open_bootstrap_file(jcr, info)) {
+      return false;
+   }
+
+   if (split_bsr_loop(jcr, info)) { /* create the split list to break volume cycle */
+      Jmsg(jcr, M_FATAL, 0, _("Found a volume cycle in the bootstrap, Virtual Full is not possible on this Job\n"));
+   }
+
+   close_bootstrap_file(info);
+
+   if (jcr->is_canceled()) {
+      return false;
+   }
+
    /*
     * Open a message channel connection with the Storage
     * daemon. This is to let him know that our client