SuSE.
For 1.33
+- I want to restore by file to some date.
+- Implement fast block rejection.
+- Is it really important to make Job name the same to find the
+ Full backup to avoid promoting an Incremental job?
+- Start label, then run job when tape labeled, it should broadcast.
- Implement a RunAfterFailedJob
- Zap illegal characters in job name for mail files (e.g. /).
- From Lars Köllers:
job. Use find_job_pool() modified in ua_output.c
- Test connect timeouts.
- Fix FreeBSD build with tcp_wrapper -- should not have -lnsl
-
* We are called here once for each JobMedia record
* for each Volume.
*/
-static void write_findex(UAContext *ua, RBSR_FINDEX *fi,
+static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi,
int32_t FirstIndex, int32_t LastIndex, FILE *fd)
{
+ uint32_t count = 0;
for ( ; fi; fi=fi->next) {
int32_t findex, findex2;
if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
if (findex == findex2) {
fprintf(fd, "FileIndex=%d\n", findex);
+ count++;
} else {
fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
+ count += findex2 - findex + 1;
}
}
}
+ return count;
}
/*
{
if (bsr) {
free_findex(bsr->fi);
- free_bsr(bsr->next);
if (bsr->VolParams) {
free(bsr->VolParams);
}
+ free_bsr(bsr->next);
free(bsr);
}
}
static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd)
{
if (bsr) {
+ uint32_t count;
/*
* For a given volume, loop over all the JobMedia records.
* VolCount is the number of JobMedia records.
fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
- fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
- bsr->VolParams[i].EndFile);
- fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
- bsr->VolParams[i].EndBlock);
-
+ if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
+ fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
+ } else {
+ fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
+ bsr->VolParams[i].EndFile);
+ }
+ if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
+ fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartBlock);
+ } else {
+ fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
+ bsr->VolParams[i].EndBlock);
+ }
// Dmsg2(000, "bsr VolParam FI=%u LI=%u\n",
// bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
- write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
- bsr->VolParams[i].LastIndex, fd);
+
+ count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
+ bsr->VolParams[i].LastIndex, fd);
+ if (count) {
+ fprintf(fd, "Count=%u\n", count);
+ }
}
write_bsr(ua, bsr->next, fd);
}
Dmsg2(100, "Vol Info for %s: %s", jcr->Job, bs->msg);
} else {
/* Not suitable volume */
- bnet_fsend(bs, "1998 Volume \"%s\" %s.\n", mr.VolumeName, reason);
+ bnet_fsend(bs, "1998 Volume \"%s\" status is %s, %s.\n", mr.VolumeName,
+ mr.VolStatus, reason);
}
} else {
return true;
} else {
/* In principle this shouldn't happen */
- *reason = "recycling of current volume failed";
+ *reason = "and recycling of current volume failed";
return false;
}
}
/* At this point, the volume is not valid for writing */
- *reason = "not Append, Purged or Recycle";
+ *reason = "but should be Append, Purged or Recycle";
/*
* What we're trying to do here is see if the current volume is
Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
return true; /* Good volume */
} else {
- *reason = "not Append, Purged or Recycle (recycling of the "
+ *reason = "but should be Append, Purged or Recycle (recycling of the "
"current volume failed)";
}
} else {
- *reason = "not Append, Purged or Recycle (cannot automatically "
+ *reason = "but should be Append, Purged or Recycle (cannot automatically "
"recycle current volume, as it still contains unpruned data)";
}
}
extern int retentioncmd(UAContext *ua, char *cmd);
extern int prunecmd(UAContext *ua, char *cmd);
extern int purgecmd(UAContext *ua, char *cmd);
-extern int restorecmd(UAContext *ua, char *cmd);
+extern int restore_cmd(UAContext *ua, char *cmd);
extern int label_cmd(UAContext *ua, char *cmd);
extern int relabel_cmd(UAContext *ua, char *cmd);
extern int update_slots(UAContext *ua); /* ua_label.c */
{ N_("quit"), quit_cmd, _("quit")},
{ N_("relabel"), relabel_cmd, _("relabel a tape")},
{ N_("release"), release_cmd, _("release <storage-name>")},
- { N_("restore"), restorecmd, _("restore files")},
+ { N_("restore"), restore_cmd, _("restore files")},
{ N_("run"), runcmd, _("run <job-name>")},
{ N_("setdebug"), setdebug_cmd, _("sets debug level")},
{ N_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
* Restore files
*
*/
-int restorecmd(UAContext *ua, char *cmd)
+int restore_cmd(UAContext *ua, char *cmd)
{
RESTORE_CTX rx; /* restore context */
JOB *job = NULL;
static void free_rx(RESTORE_CTX *rx)
{
free_bsr(rx->bsr);
+ rx->bsr = NULL;
if (rx->JobIds) {
free_pool_memory(rx->JobIds);
rx->JobIds = NULL;
}
if (name_list->name) {
free(name_list->name);
+ name_list->name = NULL;
}
name_list->max_ids = 0;
name_list->num_ids = 0;
return 0;
}
if (num_clients <= 0) {
- bsendmsg(ua, _("No clients defined. Run a job to create one.\n"));
+ bsendmsg(ua, _("No clients defined. You must run a job before using this command.\n"));
return 0;
}
if (!root_bsr || !root_bsr->use_positioning ||
!root_bsr->reposition || !dev_is_tape(dev)) {
- Dmsg2(100, "use_pos=%d repos=%d\n", root_bsr->use_positioning,
- root_bsr->reposition);
+ Dmsg2(100, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
return NULL;
}
+ Dmsg2(100, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
root_bsr->mount_next_volume = false;
for (bsr=root_bsr; bsr; bsr=bsr->next) {
if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
return return_bsr;
}
+/*
+ * Called to tell the matcher that the end of
+ * the current file has been reached.
+ * The bsr argument is not used, but is included
+ * for consistency with the other match calls.
+ *
+ * Returns: true if we should reposition
+ * : false otherwise.
+ */
+bool match_set_eof(BSR *bsr, DEV_RECORD *rec)
+{
+ BSR *rbsr = rec->bsr;
+ Dmsg1(100, "match_set %d\n", rbsr != NULL);
+ if (!rbsr) {
+ return false;
+ }
+ rec->bsr = NULL;
+ rbsr->found++;
+ if (rbsr->count && rbsr->found >= rbsr->count) {
+ rbsr->done = true;
+ rbsr->root->reposition = true;
+ Dmsg2(100, "match_set_eof reposition count=%d found=%d\n",
+ rbsr->count, rbsr->found);
+ return true;
+ }
+ return false;
+}
+
/*
* Match all the components of current record
* returns 1 on match
if (bsr->done) {
goto no_match;
}
- if (bsr->count && bsr->found >= bsr->count) {
- bsr->done = true;
- bsr->root->reposition = true;
- Dmsg0(100, "bsr done from count\n");
- goto no_match;
- }
if (!match_volume(bsr, bsr->volume, volrec, 1)) {
goto no_match;
}
if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
+ Dmsg2(100, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
+ rec->File);
goto no_match;
}
if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
+ Dmsg2(100, "Fail on sesstime. bsr=%d rec=%d\n",
+ bsr->sesstime->sesstime, rec->VolSessionTime);
goto no_match;
}
/* NOTE!! This test MUST come after the sesstime test */
if (!match_sessid(bsr, bsr->sessid, rec)) {
+ Dmsg2(100, "Fail on sessid. bsr=%d rec=%d\n",
+ bsr->sessid->sessid, rec->VolSessionId);
goto no_match;
}
/* NOTE!! This test MUST come after sesstime and sessid tests */
if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
+ Dmsg2(100, "Fail on findex. bsr=%d rec=%d\n",
+ bsr->FileIndex->findex2, rec->FileIndex);
goto no_match;
}
+ /*
+ * If a count was specified and we have a FileIndex, assume
+ * it is a Bacula created bsr (or the equivalent). We
+ * then save the bsr where the match occurred so that
+ * after processing the record or records, we can update
+ * the found count. I.e. rec->bsr points to the bsr that
+ * satisfied the match.
+ */
+ if (bsr->count && bsr->FileIndex) {
+ rec->bsr = bsr;
+ return 1; /* this is a complete match */
+ }
+
+ /*
+ * The selections below are not used by Bacula's
+ * restore command, and don't work because of
+ * the rec->bsr = bsr optimization above.
+ */
if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
goto no_match;
+
}
if (!match_job(bsr, bsr->job, sessrec, 1)) {
goto no_match;
if (!match_stream(bsr, bsr->stream, rec, 1)) {
goto no_match;
}
- bsr->found++;
return 1;
no_match:
if (volfile->done && done) {
bsr->done = true;
bsr->root->reposition = true;
- Dmsg0(100, "bsr done from volfile\n");
+ Dmsg2(100, "bsr done from volfile rec=%d volefile=%d\n",
+ rec->File, volfile->efile);
}
return 0;
}
int match_bsr_block(BSR *bsr, DEV_BLOCK *block);
void position_bsr_block(BSR *bsr, DEV_BLOCK *block);
BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev);
+bool match_set_eof(BSR *bsr, DEV_RECORD *rec);
/* From mount.c */
int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
#include "bacula.h"
#include "stored.h"
+/* Forward referenced functions */
static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
static BSR *position_to_first_file(JCR *jcr, DEVICE *dev);
+static int try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev);
#ifdef DEBUG
static char *rec_state_to_str(DEV_RECORD *rec);
#endif
Jmsg(jcr, M_INFO, 0, "Got EOF at file %u on device %s, Volume \"%s\"\n",
dev->file, dev_name(dev), jcr->VolumeName);
}
+ Dmsg3(100, "Got EOF at file %u on device %s, Volume \"%s\"\n",
+ dev->file, dev_name(dev), jcr->VolumeName);
continue;
} else if (dev_state(dev, ST_SHORT)) {
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
}
}
Dmsg2(100, "New block at position=(file:block) %d:%d\n", dev->file, dev->block_num);
- Dmsg5(100, "Read block: devblk=%d blk=%d VI=%u VT=%u blen=%d\n", dev->block_num, block->BlockNumber,
- block->VolSessionId, block->VolSessionTime, block->block_len);
#ifdef FAST_BLOCK_REJECTION
/* this does not stop when file/block are too big */
if (!match_bsr_block(jcr->bsr, block)) {
- continue;
+ if (try_repositioning(jcr, rec, dev)) {
+ break; /* get next volume */
+ }
+ continue; /* skip this record */
}
#endif
Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
block->BlockNumber, rec->remainder);
record = 0;
+ rec->state = 0;
+ Dmsg1(100, "Block empty %d\n", is_block_empty(rec));
for (rec->state=0; !is_block_empty(rec); ) {
if (!read_record_from_block(block, rec)) {
- Dmsg3(10, "!read-break. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
+ Dmsg3(00, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
block->BlockNumber, rec->remainder);
break;
}
- Dmsg5(100, "read-OK. stat=%s blk=%d rem=%d file:block=%d:%d\n",
+ Dmsg5(100, "read-OK. state=%s blk=%d rem=%d file:block=%d:%d\n",
rec_state_to_str(rec), block->BlockNumber, rec->remainder,
dev->file, dev->block_num);
/*
Dmsg6(100, "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);
- Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
- FI_to_ascii(rec->FileIndex),
- stream_to_ascii(rec->Stream, rec->FileIndex),
- rec->data_len);
if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
Dmsg0(40, "Get EOM LABEL\n");
Dmsg2(100, "All done=(file:block) %d:%d\n", dev->file, dev->block_num);
break;
} else if (stat == 0) { /* no match */
- BSR *bsr;
- bsr = find_next_bsr(jcr->bsr, dev);
- if (bsr == NULL && jcr->bsr->mount_next_volume) {
- Dmsg0(100, "Would mount next volume here\n");
- Dmsg2(100, "Current postion (file:block) %d:%d\n",
- dev->file, dev->block_num);
- jcr->bsr->mount_next_volume = false;
- dev->state |= ST_EOT;
- rec->Block = 0;
+ Dmsg4(100, "Clear rem=%d FI=%d before set_eof pos %d:%d\n",
+ rec->remainder, rec->FileIndex, dev->file, dev->block_num);
+ rec->remainder = 0;
+ rec->state &= ~REC_PARTIAL_RECORD;
+ if (try_repositioning(jcr, rec, dev)) {
break;
- }
- if (bsr) {
- Dmsg4(100, "Reposition from (file:block) %d:%d to %d:%d\n",
- dev->file, dev->block_num, bsr->volfile->sfile,
- bsr->volblock->sblock);
- if (verbose) {
- Jmsg(jcr, M_INFO, 0, "Reposition from (file:block) %d:%d to %d:%d\n",
- dev->file, dev->block_num, bsr->volfile->sfile,
- bsr->volblock->sblock);
- }
- reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
- rec->Block = 0;
- Dmsg2(100, "Now at (file:block) %d:%d\n",
- dev->file, dev->block_num);
}
- Dmsg5(100, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
- record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime,
- rec->FileIndex);
continue; /* we don't want record, read next one */
}
}
if (is_partial_record(rec)) {
- Dmsg6(10, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
+ Dmsg6(100, "Partial, break. 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);
break; /* read second part of record */
}
ok = record_cb(jcr, dev, block, rec);
+ if (rec->Stream == STREAM_MD5_SIGNATURE || rec->Stream == STREAM_SHA1_SIGNATURE) {
+ Dmsg3(100, "Done FI=%d before set_eof pos %d:%d\n", rec->FileIndex,
+ dev->file, dev->block_num);
+ if (match_set_eof(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
+ Dmsg2(100, "Break after match_set_eof pos %d:%d\n",
+ dev->file, dev->block_num);
+ break;
+ }
+ Dmsg2(100, "After set_eof pos %d:%d\n", dev->file, dev->block_num);
+ }
} /* end for loop over records */
+ Dmsg2(100, "After end records position=(file:block) %d:%d\n", dev->file, dev->block_num);
} /* end for loop over blocks */
- Dmsg2(100, "Position=(file:block) %d:%d\n", dev->file, dev->block_num);
+// Dmsg2(100, "Position=(file:block) %d:%d\n", dev->file, dev->block_num);
/* Walk down list and free all remaining allocated recs */
for (rec=(DEV_RECORD *)recs->first(); rec; ) {
return ok;
}
+/*
+ * See if we can reposition.
+ * Returns: 1 if at end of volume
+ * 0 otherwise
+ */
+static int try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
+{
+ BSR *bsr;
+ bsr = find_next_bsr(jcr->bsr, dev);
+ if (bsr == NULL && jcr->bsr->mount_next_volume) {
+ Dmsg0(100, "Would mount next volume here\n");
+ Dmsg2(100, "Current postion (file:block) %d:%d\n",
+ dev->file, dev->block_num);
+ jcr->bsr->mount_next_volume = false;
+ dev->state |= ST_EOT;
+ rec->Block = 0;
+ return 1;
+ }
+ if (bsr) {
+ if (verbose > 1) {
+ Jmsg(jcr, M_INFO, 0, "Reposition from (file:block) %d:%d to %d:%d\n",
+ dev->file, dev->block_num, bsr->volfile->sfile,
+ bsr->volblock->sblock);
+ }
+ Dmsg4(100, "Try_Reposition from (file:block) %d:%d to %d:%d\n",
+ dev->file, dev->block_num, bsr->volfile->sfile,
+ bsr->volblock->sblock);
+ reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
+ rec->Block = 0;
+ }
+ return 0;
+}
+
+/*
+ * Position to the first file on this volume
+ */
static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
{
BSR *bsr = NULL;
if (jcr->bsr) {
jcr->bsr->reposition = true; /* force repositioning */
bsr = find_next_bsr(jcr->bsr, dev);
- if (bsr) {
+ if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
bsr->volfile->sfile, bsr->volblock->sblock);
- Dmsg4(100, "Reposition new from (file:block) %d:%d to %d:%d\n",
- dev->file, dev->block_num, bsr->volfile->sfile,
- bsr->volblock->sblock);
+ Dmsg2(100, "Forward spacing to file:block %u:%u.\n",
+ bsr->volfile->sfile, bsr->volblock->sblock);
reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
- Dmsg2(100, "Now at (file:block) %d:%d\n",
- dev->file, dev->block_num);
}
}
return bsr;
*
* This is the memory structure for the record header.
*/
+struct BSR; /* satisfy forward reference */
struct DEV_RECORD {
dlink link; /* link for chaining in read_record.c */
/* File and Block are always returned during reading
uint32_t data_len; /* current record length */
uint32_t remainder; /* remaining bytes to read/write */
uint32_t state; /* state bits */
+ BSR *bsr; /* pointer to bsr that matched */
uint8_t ser_buf[WRITE_RECHDR_LENGTH]; /* serialized record header goes here */
POOLMEM *data; /* Record data. This MUST be a memory pool item */
};