UAContext *ua;
info.bs = NULL;
info.ua = NULL;
+ info.split_list = NULL;
+ info.next_split_off = NULL;
if (!jcr->RestoreBootstrap) {
return false;
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;
}
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);
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 */
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.
*/
}
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.
*/
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))) {
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
*/
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 */
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)) {
/**
* 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");
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",
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